🚀 拒绝“手搓”工具!带你硬核手写 MCP Server,解锁 Agent 的无限潜能

大家好!我是你们的 AI 领路人。👋

最近 AI 圈子里什么最火?除了各种新出的 LLM 模型,那绝对就是 Agent(智能体) 了。我们都知道一个公式:

LLM + Tools = Agent 🤖🛠️

当大模型学会了使用工具(Tools),它就从一个只会"嘴炮"的聊天机器人,进化成了能真正干活的超级助手。尝到甜头了吧?我也尝到了。

但是! 随着业务越来越复杂,我们发现了一个巨大的痛点:自己手写 Tool 实在是太累了!且完全不够用! 😫

  • 想连数据库?写个 Tool。
  • 想调 Python 脚本?写个 Tool。
  • 想用公司内部的 API?还得写 Tool。
  • 如果是跨语言、跨进程、甚至是远程服务呢?头都要炸了!🤯

就在我们被各种 glue code(胶水代码)淹没的时候,MCP (Model Context Protocol) 横空出世,宛如一道闪电划破夜空!⚡️

今天,我们就以"MCP的理解学习"为主题,结合实战代码,带大家彻底搞懂这个让 Cursor、Trae 等编辑器如虎添翼的协议,并手把手写一个属于你的 MCP Server!


🧐 第一部分:MCP 到底是个啥?

在动手写代码之前,我们先来聊聊 MCP 的"前世今生"。

1.1 MCP 的由来

MCP 全称 Model Context Protocol ,是由 AI 巨头 Anthropic(就是那个开发 Claude 的公司)在 2024 年底贡献给开源社区的一套标准协议。

1.2 为什么要用 MCP?

想象一下,在 USB 接口出现之前,鼠标用圆口,打印机用并口,键盘用串口......乱成一锅粥。MCP 就是 AI 时代的 USB 协议。 🔌

它的作用非常核心:

  • 标准化:大家都不用瞎折腾了,按这个约定来。
  • 连接万物:它能把本地资源、跨语言服务(Node 调用 Rust/Java)、第三方 SaaS 服务,统统变成 LLM 能理解的"工具"。
  • 架构解耦 :采用 Client/Server (C/S) 架构。你的编辑器(如 Cursor/Trae)是 Client,而提供工具的是 Server。

一句话总结:MCP 让 LLM 连接世界变得像插 U 盘一样简单。


🛠️ 第二部分:实战!编写你的第一个 MCP Server

光说不练假把式。我们要写一个 MCP Server,它的功能很简单:模拟一个数据库,允许 AI 查询用户信息。

2.1 准备工作

我们需要用到官方提供的 SDK:@modelcontextprotocol/sdk。 在这个架构中:

  • Client: 你的 AI 编辑器(Cursor 或 Trae)。
  • Server : 我们即将编写的 my-mcp-server.mjs

2.2 引入核心模块

首先,我们需要把"地基"打好。看看代码:

javascript 复制代码
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
  • McpServer : 这是主角。它就像我们写 Web 服务时的 ExpressKoa 实例,负责管理工具、处理请求。它把普通的函数"包裹"成符合 MCP 规范的服务。
  • StdioServerTransport: 这是通信管道。稍后我们会详细讲,它是实现"本地跨进程调用"的关键。
  • z (Zod):这是老朋友了,用于定义数据结构。AI 有时候会"幻觉",用 Zod 严格约束输入参数,能让 AI 乖乖听话。

2.3 模拟数据库:假装我们在连生产环境 💾

为了演示,我们先造一点假数据。在实际生产中,这里连接的可能是 MySQL、MongoDB 或者是一个远程 API。

javascript 复制代码
// 模拟数据库数据
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" },
    }
}

解析 : 这里定义了一个简单的 database 对象。你可以把它想象成是一个只有三条记录的用户表。AI 待会儿就要从这里面"查户口"。


2.4 创建服务器实例:启动引擎 🏎️

接下来,我们需要实例化一个 MCP Server。

javascript 复制代码
const server = new McpServer({
    name: 'my-mcp-server', // 给你的服务起个名字
    version: '1.0.0',      // 版本号,规范化开发很重要
});

解析 : 这一步非常简单,就是告诉 SDK:"嘿,我要建一个名叫 my-mcp-server 的服务,版本是 1.0.0"。这个元数据在 Client 连接时会用到,方便识别身份。


2.5 注册工具:教 AI 新技能 ✨

这是最核心的部分!我们要告诉 Server,我们提供了什么能力(Tool)。这和我们在 LangChain 或 OpenAI SDK 中定义 Tool 的语法非常像。

