MCP 协议深度拆解:一个开放标准如何终结 AI Agent 的「接口巴别塔」
2024 年 11 月,Anthropic 开源了 MCP(Model Context Protocol),当时多数人把它当作又一个"XX 协议"——每年 AI 圈都会冒出几个协议标准,大部分活不过三个月。一年半后的今天,MCP 的 GitHub 仓库已经积累了数万 Star,Claude Desktop、WorkBuddy、Cursor 等主流工具全面支持,社区贡献的 MCP Server 覆盖了数据库、文件系统、浏览器、API 网关等几乎所有常见场景。甚至有文章将 MCP、A2A、ACP 并称为 Agent 互操作领域的"三大协议",讨论它们之间的竞争与融合。
这一切发生得太快。快到很多人还没搞清楚 MCP 到底是什么、解决了什么问题、和已有的 Function Calling 有什么区别,它就已经成为 AI 基础设施的一部分了。
这篇文章的目的是把 MCP 讲透——不是产品功能介绍,而是从协议设计者的视角出发,拆解它的架构、抽象层次和设计取舍。读完你会理解:为什么 MCP 有资格被称为 AI Agent 时代的 USB-C,以及这个类比背后真正的技术内涵。
01 问题起源:AI Agent 时代的接口巴别塔
要理解 MCP 解决了什么问题,先回到 MCP 诞生之前的世界。
2023 年到 2024 年初,各种 LLM 应用和 Agent 框架如雨后春笋般涌现。每个框架都在做同一件事——让模型能够调用外部工具。但实现方式千奇百怪:
- OpenAI 定义了 Function Calling 的 JSON Schema 规范,模型输出特定格式的 JSON,应用层解析后执行对应函数
- LangChain 封装了自己的 Tools 抽象,用
@tool装饰器注册函数,内部做了一层序列化 - Claude 在 API 层直接支持 Tool Use,格式与 OpenAI 类似但细节不同
- 自研 Agent 框架则各搞一套——有的用 YAML 配置工具,有的用 Python 类继承,有的用 gRPC 远程调用
更麻烦的是,每一套工具集成都是紧耦合的。给 LangChain 写的工具只能给 LangChain 用;为 Claude Desktop 写的插件没法迁移到别的平台;企业内部的数据查询接口要分别适配三个 Agent 平台,维护三套几乎相同的代码。
这就是经典的"接口巴别塔"问题。每个基建方都在造自己的接口标准,不是因为大家技术能力不够,而是因为缺少一个被广泛认可的公共协议。
MCP 不是重新发明工具调用。它做的事情更底层:定义了一套 Client-Server 通信协议,让工具提供方(Server)和工具消费方(Client/Host)可以解耦开发。
下面这张图概括了 MCP 的核心价值——把 N×M 的适配矩阵变成 N+M:
一句话总结:工具只需实现一次 MCP Server,所有支持 MCP 的 Host 都能即插即用。 这就是 USB-C 类比的含义。USB-C 的价值不在于它比 Micro-USB 多几个引脚,而在于它统一了显示器、充电器、数据线、外设的物理接口,让"兼容性"从需要反复确认的事变成理所当然的事。MCP 在 AI 工具集成领域想做同样的事情。
02 MCP 的架构设计:为什么是 Client-Server?
理解了"why",接下来看"how"——MCP 的架构选了哪条路,以及为什么选这条路。
2.1 整体架构
MCP 的架构可以用三个角色概括:
- MCP Host:用户实际使用的 AI 应用,如 Claude Desktop、WorkBuddy、Cursor。它负责管理多个 MCP Client 实例,将 MCP Server 的能力呈现给模型。
- MCP Client:运行在 Host 内部,与一个特定的 MCP Server 维持一对一的连接。Client 负责序列化请求、管理连接生命周期、处理错误重连。
- MCP Server:独立运行的服务进程,对外暴露 Tools、Resources、Prompts 三种能力。Server 不需要知道是谁在调用它,只需要实现 MCP 协议规范。
这种架构的关键设计选择有两点:
第一,Server 是独立进程,不是库。 这意味着 MCP Server 可以用任何语言实现(Python、TypeScript、Go、Rust…),只要它能通过 stdio 或 HTTP 与 Client 通信。你公司已有的 Go 微服务、Python 脚本、Rust CLI 工具,都能包装成 MCP Server,不需要重写。
第二,Client 与 Server 是一对一的关系,但 Host 管理多个 Client。 这意味着不同的能力由不同的 Server 提供,互不干扰。数据库查询崩溃不会影响文件读写,权限隔离天然实现。
2.2 传输层设计:stdio 优先,HTTP 补充
MCP 支持两种传输方式,有明确的定位差异:
| 维度 | stdio | Streamable HTTP (SSE) |
|---|---|---|
| 进程模型 | Server 作为子进程启动 | Server 独立运行,Client 主动连接 |
| 适用场景 | 本地工具、桌面应用 | 远程服务、微服务架构 |
| 延迟 | 极低(进程间通信) | 网络延迟 |
| 安全性 | 天然进程隔离 | 需要额外的鉴权机制 |
| 复杂度 | 简单,无需网络配置 | 需要配置端口、CORS、鉴权 |
| 典型用例 | 本地文件操作、CLI 工具 | 企业内部 API 网关、数据库代理 |
stdio 优先是一个务实的决策。大多数开发者的 MCP Server 运行在本地机器上,不需要网络开销和安全配置。启动一个子进程、通过标准输入输出传递 JSON-RPC 消息,是最简单也最可靠的方案。
// MCP 使用 JSON-RPC 2.0 作为消息格式
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "read_file",
"arguments": {
"path": "/home/user/document.md"
}
}
}
需要远程场景时,切换到 Streamable HTTP 也很简单——MCP 在协议层做了传输无关的设计,Server 开发的业务逻辑代码完全不需要修改。
2.3 生命周期管理
MCP 的连接生命周期分为三个阶段,每个阶段都有明确的协议消息:
┌──────────────────────────────────────────────────┐
│ MCP 连接生命周期 │
│ │
│ 1. 初始化阶段 (Initialization) │
│ Client ── initialize ──► Server │
│ Client ◄── capabilities ── Server │
│ Client ── initialized ──► Server │
│ │
│ 2. 运行阶段 (Operation) │
│ Client ── tools/list ──► Server │
│ Client ◄── tool list ── Server │
│ Client ── tools/call ──► Server │
│ Client ◄── result ── Server │
│ Client ── resources/read ──► Server │
│ Client ◄── content ── Server │
│ │
│ 3. 关闭阶段 (Shutdown) │
│ Client/Host 终止进程或关闭连接 │
│ │
└──────────────────────────────────────────────────┘
初始化阶段,Client 和 Server 互相告知自己的能力和协议版本。这让 MCP 具备了前向兼容的基础——未来的 MCP 版本如果 Server 不支持,Client 可以在初始化时就检测到,优雅降级而不是运行时崩溃。
03 三层抽象深度拆解
MCP 的核心设计精华,浓缩在它向外暴露的三个原语中:Tools、Resources、Prompts。这三个概念构成了 MCP Server 的"能力界面",决定了模型能做什么、能读到什么、能以什么方式交互。
3.1 Tools:让模型「动手」
Tools 是 MCP 最核心的抽象。一个 Tool 本质上是一个模型可通过结构化参数调用、并获得结构化返回值的函数。它的定义包含三个关键部分:
- name:唯一标识符,模型用它来指定"我要调用哪个工具"
- description:自然语言描述,模型根据它来判断"这个工具是不是我需要的"
- inputSchema:JSON Schema 格式的参数定义,告诉模型"这个工具需要哪些参数、每个参数的类型和含义"
以下是一个完整的 MCP Tool 定义示例——一个 SQLite 数据库查询工具:
# MCP Server 端的 Tool 定义
{
"name": "query_database",
"description": "在 SQLite 数据库中执行 SELECT 查询,返回结果。"
"仅支持 SELECT 语句,不支持 INSERT/UPDATE/DELETE 等写操作。",
"inputSchema": {
"type": "object",
"properties": {
"sql": {
"type": "string",
"description": "要执行的 SQL SELECT 查询语句"
},
"params": {
"type": "array",
"items": {"type": "string"},
"description": "SQL 查询的参数列表,按顺序对应 ? 占位符"
}
},
"required": ["sql"]
}
}
这里有一个重要的设计细节:description 字段不仅仅给人看,更是模型做决策的关键依据。 模型的工具选择逻辑大致是这样的——收到用户指令后,模型会浏览所有可用 Tool 的 name 和 description,用语义匹配来决定调用哪个。description 写得模糊,模型就可能选错工具;description 写得精确,模型的决策质量会显著提升。
一个常见的误区是把 description 写得过于简短(如 “Query database”),导致模型在多个选项之间犹豫不决,反复尝试错误工具。好的 description 应当包含:工具做什么、什么情况下用、有什么限制、返回什么。
Tool 调用的完整流程如下:
注意这个流程中的关键节点:Server 收到调用请求后,有完整的自主权来决定是否执行、如何执行。 它可以在执行前做安全检查(SQL 语句是否包含危险操作)、做限流(一分钟最多调用多少次)、做审计日志。MCP 没有把安全责任推给 Host,而是让 Server 成为安全的第一道防线。
3.2 Resources:让模型「查阅」
如果说 Tools 是模型的"手"——执行操作、改变状态——那么 Resources 就是模型的"眼"——读取数据、获取上下文。
Resource 的定位和 Tool 有本质区别:
| 维度 | Tool | Resource |
|---|---|---|
| 语义 | 执行操作,产生副作用 | 读取数据,无副作用 |
| 调用方式 | 模型主动选择调用 | 模型或 Host 主动拉取 |
| 参数 | JSON Schema 定义结构化参数 | URI 模板定义资源地址 |
| 典型用例 | 发送邮件、查询数据库、创建文件 | 读取文档、获取 schema、查看日志 |
| 幂等性 | 不保证 | 保证幂等 |
Resources 使用 URI 来标识。MCP 支持两种 Resource 访问模式:
静态 Resource:URI 固定,内容固定。例如 config://app-settings 指向应用的运行时配置,每次读取返回相同的数据结构。
动态 Resource(URI 模板):URI 包含参数。例如 docs://users/{user_id},传入不同的 user_id 返回不同用户的文档。MCP 使用 RFC 6570 URI 模板标准:
{
"uri": "db://tables/{table_name}/schema",
"name": "数据库表结构",
"description": "返回指定数据表的结构信息,包括列名、类型、约束",
"mimeType": "application/json"
}
模型或 Host 在需要时可以用 resources/read 方法拉取这个资源:
// 请求
{
"method": "resources/read",
"params": {
"uri": "db://tables/orders/schema"
}
}
// 响应
{
"contents": [{
"uri": "db://tables/orders/schema",
"mimeType": "application/json",
"text": "{\"table\":\"orders\",\"columns\":[{\"name\":\"id\",\"type\":\"INTEGER\"},{\"name\":\"amount\",\"type\":\"REAL\"},{\"name\":\"date\",\"type\":\"TEXT\"}]}"
}]
}
Resources 的设计巧妙之处在于它是拉取模型而非推送模型。Server 不去猜测模型需要什么数据,而是把数据编目好、用 URI 组织好,让模型和 Host 按需来取。这避免了 Context Window 被无用的数据占据,同时保持了最大的灵活性。
3.3 Prompts:让模型「懂得怎么问」
Prompts 是三层抽象中最容易被忽视的一个,但它的设计意图值得仔细揣摩。
Prompts 是 预定义的、参数化的对话模板,存储在 Server 端。当 Host 通过 prompts/list 发现 Server 提供了某些 Prompt 模板后,可以将其展示给用户或模型作为"快捷入口"。
{
"name": "code_review",
"description": "对此代码文件进行代码审查,关注安全问题、性能问题和最佳实践",
"arguments": [
{
"name": "language",
"description": "编程语言",
"required": true
},
{
"name": "code",
"description": "要审查的代码内容",
"required": true
}
]
}
当用户选择这个 Prompt 模板并填入参数后,Server 返回完整的提示文本,Host 将其注入模型的上下文:
你是一位资深 {language} 代码审查专家。请认真审查以下代码:
{code}
请从以下维度进行分析:
1. 安全漏洞(SQL 注入、XSS、权限问题等)
2. 性能问题(不必要的循环、重复查询、内存泄漏)
3. 最佳实践偏离(命名规范、错误处理、代码结构)
4. 可维护性问题(过长的函数、过深的嵌套、重复代码)
对每个问题,请提供具体的代码行号和修改建议。
Prompts 解决了一个微妙但广泛存在的问题:用户和模型之间的"需求翻译"鸿沟。 一个有经验的 Prompt Engineer 知道怎么措辞能拿到最好的结果,但普通用户不一定会。把经过验证的 Prompt 模板嵌入 Server 端,等于让工具提供方把"怎么用好这个工具"的知识也一并打包了。
从架构角度看,Prompts 不是 MCP 中最关键的部分——即使没有 Prompts,只靠 Tools 和 Resources 也能构建完整功能。但它体现了 MCP 设计者的一种态度:协议不应该只关心"能做什么",还应该关心"怎么做更好"。
04 三足鼎立:MCP 与 A2A、ACP 的定位与关系
聊完 MCP 内部的设计,必须把镜头拉远,看看 2025-2026 年整个 Agent 协议领域的全景图。
4.1 三大协议速览
| 维度 | MCP | A2A | ACP |
|---|---|---|---|
| 提出方 | Anthropic | 社区/多厂商 | |
| 核心场景 | AI 应用 ↔ 外部工具/数据 | Agent ↔ Agent 协作 | Agent 通信基础设施 |
| 类比 | USB-C(外设接口) | HTTP REST(服务间通信) | TCP/IP(通信底层) |
| 抽象层次 | 工具调用 + 数据访问 | 任务委托 + 结果交付 | 消息路由 + 身份认证 |
| 当前状态 | 生产可用,生态蓬勃 | 规范草案,早期落地 | 讨论阶段 |
| 传输层 | stdio / HTTP SSE | HTTP + JSON | 待定 |
4.2 核心差异:纵向集成 vs 横向协作
MCP 和 A2A 的核心差异可以用一个简单的类比来理解:
MCP 解决的是「人 + AI」如何访问工具和数据的问题。 它关注的是垂直方向的整合——一个 AI 应用(Host)如何向下调用 N 个外部能力(MCP Server)。就像 USB-C 统一了电脑和外设的连接方式,MCP 统一了 AI 应用和外部工具的连接方式。
A2A 解决的是「AI + AI」如何协作的问题。 它关注的是水平方向的协作——多个 Agent 之间如何发现彼此、委托任务、交换结果。就像 HTTP REST 统一了微服务之间的通信方式,A2A 试图统一 Agent 之间的通信方式。
MCP(纵向集成) A2A(横向协作)
AI Application
│
├── MCP ── 数据库 Agent A ── A2A ── Agent B
├── MCP ── 文件系统 (任务委托) (执行并返回)
├── MCP ── GitHub API
└── MCP ── 邮件服务 Agent A ── A2A ── Agent C
(查询) (提供数据)
这两种场景不矛盾,反而是互补的。一个复杂的 Agent 系统很可能同时需要 MCP 和 A2A:MCP 让每个 Agent 能调用工具、访问数据;A2A 让多个 Agent 能协作完成复杂任务。 两者解决的是不同层次的问题。
4.3 不是战争,是生态收敛
2026 年初有一些公众号文章渲染"A协议大战""Agent 协议三国杀"的叙事,这种说法有一些误导性。MCP、A2A、ACP 不是互相竞争的替代品,而是在不同层面解决不同问题的互补协议。更准确的描述是:Agent 互操作协议生态正在从「百花齐放」进入「关键玩家浮出水面」的收敛期。
对于开发者来说,务实的选择是:
- 如果你在做 AI 应用的工具集成——关注 MCP,它是当前最成熟的方案
- 如果你在做多 Agent 系统的协作——关注 A2A,但可以先观望其成熟度
- 如果只是内部项目,先用 MCP 把单 Agent 的工具集成做好,多 Agent 协作的需求通常没那么早到来
05 实战:30 分钟构建一个 SQLite MCP Server
理论讲再多,不如动手写一个。下面用 Python 实现一个功能完整的 SQLite MCP Server,支持查询和表结构浏览。
5.1 项目结构
mcp-sqlite-server/
server.py # MCP Server 主程序
pyproject.toml # 项目配置
5.2 完整实现
# server.py
"""
SQLite MCP Server
提供数据库查询(SELECT)和表结构浏览能力。
使用 stdio 传输,实现标准的 MCP 协议。
"""
import json
import sys
import sqlite3
from typing import Any
class SQLiteMCPServer:
"""SQLite MCP Server - 实现 tools/list, tools/call, resources/list, resources/read"""
def __init__(self, db_path: str = "data.db"):
self.db_path = db_path
def handle_request(self, request: dict) -> dict | None:
"""处理 JSON-RPC 请求,返回响应或 None(通知消息不返回)"""
method = request.get("method", "")
req_id = request.get("id")
params = request.get("params", {})
# 路由到对应处理器
handlers = {
"initialize": self.handle_initialize,
"tools/list": self.handle_tools_list,
"tools/call": self.handle_tools_call,
"resources/list": self.handle_resources_list,
"resources/read": self.handle_resources_read,
}
if method in handlers:
try:
result = handlers[method](params)
return self._response(req_id, result)
except Exception as e:
return self._error(req_id, -32000, str(e))
# ping 通知不需要回复
if method == "notifications/initialized":
return None
return self._error(req_id, -32601, f"Unknown method: {method}")
def handle_initialize(self, params: dict) -> dict:
"""初始化握手:声明 Server 能力"""
return {
"protocolVersion": "2024-11-05",
"capabilities": {
"tools": {},
"resources": {},
},
"serverInfo": {
"name": "sqlite-mcp-server",
"version": "1.0.0",
},
}
def handle_tools_list(self, params: dict) -> dict:
"""返回可用的 Tool 列表"""
return {
"tools": [
{
"name": "query_database",
"description": (
"在 SQLite 数据库中执行 SELECT 查询,返回结果。"
"仅支持 SELECT 语句。结果以 JSON 数组形式返回,"
"每行是一个对象,键名为列名。最多返回 100 行。"
),
"inputSchema": {
"type": "object",
"properties": {
"sql": {
"type": "string",
"description": "要执行的 SQL SELECT 查询语句",
}
},
"required": ["sql"],
},
},
{
"name": "list_tables",
"description": "列出数据库中所有的表名",
"inputSchema": {
"type": "object",
"properties": {},
},
},
]
}
def handle_tools_call(self, params: dict) -> dict:
"""执行 Tool 调用"""
tool_name = params.get("name", "")
arguments = params.get("arguments", {})
if tool_name == "query_database":
return self._execute_query(arguments.get("sql", ""))
elif tool_name == "list_tables":
return self._list_tables()
else:
raise ValueError(f"Unknown tool: {tool_name}")
def handle_resources_list(self, params: dict) -> dict:
"""返回可用的 Resource 列表"""
return {
"resources": [
{
"uri": "db://tables",
"name": "所有数据表",
"description": "数据库中所有表名列表",
"mimeType": "application/json",
}
]
}
def handle_resources_read(self, params: dict) -> dict:
"""读取指定 Resource"""
uri = params.get("uri", "")
if uri == "db://tables":
tables = self._get_all_tables()
return {
"contents": [
{
"uri": uri,
"mimeType": "application/json",
"text": json.dumps(tables, ensure_ascii=False, indent=2),
}
]
}
# 动态 URI 模板: db://tables/{table_name}/schema
if uri.startswith("db://tables/") and uri.endswith("/schema"):
table_name = uri[len("db://tables/"):-len("/schema")]
schema = self._get_table_schema(table_name)
return {
"contents": [
{
"uri": uri,
"mimeType": "application/json",
"text": json.dumps(schema, ensure_ascii=False, indent=2),
}
]
}
raise ValueError(f"Unknown resource: {uri}")
# --- 内部实现 ---
def _execute_query(self, sql: str) -> dict:
"""安全执行 SELECT 查询"""
sql_stripped = sql.strip().upper()
if not sql_stripped.startswith("SELECT"):
raise ValueError("仅支持 SELECT 查询语句")
if any(
keyword in sql_stripped
for keyword in ["INSERT", "UPDATE", "DELETE", "DROP", "ALTER", "CREATE"]
):
raise ValueError("不允许执行写操作或 DDL 语句")
conn = sqlite3.connect(self.db_path)
conn.row_factory = sqlite3.Row
try:
cursor = conn.execute(sql)
rows = [dict(row) for row in cursor.fetchmany(100)]
return {
"content": [
{
"type": "text",
"text": json.dumps(rows, ensure_ascii=False, default=str),
}
]
}
finally:
conn.close()
def _list_tables(self) -> dict:
tables = self._get_all_tables()
return {
"content": [
{
"type": "text",
"text": json.dumps(tables, ensure_ascii=False),
}
]
}
def _get_all_tables(self) -> list[str]:
conn = sqlite3.connect(self.db_path)
try:
cursor = conn.execute(
"SELECT name FROM sqlite_master WHERE type='table' ORDER BY name"
)
return [row[0] for row in cursor.fetchall()]
finally:
conn.close()
def _get_table_schema(self, table_name: str) -> dict:
conn = sqlite3.connect(self.db_path)
try:
cursor = conn.execute(f"PRAGMA table_info({table_name})")
columns = [
{
"name": row[1],
"type": row[2],
"nullable": not row[3],
"default": row[4],
"primary_key": bool(row[5]),
}
for row in cursor.fetchall()
]
return {"table": table_name, "columns": columns}
finally:
conn.close()
# --- JSON-RPC 辅助方法 ---
def _response(self, req_id: int, result: dict) -> dict:
return {"jsonrpc": "2.0", "id": req_id, "result": result}
def _error(self, req_id: int | None, code: int, message: str) -> dict:
return {
"jsonrpc": "2.0",
"id": req_id,
"error": {"code": code, "message": message},
}
def main():
"""主循环:从 stdin 读取 JSON-RPC 请求,写入 stdout"""
server = SQLiteMCPServer(db_path="data.db")
for line in sys.stdin:
line = line.strip()
if not line:
continue
try:
request = json.loads(line)
response = server.handle_request(request)
if response is not None:
sys.stdout.write(json.dumps(response, ensure_ascii=False) + "\n")
sys.stdout.flush()
except json.JSONDecodeError:
# 非 JSON 行,忽略
pass
if __name__ == "__main__":
main()
5.3 关键设计决策解读
这个 150 行的实现中,有几个设计决策值得单独拿出来聊:
安全优先的 SQL 白名单。 _execute_query 方法没有简单地把 SQL 语句透传给 SQLite 执行,而是先检查语句是否以 SELECT 开头,并拒绝包含 INSERT/UPDATE/DELETE/DROP/ALTER/CREATE 的语句。这层白名单校验在前,SQLite 的执行在后——MCP Server 是安全的第一道防线,不应该依赖调用方来保证安全。
内容格式使用标准 MIME type。 resources/read 的返回内容标注了 mimeType: "application/json",这让 Host 端能够正确解析和展示返回数据,而不是把 JSON 当纯文本显示给模型。
错误处理分层。 最外层 handle_request 捕获所有异常并转为 JSON-RPC 错误响应,确保 Server 不会因为某个工具的异常而崩溃连接。每个工具内部也可以抛出业务异常,统一由外层处理。
5.4 在 Claude Desktop 中配置
写完 Server 后,在 Claude Desktop 的配置文件中注册:
{
"mcpServers": {
"sqlite": {
"command": "python",
"args": ["/path/to/mcp-sqlite-server/server.py"]
}
}
}
配置完成后重启 Claude Desktop,模型就能自动发现并使用 query_database 和 list_tables 两个工具。整个过程不需要写任何胶水代码——MCP Client 自动完成了工具发现、参数验证和结果解析。
06 总结与展望
核心要点回顾
- MCP 解决了 “N×M 适配矩阵” 问题。 在 MCP 之前,每个 AI 应用都要为每个工具单独写适配代码。MCP 用 Client-Server + 标准协议把 N×M 变成 N+M,让工具实现一次即可随处使用。
- 三层抽象各司其职。 Tools 负责"动手"(执行操作),Resources 负责"查阅"(获取数据),Prompts 负责"懂得怎么问"(预置交互模板)。三者共同构成了一个完整的"能力界面"。
- stdio 优先是务实的工程选择。 对绝大多数本地工具场景,子进程通信比网络通信更简单、更快、更安全。HTTP 作为远程场景的补充,而非默认选项。
- MCP、A2A、ACP 是互补关系而非竞争关系。 MCP 做纵向集成(应用到工具),A2A 做横向协作(Agent 到 Agent),ACP 做通信基础设施。它们解决的是不同层次的问题。
- MCP Server 可以很简单。 150 行 Python 代码就能实现一个功能完整的 MCP Server。协议的复杂度集中在规范文档里,落到代码上并不重。
MCP 当前面临的挑战
MCP 生态虽然蓬勃,但远未成熟。几个当前的主要挑战:
安全性仍是薄弱环节。 stdio 传输模式下的子进程隔离看似安全,但 HOST 应用本身的权限过大——Claude Desktop 如果被授予文件系统 MCP Server 的全部权限,一次错误的工具调用就可能删除重要文件。更细粒度的权限控制(按工具、按资源 URI、按操作类型)是下一步必须完善的。
工具发现和去重。 当用户装了 10 个 MCP Server,每个 Server 都暴露了不同的 Tools 和 Resources,模型如何快速判断"该调用哪个"成为一个非平凡问题。当多个 Server 提供了相似的工具(如两个不同的 query_database),现有的基于文本描述的匹配机制还不够稳健。
生态碎片化的风险。 MCP 的开放性既是优势也是隐患。如果不同平台对 MCP 规范的实现有细微差异(如 JSON-RPC 的错误码约定、工具调用的超时行为),可能产生新的"方言"碎片——就像 SQL 标准一样,每个数据库都有自己的"扩展"。
你应该现在就开始用 MCP 吗?
答案是分情况的:
如果你的团队在构建 AI 应用的工具集成层——现在就值得投入。MCP 的标准化降低了客户切换工具的成本,也为你的工具打开了更多分发渠道。
如果你在评估技术选型——MCP 是最务实的选择。它已经有足够的社区基础和工具生态,协议设计经过了一年半的生产验证,在三个协议中成熟度最高。
如果你在观望——至少花一小时按照本文第 5 节的教程写一个 MCP Server。动手比看文档有用得多——你会发现很多"看起来复杂"的设计选择,写到代码里其实很自然。
延伸阅读:
- MCP 官方规范 —— 协议定义的权威来源,200 页不到,建议通读一遍
- MCP Server 官方示例库 —— 涵盖文件系统、PostgreSQL、GitHub、Slack 等常见场景的参考实现
- Google A2A 协议介绍 —— 理解 MCP 的互补协议,有助于建立全局视角
更多推荐
所有评论(0)