模型上下文协议(Model Context Protocol,MCP)是一个协议。
一个训练好的AI模型的知识库可能是滞后的,或者它是通用性的模型,可能对某些领域并不是很了解,需要进一步学习。比如你想询问大模型今天你所在的城市天气怎么样,它可能会这样回答:
目前我无法实时获取北京的天气信息,建议您查看天气预报应用或网站(如中国天气网、Weather.com等)获取最新天气情况。如果您需要,我可以提供一些通用的天气建议,例如:
若有雨,建议携带雨具;
若有高温,注意防晒和补水;
若有寒流,注意保暖。
需要我帮您查找最新的天气信息吗?😊
这是因为大模型本身并不具备自动查询天气信息的能力,我们可以通过MCP协议自己制定一些查询天气的工具,当与大模型互动时,大模型会从我们制定的工具中选择与目前对话匹配的工具,然后调用这些工具获取天气的相关信息,并结合这些信息生成答复。
数据层
MCP协议分为客户端和服务器。MCP中的客户端与服务器端的通信方式有两种:
- stdio:标准输出输入输出;
- http:基于HTTP协议;
MCP使用JSON-RPC 2.0作为其传输格式。传输层负责将MCP协议消息转换为JSON-RPC格式以进行传输,并将接 收到的JSON-RPC消息转换回MCP协议消息。
交互流程
- 监听客户端发来的消息;
- 客户端首先发起初始化(
initialize)的消息,服务端接收到后返回响应; - 客户端收到响应后,发出
notifications/initialized消息,表明已经准备好; - 当客户端发起
tools/list消息时,服务端把它能支持的工具调用列表返回给客户端; - 客户端根据
tools列表决策使用什么工具。当选择使用某个工具时,客户端发起tools/call消息,服务端接收到后匹配tools,执行对应的函数,然后把结果返回给客户端。
大致交互流程如下:

初始化
在客户端与服务器双方通信时,客户端要发送请求以建立连接并协商支持的功能。例如下面是一个初始化的请求消息:
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-06-18",
"capabilities": {
"elicitation": {}
},
"clientInfo": {
"name": "example-client",
"version": "1.0.0"
}
}
}
其中:
jsonrpc表示JSON-RPC的版本;id是一个消息标识,服务端响应的id需要与客户端发来的id保持一致;method: "initialize"就表示是一个初始化的消息;- 而
params.protocolVersion则表示MCP协议版本,确保客户端和服务器使用兼容的协议版本。这避免了不同版本尝试交互时可能发生的通信错误; params.**capabilities**则表示客户端和服务器双方支持哪些功能,如工具(tools)、资源(resources)、提示(prompts)等;**"elicitation": {}**- 客户端声明它可以处理用户交互请求(可以接收方法调用)
params.clientInfo以及params.serverInfo则提供身份识别和版本信息,用于调试和兼容性;
当请求发出后,服务器可能返回以下消息:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": "2025-06-18",
"capabilities": {
"tools": {
"listChanged": true
},
"resources": {}
},
"serverInfo": {
"name": "example-server",
"version": "1.0.0"
}
}
}
Tools
在MCP协议中,tools/list消息格式如下(客户端):
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/list"
}
服务端对应的tools/list消息:
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"tools": [
{
"name": "addition_arithmetic",
"title": "addition",
"description": "Add two numbers",
"inputSchema": {
"type": "object",
"properties": {
"a": { "type": "number", "description": "First number" },
"b": { "type": "number", "description": "Second number" },
},
"required": ["a", "b"]
}
},
{
"name": "weather_current",
"title": "Weather Information",
"description": "Get weather information",
"inputSchema": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "City name"
},
},
"required": ["city"]
}
}
]
}
}
上面代码是将加法器与查询天气两个tools通过JSON Schema的方式声明了工具函数需要接收的参数以及参数类型、参数是否必填等信息,需要注意的是,description尽量描述的清晰一些,这更有利于AI理解该工具的使用场景,从而做出更好的决策。
客户端的tools/call消息格式如下:
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "weather_current",
"arguments": {
"city": "San Francisco"
}
}
}
例子
我们以加法器和查询天气两个tools为例,基于Node.js编写一个简单的服务端MCP应用demo。
项目初始化
- npm init
- npx tsc --init
- npm install @types/node nodemon ts-node typescript --save-dev
- 更改package.json文件,添加
"type": "module"配置 - 添加启动脚本:
nodemon --exec node --loader ts-node/esm ./MCPServer.ts
编写服务端代码
下面是完整的服务端demo的例子:
// 工具函数对应的声明
const toolsMap = new Map([
[
"add",
{
name: "add",
description: "Add two numbers",
inputSchema: {
type: "object",
properties: {
a: { type: "number", description: "First number" },
b: { type: "number", description: "Second number" },
},
required: ["a", "b"],
},
},
],
[
"weather",
{
name: "weather",
description: "Get weather information",
inputSchema: {
type: "object",
properties: {
city: { type: "string", description: "City name" },
},
required: ["city"],
},
},
],
]);
// 工具函数集
const toolsFunctions = {
add(a: number, b: number): number {
return a + b;
},
weather(city: string): string {
return `The weather in ${city} is sunny.`;
},
};
// 监听客户端发送的数据
process.stdin.on("data", (data) => {
const req = JSON.parse(data.toString());
const { id } = req;
const resultData = {
id,
jsonrpc: "2.0",
result: null as any,
};
// 当是初始化的消息时
if (req.method === "initialize") {
resultData.result = {
protocolVersion: "2025-06-18",
capabilities: {
tools: {
listChanged: true,
},
},
serverInfo: {
name: "example-server",
version: "1.0.0",
},
};
} else if (req.method === "tools/list") {
const tools = Array.from(toolsMap.values());
resultData.result = { tools };
} else if (req.method === "tools/call") {
// 当客户端发起 tools/call 消息时,读取 arguments 和 tool name,
// 然后调用tool name对应的函数,将结果保存到 result 中
const { name, arguments: args } = req.params;
const func: any = toolsFunctions[name as keyof typeof toolsFunctions];
const funcArgs = Object.values(args) as any[];
const funcResult = func(...funcArgs);
resultData.result = resultData.result = {
content: [{ type: "text", text: funcResult }]
};
} else {
return;
}
// 发给客户端
process.stdout.write(JSON.stringify(resultData) + "\n");
});
测试
除了在自己实现客户端外,还可以借助MCP Inspector工具验证。在项目目录下运行以下命令:
npx @modelcontextprotocol/inspector
运行后会自动打开浏览器,进入调试页面:

