使用 OpenTelemetry 对自托管 MCP 服务器进行插桩

本文档概述了对自托管 Model Context Protocol (MCP) 服务器进行插桩和部署的步骤,以便收集遥测数据。本文档中的 示例使用 FastMCP 构建 MCP 服务器,并使用 Cloud Run 部署 MCP 服务器。FastMCP 包含 OpenTelemetry 插桩,可从所有 MCP 操作中收集 遥测数据。

本文档介绍了以下步骤:

  1. 使用 uv 软件包管理器准备 Python 项目
  2. 创建用于数学运算的 MCP 服务器
  3. 部署到 Cloud Run
  4. 对 MCP 客户端进行身份验证
  5. 测试自托管 MCP 服务器
  6. 查看遥测数据

准备工作

  1. 登录您的 Google Cloud 账号。如果您是 Google Cloud的新用户, 请创建一个账号,以便在 真实场景中评估我们产品的性能。新客户还可获享 $300 赠金,用于 运行、测试和部署工作负载。
  2. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator role (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.

    Go to project selector

  3. Verify that billing is enabled for your Google Cloud project.

  4. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator role (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.

    Go to project selector

  5. Verify that billing is enabled for your Google Cloud project.

  6. 启用 Artifact Registry API、Cloud Run API、Cloud Build API、Telemetry API、Cloud Logging API、Cloud Monitoring API 和 Cloud Trace API。

    启用 API 所需的角色

    如需启用 API,您需要拥有 Service Usage Admin IAM 角色 (roles/serviceusage.serviceUsageAdmin),该角色包含 serviceusage.services.enable 权限。了解如何授予 角色

    启用 API

  7. 您的 Google Cloud 项目中设置 Cloud Run 开发环境。
  8. 确保您拥有部署服务的相应权限,并且您的账号已被授予 Cloud Run Admin (roles/run.admin) 和 Service Account User (roles/iam.serviceAccountUser) 角色。
  9. 向您的账号授予 Cloud Run Invoker (roles/run.invoker) 角色。此角色允许自托管 MCP 服务器访问 Cloud Run 服务。
  10. 了解如何授予角色

    控制台

    1. 在 Google Cloud 控制台中,前往 IAM 页面。

      前往 IAM
    2. 选择相应项目。
    3. 点击 授予访问权限
    4. 新的主账号字段中,输入您的用户标识符。这通常是用于部署 Cloud Run 服务的邮箱地址。

    5. 选择角色列表中,选择一个角色。
    6. 如需授予其他角色,请点击 添加其他角色,然后依次添加所需角色。
    7. 点击保存

    gcloud

    如需向您在项目中的账号授予所需的 IAM 角色,请执行以下操作:

       gcloud projects add-iam-policy-binding PROJECT_ID \
           --member=PRINCIPAL \
           --role=ROLE
       

    • PROJECT_ID:项目的标识符。
    • PRINCIPAL:您要向其授予角色的主账号的标识符。主账号标识符通常采用以下格式:PRINCIPAL-TYPE:ID。例如 user:my-user@example.com。 如需查看 PRINCIPAL 可以采用的格式的完整列表,请参阅主账号标识符
    • ROLE:IAM 角色。
  11. 如果您通过网域限制组织 政策来限制 项目的未经身份验证的调用,则必须按照 测试专用服务中的说明访问已 部署的服务。

  12. 安装 Uv(Python 软件包和项目管理器)。

准备 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 服务器源代码)
    • otel_setup.py,用于配置 OpenTelemetry。
    • test_server.py,用于测试自托管服务器。
    • 用于部署到 Cloud Run 的 Dockerfile。
    touch server.py otel_setup.py test_server.py Dockerfile
    

    您的项目目录应包含以下结构:

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

创建用于数学运算的 MCP 服务器

在本部分中,您将使用 FastMCP设置数学 MCP 服务器。 FastMCP 提供了一种使用 Python 快速构建 MCP 服务器和客户端的方法。

请按照以下步骤创建用于执行加法和减法等数学运算的 MCP 服务器。

  1. 运行以下命令,将 FastMCP 添加为 pyproject.toml 文件中的依赖项:

    uv add fastmcp==2.13.1 --no-sync
    
  2. otel_setup.py 文件中添加以下 OpenTelemetry 设置代码:

    import logging
    import google.auth
    import google.auth.transport.requests
    import grpc
    from google.auth.transport.grpc import AuthMetadataPlugin
    from opentelemetry import trace
    from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import (
        OTLPSpanExporter,
    )
    from opentelemetry.sdk.resources import SERVICE_NAME, Resource
    from opentelemetry.sdk.trace import TracerProvider
    from opentelemetry.sdk.trace.export import BatchSpanProcessor
    
    logger = logging.getLogger(__name__)
    
    
    def setup_opentelemetry(service_name: str) -> None:
        """Sets up OpenTelemetry to send traces to Google Cloud Observability."""
        credentials, project_id = google.auth.default()
        if not project_id:
            raise Exception("Could not determine Google Cloud project ID.")
    
        resource = Resource.create(
            attributes={
                SERVICE_NAME: service_name,
                "gcp.project_id": project_id,
            }
        )
    
        # Set up OTLP auth
        request = google.auth.transport.requests.Request()
        auth_metadata_plugin = AuthMetadataPlugin(credentials=credentials, request=request)
        channel_creds = grpc.composite_channel_credentials(
            grpc.ssl_channel_credentials(),
            grpc.metadata_call_credentials(auth_metadata_plugin),
        )
    
        # Set up OpenTelemetry Python SDK
        tracer_provider = TracerProvider(resource=resource)
        tracer_provider.add_span_processor(
            BatchSpanProcessor(
                OTLPSpanExporter(
                    credentials=channel_creds,
                    endpoint="https://telemetry.googleapis.com:443/v1/traces",
                )
            )
        )
        trace.set_tracer_provider(tracer_provider)
        logger.info("OpenTelemetry successfully initialized.")
    
    
  3. server.py 文件中添加以下数学 MCP 服务器源代码:

    from otel_setup import setup_opentelemetry
    setup_opentelemetry("mcp-server")
    
    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),
            )
        )
    
  4. 在 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 self-hosted-mcp-servers \
    --repository-format=docker \
    --location=us-central1 \
    --description="Repository for self-hosted MCP servers" \
    --project=PROJECT_ID
    
  2. 使用 Cloud Build 构建容器映像并将其推送到 Artifact Registry:

    gcloud builds submit --region=us-central1 --tag us-central1-docker.pkg.dev/PROJECT_ID/self-hosted-mcp-servers/mcp-server:latest
    
  3. 将 MCP 服务器容器映像部署到 Cloud Run:

    gcloud run deploy mcp-server \
    --image us-central1-docker.pkg.dev/PROJECT_ID/self-hosted-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 的测试文件,并添加以下代码:

    from otel_setup import setup_opentelemetry
    setup_opentelemetry("test-server")
    
    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.content[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.content[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
    

查看遥测数据

本部分介绍如何查看自托管 MCP 服务器生成的日志、指标和跟踪记录数据。

准备工作

如需获得查看日志、指标和跟踪记录数据所需的权限,请让管理员为您授予项目的以下 IAM 角色:

如需详细了解如何授予角色,请参阅管理对项目、文件夹和组织的访问权限

您也可以通过自定义角色或其他预定义角色来获取所需的权限。

查看遥测数据

如需了解如何查看日志、指标和跟踪记录数据,请参阅以下内容:

日志数据

在 Google Cloud 控制台中,前往 Logs Explorer 页面:

前往 Logs Explorer

如果您使用搜索栏查找此页面,请选择子标题为 Logging 的结果。

如需详细了解如何使用 Logs Explorer 页面,请参阅 查看和分析日志

指标数据

在 Google Cloud 控制台中,前往  Metrics Explorer 页面:

进入 Metrics Explorer

如果您使用搜索栏查找此页面,请选择子标题为监控 的结果。

如需详细了解如何使用 Metrics Explorer 页面,请参阅 使用 Metrics Explorer 创建图表

跟踪记录数据

在 Google Cloud 控制台中,前往 Trace 探索器 页面:

转到 Trace 探索器

您也可以使用搜索栏查找此页面。

以下屏幕截图展示了 Trace 探索器页面中的详细信息 窗格,其中显示了从 tools/call 操作生成的跟踪记录 span:

详细信息窗格,显示了跟踪记录及其关联的 span。

如需详细了解如何使用 Trace 探索器 页面,请参阅 查找和探索跟踪记录

后续步骤