使用说明
MCP服务器设置指南
本指南将引导您在Google Cloud Run上设置和部署MCP(模型上下文协议)服务器。
先决条件
- 谷歌云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 "Example of deploying an MCP server on Cloud Run" --bare --python 3.13
这有什么作用:
- 用途
uv,一个现代的Python包和项目管理器 - 在当前目录中初始化新的Python项目
- 创建一个
pyproject.toml文件(而不是requirements.txt) - 将Python 3.13设置为所需版本
- 设置项目元数据,如描述
- 这
--bareflag创建了一个没有额外文件的全新状态(如main.py,.venv,或README)
添加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 Server 🦁🐧🐻")
# Dictionary of Speakers at the GDG Kochi
GDG_KOCHI = [
{
"title": "Registration Starts",
"time": "09:30 AM - 09:35 AM",
"speaker": None,
"role": None,
"organization": None,
"duration": None,
"location": "Main Hall",
"notes": None,
},
{
"title": "Welcome Note",
"time": "09:35 AM - 09:40 AM",
"speaker": "Iqbal P B",
"role": "Organiser",
"organization": "GDG Cochin",
"duration": "5 min",
"location": "Main Hall",
"notes": None,
},
{
"title": "Keynote — Artificially intelligent? No, Creative with Google AI Studio",
"time": "09:40 AM - 10:00 AM",
"speaker": "Merin K Jacob",
"role": "Technical Account Manager",
"organization": "Google",
"duration": "20 min",
"location": "Main Hall",
"notes": None,
},
{
"title": "Keynote — Dont Call Me a Developer: Designing an AI-Accelerated Career",
"time": "10:05 AM - 10:25 AM",
"speaker": "Eric Hole",
"role": "Vice President",
"organization": "Onix",
"duration": "20 min",
"location": "Main Hall",
"notes": None,
},
{
"title": "Sponsor Talk",
"time": "10:30 AM - 10:40 AM",
"speaker": "Dr. Tom M Joseph",
"role": None,
"organization": "Jain",
"duration": "10 min",
"location": "Main Hall",
"notes": None,
},
{
"title": "Break",
"time": "10:50 AM - 11:05 AM",
"speaker": None,
"role": None,
"organization": None,
"duration": "15 min",
"location": "Main Hall",
"notes": None,
},
{
"title": "Tech Talk — Ship It Fast: AI Apps with Gemini, Firebase Studio, and $0 Infra",
"time": "11:35 AM - 12:00 PM",
"speaker": "Vishnu K S",
"role": "Senior Cloud Engineer",
"organization": "DP World",
"duration": "25 min",
"location": "Main Hall",
"notes": None,
},
{
"title": "Tech Talk — Building Conversational Agents using ADK, A2A and MCP",
"time": "12:05 PM - 12:30 PM",
"speaker": "Nishi Ajmera",
"role": "Solutions Architect",
"organization": "Publicis Sapient (UK)",
"duration": "25 min",
"location": "Main Hall",
"notes": None,
},
{
"title": "Tech Talk — Run SQL Everywhere: Presto Meets BigQuery and Cloud Storage",
"time": "12:40 PM - 01:25 PM",
"speaker": "Saurabh Mahawar",
"role": "Developer Relations Engineer",
"organization": "IBM",
"duration": "45 min",
"location": "Main Hall",
"notes": None,
},
{
"title": "Lunch Break",
"time": "12:40 PM - 01:25 PM",
"speaker": None,
"role": None,
"organization": None,
"duration": "45 min",
"location": "Main Hall",
"notes": None,
},
{
"title": "Tech Talk — Production ready AI Agents with Security, Evaluation and Memory",
"time": "01:25 PM - 01:50 PM",
"speaker": "Nikhilesh Tayal",
"role": "Google Developer Expert",
"organization": "—",
"duration": "25 min",
"location": "Main Hall",
"notes": None,
},
{
"title": "Tech Talk — Securing the Google Cloud: AI-Driven Threat Hunting Using Gemini",
"time": "01:55 PM - 02:20 PM",
"speaker": "Harisuthan S",
"role": "Senior Security Engineer",
"organization": "Renault Nissan Technology",
"duration": "25 min",
"location": "Main Hall",
"notes": None,
},
{
"title": "Tech Talk — Lessons from running Go applications in Kubernetes",
"time": "02:25 PM - 02:50 PM",
"speaker": "Adarsh K Kumar",
"role": "Principal Product Engineer",
"organization": "Rapido",
"duration": "25 min",
"location": "Main Hall",
"notes": None,
},
{
"title": "Tech Talk — Orchestrating the Modern Data Stack with Apache Airflow",
"time": "02:55 PM - 03:20 PM",
"speaker": "Jeevitha",
"role": "Staff Software Engineer",
"organization": "Juniper Networks (HP)",
"duration": "25 min",
"location": "Main Hall",
"notes": None,
},
{
"title": "Tech Talk — Building Scalable and Ethical AI Solutions with Gemma",
"time": "03:25 PM - 03:50 PM",
"speaker": "Geeta Kakrani",
"role": "Google Developer Expert",
"organization": "AI",
"duration": "25 min",
"location": "Main Hall",
"notes": None,
},
{
"title": "Tech Talk — Securing your LLM's",
"time": "03:55 PM - 04:20 PM",
"speaker": "Dharmesh Vaya",
"role": "Senior Solutions Engineer",
"organization": "Wiz",
"duration": "25 min",
"location": "Main Hall",
"notes": None,
},
{
"title": "Closing Note",
"time": "04:25 PM - 04:30 PM",
"speaker": "Malavika",
"role": "Organiser",
"organization": "GDG Cochin",
"duration": "5 min",
"location": "Main Hall",
"notes": None,
},
# Workshop / parallel track (best-effort)
{
"title": "Workshop — How to deploy a secure MCP server on Cloud Run?",
"time": "02:00 PM",
"speaker": "Ebin Babu",
"role": "CNCF Ambassador",
"organization": "Linux Foundation Projects",
"duration": None,
"location": "Workshop",
"notes": "Parallel workshop track",
},
]
@mcp.tool()
def search_sessions(query: str) -> List[Dict[str, Any]]:
"""Search GDG Kochi sessions.
Performs a case-insensitive substring search over session titles and
speaker names and returns matching session dicts from `GDG_KOCHI`.
Args:
query: A search string to match against title or speaker.
Returns:
A list of session dictionaries (may be empty).
"""
logger.info(f">>> 🛠️ Tool: 'search_sessions' called for query='{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]:
"""Return a single session's details by exact title or speaker name.
Tries to match `identifier` exactly (case-insensitive) against the
session title first, then speaker name. If no exact match is found,
returns the first partial match. If nothing matches, returns an empty dict.
Args:
identifier: Session title or speaker name to look up.
Returns:
A session dict or an empty dict when not found.
"""
logger.info(f">>> 🛠️ Tool: 'get_session_details' called for '{identifier}'")
if not identifier:
return {}
ident = identifier.strip().lower()
# Exact title match
for sess in GDG_KOCHI:
if (sess.get("title") or "").strip().lower() == ident:
return sess
# Exact speaker match
for sess in GDG_KOCHI:
if (sess.get("speaker") or "").strip().lower() == ident:
return sess
# Partial match fallback (title or speaker)
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 server started on port {port}")
asyncio.run(
mcp.run_async(
transport="http",
host="0.0.0.0",
port=port,
)
)
创建Dockerfile
创建一个 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"]
部署到云端运行
将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分钟的会话。”
- “如果我想参加关于人工智能和安全的讲座,请制定我的个人日程。”
- “写一条280个字符的推特,宣布梅林的主题演讲。”
- “为‘随处运行SQL’的演讲创建五个建议的听众问题。”
- “哪些会议重叠或平行(研讨会与主会场)?”
- “给我一份所有发言者及其组织的名单。”
- “哪些演讲者是谷歌员工?”
- “谁在上午9点40分讲话?”
出现提示时,允许从服务器“gdg-kochi-remote”使用所有工具。
故障排除
- 如果遇到身份验证错误,请确保
ID_TOKEN是最新的(令牌在一段时间后过期) - 确保中的Cloud Run服务URL
settings.json与您的实际部署区域匹配 - 验证IAM策略绑定是否已正确应用
