MCP(Model Context Protocol)入门与实践:让AI Agent实现跨进程工具调用

作者:袖梨 2026-07-13

MCP(Model Context Protocol)入门与实践:让 AI Agent 跨进程调用工具

一、问题的起点:Tool 的复用困境

在构建 AI Agent 时,我们经常需要给大模型绑定各种工具(Tool)。比如一个查询用户信息的工具:

MCP(Model Context Protocol)入门与实践:让 AI Agent 跨进程调用工具

const queryUserTool = {name: 'query_user',description: '查询用户信息',// ...}

但这样写存在两个明显的问题:

  1. 只能在这个项目中使用,不好复用——工具代码和 Agent 代码耦合在一起,换个项目就得重写。
  2. 语言绑定问题——如果工具是用 Node.js 写的,那 Java、Python、Rust 的项目怎么用?

核心诉求就是:让 Tool 独立于 LLM,实现本地/远程、跨进程、跨语言的调用。

二、MCP 协议是什么

MCP(Model Context Protocol) 是一种标准化的协议,用于规范 LLM 与 Tool、Resource 之间的通信,核心目标是 解耦 LLM 和 Tool。

通信方式

MCP 支持两种通信模式:

模式传输方式适用场景
stdio标准输入输出流本地跨进程调用
HTTP远程 HTTP 通信远程跨进程调用

本质上,MCP 就是让 Agent 能够跨进程调用工具——不管是本地进程还是远程进程,通过 MCP 协议就能搞定。

与普通 API 调用的区别

MCP 和 fetch 调用接口不一样——它不是去拿接口数据,而是要扩展 Context(工具能力 + 资源知识),让 LLM 在推理时拥有更丰富的上下文。

三、MCP 的核心概念

3.1 Tool(工具)

本质就是工具,和普通的 Function Calling / Tool Use 没有本质差别,区别在于它是跨进程提供的。Agent(MCP Client / Host)通过协议去发现和调用远程进程中的工具——就像"抛饵"出去,让别的进程来执行。

3.2 Resource(资源)

Resource 是 MCP 另一大亮点。它允许 MCP Server 提供静态资源(文档、指南等),这些资源可以作为 System Prompt 的一部分注入到 Context 中。

看一个实际的 Resource 注册例子(来自 my-mcp-server.mjs):

server.registerResource('使用指南','docs://guide', // URI 格式的访问路径{description: '使用指南',mimeType: 'text/plain'},async () => {return {contents: [{uri: 'docs://guide',mimeType: 'text/plain',text: `MCP Server 使用指南功能:提供用户查询等工具。使用:在 Cursor 等 MCP Client 中通过自然语言对话,Cursor 会自动调用相应工具。`}]}})

在 Client 端读取 Resource 的方式:

const res = await mcpClient.listResources();let resourceContent = '';for (const [serverName, resources] of Object.entries(res)) {for (const resource of resources) {const content = await mcpClient.readResource(serverName, resource.uri);resourceContent += content[0].text;}}// 将 resource 内容作为 SystemMessage 注入const messages = [new SystemMessage(resourceContent),// 资源内容成为上下文的一部分new HumanMessage(query),];

四、实战:搭建一个完整的 MCP 调用链路

下面通过实际代码展示一个完整的 MCP 工作流程。架构如下:

┌──────────────┐stdio ┌─────────────────┐│MCP Client│ ◄──────────► │MCP Server ││(LangChain) │跨进程通信 │(Node.js 进程)││││ ││- 获取工具 ││- query_user ││- 获取资源 ││- docs://guide ││- Agent 循环 ││ │└──────────────┘└─────────────────┘

4.1 MCP Server 端

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: '[email protected]', role: 'admin' },'002': { id: '002', name: '光光', email: '[email protected]', role: 'user' },'003': { id: '003', name: '小红', email: '[email protected]', role: 'user' },}}const server = new McpServer({name: 'my-mcp-server',version: '1.0.0'});// 注册工具server.registerTool('query_user', {description: '查询数据库中的用户信息',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} 不存在` }]}}return {content: [{type: 'text',text: `用户 ${user.id} 的信息是:姓名:${user.name}, 邮箱:${user.email}, 角色:${user.role}`}]}});// 注册资源server.registerResource('使用指南', 'docs://guide', { /* ... */ }, async () => { /* ... */ });// 通过 stdio 启动通信const transport = new StdioServerTransport();await server.connect(transport);

关键点:

  • McpServer创建服务实例
  • 通过registerTool注册工具,使用 Zod 定义参数 Schema
  • 通过registerResource注册静态资源
  • 使用StdioServerTransport建立 stdio 通信通道