点击Connect连接,右侧会加载出对应的页面:

可以看到左下角发送了一个initialize消息。点击顶部的Tools,然后点击List Tools可以看到所有的工具列表。右侧是对应的参数列表,这些表单是根据tools/list消息生成的。点击Run Tool后客户端会发送tools/call消息,获取到服务端响应的数据。

使用@modelcontextprotocol/sdk
@modelcontextprotocol/sdk 是对MCP协议的封装,用它来实现MCP服务端很容易,代码如下:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const NWS_API_BASE = "https://uapis.cn/api/v1/misc/weather";
const server = new McpServer({
name: "weather",
version: "1.0.0",
});
// 注册 tools
server.registerTool("weather-tool", {
description: "Get the current weather for a specified city.",
inputSchema: { // 参数定义
city: z.string().describe("The name of the city to get the weather for."),
}
}, async ({ city }) => { // 请求数据
const response = await fetch(`${NWS_API_BASE}?city=${encodeURIComponent(city)}`, {
method: 'GET'
});
if (!response.ok) {
return {
content: [
{
type: 'text',
text: `Unable to fetch weather data for ${city}.`,
}
]
}
}
const content = await response.text();
return { // 返回数据
content: [
{
type: 'text',
text: content,
}
]
}
});
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Weather MCP Server running on stdio");
}
main().catch((error) => {
console.error("Fatal error in main():", error);
process.exit(1);
});
在使用McpServer这个API时,我们直接注册Tools即可,不需要手动进行初始化操作。
在平台中添加MCP Server
接下来我们就可以验证一下MCP Server是否可以成功运行,要运行MCP Server需要MCP Client,它们两个是一一对应的关系,流程如下:
- MCP Client接收MCP Server的启动命令参数,然后内部会启动MCP Server,获取Tools列表;
- 当我们与大模型对话时,会经过MCP Client,MCP Client将用户提出的问题和Tools发给大模型;
- 如果用户的对话内容包含MCP Tools,大模型就会返给MCP Client命中的Tools;
- MCP Client调用对应的Tools函数,然后把结果返回给大模型,大模型根据拿到的信息再进一步思考,生成内容经MCP Client,最终把大模型的答复返回给用户。
有很多应用内部已经集成了MCP Client,例如VScode、Cherry Stdio、Cursor等,下面我们以在VScode和Cherry Stdio中配置MCP Client为例,说一下怎么在这两个应用中使用MCP服务。
VScode配置MCP Server
VScode 客户端支持配置MCP Server,我们可以在vscode项目中新建.vscode文件夹,然后在文件夹中添加mcp.json,配置如下:
{
"servers": {
"my-mcp-server": {
"type": "stdio",
"command": "node",
"args": [
"--loader",
"ts-node/esm",
"D:\\projects\\ai-study\\index.ts"
]
}
},
"inputs": []
}
配置完成后就会有下面这样的提示:

点击启动后,MCP 就运行起来了。在更多里有一个配置模型访问的菜单,点击后就可以选择与哪些大模型对话时启用该MCP Server。也可以在插件栏看到已安装的MCP Server列表:

打开聊天命令窗口,就可以输入内容测试了:


上面的配置方式仅限于特定项目中的使用场景,在vscode中也可以全局配置,步骤如下:
- ctrl + p 打开命令输入框,输入
> MCP: Open User Configuration回车就会打开用户配置中的mcp.json文件,然后配置包括即可。
需要注意的是,如果你使用的是TS文件作为MCP Server启动脚本,最好是使用
bun去运行(需要先全局安装),用node可能会因配置问题导致运行出错。
Cherry Stdio中配置
Cherry 是一个多元化的AI设计工具。从官网上下载后,我们可以点击右上角的设置按钮,找到MCP服务器,进入后点击添加-> 从JSON导入,按照提示导入,然后打开即可。

然后回到首页,添加一个助手,下方聊天框可以看到MCP服务:

选择对应的MCP服务器即可。
除了使用第三方平台提供的添加MCP Server外,你也可以自己实现一个。大致思路是:顶层有一个Agent类,这个类初始化时可以传入MCP Client数组,MCP Client内部会初始化MCP Server以及提供查询和执行Tools的方法。在Agent类初始化后可以拿到MCPClient类中所有的Tools,当与大模型对话时,用户的prompt会先进入Agent类,然后Agent类与大模型交互,将Tools和Prompt传给大模型,大模型得出结果后返回给Agent,Agent再将结果传给下游。