Skip to content

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本地子进程,最常见的本地工具场景
sseHTTP Server-Sent Events,适合远程服务
http标准 HTTP,适合 REST 风格的远程 Server
wsWebSocket,适合需要双向实时通信的场景

最简单也最常用的是 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 CallingMCP
工具来源开发者在代码里定义外部 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_weathersearch_docscreate_ticket 三个函数,工具定义、权限和业务逻辑都在同一个代码库里,没必要先抽成 MCP Server。

MCP 更适合这些场景:

  • 同一批工具要被 Claude Code、Cursor、内部 AI 平台同时使用
  • 工具背后接的是独立系统,比如数据库、工单、文档平台或企业内部 API
  • 工具需要由另一个团队维护,客户端只负责消费能力
  • 工具列表会动态变化,不适合每次都改 AI 应用代码再发布

一个实用判断是:如果工具只是当前应用的内部函数,用 Function Calling 就够;如果工具已经像一项可复用服务,就值得考虑 MCP。

把已有 REST API 包成 MCP Server

很多团队接 MCP 时不需要重写业务系统。已有 REST API 可以继续作为内部实现层,MCP Server 只是外面新加的一层协议适配。

最小改造路径是:

  1. 选出适合暴露给 Agent 的 API,不要一口气把整套内部系统都暴露出来
  2. 为每个能力写清楚工具名、描述和参数 Schema
  3. 在 MCP Server 的工具处理函数里调用原来的 REST API
  4. 在 Server 端做参数校验、鉴权和结果过滤
  5. 把这个 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 相关题目通常会围绕“它和普通工具调用到底差在哪”展开。可以按这几条线准备:

  1. MCP vs Function Calling:Function Calling 描述模型如何调用工具,MCP 描述外部工具和数据源如何标准化接入客户端。
  2. 三类能力:Tools 会执行动作,Resources 提供只读上下文,Prompts 组织任务模板;三者的安全边界不同。
  3. Client / Server 分工:Client 负责连接、展示和权限体验,Server 负责真实的数据访问、参数校验、鉴权和审计。
  4. 传输方式选择:stdio 适合本地可信工具,http / sse / ws 更适合远程服务,但要额外处理认证、重连和运维。
  5. 安全机制:不要只靠 prompt 约束,Server 端必须做只读限制、参数范围校验、最小权限和日志审计。
  6. 生态价值:MCP 的意义不是多一个工具格式,而是让同一个外部能力可以被多个 AI 客户端复用。

下一章:A2A 协议——MCP 解决 Agent 如何接入工具和数据源,A2A 则处理 Agent 如何发现并调用另一个 Agent。

如果你想直接上手体验 MCP,可以先看 MCP 工具入门——里面覆盖了如何在 Cursor 等客户端配置和使用现成的 MCP Server,不需要自己从零写一个。

面向开发者系统学习 AI 应用开发、RAG、Agent 与 Vibe Coding。