4.2 MCP Client 端

import { MultiServerMCPClient } from '@langchain/mcp-adapters';import { ChatOpenAI } from '@langchain/openai';const model = new ChatOpenAI({modelName: 'deepseek-v4-flash',// ...配置});// 配置 MCP Client,可以同时连接多个 MCP Serverconst mcpClient = new MultiServerMCPClient({mcpServers: {'my-mcp-server': {command: 'node',// 启动命令args: ['src/my-mcp-server.mjs'],// 脚本路径cwd: '/path/to/mcp-demo'// 工作目录}}});// 获取工具和资源const tools = await mcpClient.getTools();// 获取资源并拼成上下文字符串const res = await mcpClient.listResources();// ...拼接 resourceContent ...// 绑定工具到模型const modelWithTools = model.bindTools(tools);

关键点:

  • MultiServerMCPClient可以同时配置多个MCP Server
  • 每个 Server 配置指定启动命令、参数和工作目录
  • Client 内部通过child_process启动子进程,通过 stdio 通信

4.3 Agent 工具调用循环

async function runAgentWithTools(query, maxIterations = 30) {const messages = [new SystemMessage(resourceContent), // 资源内容作为系统提示new HumanMessage(query),];for (let i = 0; i < maxIterations; i++) {const response = await modelWithTools.invoke(messages);messages.push(response);// 没有工具调用 → 直接返回最终回复if (!response.tool_calls || response.tool_calls.length === 0) {return response.content;}// 执行每个工具调用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, // ⚠️ 必须带上 tool_call_id}));}}}return messages[messages.length - 1].content;}

这个循环有几个值得注意的细节:

  1. SystemMessage 注入 Resource:把 MCP Server 提供的文档资源作为系统提示,让模型"知道得更多"
  2. 最大迭代次数:设置 30 轮上限防止无限循环
  3. tool_call_id必须回传:ToolMessage 必须带上对应的tool_call_id,这是模型关联工具调用和结果的唯一标识
  4. tools.find()匹配:find方法找到第一个匹配项就停止,适合工具名唯一的场景

4.4 资源清理

// 关闭所有 MCP 子进程与通信通道,释放进程资源await mcpClient.close();

这一步很重要! close() 会:

  • 关闭与 MCP Server 的 stdio 连接
  • 终止child_process启动的子进程
  • 避免脚本挂起不退出

五、关键技术细节

5.1 跨进程通信(IPC)的本质

主进程 (Agent/LangChain)││child_process.spawn('node', ['my-mcp-server.mjs'])│├── stdin──►MCP Server 子进程│ (接收工具调用请求)│◄── stdout ──MCP Server 子进程(返回工具执行结果)

  • 父子进程模型:Client 通过child_process启动 Server 作为子进程
  • stdio 通信:请求通过 stdin 发送,响应通过 stdout 返回
  • 多语言支持:任何语言的进程只要实现了 MCP 协议的 stdio 通信,就能被调用

5.2 JavaScript 的异步特性

整个通信链路中,JavaScript 的单线程异步无阻塞模型保证了:

  • 主线程不会被子进程通信阻塞
  • 多个 MCP Server 可以并发通信
  • Agent 循环中的每轮推理和工具调用都是异步的

5.3 Object.entries 的妙用

在处理 MCP 返回的资源列表时,Object.entries() 非常实用:

// MCP 返回的是按 Server 分组的资源对象const res = {'my-mcp-server': [{ uri: 'docs://guide', name: '使用指南' },{ uri: 'docs://api', name: 'API文档' },]};// Object.entries 拆解为 [key, value] 遍历for (const [serverName, resources] of Object.entries(res)) {for (const resource of resources) {// 逐个读取资源内容}}

六、总结

维度传统 ToolMCP Tool
复用性绑定在项目中独立进程,任意项目可用
跨语言仅限同语言通过 stdio/http,任意语言
通信方式同进程函数调用跨进程(本地 stdio / 远程 HTTP)
扩展性手动添加配置式添加 MCP Server
上下文丰富仅 ToolTool + Resource + Prompt

MCP 协议的核心价值在于:

  1. 解耦:Tool 和 LLM 彻底分离,各自独立开发、部署、迭代
  2. 跨进程:不管是本地子进程还是远程服务,统一通过 MCP 协议通信
  3. 跨语言:Node.js 写的工具可以被 Python Agent 调用,反之亦然
  4. 标准化:统一的 Tool 定义、Resource 提供、通信方式,生态可共享

相关文章

精彩推荐