跳转到内容

MCP 协议与生态集成

MCP 协议与生态集成

学习目标

读完本章后,你将能够:

  • 理解 MCP(Model Context Protocol)协议的设计目标和核心概念
  • 掌握 MCP 的三种传输层(Stdio、Streamable HTTP、In-Process)及其适用场景
  • 设计 MCP 客户端的连接管理和工具聚合策略
  • 将现有系统暴露为 MCP 服务器

前置知识

  • JSON-RPC 2.0 基础
  • 进程间通信(IPC)基本概念

核心概念

1. 什么是 MCP

Model Context Protocol (MCP) 是 Anthropic 于 2024 年提出的开放协议,旨在标准化 AI 模型与外部工具、数据源之间的通信方式。

设计目标

  • 标准化:统一模型与工具的交互接口,取代各平台私有的插件机制
  • 可组合性:工具可以像乐高积木一样组合使用
  • 安全性:明确的权限边界和传输安全
  • 生态互通:一个 MCP 服务器可以被任何支持 MCP 的客户端使用

核心角色

  • Host:运行 AI 模型的应用程序(如 Claude Desktop、Codex CLI)
  • Client:Host 内部的 MCP 客户端,负责与服务器通信
  • Server:提供工具、资源或提示词的外部服务

2. MCP 协议基础

MCP 基于 JSON-RPC 2.0,定义了以下核心原语:

生命周期方法

  • initialize:客户端与服务器交换能力声明
  • initialized:初始化完成通知
  • notifications/initialized:服务器确认

工具相关

  • tools/list:获取服务器提供的工具列表
  • tools/call:调用指定工具
  • notifications/tools/list_changed:工具列表变更通知

资源相关

  • resources/list:获取资源列表
  • resources/read:读取资源内容
  • resources/templates/list:获取资源模板

提示词相关

  • prompts/list:获取提示词模板
  • prompts/get:获取提示词内容

3. 传输层

MCP 支持三种传输方式:

Stdio(标准输入输出)

  • 适用场景:本地工具、命令行程序
  • 优势:简单、无网络依赖、天然隔离
  • 劣势:单连接、无法远程访问

Streamable HTTP(Server-Sent Events)

  • 适用场景:远程服务、Web 应用
  • 优势:支持会话恢复、可跨网络、OAuth 集成
  • 劣势:需要 HTTP 基础设施

会话管理:HTTP 传输使用 Mcp-Session-Id 头部管理会话,支持断线重连。

In-Process(内存通道)

  • 适用场景:同一进程内的测试或内嵌场景
  • 优势:零开销、无序列化成本
  • 劣势:无隔离、仅适用于特定架构

设计模式详解

MCP 客户端聚合架构

当 Host 连接多个 MCP 服务器时,需要聚合层统一管理:

关键职责

  1. 连接生命周期:启动所有配置的服务器,监控健康状态
  2. 工具聚合:收集所有服务器的工具列表,去重、归一化名称
  3. 路由分发:按服务器名称将工具调用路由到正确的客户端
  4. 错误处理:单个服务器故障不影响其他服务器

名称归一化:不同服务器可能有同名工具。常见的解决策略:

  • 添加服务器名前缀:server_name__tool_name
  • 截断 + 哈希:当名称超过模型限制时截断并附加哈希
  • 白名单/黑名单:per-server 控制可见工具

会话恢复

HTTP 传输可能因网络波动导致会话过期:

关键机制

  • 使用锁(session_recovery_lock)防止并发恢复
  • 恢复后需要重新拉取工具列表(可能已变更)
  • 恢复次数限制,避免无限重试

Codex 作为 MCP 服务器

不仅 Agent 可以使用外部 MCP 工具,Agent 自身也可以暴露为 MCP 服务器:

暴露的能力

  • codex:接受 prompt、model、sandbox 等参数,启动新会话
  • codex-reply:基于 thread_id 继续对话
  • 事件流通知:将内部事件(审批请求、回合完成)推送给 MCP 客户端

