MCP 跨进程工具调用深度解析:从 LangChain Tool 的局限到自定义数据库查询 Server 实战

MCP 跨进程工具调用深度解析:从 LangChain Tool 的局限到自定义数据库查询 Server 实战

之前用 LangChain 的 tool() 注册的工具,只能在本项目里用。如果你想用 Java 写一个数据库查询工具、用 Python 写一个数据分析工具,然后让同一个 AI Agent 调用它们------怎么办?MCP 协议就是答案。本文从 Tool 的局限性出发,深入解析 MCP 跨进程通信的本质,并用 45 行代码实现一个完整的数据库查询 MCP Server。


前言

在前面的文章中,我们用 LangChain 的 tool() 函数注册了 read_fileexecute_command 等工具。它们工作得很好,但有一个根本性局限------只能在同一个 Node.js 进程中运行

这意味着:

  • 你用 Java 写的数据库连接工具,Node.js Agent 调不了
  • 你用 Python 写的数据分析脚本,Claude Code 用不了
  • 你在 A 项目里写的工具,B 项目里不能直接复用

MCP(Model Context Protocol) 正是为了解决这个问题而诞生的。它让 AI Agent 能够跨进程、跨语言地调用任何工具。


一、LangChain Tool 的局限性

1.1 回顾:LangChain tool() 的工作方式

javascript 复制代码
import { tool } from '@langchain/core/tools';

const readFileTool = tool(
    async ({ filePath }) => {
        const content = await fs.readFile(filePath, 'utf-8');
        return content;
    },
    {
        name: 'read_file',
        description: '读取文件内容',
        schema: z.object({ filePath: z.string() })
    }
);

// 工具直接注册到模型
const modelWithTools = model.bindTools([readFileTool]);

1.2 三个核心问题

问题 说明
项目绑定 read_file 只能在当前 Node.js 项目中使用,换一个项目就得重写
语言绑定 只能用 JavaScript/TypeScript 编写,无法调用 Java/Python/Rust 工具
进程绑定 工具函数和 LLM 运行在同一个进程中,无法独立部署、独立扩展
yaml 复制代码
LangChain Tool 的局限:

  Agent(Node.js 进程)
  ├── tool1: read_file      ← 写死在代码里
  ├── tool2: write_file     ← 写死在代码里
  └── tool3: exec_command    ← 写死在代码里

  想加一个 Java 工具?→ 不行,进程语言是 Node.js
  想在另一个项目复用?→ 不行,代码耦合在项目里

1.3 理想方案:工具独立于 LLM

css 复制代码
理想架构:

  AI Agent(任意语言/框架)
       ↓ MCP 协议
  Tool A(Node.js)── 查询数据库
  Tool B(Python)─── 数据分析
  Tool C(Java)───── 调用内部 API
  Tool D(Rust)───── 高性能计算

  工具独立开发、独立部署、独立运行
  Agent 通过统一协议调用,不关心工具用什么语言写的

二、MCP 协议:跨进程工具调用的标准化方案

2.1 MCP 的核心定义

ini 复制代码
MCP = Model Context Protocol(模型上下文协议)

目标:标准化 LLM 与 Tool / Resource 之间的通信
本质:让 LLM 和 Tool 解耦

MCP 不是工具、不是框架、不是 SDK------它是一个通信协议,就像 HTTP 是浏览器和服务器之间的通信协议一样。

2.2 MCP 的两种通信方式

通信方式 适用场景 底层技术
stdio 本地跨进程调用 标准输入输出流(stdin/stdout)
HTTP/SSE 远程跨进程调用 网络请求
stdio 本地通信
c 复制代码
AI Agent(父进程)
    ↓ spawn 启动子进程
MCP Server(子进程)
    ├── 通过 stdin 接收 Agent 的请求(JSON-RPC)
    └── 通过 stdout 返回结果(JSON-RPC)

底层原理 :当 Agent 启动一个 MCP Server 子进程时(类似 child_process.spawn),两者通过标准输入输出流通信。Agent 向子进程的 stdin 写入 JSON-RPC 消息,子进程处理后将结果写入 stdout。

HTTP 远程通信
arduino 复制代码
AI Agent(客户端)
    ↓ HTTP 请求
远程 MCP Server(服务器)
    ├── 接收请求
    ├── 调用工具
    └── 返回结果

2.3 MCP 和 fetch 的区别

笔记中有一个精准的区分:

维度 fetch / HTTP API MCP
目的 获取数据(接口调用) 扩展 Context(工具 + 资源)
协议 HTTP + REST JSON-RPC over stdio/HTTP
通信方向 单次请求-响应 持续的双向通信
能力 只能传数据 可以注册工具、暴露资源、持续交互

MCP 不是"调接口拿数据",而是"给模型装上新的能力"------它扩展的是 Context(上下文),让 LLM 能做的更多(Tool)、知道的更多(Resource)。

2.4 MCP 的本质

