图为 2025.04.27 拍摄于杭州美丽洲公园
前面两节我们讲清楚了 function/tool call 是什么以及如何手动调用工具 ,接下来我们再来理解 MCP 其实就很轻松了。
本文会通过一个实际的例子,带大家具象的看清楚 MCP 到底是个啥。
(大家下载代码后去配置一下 apikey ,就能把文中的例子跑起来)。
行文思路:
- 概念和流程讲清楚
- 把官方的例子跑起来
- 讲清楚我的理解
MCP 概念
我们先认清一个事实:
- 大模型训练完成后,其权重和参数就已经固定了
- 无法自主学习或进化
- 它对事物的认知局限于历史训练数据,之外的东西它是不知道的,比如天气预报、新闻;
模型的能力在于:
- 拥有既定的认知框架
- 具备"理解"和"推理"能力
第二点很重要,大模型能理解你的意图!还能根据你给的输入做出推理!最终预测出你要的答案。
我对于大模型的具象理解,感觉他就是一个受过高等教育的"人"。
如果仅局限于他自己的固定认知,无法和外界交互,那么在实际场景可能发挥不了多大的作用。
MCP 就是提供了一套标准,让大模型可以和外界发生交互,以发挥他最大的能量!
按照官方的定义:
MCP is an open protocol that standardizes how applications provide context to LLMs.
- MCP 是一个开放协议/标准;
- MCP 标准化了应用如何提供上下文给 llms (底层其实就是 function call,现在你应该很容易理解)
官方还贴了一张图:

现在我们尝试理解一下:
- MCP-client 基于 MCP 协议和 MCP server 建立连接,client 从 server 侧拉取它支持的工具列表;
- MCP-client 接收你的问题,传入刚刚从 server 侧拉取的 tools,然后发起大模型调用;
- 大模型会智能决策是否需要调用工具,如果是就返回工具调用信息,然后发起工具调用请求;
- 工具的具体执行在 server 侧,可能读取一些本地数据,也可以通过 web APIs 来发起远程请求;
- server 侧把工具执行的结果返回给 client
- client 拼接工具调用 message(这一步其实就是拼接上下文),然后再次调用大模型拿到回答。
来跑一个 MCP 实例吧
参考官方的实现,我已经写了一个例子放到 github 上了:github.com/StannyDai20...
开启服务的方式是,在根目录下启用一条命令行:
xml
node <path_to_client_script> <path_to_server_script>
先来讲讲里面的核心实现:
MCP-client
入口函数的核心伪代码如下,核心就是连接 mcp-server 然后开启命令行的对话服务。
这里的 chatLoop 和我们第一节里的案例如出一撤
js
async function main() {
const mcpClient = new MCPClient();
await mcpClient.connectToServer(serverPath);
await mcpClient.chatLoop();
}
main();
client 类伪代码如下:
connectToServer 核心目的就是拉取 server 端的工具啦
js
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
class MCPClient {
constructor {
this.tools = [];
this.mcp = new Client();
}
/**
* 基于 mcp sdk
* 1. 建立和 client 和 server 端的连接
* 2. 拉取 mcp-server 提供的工具列表(tool list)
*
*/
async connectToServer(serverScriptPath) {
this.transport = new StdioClientTransport({
command: process.execPath,
args: [serverScriptPath],
});
this.mcp.connect(this.transport);
// 核心目的就是拉取 server 端的工具呀
const toolsResult = await this.mcp.listTools();
this.tools = toolsResult.map(() => ...)
}
/**
* 根据给定输入,调用大模型
* 模型会自己感知需要调哪些工具
*
* 在需要工具调用的场景下
* 利用 mcp 提供的能力实现工具执行并拿到结果,然后再次调用大模型就可以拿到最后回答
*/
async processQuery(query) {
}
/**
* 开启一个命令行 chatbot,内部调用 processQuery(query) 拿到模型回答结果
*/
async chatLoop() {
}
}
StdioClientTransport 类
StdioClientTransport 类是 MCP 客户端的传输层实现,负责通过标准输入输出(stdin/stdout)与 MCP 服务器进程通信。
【核心功能】
- 启动服务器进程
- 通过标准输入输出传输消息
- 管理进程生命周期
- 处理错误和关闭事件
简化版伪代码如下
js
class StdioClientTransport {
constructor(server) {
this.process = null;
this.serverConfig = server;
}
async start() {
// 启动子进程
this.process = spawn(this.serverConfig.command, this.serverConfig.args);
// 设置事件监听
this.process.stdout.on('data', (data) => {
// 处理服务器返回的消息
});
this.process.on('error', (error) => {
// 处理错误
});
}
send(message) {
// 发送消息到服务器
this.process.stdin.write(JSON.stringify(message));
}
close() {
// 关闭连接
this.process.kill();
}
}
【核心总结】
结合案例的启动方式:node <path_to_client_script> <path_to_server_script>
这里我们发现:mcp-client 是运行在主进程,mcp-server 是运行在子进程,两者通过 stdio(标准输入输出) 实现进程间通信
接下来再看下 processQuery 的核心实现
js
async processQuery(query) {
// 1. 构建初始消息
const messages = [{ role: "user", content: query }];
// 2. 调用模型获取响应
const response = await this.callModel({
messages,
tools: this.tools
});
// 3. 处理模型响应
const { tool_calls, content } = response.choices[0].message;
// 4. 如果需要调用工具
if (tool_calls) {
// 并行执行所有工具调用
const results = await Promise.all(tool_calls.map(call =>
this.mcp.callTool({
name: call.function.name,
arguments: JSON.parse(call.function.arguments)
})
));
// 5. 将工具结果添加到消息历史
messages.push(...);
// 6. 再次调用模型获取最终答案
return await this.callModel({ messages });
}
// 7. 直接返回内容
return content;
}
MCP-server
server 侧相比 client 要直观的很多:
- 调用 sdk 初始化一个 server 实例
- 注册工具
- 建立连接,把服务跑起来
核心伪代码如下:
js
// Create server instance
const server = new McpServer({
name: "weather",
version: "1.0.0",
});
// 注册工具
server.tool(name, description, schema, handler);
// Start the server
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("MCP Server running on stdio");
}
main()
工具的定义格式
关于工具的定义我们可以统一写成对象声明:
js
{
name: "calculate",
description: "Calculate the sum of two numbers",
schema: {
// 这里定义的是【入参类型】,代码里基于 zod,是一个工具库,可以直观的生成 schema 裸数据
},
handler: async (params) => {
// 函数体内容...
}
};
代码里一共三个工具,前两个都是官方提供的,第三个是自己尝试加了一个工具
- get-alerts:查询天气预警的,会发生实际 http 调用
- get-forecast:查询天气预报的,会发生实际 http 调用
- calculate 简版计算器,本地计算
测试截图
case 1:夏威夷的天气情况查询,包含 1 次工具调用

