リモート MCP サーバーを構築して Cloud Run にデプロイする

このチュートリアルでは、ストリーミング可能な HTTP トランスポートを使用するリモート Model Context Protocol(MCP)サーバーを構築し、Cloud Run にデプロイする方法について説明します。ストリーミング可能な HTTP トランスポートを使用する MCP サーバーは、複数のクライアント接続を処理できる独立したプロセスとして動作します。

Python プロジェクトを準備する

以下の手順では、uv パッケージ マネージャーを使用して Python プロジェクトを設定する方法について説明します。

  1. デプロイ用のソースコードを保存する、mcp-on-cloudrun という名前のフォルダを作成します。

      mkdir mcp-on-cloudrun
      cd mcp-on-cloudrun
    
  2. uv ツールを使用して Python プロジェクトを作成し、pyproject.toml ファイルを生成します。

      uv init --name "mcp-on-cloudrun" --description "Example of deploying an MCP server on Cloud Run" --bare --python 3.10
    

    uv init コマンドにより、次の pyproject.toml ファイルが作成されます。

    [project]
    name = "mcp-server"
    version = "0.1.0"
    description = "Example of deploying an MCP server on Cloud Run"
    readme = "README.md"
    requires-python = ">=3.10"
    dependencies = []
    
  3. 次の新しいファイルを追加で作成します。

    • server.py: MCP サーバーのソースコード用
    • test_server.py: リモート サーバーのテスト用
    • Cloud Run にデプロイするための Dockerfile
    touch server.py test_server.py Dockerfile
    

    プロジェクト ディレクトリは次の構造になっている必要があります。

    ├── mcp-on-cloudrun
    │   ├── pyproject.toml
    │   ├── server.py
    │   ├── test_server.py
    │   └── Dockerfile
    

算術演算用の MCP サーバーを作成する

LLM の使用を改善するための重要なコンテキストを MCP によって提供するには、FastMCP を使用して数学 MCP サーバーを設定します。FastMCP は、Python で MCP のサーバーとクライアントを迅速に構築するためのツールです。

加算や減算などの数学演算用の MCP サーバーを作成する手順は次のとおりです。

  1. 次のコマンドを実行して、pyproject.toml ファイルに FastMCP を依存関係として追加します。

    uv add fastmcp==2.8.0 --no-sync
    
  2. server.py ファイルに次の数学 MCP サーバーのソースコードを追加します。

    import asyncio
    import logging
    import os
    
    from fastmcp import FastMCP 
    
    logger = logging.getLogger(__name__)
    logging.basicConfig(format="[%(levelname)s]: %(message)s", level=logging.INFO)
    
    mcp = FastMCP("MCP Server on Cloud Run")
    
    @mcp.tool()
    def add(a: int, b: int) -> int:
        """Use this to add two numbers together.
    
        Args:
            a: The first number.
            b: The second number.
    
        Returns:
            The sum of the two numbers.
        """
        logger.info(f">>> 🛠️ Tool: 'add' called with numbers '{a}' and '{b}'")
        return a + b
    
    @mcp.tool()
    def subtract(a: int, b: int) -> int:
        """Use this to subtract two numbers.
    
        Args:
            a: The first number.
            b: The second number.
    
        Returns:
            The difference of the two numbers.
        """
        logger.info(f">>> 🛠️ Tool: 'subtract' called with numbers '{a}' and '{b}'")
        return a - b
    
    if __name__ == "__main__":
        logger.info(f"🚀 MCP server started on port {os.getenv('PORT', 8080)}")
        # Could also use 'sse' transport, host="0.0.0.0" required for Cloud Run.
        asyncio.run(
            mcp.run_async(
                transport="streamable-http",
                host="0.0.0.0",
                port=os.getenv("PORT", 8080),
            )
        )
    
  3. uv ツールを使用して server.py ファイルを実行するため、Dockerfile に次のコードを追加します。

    # Use the official Python image
    FROM python:3.13-slim
    
    # Install uv
    COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
    
    # Install the project into /app
    COPY . /app
    WORKDIR /app
    
    # Allow statements and log messages to immediately appear in the logs
    ENV PYTHONUNBUFFERED=1
    
    # Install dependencies
    RUN uv sync
    
    EXPOSE $PORT
    
    # Run the FastMCP server
    CMD ["uv", "run", "server.py"]
    

