Appearance
MCP 协议
Agent 系统发展到一定阶段会遇到一个瓶颈:内置工具再多,也覆盖不了所有用户的需求。有人需要接入自己公司的数据库,有人需要操作 Notion,有人需要调用内部 API——这些都不是通用工具库能预装的。
MCP(Model Context Protocol,模型上下文协议)解决的就是这个问题:定义一套标准协议,让任何人都能把自己的服务、数据源、工具包装成 MCP Server,然后接入支持 MCP 的 AI 客户端,无需修改客户端本身。
本章目标
- 理解 MCP 是什么,它解决的核心问题是什么
- 掌握 MCP 的三类能力:工具、资源、提示词
- 知道 MCP Server 和 MCP Client 各自做什么
- 了解 MCP 配置的作用域和实际使用方式
MCP 是什么
MCP 是 Anthropic 于 2024 年底提出的开放协议,目标是标准化「AI 模型与外部数据源/工具之间的通信方式」。
在 MCP 之前,每个 AI 应用都要自己写集成代码:接 Slack 要写 Slack 客户端,接数据库要写数据库驱动,接代码仓库要写 GitHub API 调用。每次集成都是重复造轮子,也导致 AI 工具生态碎片化——A 家的 AI 写的工具,B 家的 AI 用不了。
MCP 的思路类似 USB 接口:定义一个通用插口,符合规格的设备都能连。MCP Server 提供能力,MCP Client(Claude Code、Claude Desktop、Cursor 等)消费能力,中间用标准协议通信。
MCP 本身既非新模型,也非工具库。它是一套接口规范,目标是让工具生态从专有集成走向标准接口。
但 MCP 真正要解决的问题比"多接几个工具"更根本——它要解决的是能力的标准化外部化。
没有协议时,AI 客户端和每个外部系统之间都是一对一集成。工具能力被锁死在具体产品里,迁移成本高,生态也没法共享。MCP 把工具集成从"产品内部实现细节"拉到了"跨产品可复用的协议层"。
MCP 能提供的三类能力
MCP Server 可以向客户端暴露三类东西:
1. 工具(Tools)
功能型能力,模型可以主动调用。和 Tool Calling 里的工具概念一样,但工具定义来自外部 Server,不是 AI 客户端内置的。
示例:
- 数据库查询工具(执行 SQL,返回结果)
- 文件上传工具(把本地文件传到指定平台)
- 发送 Slack 消息工具
- 调用内部 API 的工具
模型调用 MCP 工具的方式和调用内置工具完全一样——它从工具描述里理解这个工具做什么,然后用结构化参数调用,等待结果。
2. 资源(Resources)
数据型能力,可以被读取但不执行操作。资源是只读的上下文数据,比工具更轻量。
示例:
- 项目文档
- 代码库 README
- 数据库 Schema
- 配置文件
模型读取资源,相当于读取一段背景信息,用于辅助回答或执行任务。
3. 提示词模板(Prompts)
预定义的 prompt 模板,可以带参数,由用户触发。这类能力更像斜杠命令——用户选择一个预制的 prompt,填入变量,由 MCP Server 渲染成完整的 prompt 再发给模型。
示例:
/code-review→ 生成标准化代码审查 prompt/summarize-tickets→ 生成汇总 Jira 工单的 prompt
这三类能力的拆分非常值得注意,因为它们对应的是三种完全不同的使用语义:
- Tool:要执行,要产生动作或查询结果
- Resource:只读,只提供上下文材料
- Prompt:不直接执行,而是帮助组织用户和模型之间的任务描述
MCP 在协议层就把能力类型分开了,没有把所有外部能力粗暴地塞成"工具"。这个分层会直接影响安全边界、调用方式和用户体验。
MCP Server 和 MCP Client 的分工
MCP Server(服务提供方):
- 实现具体的工具、资源或提示词
- 用标准协议对外暴露能力
- 独立部署,和 AI 客户端解耦
- 任何语言都可以写:Python、TypeScript、Go……
MCP Client(能力消费方):
- AI 应用(Claude Code、Claude Desktop、Cursor 等)
- 连接 MCP Server,获取可用工具列表
- 把 MCP 工具透明地注入到 AI 模型的工具调用流程里
- 处理权限管理、连接状态
从模型视角看,MCP 工具和内置工具没有区别——只看到工具名称、描述和参数 Schema,不知道工具是内置的还是来自外部 Server。连接复杂性封装在协议层,模型和用户都不需要感知。
MCP 的核心价值:解耦
Client 不再需要知道每个外部系统的接入细节,Server 也不需要为每个 AI 客户端单独适配一套接口。协议层把两边解开之后,能力就能独立演进:
- 客户端升级自己的模型和交互界面
- Server 升级自己的业务逻辑和能力
- 双方只要仍然说同一种协议,就能继续协作
很多基础设施协议最终带来的价值都是这样的:接入的每个工具都变成了一个可被多个客户端共享的能力节点。
传输方式
MCP 协议支持多种传输方式,适应不同部署场景(来自 Claude Code v2.1.88 源码 services/mcp/types.ts):
| 传输类型 | 场景 |
|---|---|
stdio | 本地子进程,最常见的本地工具场景 |
sse | HTTP Server-Sent Events,适合远程服务 |
http | 标准 HTTP,适合 REST 风格的远程 Server |
ws | WebSocket,适合需要双向实时通信的场景 |
最简单也最常用的是 stdio:MCP Client 启动一个本地子进程作为 Server,通过标准输入/输出通信。配置只需要写明命令和参数:
json
{
"mcpServers": {
"my-tool": {
"command": "npx",
"args": ["-y", "my-mcp-server-package"]
}
}
}不同传输方式看起来只是部署差异,实际上也意味着信任边界和运维复杂度不同。
stdio更适合本地、可信、低延迟场景http/sse/ws更适合远程服务,但也会引入认证、网络抖动、重连和版本兼容等问题
所以选传输方式,不只是看"能不能连上",还要看这项能力是更像本地插件,还是更像远程基础设施。
配置作用域
MCP Server 的配置不只有一个位置,Claude Code 定义了多层作用域(来自 services/mcp/types.ts):
| 作用域 | 含义 |
|---|---|
local | 只对当前项目生效,通常在项目目录的配置文件里 |
user | 用户全局,对该用户的所有项目生效 |
project | 项目级,团队共享 |
enterprise | 企业托管,由管理员统一下发 |
dynamic | 运行时动态注册,不写入配置文件 |
多层作用域让公司可以在企业层面强制启用某些 MCP Server(比如内部数据库工具),用户也可以在个人层面加自己的工具,两者不冲突。
这背后其实是在解决另一类问题:谁有权决定模型能接入哪些外部能力。
对企业来说这其实是个治理问题。MCP Server 一旦能连数据库、消息系统、文档平台,AI 的能力边界就已经被扩展了。作用域分层,本质上是在给这种能力扩展做权限归属划分。
MCP 的连接状态管理
MCP 连接不是一次性的——Server 需要持续运行,Client 需要处理连接失败、重连、认证等情况。Claude Code 对每个 MCP Server 的连接状态做了明确的类型区分(来自 services/mcp/types.ts):
connected:正常连接,可以调用工具pending:连接中,包括重连尝试次数needs-auth:需要授权才能连接(支持 OAuth)failed:连接失败,记录了错误信息disabled:被手动禁用
MCP 集成不是「加个配置就好」。网络不稳定、认证过期、Server 崩溃都是真实场景,连接状态管理是工程实现里绕不开的部分。
MCP 是一层运行时基础设施
到了生产环境你会发现,MCP 远不只是静态工具目录,它更像一层活的运行时基础设施。客户端需要持续面对这些问题:
- 这个 Server 现在连得上吗
- 需要重新认证吗
- 当前暴露的工具和资源有没有变化
- 多个 Server 的命名和描述会不会冲突
- 出故障时是降级、隐藏,还是让用户显式感知
所以成熟客户端都会单独做连接管理、状态展示和权限控制,MCP 配置项背后有一整套运行时机制在支撑。
从 Claude Code 看 MCP 的实际使用
Claude Code 把 MCP 深度集成进了整个工具系统。当 MCP Server 连接成功后,它的工具会被合并进 Claude Code 的工具列表,模型可以像调用内置工具一样调用它们。
几个细节值得留意:
工具命名规范化:MCP 工具的名称会经过标准化处理(normalization.ts),确保和内置工具名称格式一致,不会因为 Server 命名风格不同而混乱。
Worker 也能用 MCP 工具:在多 Agent 场景下,Coordinator 的系统提示词里会明确告知 Worker 当前有哪些 MCP Server 可用,Worker 也可以直接调用 MCP 工具,不需要通过 Coordinator 中转。
官方注册表:Claude Code 有一个 officialRegistry.ts,维护了经过认证的 MCP Server 列表。用户可以从这个注册表直接安装 Server,而不用手动配置命令和参数。
插件也可以提供 MCP Server:Claude Code 的插件系统支持插件内置 MCP Server 配置,安装一个插件,就同时获得了它携带的 MCP 工具。
MCP 和普通 Tool Calling 的关系
| 维度 | 普通 Tool Calling | MCP |
|---|---|---|
| 工具来源 | 开发者在代码里定义 | 外部 Server 动态提供 |
| 扩展方式 | 修改应用代码 | 连接新的 MCP Server |
| 工具复用 | 每个应用单独实现 | 一个 Server 供多个客户端使用 |
| 部署 | 和应用一起打包 | 独立部署,可以是远程服务 |
| 标准化 | 无统一标准 | MCP 协议规范 |
写一个 MCP Server,可以同时被 Claude Code、Claude Desktop、Cursor 等支持 MCP 的工具使用。
MCP 和 Tool Calling 的关系:标准化扩展层
学过 Tool Calling 的话,可以这样理解分工:Tool Calling 管的是模型怎么决定调哪个工具、传什么参数;MCP 管的是这些工具怎样以统一协议从外部接进来。
简单说,Tool Calling 偏模型侧行为,MCP 偏系统侧扩展性。两者配合,才有了今天"模型像调内置工具一样调用外部生态能力"的体验。
什么时候不需要 MCP
如果你的项目只有两三个工具,全部都在同一个后端服务里,而且只服务一个 AI 应用,直接用 Function Calling 往往更简单。比如一个聊天助手里内置 get_weather、search_docs、create_ticket 三个函数,工具定义、权限和业务逻辑都在同一个代码库里,没必要先抽成 MCP Server。
MCP 更适合这些场景:
- 同一批工具要被 Claude Code、Cursor、内部 AI 平台同时使用
- 工具背后接的是独立系统,比如数据库、工单、文档平台或企业内部 API
- 工具需要由另一个团队维护,客户端只负责消费能力
- 工具列表会动态变化,不适合每次都改 AI 应用代码再发布
一个实用判断是:如果工具只是当前应用的内部函数,用 Function Calling 就够;如果工具已经像一项可复用服务,就值得考虑 MCP。
把已有 REST API 包成 MCP Server
很多团队接 MCP 时不需要重写业务系统。已有 REST API 可以继续作为内部实现层,MCP Server 只是外面新加的一层协议适配。
最小改造路径是:
- 选出适合暴露给 Agent 的 API,不要一口气把整套内部系统都暴露出来
- 为每个能力写清楚工具名、描述和参数 Schema
- 在 MCP Server 的工具处理函数里调用原来的 REST API
- 在 Server 端做参数校验、鉴权和结果过滤
- 把这个 MCP Server 注册到客户端配置里
结构上可以这样理解:
text
AI Client → MCP Server → 现有 REST API → 内部系统一个极简的 TypeScript 结构如下,真实项目里还要补认证、错误处理和日志:
typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const server = new McpServer({ name: "orders-api", version: "1.0.0" });
server.tool(
"get-order",
"根据订单号查询订单状态,只返回状态、金额和更新时间",
{ orderId: z.string().min(1) },
async ({ orderId }) => {
const res = await fetch(`https://internal.example.com/orders/${orderId}`, {
headers: { Authorization: `Bearer ${process.env.INTERNAL_API_TOKEN}` },
});
const order = await res.json();
return {
content: [
{
type: "text",
text: JSON.stringify({
status: order.status,
amount: order.amount,
updatedAt: order.updatedAt,
}),
},
],
};
}
);
await server.connect(new StdioServerTransport());这段代码里最关键的不是 fetch,而是只返回必要字段。内部 API 可能有手机号、地址、支付流水、客服备注,但 Agent 查询订单状态时不一定需要看到这些。MCP Server 应该做一次能力收口,而不是把内部 API 原样透传给模型。
MCP Server 的安全边界
MCP 把工具接入变容易了,也把安全责任推到了更明确的位置:Server 端必须假设来自模型的参数不可信。
几个底线最好一开始就做:
- 认证放在传输层或 Server 入口层处理,远程 MCP Server 不能默认裸奔
- 工具按最小权限暴露,一个 Server 不一定要把所有内部 API 都注册成工具
- 参数在 Server 端重新校验,不能因为模型给了 JSON 就直接拼 SQL、拼路径或拼 URL
- 写操作默认需要额外确认或幂等 key,避免模型重试导致重复副作用
- 返回结果先脱敏,尤其是 token、手机号、邮箱、内部备注和调试日志
对于 stdio 本地 Server,安全问题更偏本机权限:它能读哪些文件、能执行哪些命令、环境变量里有没有密钥。对于 http / sse 远程 Server,重点会变成认证、租户隔离、限流和审计。传输方式不同,风险位置也不同。
自己动手:最简单的 MCP Server
用 TypeScript 写一个最小的 MCP Server,只需要:
typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
const server = new McpServer({ name: "my-server", version: "1.0.0" });
server.tool("say-hello", "向某人打招呼", { name: z.string() }, async ({ name }) => ({
content: [{ type: "text", text: `你好,${name}!` }],
}));
const transport = new StdioServerTransport();
await server.connect(transport);这段代码就是一个可以被 Claude Code 使用的 MCP Server。工具的 description 字段是模型决定何时调用它的唯一依据,和写 System Prompt 是同一个道理。
容易踩的坑
Server 挂了,Agent 不报警:MCP Server 如果因为网络问题断开,模型可能只是看到工具调用失败,不会主动解释"是 Server 断了"。要做好 Server 的监控和重连。
工具描述写得太模糊:MCP 工具的 description 是模型选择调用的唯一线索,写「数据库工具」不如写「查询用户订单记录,返回最近 30 天的订单列表」。
权限没有边界:MCP Server 能做的事情,模型就能让它做。如果你的 Server 接了生产数据库,一定要在 Server 层做只读限制,不能只靠 prompt 约束。
版本不兼容:MCP 协议还在持续演进,不同版本的客户端和 Server 之间可能存在兼容性问题,接入前要核查版本要求。
安全责任到底落在哪一层
这是接 MCP 时特别需要想清楚的一件事。很多人会下意识觉得:"客户端不是会做权限控制吗,那我 Server 端是不是可以简单一点?" 这个想法很危险。
稳妥的做法是:客户端和 Server 各自负责自己的安全边界。
- Client 负责连接管理、用户确认、权限展示、能力暴露策略
- Server 负责真正的数据访问边界、只读限制、参数校验、审计和鉴权
因为一旦 Server 本身权限过大、缺乏约束,客户端即使提醒得再好,也只是把风险提前告知,而没有真正收住。
回头看:MCP 作为工程能力意味着什么
到这里可以整理一下 MCP 在工程上到底带来了什么。
MCP 最核心的价值在于把能力从产品私有集成拉到了协议级标准。一个 MCP Server 写好,多个客户端都能用,这才是生态能真正跑起来的前提。
Tools、Resources、Prompts 三类能力的拆分也不是随意的,它直接决定了外部能力以什么语义进入系统——该执行的执行,该只读的只读,该组织对话的组织对话。
传输方式的选择看起来是部署问题,但背后是信任边界和运维复杂度的取舍。stdio 和 http 不只是"本地 vs 远程",更是"可信 vs 需要认证和重连"。
还有一点容易忽视:MCP 接入之后,外部服务就成了系统运行时的一部分。状态管理和故障处理省不了,安全责任也要客户端和 Server 两边各自扛住自己那部分。
和其他章节的关系
- Tool Calling:MCP 工具的调用方式和普通 Tool Calling 完全一致,读懂了 Tool Calling 就读懂了 MCP 的模型侧。
- 多 Agent 协调:Worker 可以使用 MCP 工具,MCP 扩展了多 Agent 系统能调用的外部能力边界。
- AI 应用系统设计:在系统设计层面,MCP Server 是一个独立部署的微服务,需要考虑其稳定性、版本管理和权限控制。
常见面试考点
MCP 相关题目通常会围绕“它和普通工具调用到底差在哪”展开。可以按这几条线准备:
- MCP vs Function Calling:Function Calling 描述模型如何调用工具,MCP 描述外部工具和数据源如何标准化接入客户端。
- 三类能力:Tools 会执行动作,Resources 提供只读上下文,Prompts 组织任务模板;三者的安全边界不同。
- Client / Server 分工:Client 负责连接、展示和权限体验,Server 负责真实的数据访问、参数校验、鉴权和审计。
- 传输方式选择:stdio 适合本地可信工具,http / sse / ws 更适合远程服务,但要额外处理认证、重连和运维。
- 安全机制:不要只靠 prompt 约束,Server 端必须做只读限制、参数范围校验、最小权限和日志审计。
- 生态价值:MCP 的意义不是多一个工具格式,而是让同一个外部能力可以被多个 AI 客户端复用。
下一章:A2A 协议——MCP 解决 Agent 如何接入工具和数据源,A2A 则处理 Agent 如何发现并调用另一个 Agent。