case 2: 一句话包含 2 次工具调用

case 3:我自己加的计算器工具,测试一下

当然也有一些不太对的地方,比如这里提问 8 的 10次方,ai 回复的工具调用信息不太对,num2 应该也是 8 而非10.
当然我们目前实现中,也不支持多次工具调用

MCP 有啥好处
MCP 提供了 AI 连接外部应用的标准,这个标准大家都认
形成标准之后,各家都去开发自己的 MCP 服务器,例如 github-mcp-server、Google-map-server
设想在标准之前,想让大模型实现获取 github 数据、获取谷歌地图数据,是不是只能手动定制调用他们的开放 API,可能还不一定提供;
另外不同 LLM 提供商返回格式有差别,开发者可能需要手动处理兼容,标准出来之后一定会促进各厂商遵循基础的统一格式,允许开发者方便的在不同 LLM 之间来回切换;
所以我觉得核心的好处就是:标准形成后,生态起来了。
生态起来之后,大家的力量都汇聚在一起,未来在易用性、灵活性、安全性都会做的越来越好。
总结
本文首先结合官网关于 MCP 的概念以及官方的示意图,尝试解读 MCP 的系统流程。
然后结合一个具体的代码实例,阐述了 mcp-client 和 mcp-server 侧的核心实现,并且跑了几个工具调用的case;大家动手把代码跑起来,会有更深的体会。
最后说明了MCP标准出来后带来哪些好处。
当然了,以上都是我目前的个人理解,不一定对,欢迎大家一起参与讨论。
我相信拥有了和外界交互能力的大模型,将会渗透到我们工作和生活的方方面,发挥其更大的能量!