在 Cloud Run 上构建和部署远程 MCP 服务器

本教程介绍了如何使用可流式传输的 HTTP 传输在 Cloud Run 上构建和部署远程 Model Context Protocol (MCP) 服务器。借助可流式传输的 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. 运行以下命令,将 FastMCP 添加为 pyproject.toml 文件中的依赖项:

    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. 在 Dockerfile 中添加以下代码,以使用 uv 工具运行 server.py 文件:

    # 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:

    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 Invoker (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 服务器

您可以使用 FastMCP 客户端并访问网址 http://127.0.0.1:8080/mcp 来测试并连接到远程 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