javascript 复制代码
server.registerTool('query-user', {
    // 1. 描述工具:这句 Description 至关重要!
    // LLM 会根据这句话来判断何时调用这个工具。写得越清楚,AI 越聪明。
    description: '查询数据库中的用户信息,输入用户ID,返回该用户的详细信息(姓名,邮箱,角色)。',
    
    // 2. 定义输入 Schema:告诉 AI 传什么参数
    inputSchema: {
        userId: z.string().describe('用户ID,例如:001,002,003')
    }
}, async ({userId}) => {
    // 3. 工具的具体逻辑实现
    const user = database.users[userId];
    
    // 处理找不到用户的情况
    if (!user) {
        return { 
            content: [
                {
                    type: 'text',
                    text: `用户ID ${userId} 不存在`
                }
            ]
        };
    } else {
        // 成功找到用户,返回结构化文本
        return {
            content: [
                {
                    type: 'text',
                    text: `用户ID ${userId} 的详细信息如下:\n姓名:${user.name}\n邮箱:${user.email}\n角色:${user.role}`
                }
            ]
        };
    }
})

核心知识点解析

  1. server.registerTool: 注册函数的入口。
  2. description (描述): 这里的中文描述不是写给你看的,是写给 LLM 看的(Prompt 的一部分)。AI 看到这句话,才知道:"哦,原来用户问'张三是谁'的时候,我应该用这个工具。"
  3. inputSchema : 使用 zod 定义参数。userId 是必填项,我们还贴心地给了 describe 示例,防止 AI 瞎填。
  4. 执行逻辑 : async ({userId}) => { ... } 是真正的业务代码。
    • 它接收 AI 解析好的 userId
    • database 里查找。
    • 重点 :返回格式必须符合 MCP 规范!通常是一个对象,包含 content 数组,里面是 { type: 'text', text: '...' }。这就像是把原来的 console.log 变成了标准化的消息包。

2.6 建立连接:打通任督二脉 🔗

最后一步,也是最抽象、最体现 MCP 设计哲学的一步。

javascript 复制代码
// 链接方式 本地跨进程调用
const transport = new StdioServerTransport();
await server.connect(transport);

详细讲解(敲黑板!) 👨‍🏫:

很多同学看到这里会问:"没有 app.listen(3000) 吗?没有 HTTP 端口吗?"

没有!这就是 MCP 的精髓之一:Stdio 通信。

  1. StdioServerTransport 是什么?

    • 它代表 Standard Input/Output Transport(标准输入输出传输)。
    • 还记得我们在终端里管道操作 | 吗?MCP 利用了操作系统的标准输入(stdin)和标准输出(stdout)来进行通信。
  2. 为什么要这么做?

    • 安全性:服务只在本地进程间对话,不暴露网络端口,黑客扫不到。
    • 性能:直接通过进程管道传输数据,极其高效。
    • 跨语言:不管你是 Node.js, Python, 还是 Rust,只要能读写控制台(stdio),就能接入 MCP。
  3. 如何工作?

    • 当你在 Cursor/Trae 里配置这个 MCP Server 时,编辑器实际上是在后台运行了 node my-mcp-server.mjs 这个命令。
    • 编辑器(Client)通过 stdin 发送 JSON-RPC 请求给你的脚本。
    • 你的脚本(Server)处理完后,通过 stdout 把结果打印回去。
    • await server.connect(transport) 这行代码,就是在启动这个监听循环,时刻准备着接收来自编辑器的指令。

trae mcp运行示例:

💡 总结与思考

写到这里,我们其实完成了一次架构上的升维

以前我们写 Tool,是把逻辑硬编码在 Agent 内部。现在,我们通过 MCP 协议,把 Tool 独立成了一个微服务

  • 对于开发者 :你只需要关注业务逻辑(查库、调接口),剩下的通信、协议解析,McpServerTransport 帮你搞定。
  • 对于生态:未来 80% 的 App 可能都会以 MCP 的形式存在。大厂把自己服务封装成 MCP Server,你的 Agent 只要配置一下,立马就能拥有操作全世界的能力。

最后留个小作业 : 试着把这个 database 换成真实的 API 调用(比如查询天气),体验一下让你的 IDE 变成天气预报员的感觉吧!

Happy Coding! 🚀🚀🚀

相关推荐
林小帅16 小时前
【笔记】OpenClaw 架构浅析
前端·agent
林小帅16 小时前
【笔记】OpenClaw 生态系统的多语言实现对比分析
前端·agent
warm3snow19 小时前
Claude Code 黑客马拉松:5 个获奖项目,没有一个是"纯码农"做的
ai·大模型·llm·agent·skill·mcp
豆包MarsCode20 小时前
一文快速理解 Spec 模式
trae
程序员柒叔1 天前
Dify 流水线知识库(RAG Pipeline)深度分析
agent
回家路上绕了弯1 天前
深入解析Claude Skill:原理、特性与实战应用全指南
后端·agent
四月沐歌2 天前
测试火山引擎AgentKit记忆库和踩坑问题记录
agent
量子位2 天前
Nano Banana 2免费上线!超Pro版本100分登顶竞技场,API价格还对半砍了
agent
量子位2 天前
DeepSeek新论文剧透V4新框架!用闲置网卡加速智能体推理性能,打破PD分离瓶颈
agent