这种双向 MCP 集成实现了Agent 之间的互操作性


问题与规避

服务器启动失败

问题:某个 MCP 服务器配置错误或依赖缺失,导致启动失败。

对策

  • 独立启动:每个服务器独立启动,失败不影响其他服务器
  • 懒加载:首次使用时再启动,减少启动时间和依赖
  • 健康检查:定期检查服务器响应,失败时标记为不可用

OAuth 授权流程

问题:远程 MCP 服务器需要 OAuth 认证,用户体验复杂。

对策

  • 本地 OAuth 回调服务器:启动临时 HTTP 服务器接收授权码
  • Token 持久化:刷新 token 自动写回磁盘,避免重复授权
  • Auth Elicitation:在 pending 授权时暂停工具调用,避免超时

工具列表变更

问题:MCP 服务器动态添加或移除工具,客户端工具列表 stale。

对策

  • list_changed 通知:服务器主动推送变更
  • 定期刷新:客户端定时重新拉取工具列表
  • 版本标识:为工具列表添加版本号或哈希

设计取舍

本地 MCP vs 远程 MCP

维度本地(Stdio)远程(HTTP/SSE)
延迟低(进程内通信)高(网络往返)
安全依赖 OS 进程隔离依赖 TLS + OAuth
部署需本地安装即开即用
可扩展性受限于本地资源可水平扩展

推荐策略:开发工具、文件系统操作使用本地 MCP;API 服务、数据库查询使用远程 MCP。

同步调用 vs 异步事件流

维度同步调用异步事件流
模型体验简单,一次请求-响应复杂,需要流式处理
实时反馈差,等待全部完成好,中间状态可见
实现复杂度

MCP 标准主要采用同步调用模型,但可以通过通知机制扩展为事件流。


补充:Goose 的扩展管理与命名空间隔离

来源:Goose(aaif-goose/goose)crates/goose/src/agents/extension_manager.rsmcp_client.rs,commit 1cb5cb0

ExtensionManager 架构

Goose 的 ExtensionManager 管理一个 HashMap<String, Extension>,支持 6 种扩展类型:

类型传输说明
Stdio子进程 stdio最常见的本地 MCP 扩展
StreamableHttpHTTP + SSE远程 MCP 服务,支持 OAuth
Builtin进程内打包在 goose-mcp 中的内嵌服务器
Platform进程内一等扩展(如 developer),享有 unprefixed 工具名
InlinePython进程内Python 内嵌脚本
FrontendUI 侧由前端执行的工具

工具命名空间策略

普通扩展:extension_name__tool_name (如 developer__shell)
一等扩展:tool_name (如 shell、read_file)

为什么有前缀? 多个 MCP 服务器可能提供同名工具(如多个扩展都有 read_file)。前缀确保工具名唯一,LLM 可以明确指定调用哪个扩展的工具。

一等扩展为什么无前缀? Developer 扩展是 Goose 的核心能力(文件、Shell),用户期望直接使用 shellread_file 等自然名称。将其设为「一等」避免了前缀带来的认知负担。

版本驱动的缓存失效

ExtensionManager 缓存工具列表,避免每次 turn 都调用 tools/list

缓存键:(extension_name, tools_version_hash)
失效条件:
1. 扩展重新连接(transport 重建)
2. 收到 tools/list_changed 通知
3. 手动刷新

与通用策略的关系:这实现了 MCP 协议 中提到的「版本标识」策略,用哈希而非版本号来判断变更,避免了服务器需要维护版本号的负担。

Docker 容器内执行

扩展可以在 Docker 容器内运行:

extension:
cmd: "docker exec my-container mcp-server"
type: "stdio"

安全意义:将 MCP 服务器的执行环境隔离到容器中,避免直接访问宿主机文件系统。这对于不可信或实验性扩展特别重要。

