ENECHANGE Developer Blog

ENECHANGE開発者ブログ

Slack botからMCPサーバーを呼びたくて選んだ7つのツール

VPoTの岩本 (iwamot) です。

このたび、ENECHANGE社内で使っているSlack botからMCPサーバーを呼び出せるようにしました。おもな背景は次の2つです。

  • MCPの普及により、便利なMCPサーバーが続々と公開されている
  • 社内Slack botに「外部サイトが参照できたら便利」との意見があった

今回の記事では、機能を実現するために選んだ7つのツールをご紹介します。

MCPホストツール

Collmbo

github.com

筆者が開発・公開しているSlack botアプリです。LiteLLMを組み込んでおり、100以上のLLMとチャットできます。

MCPサーバーを呼び出す機能がなかったため、次に説明するStrands Agentsを新たに組み込んで実装しました。

トランスポートについては、以下の理由でStreamable HTTPのみをサポートしています。

  • stdioをサポートすると、Collmboのメンテナンスが大変になる。MCPサーバーとの結合度を低くしておきたい
  • すでに非推奨となっているSSEはサポートしたくない

呼び出したいMCPサーバーのURLは、環境変数の MCP_SERVER_URL で複数指定できるようにしました。| で区切って並べられる仕様です。

MCP_SERVER_URL=https://example.com/mcp|https://example.org/servers/A/mcp

実装は以下のようにしています。

  1. Collmboの起動時、各MCPサーバーのツールリストを取得する
  2. 各MCPツールを、OpenAIのfunction calling形式に変換してメモリに保持する
  3. LLMへの問い合わせ (chat completion) 時の tools パラメータにツールリストを含める
  4. LLMからMCPツール呼び出しが推奨されたら、MCPサーバーを呼び出す

下記は「各MCPサーバーのツールリストを取得し、function calling形式に変換する処理」を現状のソースコードから抜粋したものです。

# 内部的なツール名を「{MCPツール名}-{MCPサーバーのインデックス}」とする
def build_mcp_tool_name(spec_name: str, server_index: int) -> str:
    return f"{spec_name}{MCP_TOOL_NAME_SEPARATOR}{server_index}"

# MCPツールをfunction calling形式に変換する
def transform_mcp_spec_to_classic_tool(
    *,
    mcp_spec: ToolSpec,
    server_index: int,
    model: str,
) -> dict:
    parameters = deepcopy(mcp_spec["inputSchema"]["json"])

    # Remove invalid "format" property for Gemini models
    if model.startswith("gemini/"):
        for prop in parameters.get("properties", {}).values():
            if "format" in prop and prop["format"] not in ("date-time", "enum"):
                prop.pop("format")

    return {
        "type": "function",
        "function": {
            "name": build_mcp_tool_name(mcp_spec["name"], server_index),
            "description": mcp_spec["description"],
            "parameters": parameters,
        },
    }

# 各MCPサーバーのツールリストを取得する
def load_mcp_tools() -> list[dict]:
    result: list[dict] = []
    mcp_servers = split_mcp_server_url(MCP_SERVER_URL)
    for idx, url in enumerate(mcp_servers):
        try:
            tools = fetch_tools_from_mcp_server(url)
            result.extend(
                transform_mcp_spec_to_classic_tool(
                    mcp_spec=tool.tool_spec,
                    server_index=idx,
                    model=LITELLM_MODEL_TYPE,  # 会話するモデルを環境変数で指定
                )
                for tool in tools
            )
        except Exception as exc:
            logging.warning("Failed to load MCP tools from %s: %s", url, exc)
    return result

コードにある通り、内部的なツール名を {MCPツール名}-{MCPサーバーのインデックス} という形式にしています。たとえばLLMから fetch-0 の呼び出しを推奨された場合、0番目のMCPサーバーの fetch ツールを呼び出す寸法です。これで複数のMCPサーバーに対応できます。

MCPクライアントツール

Strands Agents

strandsagents.com

AWSが2025年5月に公開した、AIエージェントを作れるSDKです。AWS自身、Amazon Q Developerなどのサービスで使っているそうです。

しかし今回は、MCP Python SDKのラッパーとしてのみ使うことにしました。

MCPの公式SDKであるMCP Python SDKは、asyncなインターフェイスしか提供していません。そのため、syncなCollmboで使うのは難しい状況です。