Cloud Run にデプロイする

MCP サーバーは、コンテナ イメージまたはソースコードとしてデプロイできます。

コンテナ イメージ

コンテナ イメージとしてパッケージ化された MCP サーバーをデプロイするには、次の手順を行います。

  1. コンテナ イメージを保存する Artifact Registry リポジトリを作成します。

    gcloud artifacts repositories create remote-mcp-servers \
    --repository-format=docker \
    --location=us-central1 \
    --description="Repository for remote MCP servers" \
    --project=PROJECT_ID
    
  2. Cloud Build を使用してコンテナ イメージをビルドし、Artifact Registry に push します。

    gcloud builds submit --region=us-central1 --tag us-central1-docker.pkg.dev/PROJECT_ID/remote-mcp-servers/mcp-server:latest
    
  3. MCP サーバーのコンテナ イメージを Cloud Run にデプロイします。

    gcloud run deploy mcp-server \
    --image us-central1-docker.pkg.dev/PROJECT_ID/remote-mcp-servers/mcp-server:latest \
    --region=us-central1 \
    --no-allow-unauthenticated
    

ソース

リモート MCP サーバーは、ソースから Cloud Run にデプロイできます。

ソースからデプロイするには、次のコマンドを実行します。

gcloud run deploy mcp-server --no-allow-unauthenticated --region=us-central1 --source .

MCP クライアントを認証する

--no-allow-unauthenticated フラグを使用してサービスをデプロイした場合、リモート MCP サーバーに接続する MCP クライアントには認証が必要です。

  1. サービス アカウントに Cloud Run 起動元ロール(roles/run.invoker)を付与します。この Identity and Access Management のポリシー バインディングにより、ローカル MCP クライアントの認証に強力なセキュリティ メカニズムが使用されます。

  2. ローカルマシンで Cloud Run プロキシを実行して、リモート MCP サーバーへの認証済みトンネルを作成します。

    gcloud run services proxy mcp-server --region=us-central1
    

    Cloud Run プロキシがまだインストールされていない場合、このコマンドを実行すると、プロキシのダウンロードを求めるプロンプトが表示されます。プロンプトに従ってプロキシのダウンロードとインストールを行ってください。

Cloud Run は http://127.0.0.1:8080 へのすべてのトラフィックを認証し、リクエストをリモート MCP サーバーに転送します。

リモート MCP サーバーをテストする

リモート MCP サーバーに接続してテストするため、FastMCP クライアントを使用して URL http://127.0.0.1:8080/mcp にアクセスします。

加算と減算のメカニズムをテストして呼び出す手順は次のとおりです。

  1. テストサーバーを実行する前に、Cloud Run プロキシを実行します。

  2. test_server.py というテストファイルを作成し、次のコードを追加します。

    import asyncio
    
    from fastmcp import Client
    
    async def test_server():
        # Test the MCP server using streamable-http transport.
        # Use "/sse" endpoint if using sse transport.
        async with Client("http://localhost:8080/mcp") as client:
            # List available tools
            tools = await client.list_tools()
            for tool in tools:
                print(f">>> 🛠️  Tool found: {tool.name}")
            # Call add tool
            print(">>> 🪛  Calling add tool for 1 + 2")
            result = await client.call_tool("add", {"a": 1, "b": 2})
            print(f"<<< ✅ Result: {result[0].text}")
            # Call subtract tool
            print(">>> 🪛  Calling subtract tool for 10 - 3")
            result = await client.call_tool("subtract", {"a": 10, "b": 3})
            print(f"<<< ✅ Result: {result[0].text}")
    
    if __name__ == "__main__":
        asyncio.run(test_server())
  3. 新しいターミナルで、テストサーバーを実行します。

    uv run test_server.py
    

    次の出力が表示されます。

     🛠️ Tool found: add
     🛠️ Tool found: subtract
     🪛 Calling add tool for 1 + 2
     ✅ Result: 3
     🪛 Calling subtract tool for 10 - 3
     ✅ Result: 7