摘要
Model Context Protocol(MCP)是 Anthropic 于 2024 年 11 月开源发布的一项标准化协议,旨在解决大语言模型工具调用中的碎片化问题。MCP 通过定义统一的 Server/Client/Host 三层架构,使任意 LLM 能够以标准方式连接任意工具、数据源和服务,被誉为"AI 的 USB 接口"。本文从 Function Calling 的固有痛点出发,深入剖析 MCP 协议架构、消息格式、通信流程,并与 Function Calling 进行 7 个维度的系统对比,最后分析 MCP 生态现状与适用边界。全文包含完整的协议交互代码示例、架构图解和生态全景分析,适合正在构建 Agent 系统的工程师快速掌握 MCP 核心原理。
版本声明: 本文基于 MCP 规范 2025-03-26 版本撰写,涉及 Anthropic Claude、OpenAI 等平台的最新支持情况。MCP 规范仍在快速迭代中,部分 API 可能在后续版本中发生变化。
适用边界: 适合具备 LLM 应用开发经验、了解 Function Calling 基本原理的中高级工程师。如果你还不熟悉 Function Calling,建议先阅读本系列第 08 篇。
文章目录
-
- 摘要
- [一、MCP诞生的背景:Function Calling的碎片化问题](#一、MCP诞生的背景:Function Calling的碎片化问题)
-
- [1.1 Function Calling 的成功与局限](#1.1 Function Calling 的成功与局限)
- [1.2 碎片化带来的三大痛点](#1.2 碎片化带来的三大痛点)
- [1.3 MCP 的解题思路](#1.3 MCP 的解题思路)
- 二、MCP协议架构:Server/Client/Host三层模型
-
- [2.1 三层架构总览](#2.1 三层架构总览)
- [2.2 通信传输层](#2.2 通信传输层)
- [2.3 三大核心能力原语](#2.3 三大核心能力原语)
- [三、MCP与Function Calling的对比:7个维度差异分析](#三、MCP与Function Calling的对比:7个维度差异分析)
-
- [3.1 架构模式:内嵌 vs 独立服务](#3.1 架构模式:内嵌 vs 独立服务)
- [3.2 协议标准:碎片化 vs 统一标准](#3.2 协议标准:碎片化 vs 统一标准)
- [3.3 工具发现:静态 vs 动态](#3.3 工具发现:静态 vs 动态)
- [3.4 模型兼容:绑定 vs 无关](#3.4 模型兼容:绑定 vs 无关)
- [3.5 工具复用:项目级 vs 生态级](#3.5 工具复用:项目级 vs 生态级)
- [3.6 状态管理:无状态 vs 有状态会话](#3.6 状态管理:无状态 vs 有状态会话)
- [3.7 通信方向:单向 vs 双向](#3.7 通信方向:单向 vs 双向)
- 四、MCP消息格式与通信流程详解
-
- [4.1 JSON-RPC 2.0 基础](#4.1 JSON-RPC 2.0 基础)
- [4.2 完整通信流程](#4.2 完整通信流程)
- [4.3 工具调用的完整生命周期](#4.3 工具调用的完整生命周期)
- 五、MCP生态现状:已支持的工具、平台和框架
-
- [5.1 MCP 生态全景](#5.1 MCP 生态全景)
- [5.2 Host 端支持情况](#5.2 Host 端支持情况)
- [5.3 SDK 和框架集成](#5.3 SDK 和框架集成)
- [5.4 TypeScript/Node.js SDK](#5.4 TypeScript/Node.js SDK)
- 六、为什么MCP是Agent的"USB接口":标准化带来的生态效应
-
- [6.1 USB 的历史启示](#6.1 USB 的历史启示)
- [6.2 标准化的网络效应](#6.2 标准化的网络效应)
- [6.3 实际案例:从零到有的工具集成](#6.3 实际案例:从零到有的工具集成)
- [6.4 工具市场的前景](#6.4 工具市场的前景)
- 七、适用边界与风险提示
-
- [7.1 MCP 不是银弹](#7.1 MCP 不是银弹)
- [7.2 安全风险](#7.2 安全风险)
- [7.3 性能考量](#7.3 性能考量)
- [7.4 版本兼容性风险](#7.4 版本兼容性风险)
- 八、总结
- 参考资料
一、MCP诞生的背景:Function Calling的碎片化问题
1.1 Function Calling 的成功与局限
2023 年 6 月,OpenAI 首次推出 Function Calling 机制,允许开发者向 GPT 模型描述函数签名,模型根据对话上下文决定是否调用该函数。这一机制迅速成为 LLM 应用开发的事实标准,几乎所有主流模型厂商都跟随了这一范式:
| 厂商 | 模型 | 函数调用支持 | 时间 |
|---|---|---|---|
| OpenAI | GPT-4 / GPT-4o | ✅ 原生支持 | 2023.06 |
| Anthropic | Claude 3 / 3.5 | ✅ 原生支持 | 2024.03 |
| Gemini 1.5 | ✅ 原生支持 | 2024.02 | |
| 智谱 | GLM-4 | ✅ 原生支持 | 2024.01 |
| 百度 | ERNIE 4.0 | ✅ 原生支持 | 2023.10 |
但成功背后隐藏着深刻的碎片化问题。让我们用一个真实场景来说明:
python
# OpenAI 风格的 Function Calling
tools = [
{
"type": "function",
"function": {
"name": "search_database",
"description": "Search the customer database",
"parameters": {
"type": "object",
"properties": {
"query": {"type": "string", "description": "Search query"}
},
"required": ["query"]
}
}
}
]
response = openai_client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Find customer John"}],
tools=tools
)
上面这段代码是 OpenAI 的 Function Calling 写法。同一个功能,如果换成 Claude,参数结构略有不同;换成 Gemini,又是另一套格式。工具的定义方式、调用流程、返回格式,每家厂商都不一样 。这意味着你为 OpenAI 写的工具适配代码,无法直接用在 Claude 上。更关键的是,工具本身是"死的"------它只是一个 JSON Schema 描述,没有独立运行能力,必须在应用代码中被手动注册、手动执行、手动把结果喂回模型。
1.2 碎片化带来的三大痛点
#mermaid-svg-QQtKtyeqmwJ9QyT0{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-QQtKtyeqmwJ9QyT0 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-QQtKtyeqmwJ9QyT0 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-QQtKtyeqmwJ9QyT0 .error-icon{fill:#552222;}#mermaid-svg-QQtKtyeqmwJ9QyT0 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-QQtKtyeqmwJ9QyT0 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-QQtKtyeqmwJ9QyT0 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-QQtKtyeqmwJ9QyT0 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-QQtKtyeqmwJ9QyT0 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-QQtKtyeqmwJ9QyT0 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-QQtKtyeqmwJ9QyT0 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-QQtKtyeqmwJ9QyT0 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-QQtKtyeqmwJ9QyT0 .marker.cross{stroke:#333333;}#mermaid-svg-QQtKtyeqmwJ9QyT0 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-QQtKtyeqmwJ9QyT0 p{margin:0;}#mermaid-svg-QQtKtyeqmwJ9QyT0 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-QQtKtyeqmwJ9QyT0 .cluster-label text{fill:#333;}#mermaid-svg-QQtKtyeqmwJ9QyT0 .cluster-label span{color:#333;}#mermaid-svg-QQtKtyeqmwJ9QyT0 .cluster-label span p{background-color:transparent;}#mermaid-svg-QQtKtyeqmwJ9QyT0 .label text,#mermaid-svg-QQtKtyeqmwJ9QyT0 span{fill:#333;color:#333;}#mermaid-svg-QQtKtyeqmwJ9QyT0 .node rect,#mermaid-svg-QQtKtyeqmwJ9QyT0 .node circle,#mermaid-svg-QQtKtyeqmwJ9QyT0 .node ellipse,#mermaid-svg-QQtKtyeqmwJ9QyT0 .node polygon,#mermaid-svg-QQtKtyeqmwJ9QyT0 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-QQtKtyeqmwJ9QyT0 .rough-node .label text,#mermaid-svg-QQtKtyeqmwJ9QyT0 .node .label text,#mermaid-svg-QQtKtyeqmwJ9QyT0 .image-shape .label,#mermaid-svg-QQtKtyeqmwJ9QyT0 .icon-shape .label{text-anchor:middle;}#mermaid-svg-QQtKtyeqmwJ9QyT0 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-QQtKtyeqmwJ9QyT0 .rough-node .label,#mermaid-svg-QQtKtyeqmwJ9QyT0 .node .label,#mermaid-svg-QQtKtyeqmwJ9QyT0 .image-shape .label,#mermaid-svg-QQtKtyeqmwJ9QyT0 .icon-shape .label{text-align:center;}#mermaid-svg-QQtKtyeqmwJ9QyT0 .node.clickable{cursor:pointer;}#mermaid-svg-QQtKtyeqmwJ9QyT0 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-QQtKtyeqmwJ9QyT0 .arrowheadPath{fill:#333333;}#mermaid-svg-QQtKtyeqmwJ9QyT0 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-QQtKtyeqmwJ9QyT0 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-QQtKtyeqmwJ9QyT0 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-QQtKtyeqmwJ9QyT0 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-QQtKtyeqmwJ9QyT0 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-QQtKtyeqmwJ9QyT0 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-QQtKtyeqmwJ9QyT0 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-QQtKtyeqmwJ9QyT0 .cluster text{fill:#333;}#mermaid-svg-QQtKtyeqmwJ9QyT0 .cluster span{color:#333;}#mermaid-svg-QQtKtyeqmwJ9QyT0 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-QQtKtyeqmwJ9QyT0 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-QQtKtyeqmwJ9QyT0 rect.text{fill:none;stroke-width:0;}#mermaid-svg-QQtKtyeqmwJ9QyT0 .icon-shape,#mermaid-svg-QQtKtyeqmwJ9QyT0 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-QQtKtyeqmwJ9QyT0 .icon-shape p,#mermaid-svg-QQtKtyeqmwJ9QyT0 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-QQtKtyeqmwJ9QyT0 .icon-shape .label rect,#mermaid-svg-QQtKtyeqmwJ9QyT0 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-QQtKtyeqmwJ9QyT0 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-QQtKtyeqmwJ9QyT0 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-QQtKtyeqmwJ9QyT0 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Function Calling 碎片化问题
痛点1: 协议不统一
痛点2: 工具不可复用
痛点3: 缺乏动态发现
OpenAI/Claude/Gemini 各有格式
开发者需要维护多套适配层
迁移成本高
工具定义嵌入应用代码
不同项目需重复实现
无法跨团队共享
工具列表启动时硬编码
无法运行时增减工具
Agent无法自主发现新能力
痛点一:协议不统一。 每个模型厂商都定义了自己的工具描述格式和调用接口。OpenAI 用 tools 数组配合 function 类型,Anthropic 用 tools 但结构不同,Gemini 用 function_declarations。开发者如果想做一个支持多模型的 Agent,需要为每个模型写一套适配层,维护成本极高。
痛点二:工具不可复用。 在 Function Calling 范式中,工具只是模型侧的定义,真正的执行逻辑写在应用代码里。同一个"搜索数据库"工具,在项目 A 中实现了一遍,到项目 B 又要重新实现。没有标准的方式让工具作为独立组件被打包、分发、复用。
痛点三:缺乏动态发现。 Function Calling 的工具列表是在启动时硬编码的。如果 Agent 在运行过程中需要访问一个新的数据源或服务,开发者必须修改代码、重新部署。Agent 自己无法"发现"新工具并自主扩展能力。
1.3 MCP 的解题思路
Anthropic 提出的 MCP(Model Context Protocol)协议,核心思路非常简洁:把工具调用从应用代码中抽离出来,变成独立的、可复用的、可动态发现的服务。
就像 USB 接口统一了硬件连接方式一样,MCP 试图统一 LLM 与外部工具/数据源的连接方式。任何工具只要实现了 MCP Server 接口,就可以被任何支持 MCP 的 LLM 客户端发现和调用------无需修改应用代码,无需关心底层模型是 Claude 还是 GPT。
类比理解: 如果说 Function Calling 是"每个设备配一根专用线",那 MCP 就是"所有设备都用 USB-C 接口"。线缆标准化了,设备之间就能即插即用。
图:MCP 三层架构(Host/Client/Server)与 JSON-RPC 通信流程全景
gpt-image-2 prompt: A comprehensive technical architecture overview diagram of the Model Context Protocol (MCP) ecosystem. The diagram shows three horizontal layers: Top layer labeled "Host" in deep blue with a chat UI window, an LLM brain icon, and a Session Manager module connected by arrows. Middle layer labeled "Client" in warm orange with three MCP Client adapter boxes (Client 1, 2, 3) acting as protocol bridges. Bottom layer labeled "Server" in green with four independent server processes: Database Server (with SQL icon), File System Server (with folder icon), API Gateway Server (with cloud icon), and Search Engine Server (with magnifying glass icon). Dashed bidirectional arrows labeled "JSON-RPC 2.0" connect Clients to Servers. Side annotations explain the data flow: "initialize → tools/list → tools/call". A USB-C cable metaphor is subtly placed in the corner. Dark navy background with cyan and amber accent colors, professional software architecture diagram style, clean labels in English, 16:9 aspect ratio, no empty white space, every element annotated with technical details.
二、MCP协议架构:Server/Client/Host三层模型
2.1 三层架构总览
MCP 协议定义了一个清晰的三层架构:Host、Client 和 Server。理解这三层的关系是掌握 MCP 的关键。
#mermaid-svg-ksFh377XmnyjuqU5{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ksFh377XmnyjuqU5 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ksFh377XmnyjuqU5 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ksFh377XmnyjuqU5 .error-icon{fill:#552222;}#mermaid-svg-ksFh377XmnyjuqU5 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ksFh377XmnyjuqU5 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ksFh377XmnyjuqU5 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ksFh377XmnyjuqU5 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ksFh377XmnyjuqU5 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ksFh377XmnyjuqU5 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ksFh377XmnyjuqU5 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ksFh377XmnyjuqU5 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ksFh377XmnyjuqU5 .marker.cross{stroke:#333333;}#mermaid-svg-ksFh377XmnyjuqU5 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ksFh377XmnyjuqU5 p{margin:0;}#mermaid-svg-ksFh377XmnyjuqU5 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ksFh377XmnyjuqU5 .cluster-label text{fill:#333;}#mermaid-svg-ksFh377XmnyjuqU5 .cluster-label span{color:#333;}#mermaid-svg-ksFh377XmnyjuqU5 .cluster-label span p{background-color:transparent;}#mermaid-svg-ksFh377XmnyjuqU5 .label text,#mermaid-svg-ksFh377XmnyjuqU5 span{fill:#333;color:#333;}#mermaid-svg-ksFh377XmnyjuqU5 .node rect,#mermaid-svg-ksFh377XmnyjuqU5 .node circle,#mermaid-svg-ksFh377XmnyjuqU5 .node ellipse,#mermaid-svg-ksFh377XmnyjuqU5 .node polygon,#mermaid-svg-ksFh377XmnyjuqU5 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ksFh377XmnyjuqU5 .rough-node .label text,#mermaid-svg-ksFh377XmnyjuqU5 .node .label text,#mermaid-svg-ksFh377XmnyjuqU5 .image-shape .label,#mermaid-svg-ksFh377XmnyjuqU5 .icon-shape .label{text-anchor:middle;}#mermaid-svg-ksFh377XmnyjuqU5 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ksFh377XmnyjuqU5 .rough-node .label,#mermaid-svg-ksFh377XmnyjuqU5 .node .label,#mermaid-svg-ksFh377XmnyjuqU5 .image-shape .label,#mermaid-svg-ksFh377XmnyjuqU5 .icon-shape .label{text-align:center;}#mermaid-svg-ksFh377XmnyjuqU5 .node.clickable{cursor:pointer;}#mermaid-svg-ksFh377XmnyjuqU5 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ksFh377XmnyjuqU5 .arrowheadPath{fill:#333333;}#mermaid-svg-ksFh377XmnyjuqU5 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ksFh377XmnyjuqU5 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ksFh377XmnyjuqU5 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ksFh377XmnyjuqU5 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ksFh377XmnyjuqU5 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ksFh377XmnyjuqU5 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ksFh377XmnyjuqU5 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ksFh377XmnyjuqU5 .cluster text{fill:#333;}#mermaid-svg-ksFh377XmnyjuqU5 .cluster span{color:#333;}#mermaid-svg-ksFh377XmnyjuqU5 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ksFh377XmnyjuqU5 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ksFh377XmnyjuqU5 rect.text{fill:none;stroke-width:0;}#mermaid-svg-ksFh377XmnyjuqU5 .icon-shape,#mermaid-svg-ksFh377XmnyjuqU5 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ksFh377XmnyjuqU5 .icon-shape p,#mermaid-svg-ksFh377XmnyjuqU5 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ksFh377XmnyjuqU5 .icon-shape .label rect,#mermaid-svg-ksFh377XmnyjuqU5 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ksFh377XmnyjuqU5 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ksFh377XmnyjuqU5 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ksFh377XmnyjuqU5 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Server 层(工具服务)
Client 层(协议客户端)
Host 层(宿主应用)
JSON-RPC 2.0
JSON-RPC 2.0
JSON-RPC 2.0
JSON-RPC 2.0
LLM Agent
用户界面
Session Manager
MCP Client 1
MCP Client 2
MCP Client 3
MCP Server: 数据库
MCP Server: 文件系统
MCP Server: API 网关
MCP Server: 搜索引擎
Host(宿主应用): 这是用户直接交互的应用,比如 Claude Desktop、Cursor IDE,或者你自己构建的 Agent 应用。Host 内部运行着 LLM,管理着对话流程和用户界面。Host 的核心职责是创建和管理多个 MCP Client 实例,将 LLM 的决策路由到正确的 Client。
Client(客户端): Client 是 Host 内部的一个协议适配器,每个 Client 实例与一个 Server 维持 1:1 的连接。Client 负责初始化连接、协商能力、转发请求和接收响应。Client 本身不包含业务逻辑,它只是一个"翻译官"。
Server(服务端): Server 是工具的实际实现者。它是一个独立运行的进程,暴露一组标准化的能力(Tools、Resources、Prompts)。Server 可以是本地进程(通过 stdio 通信),也可以是远程服务(通过 SSE/WebSocket 通信)。
2.2 通信传输层
MCP 协议支持两种传输方式,分别适用于不同场景:
| 传输方式 | 通信协议 | 适用场景 | 启动速度 | 跨网络 |
|---|---|---|---|---|
| stdio | 标准输入输出 | 本地工具、CLI 集成 | 极快 | ❌ |
| SSE | Server-Sent Events + HTTP | 远程工具、云服务 | 中等 | ✅ |
| Streamable HTTP | HTTP POST + 可选 SSE | 远程工具(2025-03 新增) | 中等 | ✅ |
stdio 传输适合本地运行的 Server,比如文件系统访问、本地数据库查询。Server 作为 Host 的子进程启动,通过标准输入输出进行 JSON-RPC 2.0 通信。SSE 和 Streamable HTTP 则适合远程服务,比如访问云端的 GitHub API、Slack API 等。
python
# 一个最小的 MCP Server 实现(使用 Python SDK)
from mcp.server import Server
from mcp.types import Tool, TextContent
import mcp.server.stdio
import asyncio
server = Server("my-search-server")
@server.list_tools()
async def list_tools() -> list[Tool]:
return [
Tool(
name="search",
description="Search the web for information",
inputSchema={
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "The search query"
}
},
"required": ["query"]
}
)
]
@server.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
if name == "search":
# 实际的搜索逻辑
query = arguments["query"]
results = f"Search results for: {query}\n1. Result one\n2. Result two"
return [TextContent(type="text", text=results)]
async def main():
async with mcp.server.stdio.stdio_server() as (read, write):
await server.run(read, write, server.create_initialization_options())
if __name__ == "__main__":
asyncio.run(main())
上面这段代码实现了一个最小可运行的 MCP Server。关键点说明:Server 类是 MCP Server 的核心,构造参数 "my-search-server" 是服务名称。@server.list_tools() 装饰器注册了工具列表回调,当 Client 连接后会调用此方法获取可用工具。@server.call_tool() 装饰器注册了工具执行回调,当 LLM 决定调用工具时,实际逻辑在此执行。inputSchema 使用 JSON Schema 格式描述工具参数,这与 Function Calling 的参数定义方式一致,但 MCP 把它放在了 Server 侧,由 Server 自主声明。最后通过 stdio_server 以标准输入输出方式运行,Client 连接后自动完成能力协商。
2.3 三大核心能力原语
MCP Server 可以向 Client 暴露三种类型的能力原语(Primitives):
#mermaid-svg-5Thab1c0DN8kEgzI{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-5Thab1c0DN8kEgzI .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-5Thab1c0DN8kEgzI .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-5Thab1c0DN8kEgzI .error-icon{fill:#552222;}#mermaid-svg-5Thab1c0DN8kEgzI .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-5Thab1c0DN8kEgzI .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-5Thab1c0DN8kEgzI .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-5Thab1c0DN8kEgzI .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-5Thab1c0DN8kEgzI .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-5Thab1c0DN8kEgzI .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-5Thab1c0DN8kEgzI .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-5Thab1c0DN8kEgzI .marker{fill:#333333;stroke:#333333;}#mermaid-svg-5Thab1c0DN8kEgzI .marker.cross{stroke:#333333;}#mermaid-svg-5Thab1c0DN8kEgzI svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-5Thab1c0DN8kEgzI p{margin:0;}#mermaid-svg-5Thab1c0DN8kEgzI .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-5Thab1c0DN8kEgzI .cluster-label text{fill:#333;}#mermaid-svg-5Thab1c0DN8kEgzI .cluster-label span{color:#333;}#mermaid-svg-5Thab1c0DN8kEgzI .cluster-label span p{background-color:transparent;}#mermaid-svg-5Thab1c0DN8kEgzI .label text,#mermaid-svg-5Thab1c0DN8kEgzI span{fill:#333;color:#333;}#mermaid-svg-5Thab1c0DN8kEgzI .node rect,#mermaid-svg-5Thab1c0DN8kEgzI .node circle,#mermaid-svg-5Thab1c0DN8kEgzI .node ellipse,#mermaid-svg-5Thab1c0DN8kEgzI .node polygon,#mermaid-svg-5Thab1c0DN8kEgzI .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-5Thab1c0DN8kEgzI .rough-node .label text,#mermaid-svg-5Thab1c0DN8kEgzI .node .label text,#mermaid-svg-5Thab1c0DN8kEgzI .image-shape .label,#mermaid-svg-5Thab1c0DN8kEgzI .icon-shape .label{text-anchor:middle;}#mermaid-svg-5Thab1c0DN8kEgzI .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-5Thab1c0DN8kEgzI .rough-node .label,#mermaid-svg-5Thab1c0DN8kEgzI .node .label,#mermaid-svg-5Thab1c0DN8kEgzI .image-shape .label,#mermaid-svg-5Thab1c0DN8kEgzI .icon-shape .label{text-align:center;}#mermaid-svg-5Thab1c0DN8kEgzI .node.clickable{cursor:pointer;}#mermaid-svg-5Thab1c0DN8kEgzI .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-5Thab1c0DN8kEgzI .arrowheadPath{fill:#333333;}#mermaid-svg-5Thab1c0DN8kEgzI .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-5Thab1c0DN8kEgzI .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-5Thab1c0DN8kEgzI .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5Thab1c0DN8kEgzI .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-5Thab1c0DN8kEgzI .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5Thab1c0DN8kEgzI .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-5Thab1c0DN8kEgzI .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-5Thab1c0DN8kEgzI .cluster text{fill:#333;}#mermaid-svg-5Thab1c0DN8kEgzI .cluster span{color:#333;}#mermaid-svg-5Thab1c0DN8kEgzI div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-5Thab1c0DN8kEgzI .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-5Thab1c0DN8kEgzI rect.text{fill:none;stroke-width:0;}#mermaid-svg-5Thab1c0DN8kEgzI .icon-shape,#mermaid-svg-5Thab1c0DN8kEgzI .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5Thab1c0DN8kEgzI .icon-shape p,#mermaid-svg-5Thab1c0DN8kEgzI .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-5Thab1c0DN8kEgzI .icon-shape .label rect,#mermaid-svg-5Thab1c0DN8kEgzI .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5Thab1c0DN8kEgzI .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-5Thab1c0DN8kEgzI .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-5Thab1c0DN8kEgzI :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} MCP Server 能力原语
🔧 Tools(工具)
📄 Resources(资源)
💬 Prompts(提示模板)
模型可调用的函数
类似 Function Calling
有副作用
可读取的数据源
文件、数据库记录等
只读,无副作用
预定义的提示模板
可参数化的 prompt
引导对话方向
Tools(工具): 这是与 Function Calling 最相似的原语。工具是模型可以主动调用的函数,具有副作用(如修改数据、发起网络请求)。每个工具包含名称、描述和参数 Schema。与 Function Calling 不同的是,工具的定义和实现都在 Server 侧,Client 通过协议动态发现。
Resources(资源): 资源是 Server 暴露的只读数据源,类似于文件系统的概念。Client 可以列出可用资源、读取资源内容。资源用 URI 标识(如 file:///config.json、db://users/schema),适合暴露配置文件、数据库表结构、文档等静态或半静态数据。
Prompts(提示模板): 提示模板是预定义的、可参数化的提示消息。用户可以从 Host UI 中选择并填充模板,引导 LLM 沿特定方向工作。这在"快捷指令"场景中非常有用,比如预定义一个"代码审查"模板,用户只需提供代码即可获得结构化的审查报告。
python
# 展示三种原语的完整 Server 示例
from mcp.server import Server
from mcp.types import (
Tool, Resource, Prompt, PromptArgument,
TextContent, TextResourceContents
)
import mcp.server.stdio
import asyncio
server = Server("full-featured-server")
# ========== 1. Tools ==========
@server.list_tools()
async def list_tools() -> list[Tool]:
return [
Tool(
name="query_database",
description="Execute a SQL query and return results",
inputSchema={
"type": "object",
"properties": {
"sql": {"type": "string", "description": "SQL query"},
"limit": {"type": "integer", "default": 100}
},
"required": ["sql"]
}
),
Tool(
name="send_notification",
description="Send a notification to a user channel",
inputSchema={
"type": "object",
"properties": {
"channel": {"type": "string"},
"message": {"type": "string"}
},
"required": ["channel", "message"]
}
)
]
@server.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
if name == "query_database":
sql = arguments["sql"]
limit = arguments.get("limit", 100)
# 模拟数据库查询
return [TextContent(type="text", text=f"Query: {sql}\nReturned {limit} rows")]
elif name == "send_notification":
channel = arguments["channel"]
message = arguments["message"]
return [TextContent(type="text", text=f"Sent to {channel}: {message}")]
# ========== 2. Resources ==========
@server.list_resources()
async def list_resources() -> list[Resource]:
return [
Resource(
uri="config://app/settings",
name="Application Settings",
description="Current application configuration",
mimeType="application/json"
),
Resource(
uri="schema://database/tables",
name="Database Schema",
description="All table schemas in the database",
mimeType="application/json"
)
]
@server.read_resource()
async def read_resource(uri: str) -> str:
if uri == "config://app/settings":
return '{"theme": "dark", "language": "zh-CN", "max_connections": 50}'
elif uri == "schema://database/tables":
return '{"tables": ["users", "orders", "products"]}'
# ========== 3. Prompts ==========
@server.list_prompts()
async def list_prompts() -> list[Prompt]:
return [
Prompt(
name="code-review",
description="Review code for best practices",
arguments=[
PromptArgument(
name="code",
description="The code to review",
required=True
),
PromptArgument(
name="language",
description="Programming language",
required=False
)
]
)
]
@server.get_prompt()
async def get_prompt(name: str, arguments: dict) -> str:
if name == "code-review":
code = arguments["code"]
lang = arguments.get("language", "auto-detect")
return f"Please review the following {lang} code for best practices, potential bugs, and improvement suggestions:\n\n```\n{code}\n```"
async def main():
async with mcp.server.stdio.stdio_server() as (read, write):
await server.run(read, write, server.create_initialization_options())
if __name__ == "__main__":
asyncio.run(main())
这段代码展示了 MCP Server 的三大原语完整实现。注意几个关键设计:Tools 和 Resources 的区别在于是否有副作用 ------query_database 虽然查询数据但不修改状态,但从协议角度看它仍是 Tool,因为它接受动态参数并执行逻辑;Resource 则是静态数据暴露,URI 是其唯一标识。Prompts 原语是一种独特的设计,它让 Server 可以预置"对话脚本",用户在 Host 的 UI 中看到这些模板,填写参数后,Host 会将模板内容作为用户消息插入对话。这种设计将"专家知识"(如何提问)从应用代码中解耦,让领域专家可以直接在 Server 中定义最佳实践提示。
三、MCP与Function Calling的对比:7个维度差异分析
理解了 MCP 的基本架构后,让我们系统地对比 MCP 与 Function Calling 在 7 个关键维度上的差异。这个对比对于决定何时使用哪种方案至关重要。
| 维度 | Function Calling | MCP |
|---|---|---|
| 架构模式 | 工具定义嵌入应用代码 | 工具作为独立服务进程 |
| 协议标准 | 各厂商私有格式 | 开放标准(JSON-RPC 2.0) |
| 工具发现 | 启动时硬编码 | 运行时动态发现 |
| 模型兼容 | 绑定特定模型 | 模型无关 |
| 工具复用 | 项目内复用 | 跨项目/跨团队复用 |
| 状态管理 | 无状态(每次调用独立) | 有状态(Session 级别) |
| 通信方向 | 单向(模型→应用→模型) | 双向(Client↔Server) |
3.1 架构模式:内嵌 vs 独立服务
Function Calling 中,工具的定义(JSON Schema)和实现(业务逻辑)分散在应用代码中。开发者需要在应用启动时注册所有工具,并在每次模型调用后手动处理 tool call 响应。
MCP 将工具抽离为独立的 Server 进程。Server 自包含工具的定义、实现和能力声明。Host 只需要启动或连接 Server,不需要知道工具的具体实现细节。这种"微服务化"的设计带来了更好的模块化和可维护性。
3.2 协议标准:碎片化 vs 统一标准
python
# 同一个"搜索"工具,三种写法对比
# === OpenAI Function Calling ===
openai_tools = [{
"type": "function",
"function": {
"name": "search",
"description": "Search the web",
"parameters": {
"type": "object",
"properties": {"query": {"type": "string"}},
"required": ["query"]
}
}
}]
# === Anthropic Tool Use ===
anthropic_tools = [{
"name": "search",
"description": "Search the web",
"input_schema": { # 注意:是 input_schema 不是 parameters
"type": "object",
"properties": {"query": {"type": "string"}},
"required": ["query"]
}
}]
# === MCP Server(一次定义,处处可用)===
from mcp.types import Tool
mcp_tool = Tool(
name="search",
description="Search the web",
inputSchema={ # 注意:是 inputSchema
"type": "object",
"properties": {"query": {"type": "string"}},
"required": ["query"]
}
)
# MCP Server 声明后,任何支持 MCP 的 Host 都能使用此工具
这段代码直观展示了碎片化问题。OpenAI 用 parameters,Anthropic 用 input_schema,MCP 用 inputSchema------字段名不同但语义相同。更麻烦的是,调用的 API 端点、请求格式、响应解析逻辑也各不相同。MCP 的价值在于:Server 侧一次定义,任何支持 MCP 协议的 Host 都能发现并调用这个工具,开发者不再需要为每个模型厂商写适配层。注意 MCP 的 Tool 类型用 inputSchema(驼峰命名),这是 JSON-RPC 惯例与 JSON Schema 的结合。
3.3 工具发现:静态 vs 动态
Function Calling 的工具列表在应用启动时确定。如果需要新增工具,开发者必须修改代码、重新部署。MCP 支持运行时动态发现------Client 连接 Server 后,通过 tools/list 请求获取当前可用工具列表。Server 可以根据自身状态动态增减工具,比如在用户登录后暴露更多工具,或在服务降级时隐藏部分工具。
3.4 模型兼容:绑定 vs 无关
Function Calling 工具与模型绑定------为 GPT-4o 写的工具不能直接用在 Claude 上,反之亦然。MCP 的工具定义在 Server 侧,与模型完全解耦。只要 Host 支持 MCP 协议,无论底层用的是 Claude、GPT 还是 Gemini,都能使用同一个 MCP Server 暴露的工具。
3.5 工具复用:项目级 vs 生态级
MCP Server 可以作为独立包发布和分发。比如你可以发布一个 mcp-server-github,其他开发者只需配置连接地址即可在自己的 Agent 中使用 GitHub 工具,无需重复实现。这种"工具市场"效应是 Function Calling 无法实现的。
3.6 状态管理:无状态 vs 有状态会话
Function Calling 的每次工具调用都是无状态的------模型发起调用,应用执行并返回结果,不维护会话状态。MCP 引入了 Session 概念,Client 和 Server 之间的连接是有状态的,Server 可以在会话期间维护上下文(如数据库连接、认证状态等),这使得工具的实现更加高效和安全。
3.7 通信方向:单向 vs 双向
Function Calling 是单向的------模型决定调用工具,应用执行后把结果喂回模型。MCP 支持双向通信------Server 也可以主动向 Client 发送通知(notifications/*),比如在长时间运行的任务中推送进度更新,或者在资源状态变化时通知 Client。
图:MCP 与 Function Calling 在架构模式、工具发现、模型兼容性等维度的系统对比
gpt-image-2 prompt: A detailed side-by-side comparison infographic of MCP (Model Context Protocol) versus Function Calling. Left column in red/orange theme labeled "Function Calling" shows: tools embedded inside application code box, hardcoded tool list at startup, N×M adapter matrix connecting different model vendors (OpenAI, Anthropic, Google) to different tools with tangled crossing lines, a unidirectional arrow from model to application. Right column in blue/green theme labeled "MCP" shows: tools as independent server processes outside the app, dynamic tool discovery via tools/list request, an N+M architecture where all models and tools connect through a central MCP Protocol hub with clean radial lines, bidirectional arrows between client and servers. Center bottom shows a comparison table with 7 rows: Architecture, Protocol Standard, Tool Discovery, Model Compatibility, Tool Reusability, State Management, Communication Direction. Clean white background, professional technical infographic style, 16:9 aspect ratio, every element labeled in English, no empty spaces.
四、MCP消息格式与通信流程详解
4.1 JSON-RPC 2.0 基础
MCP 协议基于 JSON-RPC 2.0 标准,这是一个轻量级的远程过程调用协议。理解 JSON-RPC 2.0 的消息格式是理解 MCP 通信的基础。
json
// 1. 请求消息(Client → Server)
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-03-26",
"capabilities": {
"roots": {
"listChanged": true
},
"sampling": {}
},
"clientInfo": {
"name": "claude-desktop",
"version": "1.0.0"
}
}
}
// 2. 响应消息(Server → Client)
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": "2025-03-26",
"capabilities": {
"tools": {
"listChanged": true
},
"resources": {
"subscribe": true,
"listChanged": true
},
"prompts": {
"listChanged": true
}
},
"serverInfo": {
"name": "my-search-server",
"version": "1.2.0"
}
}
}
// 3. 通知消息(双向,无 id,无需响应)
{
"jsonrpc": "2.0",
"method": "notifications/initialized"
}
JSON-RPC 2.0 的三种消息类型:请求消息 包含 id、method 和 params,发送方期望收到响应;响应消息 包含相同的 id 和 result(成功)或 error(失败);通知消息 没有 id,不需要响应,用于单向事件通知。MCP 的所有通信都基于这三种消息类型。initialize 是连接建立后的第一个请求,Client 和 Server 在此协商协议版本和能力。notifications/initialized 是一个通知,Client 发送给 Server 表示初始化完成,可以开始正常通信。
4.2 完整通信流程
Tool Executor MCP Server MCP Client Host (LLM) Tool Executor MCP Server MCP Client Host (LLM) #mermaid-svg-ISdAe7axXhYhtUYW{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ISdAe7axXhYhtUYW .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ISdAe7axXhYhtUYW .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ISdAe7axXhYhtUYW .error-icon{fill:#552222;}#mermaid-svg-ISdAe7axXhYhtUYW .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ISdAe7axXhYhtUYW .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ISdAe7axXhYhtUYW .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ISdAe7axXhYhtUYW .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ISdAe7axXhYhtUYW .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ISdAe7axXhYhtUYW .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ISdAe7axXhYhtUYW .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ISdAe7axXhYhtUYW .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ISdAe7axXhYhtUYW .marker.cross{stroke:#333333;}#mermaid-svg-ISdAe7axXhYhtUYW svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ISdAe7axXhYhtUYW p{margin:0;}#mermaid-svg-ISdAe7axXhYhtUYW .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-ISdAe7axXhYhtUYW text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-ISdAe7axXhYhtUYW .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-ISdAe7axXhYhtUYW .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-ISdAe7axXhYhtUYW .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-ISdAe7axXhYhtUYW .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-ISdAe7axXhYhtUYW #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-ISdAe7axXhYhtUYW .sequenceNumber{fill:white;}#mermaid-svg-ISdAe7axXhYhtUYW #sequencenumber{fill:#333;}#mermaid-svg-ISdAe7axXhYhtUYW #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-ISdAe7axXhYhtUYW .messageText{fill:#333;stroke:none;}#mermaid-svg-ISdAe7axXhYhtUYW .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-ISdAe7axXhYhtUYW .labelText,#mermaid-svg-ISdAe7axXhYhtUYW .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-ISdAe7axXhYhtUYW .loopText,#mermaid-svg-ISdAe7axXhYhtUYW .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-ISdAe7axXhYhtUYW .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-ISdAe7axXhYhtUYW .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-ISdAe7axXhYhtUYW .noteText,#mermaid-svg-ISdAe7axXhYhtUYW .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-ISdAe7axXhYhtUYW .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-ISdAe7axXhYhtUYW .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-ISdAe7axXhYhtUYW .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-ISdAe7axXhYhtUYW .actorPopupMenu{position:absolute;}#mermaid-svg-ISdAe7axXhYhtUYW .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-ISdAe7axXhYhtUYW .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-ISdAe7axXhYhtUYW .actor-man circle,#mermaid-svg-ISdAe7axXhYhtUYW line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-ISdAe7axXhYhtUYW :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 阶段1: 连接初始化 阶段2: 能力发现 阶段3: 工具调用 阶段4: 资源订阅(可选) 阶段5: 状态变更通知 initialize(protocolVersion, capabilities, clientInfo) result(protocolVersion, capabilities, serverInfo) notifications/initialized "用户要搜索数据库" tools/list result({name, description, inputSchema}...) LLM决定调用 query_database(sql="SELECT * FROM users") tools/call(name="query_database", arguments={sql: "SELECT * FROM users"}) 执行SQL查询 查询结果 result({content: {type: "text", text: "..."}}) 返回工具结果 LLM处理结果并生成回复 resources/subscribe(uri="config://app/settings") 请求确认 notifications/resources/updated(uri="config://app/settings") resources/read(uri="config://app/settings") result({contents: {uri, mimeType, text}})
整个 MCP 通信流程分为五个阶段:连接初始化 阶段,Client 和 Server 协商协议版本和各自支持的能力。Client 在 initialize 请求中声明自己支持的 capability(如 roots 表示可以提供文件系统根目录,sampling 表示可以让 Server 发起 LLM 采样请求),Server 在响应中声明自己支持的原语(tools、resources、prompts)。能力发现 阶段,Client 调用 tools/list 获取 Server 暴露的所有工具,这些工具会被组装成 LLM 可以理解的格式注入到对话上下文中。工具调用 阶段,当 LLM 决定调用某个工具时,Host 通过 Client 向 Server 发送 tools/call 请求,Server 执行实际逻辑并返回结果。资源订阅 和状态变更通知是可选的高级特性,允许 Client 订阅特定资源的变化,Server 在资源更新时主动推送通知。
4.3 工具调用的完整生命周期
python
# 模拟 MCP Client 端的完整工具调用生命周期
import json
import subprocess
import asyncio
class MCPSimulatedClient:
"""模拟 MCP Client 的完整通信流程"""
def __init__(self, server_command: list[str]):
self.server_command = server_command
self.process = None
self.request_id = 0
self.initialized = False
self.server_capabilities = {}
self.server_info = {}
async def connect(self):
"""启动 Server 并完成初始化握手"""
self.process = subprocess.Popen(
self.server_command,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
# 阶段1: 发送 initialize 请求
init_response = await self._send_request("initialize", {
"protocolVersion": "2025-03-26",
"capabilities": {
"roots": {"listChanged": True},
"sampling": {}
},
"clientInfo": {
"name": "my-agent-client",
"version": "1.0.0"
}
})
self.server_capabilities = init_response.get("capabilities", {})
self.server_info = init_response.get("serverInfo", {})
print(f"[Client] Connected to {self.server_info.get('name', 'unknown')}")
print(f"[Client] Server capabilities: {list(self.server_capabilities.keys())}")
# 阶段2: 发送 initialized 通知
self._send_notification("notifications/initialized")
self.initialized = True
async def list_tools(self) -> list[dict]:
"""获取 Server 暴露的工具列表"""
response = await self._send_request("tools/list", {})
return response.get("tools", [])
async def call_tool(self, name: str, arguments: dict) -> dict:
"""调用指定工具"""
response = await self._send_request("tools/call", {
"name": name,
"arguments": arguments
})
return response
async def list_resources(self) -> list[dict]:
"""获取 Server 暴露的资源列表"""
response = await self._send_request("resources/list", {})
return response.get("resources", [])
async def read_resource(self, uri: str) -> str:
"""读取指定资源内容"""
response = await self._send_request("resources/read", {"uri": uri})
contents = response.get("contents", [])
if contents:
return contents[0].get("text", "")
return ""
async def _send_request(self, method: str, params: dict) -> dict:
"""发送 JSON-RPC 请求并等待响应"""
self.request_id += 1
message = {
"jsonrpc": "2.0",
"id": self.request_id,
"method": method,
"params": params
}
line = json.dumps(message) + "\n"
self.process.stdin.write(line)
self.process.stdin.flush()
# 读取响应行
response_line = self.process.stdout.readline()
response = json.loads(response_line)
if "error" in response:
raise Exception(f"RPC Error: {response['error']}")
return response.get("result", {})
def _send_notification(self, method: str, params: dict = None):
"""发送 JSON-RPC 通知(无需响应)"""
message = {
"jsonrpc": "2.0",
"method": method
}
if params:
message["params"] = params
line = json.dumps(message) + "\n"
self.process.stdin.write(line)
self.process.stdin.flush()
async def close(self):
"""关闭连接"""
if self.process:
self.process.terminate()
self.process.wait()
# 使用示例
async def demo():
client = MCPSimulatedClient(["python", "search_server.py"])
try:
# 1. 连接并初始化
await client.connect()
# 2. 发现工具
tools = await client.list_tools()
print(f"Available tools: {[t['name'] for t in tools]}")
# 3. 调用工具
result = await client.call_tool("search", {"query": "MCP protocol"})
print(f"Search result: {result}")
# 4. 读取资源
resources = await client.list_resources()
print(f"Available resources: {[r['uri'] for r in resources]}")
if resources:
content = await client.read_resource(resources[0]["uri"])
print(f"Resource content: {content[:200]}")
finally:
await client.close()
# asyncio.run(demo())
这段代码模拟了 MCP Client 端的完整通信流程。MCPSimulatedClient 类封装了 JSON-RPC 2.0 协议的核心逻辑:connect() 方法启动 Server 子进程并通过 initialize 请求完成握手;list_tools() 和 call_tool() 分别对应能力发现和工具调用;_send_request 是底层通信方法,它将 JSON-RPC 消息写入 Server 的 stdin,并从 stdout 读取响应。注意 request_id 是自增的,JSON-RPC 2.0 要求每个请求有唯一 ID,响应通过匹配 ID 关联请求。实际生产中应使用 MCP SDK 提供的 Client 类,它处理了异步 I/O、错误重试、超时等细节。这里手动实现是为了展示协议的底层细节。
五、MCP生态现状:已支持的工具、平台和框架
5.1 MCP 生态全景
自 2024 年 11 月发布以来,MCP 生态经历了爆发式增长。截至 2025 年中,已有超过 200 个 MCP Server 实现,覆盖了主流的开发工具、云服务和企业系统。
| 类别 | 代表项目 | 说明 |
|---|---|---|
| 开发工具 | GitHub MCP, GitLab MCP | 仓库管理、PR、Issue 操作 |
| 文件系统 | Filesystem MCP | 本地文件读写、目录遍历 |
| 数据库 | PostgreSQL MCP, SQLite MCP | SQL 查询、Schema 浏览 |
| 云服务 | AWS MCP, Cloudflare MCP | 云资源管理 |
| 搜索引擎 | Brave Search MCP, Google Search MCP | Web 搜索 |
| 协作工具 | Slack MCP, Notion MCP | 消息发送、文档管理 |
| 编程语言 | Python MCP, Node.js MCP | SDK 和运行时 |
| Agent 框架 | LangChain, AutoGen, CrewAI | Agent 编排集成 |
5.2 Host 端支持情况
python
# 在 Claude Desktop 中配置 MCP Server
# 配置文件路径:
# macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
# Windows: %APPDATA%\Claude\claude_desktop_config.json
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"/Users/username/projects",
"/Users/username/documents"
]
},
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "ghp_xxxxxxxxxxxx"
}
},
"postgres": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-postgres",
"postgresql://localhost:5432/mydb"
]
}
}
}
这是 Claude Desktop 的 MCP Server 配置示例。每个 Server 配置包含 command(启动命令)和 args(参数)。npx -y 会自动安装并运行对应的 MCP Server npm 包。env 字段可以设置环境变量,常用于传递 API Token。配置完成后重启 Claude Desktop,它会自动启动这些 Server,用户可以在对话框中直接使用 GitHub、文件系统和 PostgreSQL 数据库的工具,Claude 会自动决定何时调用哪个工具。注意路径参数是 Server 的启动参数,比如 filesystem Server 接受多个目录路径作为允许访问的根目录。
5.3 SDK 和框架集成
python
# 使用 LangChain 集成 MCP Server
from langchain_mcp_adapters.client import MultiServerMCPClient
from langchain_anthropic import ChatAnthropic
from langchain.agents import create_tool_calling_agent
from langchain_core.prompts import ChatPromptTemplate
import asyncio
async def create_mcp_agent():
# 配置多个 MCP Server
client = MultiServerMCPClient({
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"],
"transport": "stdio"
},
"search": {
"url": "http://localhost:8080/sse",
"transport": "sse"
}
})
# 动态获取所有 Server 的工具
tools = await client.get_tools()
print(f"Loaded {len(tools)} tools from MCP servers")
for tool in tools:
print(f" - {tool.name}: {tool.description[:60]}")
# 创建 Agent
llm = ChatAnthropic(model="claude-3-5-sonnet-20241022")
prompt = ChatPromptTemplate.from_messages([
("system", "You are a helpful assistant with access to file and search tools."),
("human", "{input}"),
("placeholder", "{agent_scratchpad}")
])
agent = create_tool_calling_agent(llm, tools, prompt)
return agent
async def main():
agent = await create_mcp_agent()
# 使用 Agent
result = await agent.ainvoke({
"input": "Search for files containing 'budget' in /tmp and show me their contents"
})
print(result["output"])
# asyncio.run(main())
这段代码展示了 LangChain 与 MCP 的集成方式。MultiServerMCPClient 可以同时连接多个 MCP Server------stdio 传输的本地 Server 和 SSE 传输的远程 Server 可以混合配置。get_tools() 方法会向所有已连接的 Server 发送 tools/list 请求,汇总所有工具并转换为 LangChain 的 Tool 格式。这意味着你不需要手动在 LangChain 中定义每个工具,MCP Server 的工具会自动注入。当 LangChain Agent 决定调用某个工具时,langchain-mcp-adapters 会自动将调用路由到正确的 MCP Server。这种集成方式让 Agent 的能力扩展变得极其简单------只需在配置中添加一个新的 MCP Server,Agent 就自动获得了新工具。
5.4 TypeScript/Node.js SDK
typescript
// 使用 TypeScript SDK 创建 MCP Server
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
const server = new Server(
{ name: "weather-server", version: "1.0.0" },
{ capabilities: { tools: {} } }
);
// 注册工具列表
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: "get_weather",
description: "Get current weather for a city",
inputSchema: {
type: "object",
properties: {
city: { type: "string", description: "City name" },
units: { type: "string", enum: ["celsius", "fahrenheit"], default: "celsius" }
},
required: ["city"]
}
}
]
}));
// 注册工具调用处理
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
if (name === "get_weather") {
const city = args.city as string;
const units = (args.units as string) || "celsius";
// 模拟天气 API 调用
const weather = {
city,
temperature: units === "celsius" ? 22 : 72,
condition: "Sunny",
humidity: 45
};
return {
content: [
{
type: "text",
text: `Weather in ${city}: ${weather.temperature}°${units === "celsius" ? "C" : "F"}, ${weather.condition}, Humidity: ${weather.humidity}%`
}
]
};
}
throw new Error(`Unknown tool: ${name}`);
});
// 启动 Server
const transport = new StdioServerTransport();
await server.connect(transport);
这是使用 TypeScript SDK 实现 MCP Server 的示例。与 Python SDK 类似,通过 setRequestHandler 注册不同 JSON-RPC 方法的处理器。ListToolsRequestSchema 和 CallToolRequestSchema 是 SDK 提供的类型验证器,确保请求参数符合 MCP 规范。StdioServerTransport 负责底层的 stdio 通信,开发者只需关注业务逻辑。TypeScript SDK 的优势在于类型安全------工具定义、请求处理、响应构造都有完整的类型提示,这对大型项目尤为重要。
六、为什么MCP是Agent的"USB接口":标准化带来的生态效应
6.1 USB 的历史启示
1996 年,USB 1.0 规范发布时,个人电脑后面板上有 PS/2、串口、并口、SCSI 等十几种不同的接口。每个外设都需要专用接口和驱动程序,用户体验极差。USB 的出现统一了这一切------一个接口、一套协议、即插即用。
MCP 之于 AI Agent,正如 USB 之于个人电脑。在 MCP 之前,每个 Agent 框架都有自己的工具集成方式,每个模型厂商都有自己的函数调用格式。MCP 通过定义统一的协议,让工具开发者和 Agent 开发者可以独立工作,只要双方都遵循 MCP 标准,就能即插即用。
6.2 标准化的网络效应
#mermaid-svg-eipx4Xg8EEmXS2Zy{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-eipx4Xg8EEmXS2Zy .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-eipx4Xg8EEmXS2Zy .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-eipx4Xg8EEmXS2Zy .error-icon{fill:#552222;}#mermaid-svg-eipx4Xg8EEmXS2Zy .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-eipx4Xg8EEmXS2Zy .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-eipx4Xg8EEmXS2Zy .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-eipx4Xg8EEmXS2Zy .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-eipx4Xg8EEmXS2Zy .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-eipx4Xg8EEmXS2Zy .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-eipx4Xg8EEmXS2Zy .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-eipx4Xg8EEmXS2Zy .marker{fill:#333333;stroke:#333333;}#mermaid-svg-eipx4Xg8EEmXS2Zy .marker.cross{stroke:#333333;}#mermaid-svg-eipx4Xg8EEmXS2Zy svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-eipx4Xg8EEmXS2Zy p{margin:0;}#mermaid-svg-eipx4Xg8EEmXS2Zy .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-eipx4Xg8EEmXS2Zy .cluster-label text{fill:#333;}#mermaid-svg-eipx4Xg8EEmXS2Zy .cluster-label span{color:#333;}#mermaid-svg-eipx4Xg8EEmXS2Zy .cluster-label span p{background-color:transparent;}#mermaid-svg-eipx4Xg8EEmXS2Zy .label text,#mermaid-svg-eipx4Xg8EEmXS2Zy span{fill:#333;color:#333;}#mermaid-svg-eipx4Xg8EEmXS2Zy .node rect,#mermaid-svg-eipx4Xg8EEmXS2Zy .node circle,#mermaid-svg-eipx4Xg8EEmXS2Zy .node ellipse,#mermaid-svg-eipx4Xg8EEmXS2Zy .node polygon,#mermaid-svg-eipx4Xg8EEmXS2Zy .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-eipx4Xg8EEmXS2Zy .rough-node .label text,#mermaid-svg-eipx4Xg8EEmXS2Zy .node .label text,#mermaid-svg-eipx4Xg8EEmXS2Zy .image-shape .label,#mermaid-svg-eipx4Xg8EEmXS2Zy .icon-shape .label{text-anchor:middle;}#mermaid-svg-eipx4Xg8EEmXS2Zy .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-eipx4Xg8EEmXS2Zy .rough-node .label,#mermaid-svg-eipx4Xg8EEmXS2Zy .node .label,#mermaid-svg-eipx4Xg8EEmXS2Zy .image-shape .label,#mermaid-svg-eipx4Xg8EEmXS2Zy .icon-shape .label{text-align:center;}#mermaid-svg-eipx4Xg8EEmXS2Zy .node.clickable{cursor:pointer;}#mermaid-svg-eipx4Xg8EEmXS2Zy .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-eipx4Xg8EEmXS2Zy .arrowheadPath{fill:#333333;}#mermaid-svg-eipx4Xg8EEmXS2Zy .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-eipx4Xg8EEmXS2Zy .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-eipx4Xg8EEmXS2Zy .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-eipx4Xg8EEmXS2Zy .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-eipx4Xg8EEmXS2Zy .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-eipx4Xg8EEmXS2Zy .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-eipx4Xg8EEmXS2Zy .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-eipx4Xg8EEmXS2Zy .cluster text{fill:#333;}#mermaid-svg-eipx4Xg8EEmXS2Zy .cluster span{color:#333;}#mermaid-svg-eipx4Xg8EEmXS2Zy div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-eipx4Xg8EEmXS2Zy .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-eipx4Xg8EEmXS2Zy rect.text{fill:none;stroke-width:0;}#mermaid-svg-eipx4Xg8EEmXS2Zy .icon-shape,#mermaid-svg-eipx4Xg8EEmXS2Zy .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-eipx4Xg8EEmXS2Zy .icon-shape p,#mermaid-svg-eipx4Xg8EEmXS2Zy .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-eipx4Xg8EEmXS2Zy .icon-shape .label rect,#mermaid-svg-eipx4Xg8EEmXS2Zy .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-eipx4Xg8EEmXS2Zy .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-eipx4Xg8EEmXS2Zy .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-eipx4Xg8EEmXS2Zy :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} MCP 之后:N+M 问题
模型 A
MCP 协议
模型 B
模型 C
工具 1
工具 2
工具 3
MCP 之前:N×M 问题
模型 A
工具 1
工具 2
工具 3
模型 B
模型 C
在 MCP 之前,如果有 N 个模型和 M 个工具,需要 N×M 个适配器。每新增一个模型,需要为所有 M 个工具写适配;每新增一个工具,需要为所有 N 个模型写适配。这是一个 O(N×M) 复杂度的问题,随着生态增长会变得不可维护。
MCP 将问题降维为 N+M:N 个模型各自实现 MCP Client(Host 侧),M 个工具各自实现 MCP Server。新增模型只需实现 MCP Client,新增工具只需实现 MCP Server,两边无需感知对方的存在。这种标准化带来的网络效应是 MCP 最核心的价值主张。
6.3 实际案例:从零到有的工具集成
python
# 传统方式:为一个 Agent 手动集成 5 个工具
# 每个工具需要:1) 写函数定义 2) 写执行逻辑 3) 注册到 Agent 4) 处理模型返回
class TraditionalAgent:
def __init__(self):
self.tools = {
"search_web": self._search_web,
"read_file": self._read_file,
"query_db": self._query_db,
"send_email": self._send_email,
"create_ticket": self._create_ticket,
}
self.tool_definitions = [
{"name": "search_web", "description": "...", "parameters": {...}},
{"name": "read_file", "description": "...", "parameters": {...}},
{"name": "query_db", "description": "...", "parameters": {...}},
{"name": "send_email", "description": "...", "parameters": {...}},
{"name": "create_ticket", "description": "...", "parameters": {...}},
]
# 每个工具都要手写定义、实现和注册...
def _search_web(self, query: str): ...
def _read_file(self, path: str): ...
def _query_db(self, sql: str): ...
def _send_email(self, to: str, subject: str, body: str): ...
def _create_ticket(self, title: str, description: str): ...
# MCP 方式:同样的 5 个工具,只需配置连接
mcp_config = {
"mcpServers": {
"search": {"command": "npx", "args": ["-y", "@mcp/server-brave-search"]},
"filesystem": {"command": "npx", "args": ["-y", "@mcp/server-filesystem", "/workspace"]},
"postgres": {"command": "npx", "args": ["-y", "@mcp/server-postgres", DB_URL]},
"email": {"command": "npx", "args": ["-y", "@mcp/server-sendgrid"]},
"jira": {"command": "npx", "args": ["-y", "@mcp/server-jira"]},
}
}
# 工具的定义、实现、发现全部由 MCP Server 处理
# Agent 只需要连接这些 Server,工具会自动出现
对比两种方式:传统方式中,每个工具都需要在 Agent 代码中定义函数签名、实现执行逻辑、注册到工具列表,5 个工具意味着大量重复代码。MCP 方式中,Agent 只需在配置文件中声明 5 个 MCP Server 的启动命令,所有工具的定义、实现、发现都由各自的 Server 处理。更关键的是,如果第 6 个工具(比如"创建日历事件")需要加入,传统方式需要修改 Agent 代码并重新部署,而 MCP 方式只需在配置中加一行------这就是标准化带来的即插即用效应。
6.4 工具市场的前景
MCP 的标准化正在催生一个"工具市场"。开发者可以像发布 npm 包一样发布 MCP Server,其他开发者只需一行配置就能在自己的 Agent 中使用。Anthropic 维护了一个官方的 MCP Server 仓库(@modelcontextprotocol/servers),社区也在积极贡献各种 Server 实现。这种"工具即服务"的模式,正在改变 Agent 开发的范式。
核心洞察: MCP 的价值不在于协议本身有多精妙,而在于它让工具变成了可分发、可复用、可组合的独立组件。就像 npm 之于 JavaScript 生态,MCP 正在成为 AI Agent 工具生态的基础设施。
图:MCP 生态系统中已支持的 Server、Host、SDK 及框架集成全景
gpt-image-2 prompt: A sprawling ecosystem map diagram of the MCP (Model Context Protocol) landscape. Center shows a glowing hexagonal MCP Protocol hub. Radiating outward in concentric rings: Inner ring labeled "Hosts" shows logos/cards for Claude Desktop, Cursor IDE, VS Code Copilot, and a custom Agent app. Middle ring labeled "SDKs & Frameworks" shows Python SDK, TypeScript SDK, LangChain integration, AutoGen, CrewAI cards. Outer ring labeled "MCP Servers (200+)" shows categorized server cards grouped by color: Development Tools (GitHub, GitLab), File System, Databases (PostgreSQL, SQLite), Cloud Services (AWS, Cloudflare), Search (Brave, Google), Collaboration (Slack, Notion). Connection lines from each ring element connect to the central MCP hub. A statistics panel in the corner shows "200+ Servers, 4+ Hosts, 2 Official SDKs, 5+ Framework Integrations". Dark gradient background from deep blue to purple, with glowing cyan connection lines, professional tech ecosystem map style, 16:9 aspect ratio, all text in English, no empty spaces, every card has an icon and label.
七、适用边界与风险提示
7.1 MCP 不是银弹
MCP 在解决工具调用标准化问题的同时,也引入了新的复杂性。在以下场景中,MCP 可能不是最佳选择:
场景一:简单的单模型应用。 如果你的 Agent 只用一个模型、两三个工具,且不打算复用这些工具,直接使用 Function Calling 更简单。MCP 的 Server/Client 架构引入了进程间通信的开销和调试复杂性,对于简单应用是过度工程。
场景二:极低延迟要求。 MCP 的 JSON-RPC 通信和进程间通信会引入额外延迟。stdio 传输通常增加 1-5ms 延迟,SSE 传输可能增加 50-200ms。对于需要毫秒级响应的场景(如实时交易),这个延迟不可接受。
场景三:高度定制化的工具调用逻辑。 如果你的工具调用需要复杂的中间件逻辑(如自定义重试策略、多步事务、工具间依赖编排),MCP 的标准协议可能不够灵活。你仍然需要在应用层实现这些逻辑。
7.2 安全风险
python
# MCP 安全配置最佳实践
mcp_security_config = {
# 1. 最小权限原则:Server 只暴露必要的工具
"server_config": {
"filesystem": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
# 只暴露特定目录,不要暴露整个文件系统
"/home/user/projects/safe-directory"
# 绝对不要: "/"
]
}
},
# 2. 环境变量管理:敏感信息通过环境变量传递
"env_management": {
"GITHUB_TOKEN": "${GITHUB_TOKEN}", # 从系统环境变量读取
"DATABASE_URL": "${DATABASE_URL}",
# 绝对不要在配置文件中硬编码 token
},
# 3. 网络隔离:远程 Server 使用 HTTPS
"remote_servers": {
"api_gateway": {
"url": "https://internal-api.company.com/mcp",
"transport": "streamable-http",
"headers": {
"Authorization": "Bearer ${MCP_AUTH_TOKEN}"
}
}
}
}
# 4. 工具调用审计日志
import logging
logger = logging.getLogger("mcp_audit")
def audit_tool_call(server_name: str, tool_name: str, arguments: dict):
"""记录所有工具调用,用于安全审计"""
logger.info(json.dumps({
"timestamp": "2025-01-15T10:30:00Z",
"server": server_name,
"tool": tool_name,
"arguments": _sanitize_arguments(arguments),
"user": "current_user_id"
}))
def _sanitize_arguments(args: dict) -> dict:
"""脱敏处理:隐藏密码、token 等敏感参数"""
sensitive_keys = ["password", "token", "secret", "api_key"]
sanitized = {}
for k, v in args.items():
if any(sk in k.lower() for sk in sensitive_keys):
sanitized[k] = "***REDACTED***"
else:
sanitized[k] = v
return sanitized
MCP 的安全配置需要关注三个层面。第一是 Server 的能力边界 :filesystem Server 只应暴露工作目录而非根目录,数据库 Server 应使用只读账户。第二是凭证管理 :API Token 应通过环境变量或密钥管理服务传递,绝不应硬编码在配置文件中。第三是审计追踪:所有工具调用应记录审计日志,包括调用时间、Server 名称、工具名称和参数(敏感信息脱敏)。MCP 协议本身不提供认证和授权机制------这是设计选择而非缺陷,MCP 将安全责任交给 Host 和部署环境。在生产环境中,你应该在 MCP Client 和 Server 之间添加认证层(如 mTLS 或 OAuth)。
7.3 性能考量
| 因素 | 影响 | 缓解策略 |
|---|---|---|
| 进程启动开销 | 每个 stdio Server 是独立进程 | 使用长连接,避免频繁启停 |
| JSON 序列化 | 每次请求/响应都需序列化 | 减少大数据传递,使用分页 |
| 能力发现延迟 | 首次连接需 list tools | 缓存工具列表,监听变更通知 |
| 并发限制 | JSON-RPC 2.0 请求串行处理 | 使用批量请求或多个连接 |
| 网络延迟 | 远程 Server 的通信延迟 | 就近部署,使用 CDN |
7.4 版本兼容性风险
MCP 规范仍在快速迭代中。从 2024 年 11 月的初始版本到 2025 年 3 月版本,协议已经经历了多次变更。不同版本的 SDK 和 Server 之间可能存在兼容性问题。建议在生产环境中锁定特定的协议版本和 SDK 版本,并在升级前进行充分测试。
八、总结
MCP(Model Context Protocol)作为 Anthropic 在 2024 年底开源发布的标准化协议,正在以前所未有的速度重塑 AI Agent 的工具调用生态。回顾全文,我们看到了三个层面的核心价值:
协议层面 ,MCP 基于 JSON-RPC 2.0 定义了 Host/Client/Server 三层架构,通过 initialize → tools/list → tools/call 的标准通信流程,实现了工具的动态发现和调用。三种原语(Tools、Resources、Prompts)覆盖了 Agent 与外部系统交互的主要模式。
架构层面,MCP 将工具从应用代码中抽离为独立服务,解决了 Function Calling 的三大碎片化痛点:协议不统一、工具不可复用、缺乏动态发现。N+M 替代 N×M 的网络效应使得工具市场和工具复用成为可能。
生态层面,Claude Desktop、Cursor、LangChain 等主流平台和框架已全面支持 MCP,社区维护的 Server 实现超过 200 个,覆盖开发工具、数据库、云服务、协作平台等主要场景。这个生态仍在快速增长中。
但 MCP 并非银弹。对于简单应用,Function Calling 仍然是更轻量的选择。MCP 引入的进程间通信、安全配置、版本管理等复杂性需要被认真对待。正确的判断是:当你的 Agent 需要集成多个外部工具、需要跨模型复用工具、或者需要让非开发人员贡献工具能力时,MCP 的标准化收益远大于其引入的复杂性。
在下一篇《实现 MCP Server》中,我们将从零开始构建一个完整的 MCP Server,包括项目搭建、工具定义、测试和发布的全流程。如果你在阅读本文后想要动手实践,那篇文章会是最好的起点。
参考资料
-
Model Context Protocol 规范 --- Anthropic, 2025-03-26 版本
-
MCP 官方文档 --- Model Context Protocol
-
MCP 官方 Server 仓库 --- GitHub
-
MCP Python SDK --- GitHub
-
MCP TypeScript SDK --- GitHub
-
Introducing MCP --- Anthropic Blog, 2024-11-25
-
LangChain MCP Adapters --- LangChain Documentation
-
JSON-RPC 2.0 规范 --- JSON-RPC Working Group
-
Claude Desktop MCP 配置指南 --- Anthropic Documentation
-
本系列第 08 篇:《Function Calling深度解析》