返回市场
麦克佩实验室

麦克佩实验室

作者:ebinbabu4 星标更新:2025-11-01

项目介绍

MCP 服务器设置指南

本指南将引导您在 Google Cloud Run 上设置和部署一个 MCP(模型上下文协议)服务器。

预备条件

  • 已安装并配置了 Google Cloud SDK (gcloud)
  • 可访问 Google Cloud 项目
  • 安装了 uv Python 包管理器
  • 安装了 Gemini CLI(用于测试)

MCP 服务器设置

选择项目

首先,选择您的 Google Cloud 项目:

gcloud projects list

设置您的项目:

gcloud config set project [PROJECT_ID]

启用 API

启用所需的 Google Cloud API:

gcloud services enable \
  run.googleapis.com \
  artifactregistry.googleapis.com \
  cloudbuild.googleapis.com

创建项目目录

创建 MCP 服务器的目录:

mkdir mcp

cd mcp/

初始化 Python 环境

使用 uv 初始化一个新的 Python 项目:

uv init --description "在 Cloud Run 上部署 MCP 服务器的示例" --bare --python 3.13

这会执行以下操作:

  • 使用 uv,这是一个现代的 Python 包和项目管理器
  • 在当前目录中初始化一个新的 Python 项目
  • 创建一个 pyproject.toml 文件(而不是 requirements.txt
  • 设置 Python 3.13 作为所需版本
  • 设置项目元数据,如描述
  • --bare 标志创建一个干净的环境,不包含额外的文件(如 main.py.venvREADME

添加 FastMCP 依赖项

添加 FastMCP 作为依赖项:

uv add fastmcp==2.12.4 --no-sync

创建服务器

打开文件编辑器

cloudshell edit ~/mcp/server.py

创建 server.py,内容如下:

import asyncio
import logging
import os
from typing import List, Dict, Any
from fastmcp import FastMCP

logger = logging.getLogger(__name__)
logging.basicConfig(format="[%(levelname)s]: %(message)s", level=logging.INFO)

mcp = FastMCP("GDG MCP 服务器 🦁🐧🐻")

# GDG Kochi 的演讲者字典
GDG_KOCHI = [
    {
        "title": "注册开始",
        "time": "上午 09:30 - 09:35",
        "speaker": None,
        "role": None,
        "organization": None,
        "duration": None,
        "location": "主厅",
        "notes": None,
    },
    {
        "title": "欢迎致辞",
        "time": "上午 09:35 - 09:40",
        "speaker": "Iqbal P B",
        "role": "组织者",
        "organization": "GDG Cochin",
        "duration": "5 分钟",
        "location": "主厅",
        "notes": None,
    },
    {
        "title": "主题演讲 — 人工智能?不,与 Google AI Studio 创造",
        "time": "上午 09:40 - 10:00",
        "speaker": "Merin K Jacob",
        "role": "技术账户经理",
        "organization": "Google",
        "duration": "20 分钟",
        "location": "主厅",
        "notes": None,
    },
    {
        "title": "主题演讲 — 不要叫我开发者:设计加速职业生涯",
        "time": "上午 10:05 - 10:25",
        "speaker": "Eric Hole",
        "role": "副总裁",
        "organization": "Onix",
        "duration": "20 分钟",
        "location": "主厅",
        "notes": None,
    },
    {
        "title": "赞助商演讲",
        "time": "上午 10:30 - 10:40",
        "speaker": "Dr. Tom M Joseph",
        "role": None,
        "organization": "Jain",
        "duration": "10 分钟",
        "location": "主厅",
        "notes": None,
    },
    {
        "title": "休息时间",
        "time": "上午 10:50 - 11:05",
        "speaker": None,
        "role": None,
        "organization": None,
        "duration": "15 分钟",
        "location": "主厅",
        "notes": None,
    },
    {
        "title": "技术演讲 — 快速发布:使用 Gemini、Firebase Studio 和零基础设施的 AI 应用",
        "time": "上午 11:35 - 12:00",
        "speaker": "Vishnu K S",
        "role": "高级云工程师",
        "organization": "DP World",
        "duration": "25 分钟",
        "location": "主厅",
        "notes": None,
    },
    {
        "title": "技术演讲 — 使用 ADK、A2A 和 MCP 构建对话代理",
        "time": "下午 12:05 - 12:30",
        "speaker": "Nishi Ajmera",
        "role": "解决方案架构师",
        "organization": "Publicis Sapient (UK)",
        "duration": "25 分钟",
        "location": "主厅",
        "notes": None,
    },
    {
        "title": "技术演讲 — 在任何地方运行 SQL:Presto 遇到 BigQuery 和云存储",
        "time": "下午 12:40 - 01:25",
        "speaker": "Saurabh Mahawar",
        "role": "开发者关系工程师",
        "organization": "IBM",
        "duration": "45 分钟",
        "location": "主厅",
        "notes": None,
    },
    {
        "title": "午餐时间",
        "time": "下午 12:40 - 01:25",
        "speaker": None,
        "role": None,
        "organization": None,
        "duration": "45 分钟",
        "location": "主厅",
        "notes": None,
    },
    {
        "title": "技术演讲 — 具有安全性、评估和记忆的生产就绪 AI 代理",
        "time": "下午 01:25 - 01:50",
        "speaker": "Nikhilesh Tayal",
        "role": "Google 开发者专家",
        "organization": "—",
        "duration": "25 分钟",
        "location": "主厅",
        "notes": None,
    },
    {
        "title": "技术演讲 — 使用 Gemini 进行 Google Cloud 安全威胁狩猎",
        "time": "下午 01:55 - 02:20",
        "speaker": "Harisuthan S",
        "role": "高级安全工程师",
        "organization": "Renault Nissan Technology",
        "duration": "25 分钟",
        "location": "主厅",
        "notes": None,
    },
    {
        "title": "技术演讲 — 在 Kubernetes 中运行 Go 应用程序的经验教训",
        "time": "下午 02:25 - 02:50",
        "speaker": "Adarsh K Kumar",
        "role": "首席产品工程师",
        "organization": "Rapido",
        "duration": "25 分钟",
        "location": "主厅",
        "notes": None,
    },
    {
        "title": "技术演讲 — 使用 Apache Airflow 编排现代数据堆栈",
        "time": "下午 02:55 - 03:20",
        "speaker": "Jeevitha",
        "role": "高级软件工程师",
        "organization": "Juniper Networks (HP)",
        "duration": "25 分钟",
        "location": "主厅",
        "notes": None,
    },
    {
        "title": "技术演讲 — 使用 Gemma 构建可扩展和道德的 AI 解决方案",
        "time": "下午 03:25 - 03:50",
        "speaker": "Geeta Kakrani",
        "role": "Google 开发者专家",
        "organization": "AI",
        "duration": "25 分钟",
        "location": "主厅",
        "notes": None,
    },
    {
        "title": "技术演讲 — 保护您的 LLMs",
        "time": "下午 03:55 - 04:20",
        "speaker": "Dharmesh Vaya",
        "role": "高级解决方案工程师",
        "organization": "Wiz",
        "duration": "25 分钟",
        "location": "主厅",
        "notes": None,
    },
    {
        "title": "闭幕致辞",
        "time": "下午 04:25 - 04:30",
        "speaker": "Malavika",
        "role": "组织者",
        "organization": "GDG Cochin",
        "duration": "5 分钟",
        "location": "主厅",
        "notes": None,
    },
    # 工作坊 / 并行轨道(尽力而为)
    {
        "title": "工作坊 — 如何在 Cloud Run 上部署一个安全的 MCP 服务器?",
        "time": "下午 02:00",
        "speaker": "Ebin Babu",
        "role": "CNCF 大使",
        "organization": "Linux Foundation Projects",
        "duration": None,
        "location": "工作坊",
        "notes": "并行工作坊轨道",
    },
]

@mcp.tool()
def search_sessions(query: str) -> List[Dict[str, Any]]:
    """搜索 GDG Kochi 会议。

    对会议标题和演讲者名称进行大小写不敏感的子字符串搜索,并返回匹配的会议字典列表。

    参数:
        query: 用于匹配标题或演讲者的搜索字符串。

    返回值:
        会议字典列表(可能为空)。
    """
    logger.info(f">>> 🛠️ 工具:'search_sessions' 调用查询='{query}'")
    q = (query or "").strip().lower()
    if not q:
        return []
    results: List[Dict[str, Any]] = []
    for sess in GDG_KOCHI:
        title = (sess.get("title") or "").lower()
        speaker = (sess.get("speaker") or "").lower()
        if q in title or q in speaker:
            results.append(sess)
    return results

@mcp.tool()
def get_session_details(identifier: str) -> Dict[str, Any]:
    """通过精确的标题或演讲者名称返回单个会议的详细信息。

    尝试精确匹配(大小写不敏感)`identifier` 对应的会议标题,然后是演讲者名称。如果没有找到精确匹配,则返回第一个部分匹配。如果没有任何匹配,则返回空字典。

    参数:
        identifier: 要查找的会议标题或演讲者名称。

    返回值:
        会议字典或未找到时的空字典。
    """
    logger.info(f">>> 🛠️ 工具:'get_session_details' 调用'{identifier}'")
    if not identifier:
        return {}
    ident = identifier.strip().lower()

    # 精确标题匹配
    for sess in GDG_KOCHI:
        if (sess.get("title") or "").strip().lower() == ident:
            return sess

    # 精确演讲者匹配
    for sess in GDG_KOCHI:
        if (sess.get("speaker") or "").strip().lower() == ident:
            return sess

    # 部分匹配回退(标题或演讲者)
    for sess in GDG_KOCHI:
        title = (sess.get("title") or "").lower()
        speaker = (sess.get("speaker") or "").lower()
        if ident in title or ident in speaker:
            return sess

    return {}

if __name__ == "__main__":
    port = int(os.getenv("PORT", 8080))
    logger.info(f"🚀 MCP 服务器已在端口 {port} 启动")
    asyncio.run(
        mcp.run_async(
            transport="http",
            host="0.0.0.0",
            port=port,
        )
    )

创建 Dockerfile

创建一个 Dockerfile

# 使用官方的 Python 镜像
FROM python:3.13-slim

# 安装 uv
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/

# 将项目安装到 /app
COPY . /app
WORKDIR /app

# 允许语句和日志消息立即出现在日志中
ENV PYTHONUNBUFFERED=1

# 安装依赖项
RUN uv sync

EXPOSE $PORT

# 运行 FastMCP 服务器
CMD ["uv", "run", "server.py"]

部署到 Cloud Run

将您的 MCP 服务器部署到 Cloud Run:

gcloud run deploy gdg-kochi-mcp-server \
    --no-allow-unauthenticated \
    --region=asia-south1 \
    --source=. \
    --labels=dev-tutorial=codelab-mcp

注意: 根据需要调整 --region 参数。

配置 Gemini CLI

授予权限

添加 IAM 策略绑定以允许自己调用 Cloud Run 服务:

gcloud projects add-iam-policy-binding $GOOGLE_CLOUD_PROJECT \
    --member=user:$(gcloud config get-value account) \
    --role='roles/run.invoker'

设置环境变量

保存项目编号和 ID 令牌作为环境变量:

export PROJECT_NUMBER=$(gcloud projects describe $GOOGLE_CLOUD_PROJECT --format="value(projectNumber)")
export ID_TOKEN=$(gcloud auth print-identity-token)

创建 Gemini 配置

创建 Gemini 配置目录:

mkdir -p ~/.gemini

创建 Gemini 设置文件 ~/.gemini/settings.json

{
    "ide": {
        "hasSeenNudge": true
    },
    "mcpServers": {
        "gdg-remote": {
            "httpUrl": "https://gdg-kochi-mcp-server-$PROJECT_NUMBER.europe-west1.run.app/mcp",
            "headers": {
                "Authorization": "Bearer $ID_TOKEN"
            }
        }
    },
    "security": {
        "auth": {
            "selectedType": "cloud-shell"
        }
    }
}

注意: 确保:

  • 如果变量没有自动展开,请替换 $PROJECT_NUMBER 为您实际的项目编号
  • 如果变量没有自动展开,请替换 $ID_TOKEN 为您实际的 ID 令牌

测试 MCP 服务器

启动 Gemini CLI:

gemini

在 Gemini CLI 中导航到:

/mcp

示例问题

尝试提问类似的问题:

  • “显示超过 20 分钟的会议。”
  • “构建我的个人日程,如果我想参加关于 AI 和安全的演讲。”
  • “撰写一条 280 字的推特,宣布 Merin 的主题演讲。”
  • “为‘在任何地方运行 SQL’的演讲创建五个建议的观众问题。”
  • “哪些会议重叠或并行(工作坊 vs 主厅)?”
  • “给我列出所有演讲者及其所属机构。”
  • “哪些演讲者是 Google 员工?”
  • “谁在上午 09:40 发言?”

当提示时,允许来自服务器“gdg-kochi-remote”的所有工具。

故障排除

  • 如果遇到身份验证错误,请确保您的 ID_TOKEN 是最新的(令牌会在一段时间后过期)
  • 确保 settings.json 中的 Cloud Run 服务 URL 与您的实际部署区域匹配
  • 验证 IAM 策略绑定是否正确应用