0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Cloud RunでMCP Serverを運用する:サイドカーパターン【multi-Agent LINE bot ④】

Posted at

概要

Model Context Protocol(MCP)は、生成AIエージェントと外部リソース間の通信を標準化するプロトコルです。
しかし、標準入出力(stdio)で通信するMCPサーバーをコンテナ環境(特にCloud Run)で運用するには課題があります。

本記事では、Cloud Runのマルチコンテナ機能を使ったサイドカーパターンを採用し、FastAPI×LINE Messaging APIアプリケーションにおいて複数のMCPサーバーを効率よく動作させた実践例を解説します。

親記事:マルチエージェントと対話できるLINE botを開発しよう
サンプルコード:https://github.com/MokonaSato/line-multi-agent-sample

参考文献

Agent Development Kit (ADK) と MCP サーバー を Cloud Run で動かすための実践ガイド

環境・ライブラリなど

開発環境

  • Python: 3.12
  • フレームワーク: FastAPI 0.115.12
  • クラウドリソース: Google Cloud Run + Cloud Build

使用ライブラリ

  • LINE連携: line-bot-sdk 3.17.1
  • AI連携: google-adk 0.5.0, litellm 1.69.2
  • MCP(Model Context Protocol): mcp 1.8.1

インフラ・デプロイ構成

  • メインアプリ: Cloud Run
  • サイドカー構成: Filesystem MCP Server, Notion MCP Server
  • CI/CD: Cloud Build
  • シークレット管理: Secret Manager

Supergatewayとは

Supergateway は、MCP サーバー(通常は stdio ベース)とクライアントとの通信手段を変換する ライブラリ/CLI ツールです。
stdio を SSE(Server-Sent Events)や WebSocket(WS)に変換したり、逆に SSE を stdio に変換するためのトランスポート・ブリッジとして機能します。

Notion MCP ServerはSSE対応していなさそうだったため、今回はSupergatewayを利用します。

実装ポイント

1. Cloud Run マルチコンテナ構成例

現状、下記の設定で快適に動作しています。

  • メインコンテナ(line-multi-agent)は1CPU、1.5GB RAM。
  • MCPサイドカー(Filesystem, Notion)はそれぞれ0.25CPU、256MB RAM。
/cloud-run-service.yaml
spec:
  template:
    spec:
      containers:
      - name: line-multi-agent
        image: gcr.io/PROJECT_ID/line-multi-agent:BUILD_ID
        ports:
          - containerPort: 8080
            name: http1
        env:
          - name: NOTION_MCP_URL
            value: "http://localhost:3001/sse"
          - name: FILESYSTEM_MCP_URL
            value: "http://localhost:8000/sse"
        resources:
          limits:
            cpu: "1"
            memory: "1.5Gi"

2. supergatewayによるプロトコル変換

stdioベースのMCPサーバーをHTTP(SSE)対応させるためにsupergatewayを使用します。

Filesystem MCP サーバーの設定

spec:
    template:
        spec:
            containers:
            # 中略
            - name: filesystem-mcp-server
              image: node:20-alpine
              env:
              - name: PORT
                value: "8000"
              command: ["/bin/sh"]
              args:
              - -c
              - |
                set -e
                npm install -g @modelcontextprotocol/server-filesystem supergateway
                exec npx supergateway --stdio "npx @modelcontextprotocol/server-filesystem /tmp" --port 8000 --healthEndpoint /health

Notion MCP サーバーの設定

spec:
    template:
        spec:
            containers:
            # 中略
            - name: notion-mcp-server
              image: node:20-alpine
              env:
              - name: PORT
                value: "3001"
              - name: NOTION_TOKEN
                valueFrom:
                  secretKeyRef:
                    name: notion-api-key
                    key: latest
              args:
              - -c
              - |
                set -e
                npm install -g @notionhq/notion-mcp-server supergateway
                CLEAN_TOKEN=$(echo "$NOTION_TOKEN" | tr -d '\n\r' | xargs)
                export OPENAPI_MCP_HEADERS="{\"Authorization\":\"Bearer ${CLEAN_TOKEN}\",\"Notion-Version\":\"2022-06-28\"}"
                exec npx supergateway --stdio "npx @notionhq/notion-mcp-server" --port 3001 --healthEndpoint /health

3. MCP統合モジュールの実装 (Python)

/src/tools/mcp_integration.py
async def get_tools_async() -> Tuple[
    Optional[MCPToolset],
    Optional[MCPToolset],
    AsyncExitStack,
]:
    """Cloud Run上のMCPサイドカーからツールを取得する

    Google ADKのサンプルコードに基づいて、FilesystemとNotionの
    MCPサーバーからツールを取得します。タイムアウト機能付き。

    Returns:
        Tuple[Optional[MCPToolset], Optional[MCPToolset], AsyncExitStack]:
            (Filesystemツール, Notionツール, exitスタック)
    """
    exit_stack = AsyncExitStack()
    filesystem_tools = None
    notion_tools = None

    # MCP機能が無効化されている場合はスキップ
    if not MCP_ENABLED:
        return filesystem_tools, notion_tools, exit_stack

    # Filesystem MCP (supergateway@localhost:8000) へSSEで接続
    try:
        filesystem_tools, fs_exit_stack = await asyncio.wait_for(
            MCPToolset.from_server(
                connection_params=SseServerParams(url=FILESYSTEM_MCP_URL)
            ),
            timeout=MCP_TIMEOUT_SECONDS,
        )
        await exit_stack.enter_async_context(fs_exit_stack)
    except asyncio.TimeoutError:
        # 省略

    # Notion MCP (localhost:3001) へSSEで接続
    try:
        notion_tools, notion_exit_stack = await asyncio.wait_for(
            MCPToolset.from_server(
                connection_params=SseServerParams(url=NOTION_MCP_URL)
            ),
            timeout=MCP_TIMEOUT_SECONDS,
        )
        await exit_stack.enter_async_context(notion_exit_stack)
    except asyncio.TimeoutError:
        # 省略

    return filesystem_tools, notion_tools, exit_stack

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?