MCP 初识到实操:打造 AI 的“USB-C”接口,让大模型真正“手眼通天”
导读:大模型很聪明,但它被关在“沙盒”里,看不见你的文件,动不了你的数据库。如何打破这层壁垒?Anthropic 推出的 MCP (Model Context Protocol) 给出了标准答案。本文将带你从理论到实战,用 Node.js 手写一个 MCP Server,并让它无缝接入 Cursor 和 LangChain,实现真正的“AI 自动化”。
一、痛点:LLM 的“残疾”与 Tool 的局限
我们早已习惯让大模型写代码、做规划。但你是否发现,传统的 LLM with Tools 模式存在天然瓶颈:
- 语言绑定:如果你的 Agent 是 Node.js 写的,那你的 Tool 也得是 JS/TS。那些用 Python 写的数据分析脚本、用 Rust 写的高性能计算器,难道要全部重写?
- 耦合严重:Tool 的逻辑硬编码在 Agent 项目里,每次新增功能都要重启主进程,难以复用。
- 部署困难:想在公司内部推广一个“查询内网数据库”的 Tool,难道要让每个员工的 Agent 都安装一遍依赖?
我们需要一种“插件化”的架构:Agent 只负责思考,具体的“手脚”(工具)可以独立开发、独立部署、热插拔。
这就是 MCP (Model Context Protocol) 诞生的背景。
二、什么是 MCP?AI 世界的 USB-C
MCP (Model Context Protocol) 是由 AI 巨头 Anthropic 于 2024 年 11 月发起,并在 2025 年 12 月正式捐赠给 Linux 基金会 下属的 Agentic AI Foundation (AAIF) 维护的开放标准协议。
你可以把它理解为 AI 领域的 USB-C 接口:
- 以前:每个设备(AI 应用)都需要特定的转接头(定制代码)才能连接打印机(工具)。
- 现在:只要设备支持 USB-C(MCP 协议),任何符合标准的打印机(MCP Server)插上就能用。
1. 核心架构:三位一体
MCP 架构清晰地将角色分为三类:
| 角色 | 英文名称 | 职责比喻 | 典型代表 |
|---|---|---|---|
| Host | MCP Host | 大脑/宿主:运行大模型,决定何时调用工具。 | Cursor, Claude Desktop, 自定义 Agent |
| Client | MCP Client | 翻译官:内置在 Host 中,负责通过协议与 Server 通信。 | (SDK 内部自动处理) |
| Server | MCP Server | 手脚/服务:封装具体能力(读写文件、查库、调用 API),对外暴露标准接口。 | 你写的 my-mcp-server.mjs |
2. 通信方式:本地与远程的统一
MCP 最强大的地方在于它屏蔽了底层通信的差异:
-
本地通信 (Stdio):
- 场景:Cursor 调用你本地的脚本。
- 原理:类似“投币售货机”。Host 启动 Server 进程,通过 标准输入输出流 (stdin/stdout) 传递 JSON 消息。
- 优势:零网络延迟,无需开放端口,安全性高。
-
远程通信 (HTTP/SSE):
- 场景:云端 Agent 调用公司内部的数据库服务。
- 原理:Server 作为一个 Web 服务运行,Host 通过 HTTP 请求交互。
- 优势:跨机器、跨网络,适合微服务架构。
三、实战:手把手编写第一个 MCP Server
我们将用 Node.js 编写一个最简单的 MCP Server,提供两个核心能力:
- Tool:
query-user(查询用户信息)。 - Resource:
使用指南(供模型阅读的文档)。
1. 环境准备
确保已安装 Node.js (v18+),初始化项目并安装 SDK:
mkdir my-mcp-server
cd my-mcp-server
npm init -y
npm install @modelcontextprotocol/sdk zod
2. 代码实现 (my-mcp-server.mjs)
新建文件 my-mcp-server.mjs,填入以下代码。注意注释中的关键点:
#!/usr/bin/env node
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { z } from 'zod';
// 1. 模拟数据库
const database = {
users: {
"001": { id: "001", name: "张三", email: "zhangsan@example.com", role: "admin" },
"002": { id: "002", name: "李四", email: "lisi@example.com", role: "user" },
"003": { id: "003", name: "王五", email: "wangwu@example.com", role: "user" },
}
};
// 2. 创建 Server 实例
const server = new McpServer({
name: 'demo-user-server',
version: '1.0.0',
});
// 3. 注册 Tool (工具)
// 核心:定义名字、描述、参数 Schema (Zod),以及执行函数
server.registerTool('query-user', {
description: '查询数据库中的用户信息。输入用户ID, 返回该用户的详细信息(姓名、邮箱、角色)。',
inputSchema: {
// 使用 Zod 定义参数格式,模型会自动遵循此格式传参
userId: z.string().describe("用户 ID, 例如:001, 002, 003")
}
}, async ({ userId }) => {
// ⚠️ 注意:这里不要使用 console.log,会污染通信协议!
// 调试请只用 console.error
console.error(`[DEBUG] Received request for user: ${userId}`);
const user = database.users[userId];
if (!user) {
return {
content: [
{ type: 'text', text: `❌ 用户ID ${userId} 不存在。可用的ID: 001, 002, 003` }
]
};
} else {
return {
content: [
{
type: 'text',
text: `✅ 找到用户:\n- ID: ${user.id}\n- 姓名: ${user.name}\n- 邮箱: ${user.email}\n- 角色: ${user.role}`
}
]
};
}
});
// 4. 注册 Resource (资源)
// 资源不是用来“执行”的,而是给模型“阅读”的上下文(类似 RAG)
server.registerResource('使用指南', 'docs://guide', {
description: 'MCP Server 使用文档,包含功能说明和使用建议。',
mimeType: 'text/plain',
}, async () => {
return {
contents: [
{
uri: 'docs://guide',
mimeType: 'text/plain',
text: `MCP Server 使用指南\n功能:提供用户查询工具。\n使用:在对话中直接问“帮我查一下 001 号用户”,系统会自动调用 query-user 工具。`,
}
]
};
});
// 5. 启动服务
// 使用 Stdio 传输层,通过标准输入输出与 Host 通信
const transport = new StdioServerTransport();
await server.connect(transport);
console.error('✅ MCP Server started successfully via Stdio.');
3. 关键注意点 (避坑指南)
- 严禁
console.log:MCP 通过stdout传输 JSON。如果你打印了"Server starting...",客户端解析 JSON 会失败,导致连接立即断开 (Client closed)。调试请务必使用console.error。 - Zod 是必须的:
inputSchema必须使用 Zod 定义,这是模型理解参数类型的唯一依据。 - 路径问题:Windows 用户在配置路径时,建议使用正斜杠
/或双反斜杠\\,避免转义错误。
四、部署与集成:让 AI 真正用起来
写好 Server 只是第一步,关键在于如何让它被 AI 调用。
场景 A:在 Cursor / Claude Desktop 中直接使用 (本地 Stdio)
这是最典型的用法,让编辑器直接拥有查询数据库的能力。
- 找到配置文件:
- 在cursor中找到设置进入 tools &mcp设置
- 点击新增一个mcp server
- 进入host json 文件进行配置
-
添加配置: 将以下配置加入 JSON 文件中。注意替换为你本地的绝对路径。
{ "mcpServers": { "user-db-lookup": { "command": "node", "args": [ "D:/workspace/my-mcp-server/my-mcp-server.mjs" ], "env": { "NODE_ENV": "production" } } } } -
验证:
- 重启 Cursor/Claude Desktop。
- 在对话框输入:“帮我查一下用户 002 的邮箱是多少?”
- 现象:AI 会自动识别意图,调用
query-user工具,并返回结果:“李四的邮箱是 lisi@example.com”。
场景 B:在 LangChain Agent 中集成 (代码级调用)
如果你想在自己的 Python/Node Agent 程序中复用这个 MCP Server,可以使用官方适配器。
安装适配器:
pnpm install @langchain/mcp-adapters
# 或者 python
pip install langchain-mcp-adapters
代码示例 (TypeScript):
import { McpClient } from '@langchain/mcp-adapters';
import { AgentExecutor, createReactAgent } from "langchain/agents";
// ... 其他导入
async function runAgent() {
// 1. 连接到本地 MCP Server
const client = new McpClient({
command: "node",
args: ["D:/workspace/my-mcp-server/my-mcp-server.mjs"],
transportType: "stdio"
});
await client.connect();
// 2. 将 MCP Tools 转换为 LangChain Tools
const tools = await client.getTools();
// 3. 构建 Agent
const agent = createReactAgent({
llm: model,
tools: tools, // 这里直接注入了 MCP 提供的能力
prompt: promptTemplate
});
const executor = new AgentExecutor({ agent, tools });
// 4. 执行
const result = await executor.invoke({ input: "查一下 001 号用户是谁?" });
console.log(result.output);
await client.close();
}
意义:看!你的 LangChain 程序完全不需要知道数据库怎么查,也不需要引入 mysql 驱动。它只是调用了一个标准的 MCP Tool。解耦就此实现。
五、总结与展望
为什么 MCP 是必然趋势?
- 语言无关性:你可以用 Rust 写高性能计算 Server,用 Python 写数据分析 Server,用 Go 写网络爬虫 Server,然后让一个 JS 写的 Agent 统一调度它们。
- 安全边界:Server 运行在独立进程中,即使崩溃或被攻击,也不会影响 Host (主程序) 的稳定性。
- 生态爆发:随着 Linux 基金会的介入,未来会出现海量的公共 MCP Server(如“查询天气”、“操作 Jira”、“读取 Notion”),开发者只需“插拔”即可使用,无需重复造轮子。
下一步做什么?
- 尝试 HTTP 传输:将
StdioServerTransport改为StreamableHTTPServerTransport,把你的 Tool 发布为局域网服务。 - 探索 Resources & Prompts:除了 Tool,利用 Resource 注入私有知识库,利用 Prompt 模板规范模型行为。
- 贡献社区:将你现有的脚本封装成 MCP Server,分享给团队。
MCP 不仅仅是一个协议,它是 AI Agent 从“聊天机器人”进化为“超级员工”的基础设施。 现在,就动手写下你的第一个 Server 吧!