恶意软件检查

Stdio 扩展在启动前进行恶意软件检查:

  • 检查命令路径是否为已知的合法位置
  • 拒绝在系统目录中执行未知二进制文件
  • 验证扩展配置的签名或来源

MCP Client 的双向通信

Goose 的 MCP Client 不仅消费工具,还实现双向交互:

  • list_roots:向 MCP 服务器暴露工作目录
  • create_message:当 MCP 服务器需要 LLM 采样时,Goose 将请求委托给自己的 Provider
  • create_elicitation:MCP 服务器需要用户交互时,通过 ActionRequiredManager 路由到前端
  • Session 上下文注入:每个 MCP 请求附带 Extensions._meta 携带 session_idworking_dir

Flow-as-MCP 模式

可视化 AI 工作流平台不仅将外部 MCP 工具引入画布,还将整个工作流暴露为 MCP 工具供外部 Agent 调用。

两种部署模式

独立模式(stdio)

  • MCP Server 作为独立进程运行,通过 REST API 连接平台后端
  • 工具集:Flow 构建(添加组件、连线、配置)、Flow 执行、组件搜索
  • 适用场景:Agent 通过自然语言指令构建和修改工作流
  • 关键特性:零缓存策略,每次 mutating 操作执行 GET → modify → PATCH

内嵌模式(SSE)

  • MCP 工具直接挂载在平台的 FastAPI 路由上
  • 工具集:模板搜索、组件查询、Flow 图摘要、流式执行
  • 适用场景:Agent 直接调用已部署的 Flow 作为工具
  • 关键特性:工具通过 tool_mode=True 自动从组件输出推导

组件注册表懒加载

MCP Server 首次访问时从平台 API 拉取完整组件注册表,后续请求复用。在多 Agent 并发场景下(SSE 模式),使用 contextvars 实现 per-session 隔离,避免不同 Agent 的注册表互相污染。

Flow 构建工具集

工具功能
list_components搜索可用组件(按名称、类型、描述)
add_component向 Flow 添加新组件节点
configure_component修改组件的输入参数
add_connection在两个组件间建立数据连线
remove_component从 Flow 移除组件
flow_to_spec_summary将 Flow 导出为可读摘要

这些原子工具组合起来,使得 Agent 可以通过多轮对话式交互逐步构建复杂的 AI 工作流。

Skill-Embedded MCP 按需加载模式

传统 MCP 集成中,MCP 服务器在 Agent 启动时全部连接,每个服务器的工具描述都注入到系统提示中。这在多 MCP 场景下会显著膨胀上下文窗口,消耗宝贵的 token 预算。

Skill-Embedded MCP 是一种替代模式:Skill(专业技能包)自带 MCP 服务器声明,按需启动、作用域限定、任务完成即释放。

工作流程

关键设计决策

维度传统常驻 MCPSkill-Embedded MCP
生命周期Agent 启动时连接,全程保持按需启动,任务完成关闭
上下文开销所有工具描述始终在 prompt 中仅激活 Skill 的工具描述出现
适用场景核心、高频使用的工具专业、低频使用的工具
隔离性全局共享per-Skill 隔离

实现要点

  • Skill 的 MCP 声明通常在 SKILL.md 的 frontmatter 中
  • Skill MCP Manager 负责生命周期管理(启动、注入、关闭)
  • 工具注入和移除与 session 工具集管理集成
  • 启动失败时降级为 Skill 可用但 MCP 工具不可用的状态

陷阱

  • MCP 启动延迟影响 Agent 首次调用该 Skill 的响应时间
  • 并发激活同一 Skill 的多个实例时需要共享或隔离策略
  • MCP 服务器进程泄漏(任务完成后未正确关闭)需要定时清理机制

CLI-Anything 的 MCP 后端封装模式

