MCP协议深度解析:代码实例解析跨进程工具调用
时间:2026-07-18 | 作者:夜鞌不睡 | 阅读:0MCP协议深度解析:从代码实例看跨进程工具调用
在AI Agent的实践中,如何让模型调用外部工具一直是个核心问题。传统方式把工具硬编码在项目里,不仅复用性差,还绑死了语言环境。但MCP协议的出现,彻底改变了这一局面。今天,咱们就从代码实例出发,聊聊MCP是如何实现跨进程工具调用的。
一、MCP协议的核心思想
从readme.md中我们可以提炼出MCP协议的核心价值:
想象一下,在没有MCP之前,我们的工具调用是什么样的?
// 传统方式:工具被硬编码在项目中
function queryUser(userId) {
// 只能在当前项目中使用
return database.users[userId];
}
// LLM调用时必须通过固定接口
const result = queryUser('001');
这种方式存在两个致命问题:
- 工具无法复用 - 每个项目都需要重新实现
- 语言绑定 - Node.js项目只能用Node.js写的工具
而MCP的出现,彻底改变了这一局面。下面这张图清晰地展示了MCP协议在跨语言调用中的桥梁作用:
二、MCP的两种通信方式
MCP协议支持两种通信方式,分别对应不同的使用场景:
| 通信方式 | 适用场景 | 特点 |
|---|---|---|
| stdio | 本地进程间通信 | 低延迟、无需网络、适合本地工具 |
| HTTP | 远程进程间通信 | 支持分布式部署、适合微服务架构 |
下面的架构图展示了MCP Client与MCP Server之间的完整调用关系:
三、MCP Server端实现解析
让我们从my-mcp-server.mjs开始,看看一个MCP Server是如何构建的:
3.1 创建MCP Server实例
const server = new McpServer({
name: 'my-mcp-server',
version: '1.0.0',
});
这里创建了一个名为my-mcp-server的MCP服务器实例。这个命名很重要,因为在客户端配置时需要通过这个名字来引用该服务器。
3.2 注册Tool
server.registerTool('query_user', {
description: `查询数据库中的用户信息.输入用户ID,返回该用户的详细信息(姓名,邮箱,角色)`,
inputSchema: z.object({
userId: z.string().describe('用户ID, 例如: 001')
})
}, async ({ userId }) => {
// 工具实现逻辑
});
这里注册了一个名为query_user的工具,包含了三个关键要素:
- 工具名称:
query_user- 用于客户端调用时的标识 - 工具描述:告诉LLM这个工具是做什么的,什么时候应该使用它
- 输入Schema:使用Zod定义参数结构,确保类型安全
3.3 工具实现与返回结果
const user = database.users[userId];
if(!user) {
return {
content:[{
type: 'text',
text: `用户 ${userId} 不存在. 可用的用户ID: ${Object.keys(database.users).join(', ')}`,
}]
}
}
return {
content:[{
type: 'text',
text: `用户 ${user.id} 信息: ${JSON.stringify(user)}`
}]
}
这里返回的结果格式遵循MCP协议规范,所有结果都封装在content数组中,每个内容项可以指定类型(如text)。
3.4 启动服务器(进程通信)
const transport = new StdioServerTransport();
await server.connect(transport);
这是最关键的一步!StdioServerTransport使用标准输入输出流(stdio)进行进程间通信。这意味着:
- Server进程通过
stdin接收请求 - Server进程通过
stdout返回响应 - 所有错误信息输出到
stderr
结合第二张架构图,这里的StdioServerTransport对应图中"本地MCP Server"与"MCP Client"之间的stdio连接。
四、MCP Client端实现解析
再看langchain-mcp-test.mjs,这是一个使用LangChain框架的MCP客户端:
4.1 配置MultiServerMCPClient
const mcpClient = new MultiServerMCPClient({
mcpServers: [{
'my-mcp-server':{
command: 'stdio://localhost:3000',
args: [],
},
},],
});
这里配置了一个MCP服务器连接。注意command: 'stdio://localhost:3000',虽然看起来像URL,但实际上是告诉客户端通过stdio方式连接到本地进程。
4.2 获取工具并绑定到模型
const tools = await mcpClient.getTools();
const modelWithTools = model.bindTools(tools);
这是LangChain框架的优雅之处:
mcpClient.getTools()自动从MCP Server获取所有可用的工具定义model.bindTools(tools)将这些工具绑定到LLM模型上
4.3 执行Agent
async function runAgentWithTools(query, maxIterations=30) {
const messages = [
new SystemMessage({
content: '你是一个专业的数据库查询助手. 你可以根据用户的问题,查询数据库中的用户信息. 你只能查询用户信息, 不能执行其他操作.',
}),
new HumanMessage({
content: query,
}),
];
const response = await modelWithTools.invoke(messages);
console.log(chalk.green('Response:'));
console.log(response.content);
}
这里的核心流程是:
- 构造对话消息(系统提示 + 用户问题)
- 调用绑定了工具的模型
- 模型自动判断是否需要调用工具
- 如果需要,MCP Client会通过stdio与Server通信执行工具
- 工具执行结果返回给模型
- 模型生成最终回复
五、完整的跨进程调用流程
结合两张架构图和两端代码,完整的调用流程如下:
┌─────────────────────────────────────────────────────────────────┐
│ Agent进程 (MCP Client) │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ 1. 用户提问: "查询用户001的信息" │ │
│ │ 2. LLM分析: 需要使用query_user工具 │ │
│ │ 3. MCP Client通过stdio发送: { │ │
│ │ "tool": "query_user", │ │
│ │ "params": {"userId": "001"} │ │
│ │ } │ │
│ └──────────────────────────┬───────────────────────────────┘ │
│ │ │
│ ┌───────▼────────┐ │
│ │ stdio通信 │ │
│ │ (跨进程) │ │
│ └───────┬────────┘ │
└─────────────────────────────┼────────────────────────────────────┘
│
┌─────────────────────────────▼────────────────────────────────────┐
│ MCP Server进程 (my-mcp-server.mjs) │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ 1. 接收stdin请求 │ │
│ │ 2. 解析请求: tool=query_user, userId=001 │ │
│ │ 3. 执行数据库查询: database.users['001'] │ │
│ │ 4. 通过stdout返回结果: {"id":"001","name":"张三"...} │ │
│ └──────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
参考第一张图,这个流程体现了MCP如何桥接不同语言环境——LLM(Ja vaScript环境)通过协议调用Ja va进程,完全不需要关心对方的语言实现细节。而第二张图则展示了整体架构中,MCP Client如何同时管理本地和远程的多个MCP Server。
六、MCP的核心优势
6.1 跨语言支持(对应图一)
MCP使用stdio作为通信基础,而所有编程语言都支持标准输入输出。这就意味着:
- Node.js可以调用Python写的工具
- Ja va可以调用Rust写的工具
- 任何语言的组合都可以通过MCP协作
如图一所示,无论是通过stdio调用本地Ja va进程,还是通过HTTP调用远程Ja va服务,MCP协议都提供了统一的调用方式。
6.2 跨进程隔离(对应图二)
从图二可以看出,MCP Server独立于MCP Client运行,带来两个好处:
- 稳定性 - 工具崩溃不会影响Agent主进程
- 安全性 - 工具只能访问被授权的资源
6.3 标准化通信
MCP定义了统一的协议格式:
- 请求格式:
{ tool: string, params: object } - 响应格式:
{ content: [{ type: string, text: string }] }
这使得任何兼容MCP的工具都可以被任何MCP客户端调用。
6.4 本地与远程统一(对应图二)
如图二所示,通过切换Transport类型,工具可以无缝地从本地迁移到远程:
// 本地调用 - 使用stdio(对应图二左侧)
const transport = new StdioServerTransport();
// 远程调用 - 使用HTTP/SSE(对应图二右侧)
// const transport = new SSEClientTransport(new URL('http://remote-server:3000'));
七、总结
MCP协议的本质是一个进程间通信协议,专门为AI Agent与外部工具、资源的交互而设计。它通过标准化的通信格式和多种传输方式(stdio/HTTP),实现了:
- 工具与LLM的解耦 - 工具可以独立开发、部署
- 跨语言调用 - 任何语言都可以实现MCP Server(如图一所示,Ja va工具可被Node.js LLM调用)
- 跨进程安全 - 工具在独立进程中运行
- 本地远程统一 - 同样的代码可以本地或远程部署(如图二所示)
从代码实例和架构图中我们可以看到,MCP不是简单的一个API调用,而是一个完整的生态系统。它让AI Agent能够以标准化的方式扩展自己的能力边界——无论工具是用什么语言写的、部署在本地还是远程,只要实现了MCP协议,就能被AI Agent轻松调用。这种"语言无关、位置透明"的设计理念,正是MCP协议最核心的价值所在。
免责声明:文中图文均来自网络,如有侵权请联系删除,心愿游戏发布此文仅为传递信息,不代表心愿游戏认同其观点或证实其描述。
相关文章
更多-
- 年AI Agent编程爆发:MCP协议与多智能体开发趋势
- 时间:2026-08-17
-
- MCP协议迎来最大改版:彻底无状态并适配Serverless
- 时间:2026-08-13
-
- MCP协议落地催生Agent开发新范式代码级实践指南
- 时间:2026-07-28
-
- MCP协议重塑AI接入方式,被低估的关键场景
- 时间:2026-07-24
-
- 谷歌A2A协议怎么用?A2A智能体协议和MCP有什么不同?
- 时间:2025-04-29
-
- 谷歌A2A协议和MCP协议有什么区别?A2A和MCP的差异是什么?
- 时间:2025-04-24
-
- 国内MCP服务有哪些?MCP服务器搜索引擎哪家好?
- 时间:2025-04-11
-
精选合集
更多大家都在玩
大家都在看
更多-
- 糖尿病完全不能吃糖吗
- 时间:2026-09-15
-
- 蚂蚁庄园小课堂2026年9月16日最新题目答案
- 时间:2026-09-15
-
- 小鸡答题今天的答案是什么2026年9月16日
- 时间:2026-09-15
-
- 蚂蚁庄园每日答题答案2026年9月16日
- 时间:2026-09-15
-
- 以下哪种粮食是酿造绍兴黄酒的主要原料 蚂蚁庄园今日答案9月16日
- 时间:2026-09-15
-
- 劝学名句“及时当勉励,岁月不待人”出自哪位诗人 蚂蚁庄园今日答案9.16
- 时间:2026-09-15
-
- 蚂蚁庄园今天答题答案2026年9月16日
- 时间:2026-09-15
-
- 蚂蚁庄园答题今日答案2026年9月16日
- 时间:2026-09-15