MCP:工具的标准化接口
# MCP:工具的标准化接口
本章讲解 MCP(Model Context Protocol)协议的设计理念、通信模型以及工具调用流程。
本章核心问题: 假设我们要给 AI Agent 加上"天气预报"和"发送邮件"两个工具。不同的大模型对工具的描述格式不同(OpenAI 用 JSON Schema,Anthropic Claude 有自己的格式),同时不同工具的实现方式也不同(有的调用 HTTP API,有的读取本地文件,有的需要 OAuth 认证)。如果每对接一个大模型就重写一遍工具代码,显然会陷入"适配器地狱"。MCP 的解决方案是:让工具提供方按照统一协议封装工具,让模型消费方也按照统一协议发现和调用工具,从而解耦。
# 7.1 为什么需要 MCP
在过去的大模型应用中,工具调用面临以下痛点:
- 格式不统一:不同模型对工具描述参数的要求不同,需要为每个模型适配。
- 安全风险:用户下载的第三方工具代码可能带来安全隐患。
- 生态割裂:每个 Agent 框架都有自己的工具定义方式,工具无法跨框架复用。
MCP(Model Context Protocol)由 Anthropic 提出,旨在标准化 AI 应用与外部工具/数据源的交互方式。它参考了 LSP(Language Server Protocol,语言服务器协议)的设计思路,采用客户端-服务器架构。
# 7.1.1 MCP 的三大设计目标
- 可替换性:前端(LLM 应用)和后端(工具/数据源)可以独立开发和替换,只需遵循 MCP 协议即可对接。
- 安全性:工具运行在独立的 MCP Server 进程中,与 LLM 应用进程隔离。MCP Server 可以运行在本地(子进程)或远程(独立服务),通过能力声明和权限控制实现最小权限原则。
- 生态互通:任何符合 MCP 规范的工具都可以在任何支持 MCP 的客户端中使用。
# 7.1.2 MCP 的核心概念
| 概念 | 说明 |
|---|---|
| MCP Host | 需要访问数据的 LLM 应用程序(如 Claude Desktop、IDE 插件、自定义 Agent) |
| MCP Client | 与 MCP Server 建立一对一连接的协议客户端 |
| MCP Server | 通过标准协议对外暴露特定能力的轻量级服务程序 |
| Transport | 传输层,定义客户端与服务器之间的通信机制(stdio、SSE 等) |
| Capability | 能力声明,Server 在初始化时告知 Client 自己支持哪些功能 |
| Resource | 数据资源,类似 GET 接口,用于获取静态或动态数据(文件内容、数据库记录等) |
| Tool | 工具,类似 POST 接口,用于执行操作(发送邮件、调用 API 等) |
| Prompt | 提示模板,Server 可以提供可复用的提示模板供 Client 使用 |
| Sampling | 采样请求,Server 可以反向请求 LLM 生成文本(反向调用) |
| Roots | 根目录列表,Client 告知 Server 可访问的项目根目录 |
# 7.2 MCP 的通信模型
MCP 采用客户端-服务器架构,通信协议基于 JSON-RPC 2.0。
# 7.2.1 JSON-RPC 2.0 基础
JSON-RPC 2.0 是一种轻量级的远程过程调用协议,使用 JSON 格式进行数据交换。MCP 中的每个请求和响应都遵循 JSON-RPC 2.0 规范。
请求格式示例:
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "get_weather",
"arguments": {
"city": "北京"
}
}
}
2
3
4
5
6
7
8
9
10
11
成功响应:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [
{
"type": "text",
"text": "北京当前温度 26°C,晴,湿度 45%"
}
],
"isError": false
}
}
2
3
4
5
6
7
8
9
10
11
12
13
错误响应:
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32603,
"message": "Internal error",
"data": {
"details": "API 请求超时"
}
}
}
2
3
4
5
6
7
8
9
10
11
# 7.2.2 传输层(Transport)
MCP 支持两种传输方式:
stdio 传输:客户端将 MCP Server 作为子进程启动,通过标准输入/输出进行通信。
- 适用于本地工具、开发环境。
- 进程级隔离,安全性较好。
- 启动快,无需网络配置。
SSE(Server-Sent Events)传输:客户端通过 HTTP 与远程 MCP Server 通信。
- 适用于远程服务、生产环境。
- 支持跨网络访问。
- 需要处理认证和授权。
# 7.2.3 生命周期
MCP 连接的生命周期分为三个阶段:
- 初始化:Client 和 Server 互相交换能力信息,确认协议版本。
- 运行:进行工具调用、资源访问、提示模板获取等操作。
- 关闭:终止连接,释放资源。
# 7.3 MCP Server 开发实战
下面通过一个完整的天气预报 MCP Server 示例,展示如何使用 Node.js 开发 MCP Server。
# 7.3.1 环境准备
# 创建项目目录
mkdir mcp-weather-server
cd mcp-weather-server
# 初始化项目
npm init -y
# 安装 MCP SDK
npm install @modelcontextprotocol/sdk
# 安装 TypeScript 相关依赖
npm install typescript @types/node ts-node --save-dev
2
3
4
5
6
7
8
9
10
11
12
# 7.3.2 实现 MCP Server
TypeScript 实现:
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
// 创建 Server 实例
const server = new Server(
{
name: "weather-server",
version: "1.0.0",
},
{
capabilities: {
tools: {},
},
}
);
// 声明工具列表
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "get_weather",
description: "获取指定城市的天气预报",
inputSchema: {
type: "object",
properties: {
city: {
type: "string",
description: "城市名称,如:北京、上海",
},
},
required: ["city"],
},
},
],
};
});
// 处理工具调用
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
if (name === "get_weather") {
const weather = await fetchWeather(args.city);
return {
content: [{ type: "text", text: weather }],
};
}
throw new Error(`Unknown tool: ${name}`);
});
// 启动服务器
const transport = new StdioServerTransport();
await server.connect(transport);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
Python 实现(使用 MCP Python SDK):
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import Tool, TextContent
# 创建 Server 实例
server = Server("weather-server")
@server.list_tools()
async def list_tools() -> list[Tool]:
return [
Tool(
name="get_weather",
description="获取指定城市的天气预报",
inputSchema={
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "城市名称,如:北京、上海",
},
},
"required": ["city"],
},
)
]
@server.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
if name == "get_weather":
weather = await fetch_weather(arguments["city"])
return [TextContent(type="text", text=weather)]
raise ValueError(f"Unknown tool: {name}")
async def main():
async with stdio_server() as (read_stream, write_stream):
await server.run(read_stream, write_stream)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# 7.3.3 配置与运行
在 Claude Desktop 中配置:
{
"mcpServers": {
"weather": {
"command": "node",
"args": ["path/to/weather-server/index.js"],
"env": {
"API_KEY": "your-api-key-here"
}
}
}
}
2
3
4
5
6
7
8
9
10
11
# 7.4 MCP 工具调用的完整流程
下图展示了从用户提问到工具调用返回的完整流程:
用户提问 → LLM 分析 → 需要调用工具
↓
Host 请求 Client 获取工具列表
↓
Client 向 Server 发送 ListTools 请求
↓
Server 返回工具列表(名称、描述、参数 Schema)
↓
Client 将工具列表传递给 Host
↓
Host 将工具描述注入 LLM 的 Prompt
↓
LLM 返回工具调用请求(函数名 + 参数)
↓
Host 将调用请求交给 Client
↓
Client 向 Server 发送 CallTool 请求
↓
Server 执行具体操作并返回结果
↓
Client 将结果返回 Host
↓
Host 将结果注入 LLM 的上下文
↓
LLM 根据结果生成最终回复
↓
用户得到答案
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 7.4.1 关键协议方法
MCP 定义了以下核心协议方法:
生命周期方法:
| 方法 | 方向 | 说明 |
|---|---|---|
initialize | Client → Server | 初始化连接,协商能力 |
initialized | Client → Server | 通知 Server 初始化完成 |
ping | 双向 | 心跳检测 |
notifications/initialized | Client → Server | 初始化完成通知 |
工具相关方法:
| 方法 | 方向 | 说明 |
|---|---|---|
tools/list | Client → Server | 获取可用工具列表 |
tools/call | Client → Server | 调用指定工具 |
资源相关方法:
| 方法 | 方向 | 说明 |
|---|---|---|
resources/list | Client → Server | 列举可用的资源 URI |
resources/read | Client → Server | 读取指定资源 |
resources/subscribe | Client → Server | 订阅资源变更通知 |
resources/unsubscribe | Client → Server | 取消订阅 |
提示模板相关方法:
| 方法 | 方向 | 说明 |
|---|---|---|
prompts/list | Client → Server | 获取提示模板列表 |
prompts/get | Client → Server | 获取指定模板的内容 |
# 7.5 MCP 的高级特性
# 7.5.1 资源模板(Resource Templates)
资源模板允许 Server 声明动态资源 URI 的模式,例如 file://{path} 可以匹配任意文件路径。
server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => ({
resourceTemplates: [
{
uriTemplate: "file://{path}",
name: "Project File",
description: "访问项目中的任意文件",
},
],
}));
2
3
4
5
6
7
8
9
# 7.5.2 提示模板(Prompts)
Server 可以提供可复用的提示模板,包含动态参数。
server.setRequestHandler(ListPromptsRequestSchema, async () => ({
prompts: [
{
name: "code_review",
description: "代码审查提示模板",
arguments: [
{
name: "language",
description: "编程语言",
required: true,
},
],
},
],
}));
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 7.5.3 Roots(根目录列表)
Client 在初始化时可以告知 Server 允许访问的根目录列表,实现沙箱化的文件访问。
{
"roots": [
{
"uri": "file:///home/user/projects/my-app",
"name": "My App"
}
]
}
2
3
4
5
6
7
8
# 7.5.4 Sampling(反向采样)
MCP 允许 Server 反向请求 LLM 生成文本。这为工具提供了"主动思考"的能力。例如,一个代码分析工具在发现代码异味时,可以请求 LLM 生成改进建议。Sampling 请求会经过 Client,Client 可以执行内容安全策略(如敏感信息过滤)。
// Server 端发起 Sampling 请求
const response = await server.requestSampling({
messages: [
{
role: "user",
content: {
type: "text",
text: "分析这段代码中的设计模式",
},
},
],
maxTokens: 1000,
});
2
3
4
5
6
7
8
9
10
11
12
13
# 7.5.5 通知机制
MCP 支持资源变更通知。当资源发生变化时,Server 可以主动通知 Client,Client 再决定是否需要重新获取。
// Server 端发送通知
server.sendResourceUpdated("file:///config.json");
2
# 7.6 开发与调试工具
# MCP Inspector
MCP Inspector 是官方提供的可视化调试工具,可以连接到 MCP Server 并进行交互式测试。
npx @modelcontextprotocol/inspector node path/to/server.js
该工具提供:
- 工具列表查看
- 工具调用测试
- 资源浏览
- 实时日志查看
# MCP 的开发原则
- 单一职责:每个 MCP Server 专注于一个领域的能力,避免大而全的"上帝服务"。
- 明确的能力声明:在初始化时清晰地声明支持的能力,方便 Client 进行调度决策。
- 完善的错误处理:通过错误码和错误信息提供可操作的反馈,而不是简单的 "Error occurred"。
- 安全的参数校验:对输入参数进行严格的校验和清理,防止注入攻击。
- 幂等性设计:对于可能重复执行的工具调用(如发送邮件),设计为幂等操作或提供去重机制。
- 恰当的缓存策略:对于耗时的资源读取操作,实现合适的缓存机制。
# 补充八股
Q:MCP 与 Function Calling 有什么关系?
A: Function Calling 是大模型本身的能力,指模型能够理解并输出结构化的函数调用参数。而 MCP 是工具调用流程的标准化协议,它包括了工具发现、调用、结果返回的完整流程。两者是互补关系:Function Calling 解决"模型如何理解工具",MCP 解决"工具如何被调用和管理"。
Q:MCP 如何保证安全?
A: MCP 通过多层机制保障安全:第一层是进程隔离,工具运行在独立的 Server 进程中;第二层是 Roots 机制限制文件系统访问范围;第三层是 Sampling 请求经过 Client 的内容安全策略过滤;第四层是 Server 的能力声明机制让 Client 能够精确控制开放的能力。
Q:MCP 和普通 API 有什么区别?
A: 普通 API 是静态的、预定义的接口,需要开发者手动集成和调用。MCP 提供了动态发现能力,LLM 应用可以在运行时动态获取工具列表和调用方式,同时 MCP 还提供了标准化的生命周期管理、安全隔离、通知机制等能力,更适合 AI Agent 场景。
Q:MCP Server 可以调用其他 MCP Server 吗?
A: 可以。MCP Server 可以同时作为另一个 MCP Server 的 Client,形成服务链。这种架构适合需要组合多个工具能力的场景,但需要注意循环调用和性能问题。
Q:MCP 的 Resource 和 Tool 有什么区别?
A: Resource 主要用于读取数据,是只读的,类似于 RESTful 中的 GET 请求。Tool 用于执行操作,可以修改状态,类似于 POST 请求。Resource 通过 URI 标识和访问,支持订阅变更通知;Tool 通过名称调用,可以接受参数并返回执行结果。
# 面试八股
Q:MCP 的通信协议基于什么标准?
A: MCP 基于 JSON-RPC 2.0 协议,所有请求和响应都遵循 JSON-RPC 2.0 规范。支持 stdio 和 SSE 两种传输方式,分别适用于本地和远程场景。
Q:MCP 的核心概念有哪些?
A: MCP Host(LLM 应用)、MCP Client(协议客户端)、MCP Server(工具服务)、Transport(传输层,stdio/SSE)、Capability(能力声明)、Resource(数据资源)、Tool(工具)、Prompt(提示模板)、Sampling(反向采样)、Roots(根目录列表)。
Q:MCP 连接的生命周期包括哪些阶段?
A: 三个阶段:初始化(交换能力信息、确认协议版本)、运行(工具调用、资源访问等操作)、关闭(终止连接、释放资源)。
Q:MCP Server 如何声明支持的能力?
A: 在初始化时通过 capabilities 字段声明,包括 tools、resources、prompts 等能力。Client 可以根据 Server 的能力声明来决定如何使用该 Server。
Q:MCP 中的 Sampling 机制是什么?
A: Sampling 允许 Server 反向请求 LLM 生成文本,为工具提供"主动思考"的能力。该请求会经过 Client,Client 可以执行内容安全策略过滤。Server 在调用 sampling 时可以提供 system prompt、messages、模型参数等。
Q:MCP 如何实现资源变更通知?
A: 通过 resources/subscribe 和 resources/unsubscribe 方法实现。Client 可以订阅特定资源的变更通知,当资源发生变化时 Server 会主动发送通知给 Client。
# 课后练习
# 第 1 题(单选)
MCP 协议基于以下哪种协议标准?
A. RESTful API B. GraphQL C. JSON-RPC 2.0 D. gRPC
答案与解析
答案:C
解析: MCP 协议基于 JSON-RPC 2.0 标准。JSON-RPC 2.0 是一种轻量级的远程过程调用协议,使用 JSON 格式进行数据交换。MCP 中的每个请求和响应都遵循 JSON-RPC 2.0 规范,包括固定的字段结构(jsonrpc、id、method、params 等)。
# 第 2 题(单选)
以下哪项不是 MCP 支持的传输方式?
A. stdio(标准输入输出) B. SSE(Server-Sent Events) C. WebSocket D. gRPC
答案与解析
答案:D
解析: MCP 支持两种传输方式:stdio 和 SSE。stdio 适用于本地工具和开发环境,通过进程间通信实现;SSE 适用于远程服务和生产环境,基于 HTTP 协议。gRPC 和 WebSocket 目前不是 MCP 标准支持的传输方式。
# 第 3 题(多选)
MCP 中的核心概念包括哪些?
A. MCP Host B. MCP Client C. MCP Server D. MCP Router
答案与解析
答案:A、B、C
解析: MCP 的核心概念包括 MCP Host(需要访问数据的 LLM 应用程序)、MCP Client(与 MCP Server 建立一对一连接的协议客户端)、MCP Server(通过标准协议对外暴露特定能力的轻量级服务程序)。MCP Router 不是 MCP 规范中的核心概念。
# 第 4 题(单选)
在 MCP 中,以下哪种机制用于限制文件系统访问范围?
A. Capability B. Roots C. Sampling D. Transport
答案与解析
答案:B
解析: Roots(根目录列表)是 MCP 中用于实现沙箱化文件访问的机制。Client 在初始化时可以告知 Server 允许访问的根目录列表,Server 只能访问这些指定目录中的文件,从而实现安全隔离。
# 第 5 题(多选)
以下哪些属于 MCP 定义的协议方法?
A. tools/list B. tools/call C. tools/execute D. tools/register
答案与解析
答案:A、B
解析: MCP 定义了 tools/list 用于获取可用工具列表,tools/call 用于调用指定工具。tools/execute 和 tools/register 不是 MCP 协议标准方法。
# 第 6 题(单选)
MCP 连接的生命周期顺序是?
A. 运行 → 初始化 → 关闭 B. 初始化 → 关闭 → 运行 C. 初始化 → 运行 → 关闭 D. 运行 → 关闭 → 初始化
答案与解析
答案:C
解析: MCP 连接的生命周期分为三个阶段:初始化(交换能力信息,确认协议版本)、运行(进行工具调用、资源访问等操作)、关闭(终止连接,释放资源)。
# 第 7 题(单选)
MCP 中,Sampling 机制的主要作用是?
A. 提高工具调用性能 B. 允许 Server 反向请求 LLM 生成文本 C. 实现负载均衡 D. 缓存工具调用结果
答案与解析
答案:B
解析: Sampling 机制允许 MCP Server 反向请求 LLM 生成文本,为工具提供了"主动思考"的能力。例如,代码分析工具在发现代码问题时,可以请求 LLM 生成改进建议。Sampling 请求会经过 Client,由 Client 执行内容安全策略过滤。
# 第 8 题(单选)
MCP 中 Resource 和 Tool 的主要区别是?
A. Resource 只读,Tool 可执行操作 B. Resource 更快,Tool 更慢 C. Resource 用于本地,Tool 用于远程 D. 没有区别
答案与解析
答案:A
解析: Resource 主要用于读取数据,是只读的,类似于 RESTful 中的 GET 请求。Tool 用于执行操作,可以修改状态,类似于 POST 请求。Resource 通过 URI 标识和访问,支持订阅变更通知;Tool 通过名称调用,可以接受参数并返回执行结果。
# 第 9 题(多选)
MCP 支持的安全机制包括?
A. 进程隔离 B. Roots 目录限制 C. Sampling 内容过滤 D. 能力声明机制
答案与解析
答案:A、B、C、D
解析: MCP 通过多层机制保障安全:进程隔离(工具运行在独立的 Server 进程中)、Roots 机制(限制文件系统访问范围)、Sampling 内容过滤(Client 执行内容安全策略)、能力声明机制(Client 精确控制开放的能力)。这些都是 MCP 安全体系的重要组成部分。
# 第 10 题(单选)
MCP Server 的初始化顺序是?
A. Client 先声明能力,Server 再声明能力 B. Server 先声明能力,Client 再声明能力 C. Client 和 Server 同时声明能力 D. 不需要能力声明
答案与解析
答案:B
解析: 在 MCP 的初始化阶段,Client 先发送 initialize 请求,Server 回复自己的能力信息(包括协议版本、支持的功能等),然后 Client 确认并发送 initialized 通知,之后 Client 再声明自己的能力。这种顺序确保了连接的建立和能力的协商是清晰、有序的。