来源:CLI-Anything(HKUDS/CLI-Anything)cli-anything-plugin/guides/mcp-backend.md + browser/agent-harness/,commit 7862c92

CLI-Anything 展示了将 MCP 服务器作为CLI 后端的封装模式——与”Agent 连接 MCP Server”不同,这里是 CLI 工具在内部 spawn MCP Server,为上层 CLI 命令提供底层能力。

架构差异

关键差异

  • Agent 不直接连接 MCP Server,而是通过 CLI 工具间接调用
  • CLI 工具每命令 spawn MCP Server(无状态模式)或使用 Daemon 模式(一次 spawn,多命令复用)
  • CLI 维护业务状态(URL、工作目录、导航历史),MCP Server 只是底层能力提供者

同步包装异步调用

# 伪代码:sync wrapper for async MCP calls
def ls(path: str = "/") -> dict:
"""List directory contents via MCP."""
return asyncio.run(_call_tool("domshell_ls", {"path": path}))

MCP Python SDK 是异步的,但 CLI 命令需要同步接口。使用 asyncio.run() 包装每个调用。

Daemon 模式

# 显式启动/停止
cli-tool mcp start # 启动 MCP server,保持连接
cli-tool mcp stop # 关闭连接
# 中间命令复用同一连接

优势:减少每命令的 spawn 开销,适合 REPL 交互。 代价:需要管理连接生命周期,连接失败时的错误处理更复杂。

适用场景

条件推荐模式
软件有官方/社区 MCP ServerMCP 后端模式
无原生 CLI 存在MCP 后端模式
MCP 比 CLI 提供更好的功能MCP 后端模式
软件有成熟的 CLI(如 ffmpeg)传统 CLI 后端模式

典型案例:Browser/DOMShell —— 通过 npx @apireno/domshell 提供浏览器自动化 MCP 工具,CLI 封装层将这些工具映射为 browse navigatebrowse clickbrowse screenshot 等命令。


参考来源


补充:企业级平台中的 MCP 连接生命周期与工具同步

来源:JeecgBoot v3.9.2,AiragMcpServiceImpl.javaMcpToolProviderWrapperAIChatHandler.java,commit 3f826c4

三种传输模式的企业级应用

基于 langchain4j-mcp,企业级平台可实现三种 MCP 传输模式:

传输模式实现类适用场景
HTTP SSEHttpMcpTransport远程 MCP Server,支持 SSE 流式响应
Streamable HTTPStreamableHttpMcpTransport新一代 MCP HTTP 标准,支持双向流
StdioStdioMcpTransport本地进程 MCP,适合开发工具集成

MCP 工具同步机制

当新增或编辑 MCP Server 配置后,平台通过 syncMcpTools 从远程服务器拉取工具列表并持久化:

用户保存 MCP 配置
创建临时 McpClient(对应传输模式)
调用 tools/list 获取工具列表
转换为 AiragMcp.tools(JSON 存储)
synced = 1(已同步)
关闭临时 McpClient

陷阱:如果 MCP Server 在同步过程中不可达,synced 保持为 0(未同步),用户在工具列表中看到灰色状态。

连接生命周期管理

在生产环境中,MCP 连接的创建和释放直接影响系统稳定性:

关键设计:使用 McpToolProviderWrapper 包装 McpToolProvider,保存连接引用以便后续关闭。如果不关闭,每次聊天都会创建新的 MCP 连接,最终导致连接泄漏和服务器资源耗尽。

插件与 MCP 统一分类

平台将 MCP Server 和自定义 REST API 插件统一归类为 “plugin” 类别,通过 category 字段区分:

  • category = "mcp":标准 MCP 协议,使用 McpToolProvider 构建工具
  • category = "plugin":REST API 插件,使用 PluginToolBuilder 构建 ToolSpecification + ToolExecutor

这种统一分类使得用户可以在同一个界面管理 MCP 服务和自定义 API 插件,AI 应用可无缝使用两者。