MCP 协议集成
Gemini CLI — MCP 协议集成
学习目标
- 理解 Gemini CLI 的 MCP 服务器配置格式和工具发现机制
- 掌握 MCP 工具的前缀命名空间和通配符策略规则
- 分析 MCP OAuth 认证自动化流程
前置知识
本章涉及 MCP 协议的通用原理,建议先阅读:
下文假设你已理解 MCP 的传输层和客户端/服务器架构,直接聚焦 Gemini CLI 的具体集成实现。
项目实践
MCP 服务器配置
用户在 ~/.gemini/settings.json 或项目级配置中声明 MCP 服务器:
{ "mcpServers": { "github": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-github"], "env": { "GITHUB_TOKEN": "..." } }, "filesystem": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/allowed"] } }}支持的传输类型:
- stdio:通过子进程启动 MCP 服务器,标准输入输出通信(最常见)
- SSE:通过 Server-Sent Events 连接到远程服务器
- HTTP:HTTP 端点连接
- TCP:TCP socket 连接
工具发现与注册
McpClientManager 负责管理所有 MCP 服务器的连接和工具发现:
McpClientManager.connectToServer(serverConfig): 1. 根据传输类型创建 MCP Client(stdio/SSE/HTTP/TCP) 2. 建立连接,获取服务器能力 3. 调用 tools/list 获取工具列表 4. 为每个工具创建 DiscoveredMCPTool 实例 5. 注册到 ToolRegistry工具命名约定:MCP 工具注册时使用前缀命名空间:
mcp_{serverName}_{toolName}例如 github 服务器的 list_pull_requests 工具注册为 mcp_githubfox_list_pull_requests(实际前缀为 mcp_ + 服务器名 + _ + 工具名)。
这使得:
- 不同服务器的同名工具不会冲突
- 工具名称可追溯来源服务器
- 策略引擎可针对特定服务器制定规则
MCP 工具的执行流程
LLM: functionCall("mcp_github_list_prs", { state: "open" }) ↓ToolRegistry: 识别为 MCP 工具(DISCOVERED_TOOL_PREFIX) ↓DiscoveredMCPTool.execute(): ├── 通过 McpClientManager 找到对应服务器 ├── 发送 tools/call 请求 ├── 接收 tools/call 响应 └── 返回格式化结果进度追踪:长时间运行的 MCP 操作通过 CoreEvent.McpProgress 事件向 Scheduler 推送进度更新,实时更新 UI 显示。
MCP OAuth 认证自动化
Gemini CLI 为 MCP 服务器提供自动 OAuth 处理(packages/core/src/mcp/):
| 认证类型 | 说明 |
|---|---|
google-credentials | 使用 Google 账户自动认证(内置 Google Auth Provider) |
oauth | 标准 OAuth 2.0 流程,支持自定义 client_id/scopes |
| 无认证 | 不需要认证的服务器 |
OAuth Token 存储(oauth-token-storage.ts):
- Token 持久化到本地安全存储
- 自动检测 token 过期并刷新
- 支持多服务器独立 Token 管理
MCP 资源访问
除了工具调用,Gemini CLI 还提供两个专用 MCP 资源工具:
list-mcp-resources:列出 MCP 服务器提供的资源read-mcp-resource:读取指定 MCP 资源的内容
这使得 MCP 服务器不仅可以提供工具,还可以提供上下文资源(如文档、配置文件等)。
MCP 通配符策略规则
Policy Engine 支持 MCP 工具的通配符授权:
{ "rules": [ { "name": "mcp_github_*", "action": "allow" }, { "name": "mcp_*", "action": "ask_user" } ]}通配符匹配逻辑(policy-engine.ts):
| 模式 | 含义 |
|---|---|
* | 允许所有工具 |
mcp_* | 允许所有 MCP 工具 |
mcp_{serverName}_* | 允许指定 MCP 服务器的所有工具 |
实现细节:
matchesWildcard(pattern, toolName, serverName): if pattern == "*": return true if pattern == "mcp_*": return serverName !== undefined if pattern startsWith "mcp_" and endsWith "_*": expectedServerName = pattern.slice("mcp_".length, -2) return serverName == expectedServerName && toolName.startsWith("mcp_" + expectedServerName + "_")这使得管理员可以对 MCP 工具进行批量授权,而无需逐工具配置。
问题与规避
MCP 服务器启动失败
问题:MCP 服务器的 stdio 子进程可能因依赖缺失或配置错误而无法启动。
对策:McpClient 在连接失败时记录详细错误信息(stderr 输出),并通过 ToolRegistry 将工具标记为不可用。用户可在启动日志中看到具体的连接错误。
MCP 工具调用的超时
问题:MCP 服务器的工具调用可能挂起,阻塞整个 Agent 循环。
对策:
- 每个 MCP 服务器配置支持
timeout字段 McpClient在超时后终止子进程并返回错误- Scheduler 的
signal机制支持用户主动取消
MCP 工具参数验证
问题:MCP 服务器返回的工具 schema 可能不完整或不正确。
对策:DiscoveredMCPTool 在 build() 阶段使用 Zod schema 验证参数。如果 MCP 服务器未提供 schema,工具仍可调用,但参数验证较弱。
设计取舍
前缀命名空间 vs 独立工具注册
| 方案 | 优势 | 代价 |
|---|---|---|
| 前缀命名空间 | 实现简单、可追溯来源、支持通配符策略 | 工具名称较长,用户需记住前缀 |
| 独立注册 | 工具名称简洁 | 无法追溯来源,不同服务器同名工具可能冲突 |
Gemini CLI 的选择:前缀命名空间,因为 MCP 工具数量可能很多,且需要支持通配符策略规则。