# MCP Python SDKの利用例
async with streamablehttp_client("example/mcp") as (
    read_stream,
    write_stream,
    _,
):
    async with ClientSession(read_stream, write_stream) as session:
        await session.initialize()

        # MCPツールリストの取得 (async)
        tools = await session.list_tools()

        # MCPツールの呼び出し (async)
        result = await session.call_tool("tool-name", arguments={"arg1": "value"})

一方Strands Agentsでは、MCP Python SDKをラップしたsyncなインターフェイスが提供されています。

# Strands Agentsの利用例
with mcp_client:

    # MCPツールリストの取得 (sync)
    tools = mcp_client.list_tools_sync()

    # MCPツールの呼び出し (sync)
    result = mcp_client.call_tool_sync(
        tool_use_id="tool-123",
        name="tool-name",
        arguments={"arg1": "value"}
    )

Strands Agentsを使うことで、syncなCollmboでもMCPクライアント機能が簡単に実装できました。MCPを扱う詳しい方法については、Strands Agentsのドキュメントをご参照ください。

MCPサーバーツール

mcp-proxy

github.com

MCPのトランスポートを変換できるプロキシツールです。これを使うと、たとえばstdioなMCPサーバーをStreamable HTTP経由で呼び出せます。

Streamable HTTPに対応したMCPサーバーは、まだ多くありません。次に説明するFetch MCP ServerやAWS Documentation MCP Serverも、現時点ではstdioのみサポートしています。

そこでmcp-proxyを使って、stdioなMCPサーバーをStreamable HTTP経由で呼び出せるようにしました。

mcp-proxy --stateless --port 8000 \
          --named-server-config /path/to/named-server-config.json

named-server-config.jsonの内容は、現状では下記の通りです。

{
  "mcpServers": {
    "fetch": {
      "disabled": false,
      "timeout": 60,
      "command": "uvx",
      "args": [
        "mcp-server-fetch"
      ],
      "transportType": "stdio"
    },
    "aws-documentation": {
      "disabled": false,
      "timeout": 60,
      "command": "uvx",
      "args": [
        "awslabs.aws-documentation-mcp-server@latest"
      ],
      "env": {
        "FASTMCP_LOG_LEVEL": "ERROR",
        "AWS_DOCUMENTATION_PARTITION": "aws"
      },
      "transportType": "stdio"
    }
  }
}

Fetch MCP Server

github.com

外部サイトが参照できるMCPサーバーです。MCP公式によるリファレンス実装ですが、公式であれば信頼性が高いと判断して選びました。

導入の目的は、もちろん「外部サイトが参照できたら便利」との同僚からの意見に応えることです。他に適切そうなMCPサーバーが登場したら、乗り換えるかもしれません。

AWS Documentation MCP Server

awslabs.github.io

AWSのドキュメントが参照できるMCPサーバーです。AWS公式。

複数のMCPサーバーが呼び出せることを示すため、まずは安全で便利そうなものを選びました。

運用ツール

Amazon ECS

aws.amazon.com

AWSのコンテナ実行サービスです。社内Slack botの運用環境として、もともと活用しています。今回はMCPサーバーのコンテナを追加しました。

細かい話ですが、mcp-proxyはヘルスチェック用のURL /status を用意しています。そのためECSのタスク定義で下記のチェックを指定しました。

healthCheck = {
  command = [
    "CMD-SHELL",
    "wget -q --spider http://localhost/status >>/proc/1/fd/1 2>&1 || exit 1",
  ]
}

Amazon GuardDuty

aws.amazon.com

AWSの脅威検出サービスです。AWS環境での不審なアクティビティを検出してくれます。

今回はECSのランタイムモニタリングを有効化しました。外部サイトへのアクセスを許可したので、監視したほうが安全だと判断したものです。

具体的には下記の手順で進めました。

  1. GuardDutyのランタイムモニタリング設定を有効化
  2. ECSクラスターに GuardDutyManaged = true タグを付与
  3. ECSサービスを「新しいデプロイの強制」で更新 (aws ecs update-service --cluster my-cluster --service my-service --force-new-deployment)

3で再デプロイしているのは、GuardDutyの仕様によります。そうしないと、ECSのランタイムモニタリングが有効になりません。

構成図

ツールの関係性を分かりやすく示すため、全体の構成図をご紹介します。図中で太字になっている要素が、今回選んだ7つのツールです。

おわりに

7つのツールのおかげで、Slack botからMCPサーバーを呼び出す機能が簡単に実現できました。特にStrands Agentsとmcp-proxyは便利です。

MCP関連ツールが充実し、手軽に試せる時代になってきました。ぜひ一度お試しください。