复制代码
MCP 的本质 = 给 Model 扩展 Context

  Tool(工具)  → 让 LLM 能做更多(读文件、查数据库、发邮件)
  Resource(资源)→ 让 LLM 知道更多(内部文档、数据、配置)

三、实战:自定义数据库查询 MCP Server

3.1 项目结构

perl 复制代码
my-mcp-server/
├── my-mcp-server.mjs   # MCP Server 代码(45行)
├── package.json
└── .mcp.json           # Claude Code 接入配置

3.2 完整代码

my-mcp-server.mjs

javascript 复制代码
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: 'zh@qq.com', role: 'admin' },
    '002': { id: '002', name: '光光', email: 'gg@qq.com', role: 'user' },
    '003': { id: '003', name: '小红', email: 'xh@qq.com', role: 'user' },
  }
};

// 创建 MCP Server 实例
const server = new McpServer({
  name: 'my-mcp-server',
  version: '1.0.0'
});

// 注册工具
server.registerTool('query_user', {
  description: `查询数据库中的用户信息。输入用户ID, 
    返回该用户的详细信息(姓名、邮箱、角色)`,
  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} 不存在。
        可用的ID: 001, 002, 003`}
      ]
    };
  }
  return {
    content: [
      { 
        type: 'text', 
        text: `用户 ${user.id} 的信息是:
        姓名:${user.name}, 邮箱:${user.email}, 角色:${user.role}`
      }
    ]
  };
});

// 启动 stdio 传输
const transport = new StdioServerTransport();
await server.connect(transport);

3.3 代码逐行解析

第一步:模拟数据库

javascript 复制代码
const database = {
  users: {
    '001': { id: '001', name: '祖豪', email: 'zh@qq.com', role: 'admin' },
    // ...
  }
};

这里用内存对象模拟数据库。实际项目中,可以替换为 MySQL、PostgreSQL、MongoDB 等真实数据库连接。MCP Server 的魅力就在于此------底层实现可以随意替换,对 Agent 来说接口不变

第二步:注册工具(registerTool vs tool)

注意这里用的是 server.registerTool,不是 LangChain 的 tool()

对比项 LangChain tool() MCP server.registerTool()
注册位置 LangChain 模型绑定 MCP Server 实例
调用方式 同进程函数调用 跨进程 JSON-RPC
语言限制 JavaScript only 任何语言(有 MCP SDK 即可)
复用性 项目内 跨项目

第三步:输入参数

javascript 复制代码
inputSchema: {
  userId: z.string().describe('用户ID, 例如:001, 002, 003')
}

Zod Schema 定义了工具接受的参数类型和描述。MCP SDK 会将其自动转换为 JSON Schema 发送给 LLM。

第四步:返回格式

javascript 复制代码
return {
  content: [
    { type: 'text', text: '...' }
  ]
};

MCP 标准返回格式:content 数组,每个元素包含 typetext(或 image)。

3.4 接入 Claude Code

.mcp.json

json 复制代码
{
  "mcpServers": {
    "my-db": {
      "type": "stdio",
      "command": "node",
      "args": ["/path/to/my-mcp-server.mjs"]
    }
  }
}

使用方式

perl 复制代码
⟩ 使用 my-db 工具查询用户 001 的信息

Claude Code 会自动:

  1. 分析请求 → 选择 my-db MCP Client
  2. 生成调用参数 { "userId": "001" }
  3. 通过 stdin 发送给我们的 MCP Server
  4. Server 查询数据库 → 返回结果
  5. LLM 基于结果生成回答

四、LangChain Tool vs MCP Tool 对比

4.1 架构对比

arduino 复制代码
LangChain Tool(同进程):

  ┌─────────────────────────────┐
  │  Node.js 进程               │
  │                             │
  │  LLM ←→ tool() ←→ 函数体    │
  │       全部在一个进程内        │
  └─────────────────────────────┘


MCP Tool(跨进程):

  ┌─────────────────────┐      ┌─────────────────────┐
  │  Agent 进程(Host)    │      │  MCP Server 进程      │
  │                     │      │                     │
  │  LLM ←→ MCP Client │ ←──→ │  MCP Server ←→ 工具  │
  │                     │ stdio│                     │
  └─────────────────────┘      └─────────────────────┘

4.2 完整对比表

维度 LangChain Tool MCP Tool
通信方式 同进程函数调用 跨进程 JSON-RPC
语言支持 JavaScript only Node.js / Python / Java / Rust ...
复用性 项目内绑定 跨项目、跨 Agent 复用
部署 随 Agent 一起部署 独立部署、独立运行
数据源 内存中的函数 任意(数据库、API、文件)
适用场景 快速原型、简单工具 生产环境、复杂工具链

4.3 何时用哪种?

场景 推荐
项目 Demo / 快速验证 LangChain tool()
只需在本项目使用的简单工具 LangChain tool()
需要跨项目复用的工具 MCP Server
需要用其他语言编写的工具 MCP Server
需要独立部署的数据库/API 工具 MCP Server
多个 Agent 共享同一套工具 MCP Server

五、从内存数据库到真实数据库

5.1 替换为 MySQL

javascript 复制代码
import mysql from 'mysql2/promise';

const pool = mysql.createPool({
  host: 'localhost',
  user: 'root',
  password: 'password',
  database: 'my_app'
});

server.registerTool('query_user', {
  description: '查询 MySQL 数据库中的用户信息',
  inputSchema: {
    userId: z.string().describe('用户ID')
  }
}, async (userId) => {
  const [rows] = await pool.execute(
    'SELECT id, name, email, role FROM users WHERE id = ?', [userId]
  );
  if (rows.length === 0) {
    return { content: [{ type: 'text', text: `用户 ${userId} 不存在` }] };
  }
  const user = rows[0];
  return {
    content: [{
      type: 'text',
      text: `姓名:${user.name}, 邮箱:${user.email}, 角色:${user.role}`
    }]
  };
});

关键点 :MCP Server 的接口(注册工具、返回格式)不变,只是底层实现从内存对象换成了 MySQL 连接。Agent 完全感知不到这个变化------这就是协议解耦的威力。


知识树

vbscript 复制代码
MCP 跨进程工具调用深度解析
├── LangChain Tool 的局限
│   ├── 项目绑定(只能在本项目用)
│   ├── 语言绑定(只能用 JS)
│   └── 进程绑定(和 LLM 在同一进程)
├── MCP 协议
│   ├── Model Context Protocol(模型上下文协议)
│   ├── 核心目标:LLM 与 Tool/Resource 解耦
│   ├── 两种通信方式
│   │   ├── stdio(本地跨进程:stdin/stdout)
│   │   └── HTTP/SSE(远程跨进程)
│   ├── MCP vs fetch(扩展 Context vs 获取数据)
│   └── MCP 本质:给 Model 扩展 Context
├── 实战:数据库查询 MCP Server
│   ├── 模拟数据库 → 可替换为 MySQL/PG
│   ├── server.registerTool() 注册工具
│   ├── Zod Schema 参数定义
│   ├── MCP 标准返回格式(content 数组)
│   └── .mcp.json 接入 Claude Code
├── LangChain Tool vs MCP Tool 对比
│   ├── 同进程 vs 跨进程
│   ├── JS only vs 多语言
│   └── 何时用哪种
└── 进阶方向
    ├── 替换为真实数据库(MySQL)
    ├── 添加更多工具(增删改查)
    └── 多 Agent 共享同一 MCP Server

结语

从 LangChain tool() 到 MCP server.registerTool(),表面上看只是换了一种注册方式,本质上是一次架构升级

  • tool()进程内函数调用------简单直接,但受限于语言和项目
  • registerTool()跨进程协议通信------复杂一些,但打破了所有边界

理解了这个区别,你就理解了 MCP 的核心价值:让工具独立于 LLM,让能力可以被任何 Agent 复用

45 行代码,一个跨进程、可复用的数据库查询工具。这就是 MCP 协议的力量------工具不再被锁在某个项目里,而是成为所有 AI Agent 的公共能力。


参考与拓展阅读:

  • MCP 官方规范(Model Context Protocol Specification)
  • @modelcontextprotocol/sdk GitHub 仓库
  • 《MCP 协议深度解析》------ AI 界的 USB-C 实战指南
  • 《手写 MCP Server 全栈实战》------ Node.js + Zod 构建文件读取工具
  • 《从零手写 AI Agent》------ LangChain + Tool 入门

如果本文帮你理解了 MCP 跨进程工具调用的原理和实战方法,欢迎点赞 + 收藏。你打算用 MCP 构建什么工具?欢迎在评论区分享你的想法 👇

#MCP #Node.js #跨进程通信 #AIAgent #ToolCalling #掘金技术社区

相关推荐
用户2930750976696 小时前
深入理解 MCP 协议:让 AI Agent 工具调用突破边界
mcp
孟无岐6 小时前
从零到可玩:用 WorkBuddy + TapTap 制造做出一款放置挂机游戏(完整实战记录)
mcp·ai游戏开发·workbuddy·taptap制造·放置挂机游戏·零代码游戏
冬奇Lab18 小时前
MCP 系列(01):MCP 是什么——为什么 Function Calling 不够
人工智能·llm·mcp
Esaka_Forever20 小时前
MCP Prompt(模型上下文协议 提示模板)完整详解
mcp
元Y亨H1 天前
Model Context Protocol (MCP) 全方位解析
llm·mcp
love530love1 天前
GPT + Codex CLI + CodexPro 三位一体:AI 编程协作工作流
人工智能·windows·gpt·chatgpt·devops·mcp
VIP_CQCRE1 天前
把 Ace Data Cloud 接入 AI 助手:MCP 平台管理与文档检索实战
ai·大模型·api·mcp·acedatacloud
cypking2 天前
TypeScript 开发AI Agents MCP 完整实战指南
mcp
Esaka_Forever2 天前
MCP 协议 Resources(资源)完整解读
mcp