一次搞懂 Model Context Protocol 是什么、为什么要用它、以及怎么用。
前言:从 LLM + Tools = Agent 说起
如果你玩过 AI 编程助手(Cursor、Trae 等)或者搭建过 LLM Agent,一定对下面这个公式不陌生:
LLM + Tools = Agent
大模型本身只会"说话",但配上读文件、写文件、执行命令等工具(Tools)之后,它就变成了一个能干活儿的 Agent。
这时候问题就来了:
- 工具是硬编码在 Agent 里的,每加一个工具都要改代码、重新部署。
- 外部团队 / 第三方想把他们的服务暴露给 AI 用,没有统一标准,对接成本极高。
- 不同语言写的工具(Node 调 Python、调 Java、调 Rust)彼此割裂。
- 本地工具和远程工具通信方式不一样,适配起来很痛苦。
MCP 就是为了解决这个"碎片化"问题而生的。
小思考:80% 的 App 未来可能会消失——因为当 AI 能直接通过 MCP 调用各种服务时,用户不再需要打开一个个 App,而是直接和 AI 对话就能完成所有操作。
一、MCP 是什么?
MCP(Model Context Protocol,模型上下文协议) 是由 Anthropic 于 2024 年底提出、2025 年贡献给开源社区的一套开放协议。它本质上是一个为 AI 大模型设计的通用接口标准。
我用一个比喻来帮你理解:
在过去,AI 要调 10 个不同厂商的工具,就像你出门要带 10 个不同接口的充电器;有了 MCP,就像用上了"万能充电器",一套协议就能连接所有服务。
MCP 的核心思想是:把具体的工具(Tools)和"如何调用工具"的规则(Schema),打包成一个独立的服务器进程(Server)。
MCP 和 Tool 不是并列关系,而是**"容器"和"内容"**的关系:
- 没有 MCP 之前:AI 调用 Tools 是硬编码的,每加一个工具都要写胶水代码。
- 有了 MCP 之后:变成动态发现,Host 启动时自动扫描 Server 暴露了哪些工具,运行时按需调用。
二、MCP 的架构:Host / Client / Server 三者关系
MCP 采用经典的客户端-服务器(Client-Server)架构,包含三个核心角色:
| 角色 | 说明 | 举例 |
|---|---|---|
| MCP Host(主机) | 需要连接外部世界的 AI 应用程序,是发起请求的"大脑" | Cursor、Claude Desktop、Trae |
| MCP Client(客户端) | 在 Host 内部运行,负责与 MCP Server 建立一对一连接 | Cursor 内置的 MCP Client,读取 mcp.json 并建立通信 |
| MCP Server(服务器) | 轻量级程序,通过标准协议对外暴露能力 | 你自己写的 my-mcp-server.mjs |
每一个 MCP Server 可以对外暴露三种核心能力:
| 能力 | 说明 | 示例 |
|---|---|---|
| Tools(工具) | AI 可以执行的函数 | 查询用户、发送邮件、读文件 |
| Resources(资源) | AI 可以引用的上下文数据 | 数据库记录、本地文件、API 返回内容 |
| Prompts(提示) | 可复用的预定义交互模板 | 引导 AI 完成特定任务的对话模板 |
一句话总结 MCP 协议:
Context = Tool + Resource + PromptTemplate
三、MCP 的调用流程(7 个步骤)
当你在 Cursor 里说了一句 "帮我查一下用户 002 的信息",背后到底发生了什么?
用户输入 prompt
↓
① Host 配置 mcp.json → 声明要连接哪些 Server
↓
② Client 发送 initialize 请求 → Server 返回所有 tools 列表(动态发现)
↓
③ 用户输入任务 prompt → Host 接收
↓
④ Host 分析 prompt → 从已发现的 tools 中检索匹配项
↓
⑤ Client 发起 tool 调用 → 通过 stdio / HTTP 传到 MCP Server
↓
⑥ Server 执行 tool 逻辑 → 返回结果
↓
⑦ 结果注入 LLM 上下文(ToolMessage) → LLM 继续推理 → 生成最终回复
关键一步:第 ② 步的"动态发现"
这是 MCP 最大的亮点。Host 不需要在代码里写死"有哪些工具可用",而是启动时主动问 Server:"你能提供什么能力?" Server 返回自己注册的所有 tools 的名称、描述、参数 schema。然后当用户发任务时,Host 就能自动匹配合适的工具去执行。
配置示例(mcp.json)
{
"mcpServers": {
"my-mcp-server": {
"command": "node",
"args": ["my-mcp-server.mjs"]
}
}
}
四、MCP 的通信方式
MCP 支持两种通信传输方式,覆盖本地和远程场景:
| 传输方式 | 适用场景 | 实现类 |
|---|---|---|
| stdio(标准输入输出流) | 本地子进程调用 | StdioServerTransport |
| HTTP(Streamable HTTP) | 远程服务器调用 | HttpServerTransport / Streamable HTTP |
这意味着你可以:
- 本地:通过子进程调用一个 MCP Server(哪怕它是 Node / Python / Java / Rust 写的)
- 远程:通过 HTTP 调用部署在云端的 MCP Server,享受第三方服务
MCP 最大的意义之一:跨进程、跨语言调用工具,让 LLM 能干更强大的任务。
五、手写一个 MCP Server(代码实战)
我们来看一个完整的例子:一个提供"查询用户"能力的 MCP Server。
5.1 依赖安装
pnpm add @modelcontextprotocol/sdk zod
5.2 完整代码:my-mcp-server.mjs
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { z } from 'zod';
// 模拟数据库
const database = {
users: {
"001": { id: "001", name: "张三", email: "zhangsan@example.com", role: "admin" },
"002": { id: "002", name: "李四", email: "lisi@example.com", role: "user" },
"003": { id: "003", name: "王五", email: "wangwu@example.com", role: "user" },
}
};
// ① 创建 MCP Server 实例
const server = new McpServer({
name: 'my-mcp-server',
version: '1.0.0',
});
// ② 注册 Tool:查询用户
server.registerTool('query-user', {
description: '查询数据库中的用户信息。输入用户ID,返回该用户的详细信息(姓名、邮箱、角色)。',
inputSchema: {
userId: z.string().describe("用户 ID,例如:001, 002, 003"),
},
}, async ({ userId }) => {
const user = database.users[userId];
if (!user) {
return {
content: [{
type: 'text',
text: `用户ID ${userId} 不存在。可用的ID: 001, 002, 003`,
}],
};
}
return {
content: [{
type: 'text',
text: `用户信息:\n- ID: ${user.id}\n- 姓名: ${user.name}\n- 邮箱: ${user.email}\n- 角色: ${user.role}`,
}],
};
});
// ③ 注册 Resource:使用指南
server.registerResource('使用指南', 'docs://guide', {
description: 'MCP Server 使用文档',
mimeType: 'text/plain',
}, async () => {
return {
contents: [{
uri: 'docs://guide',
mimeType: 'text/plain',
text: 'MCP Server 使用指南\n功能:提供用户查询等工具。',
}],
};
});
// ④ 选择通信方式并启动
const transport = new StdioServerTransport();
await server.connect(transport);
5.3 MCP 开发流程总结
new McpServer() → 创建 Server 实例
server.registerTool() → 注册工具(名称、描述、入参 schema、执行回调)
server.registerResource() → 注册资源(名称、URI、描述、内容回调)
server.registerPrompt() → 注册提示模板(可选)
new StdioServerTransport() → 选择通信方式
server.connect(transport) → 启动 Server,等待 Host 连接
六、把 MCP 集成到自己的 Agent 程序里
MCP Server 写好了,不仅可以在 Cursor 里用,还可以通过 LangChain 等框架直接嵌入到你自己的 Agent 程序中,实现"可拔插"的工具集成。
下面是用 LangChain + MCP Adapters 的示例:
import { MultiServerMCPClient } from '@langchain/mcp-adapters';
import { ChatOpenAI } from '@langchain/openai';
import { HumanMessage, ToolMessage } from '@langchain/core/messages';
const model = new ChatOpenAI({
modelName: process.env.MODEL_NAME,
apiKey: process.env.OPENAI_API_KEY,
configuration: { baseURL: process.env.OPENAI_BASE_URL },
});
// ① 创建 MCP Client,连接 MCP Server
const mcpClient = new MultiServerMCPClient({
mcpServers: {
'my-mcp-server': {
command: 'node',
args: ['/path/to/my-mcp-server.mjs'],
},
},
});
// ② 动态发现 + 获取 tools
const tools = await mcpClient.getTools();
// ③ 把 tools 绑定到模型上
const modelWithTools = model.bindTools(tools);
// ④ Agent 循环:推理 → 调用 tool → 拿结果 → 再推理
async function runAgent(query, maxIterations = 30) {
const messages = [new HumanMessage(query)];
for (let i = 0; i < maxIterations; i++) {
const response = await modelWithTools.invoke(messages);
messages.push(response);
// 没有 tool_calls 说明推理结束
if (!response.tool_calls || response.tool_calls.length === 0) {
return response.content;
}
// 执行每个 tool_call,把结果注入上下文
for (const toolCall of response.tool_calls) {
const foundTool = tools.find(t => t.name === toolCall.name);
if (foundTool) {
const toolResult = await foundTool.invoke(toolCall.args);
messages.push(new ToolMessage({
content: toolResult,
tool_call_id: toolCall.id,
}));
}
}
}
return messages[messages.length - 1].content;
}
// ⑤ 使用
const result = await runAgent("查一下用户 002 的信息");
console.log(result);
await mcpClient.close();
这个例子的关键点:
getTools()就是"动态发现"——你的程序不需要硬编码有哪些工具。bindTools(tools)把这些动态发现的工具注入 LLM,让它"知道"自己能调什么。- Agent 循环中,LLM 决定是否调 tool、调哪个 tool,执行后结果以
ToolMessage的形式喂回 LLM,形成闭环。
七、MCP 的核心价值总结
| 维度 | 传统方式 | MCP 方式 |
|---|---|---|
| 工具集成 | 硬编码,一改全改 | 动态发现,即插即用 |
| 跨语言调用 | 需要自己写 IPC / RPC 胶水 | 统一协议,天然跨语言 |
| 第三方接入 | 各自定义接口,对接成本高 | 一套规范,大家都遵守 |
| 本地 vs 远程 | 需要分别适配 | stdio / HTTP 双模式原生支持 |
| 生态扩展 | 封闭 | 开放,大厂可将服务以 MCP 方式对外提供 |
八、写在最后
MCP 的野心远不止是"又一个工具调用协议"。它试图定义的是 AI 时代的**"USB-C 接口"**——一套通用的、开放的标准,让 AI 可以无缝连接这个世界的各种服务和数据。
理解了 MCP,你就能理解为什么有人说"80% 的 App 会消失":当 AI 成为一个超级入口,用户不再需要打开一个个独立的 App,而是直接对 AI 说"帮我查一下快递""帮我订一张票",AI 通过 MCP 在背后调用对应的服务,一切都变得透明。
希望这篇文章能帮你建立起对 MCP 的完整认知。现在就动手写一个自己的 MCP Server 吧,你会发现它比想象中简单得多。
本文参考: