🔥 50 行代码,我手写了一个 MCP Server 给 Claude 用!

🔥 50 行代码,我手写了一个 MCP Server 给 Claude 用!

摘要:MCP 到底是什么?和 Function Call 什么关系?本文带你用 50 行代码从零手写一个 MCP Server,边写边学,一次性把 MCP 的核心原理、调用流程、踩坑经验全搞清楚。


📌 前言

最近在学 AI 开发,到处都能看到 MCP 这个词。什么"AI 界的 USB-C"、"统一工具调用协议"......概念看了一堆,但总觉得隔着一层纱。

直到我自己动手写了一个 MCP Server,才真正搞明白它到底是怎么回事。

今天把整个学习过程分享出来,如果你也是刚接触 MCP 的新人,相信看完会有收获。

🎯 本文适合谁

  • 刚接触 MCP,想搞懂它原理的 AI 新人
  • 用 Claude Code / Cursor,想了解背后 MCP 机制的同学
  • 想动手写一个自己的 MCP Server 的开发者

📚 MCP 到底是什么?

在写代码之前,先理清几个基本概念。

一句话理解

MCP(Model Context Protocol)是一个协议,它规定了 AI 模型和外部工具/数据之间怎么通信。

你可以把它类比成 HTTP 协议:HTTP 规定了浏览器和服务器怎么通信,MCP 规定了 AI 模型和外部工具怎么通信。

有图有真相

三个核心角色

text 复制代码
┌─────────────────────────────────────────────────┐
│                    MCP Host                      │
│            (Claude Code / Cursor)                │
│                                                  │
│   ┌──────────────┐       ┌──────────────┐       │
│   │  MCP Client  │       │  MCP Client  │       │
│   └──────┬───────┘       └──────┬───────┘       │
│          │                      │                │
└──────────┼──────────────────────┼────────────────┘
           │                      │
           ▼                      ▼
   ┌──────────────┐       ┌──────────────┐
   │  MCP Server  │       │  MCP Server  │
   │  (文件系统)   │       │  (数据库)     │
   └──────────────┘       └──────────────┘
  • MCP Host:宿主程序,比如 Claude Code、Cursor,我们平时用的 AI 工具
  • MCP Client:配置在 Host 里,负责和 Server 通信的客户端
  • MCP Server:服务端,提供具体的工具能力(比如读文件、查数据库)

我们在 .mcp.json 里配置的就是 MCP Server 的连接信息,Host 会自动创建 Client 去连接它。

MCP 和 Function Call 是什么关系?

这是我学 MCP 时最大的困惑,后来才搞明白:

MCP 底层用的就是 Function Call,它没有给模型带来新能力,而是把工具调用的方式标准化了。

先看一下 MCP Client 端是怎么调用 AI 的,帮助理解两者的关系:

javascript 复制代码
// MCP 客户端调用 AI 的代码
const response = await this.openai.chat.completions.create({
    model: model,
    messages,
    tools: this.tools, // tools 就是从 MCP Server 拿到的工具列表
});

最终还是走的 tools 参数------这就是 Function Call。

那 MCP 的价值在哪?标准化。以前每个工具都要单独写对接代码,有了 MCP,任何 MCP Server 都能被任何 MCP Client 直接调用,就像 USB-C 一样插上就能用。

MCP vs 直接用 Function Call

对比项 直接用 Function Call 用 MCP
工具定义 手写 JSON Schema Server 注册,Client 自动获取
调用方式 代码里直接调函数 通过协议通信(stdio/SSE)
可复用性 绑定在自己的项目里 任何 MCP Client 都能用
学习成本 更低 多了一层协议概念

本质上流程是一样的。 MCP 多了一层标准化的协议抽象,换来的是跨客户端的通用性。

如果你写的工具只给自己项目用,直接 Function Call 更简单。但如果你想让工具能被 Claude Code、Cursor 等多个 AI 工具复用,MCP 就有它的价值了。

你觉得 MCP 的标准化值不值?欢迎评论区讨论 💬


🔧 动手:50 行代码写一个 MCP Server

理解了概念,接下来动手实现。我写的是一个简单的文件读取 Server,功能单一但能完整展示 MCP 的核心流程。

依赖说明

json 复制代码
{
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.29.0",
    "zod": "^3.23.0"
  }
}
  • @modelcontextprotocol/sdk:MCP 官方 SDK,处理协议通信
  • zod:数据验证库,用于定义工具参数的 Schema

初始化项目

bash 复制代码
mkdir simple-read-mcp && cd simple-read-mcp
npm init -y
npm install @modelcontextprotocol/sdk zod

然后在 package.json 中添加 "type": "module"(因为 MCP SDK 用的是 ESM)。

核心代码拆解

第一步:创建 MCP Server 实例
javascript 复制代码
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { ListToolsRequestSchema, CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js';
import fs from 'fs/promises';

const server = new Server(
    { name: "simple-read-mcp", version: "1.0.0" },
    { capabilities: { tools: {} } }
);

💡 capabilities 是告诉 Client:"我这个 Server 支持哪些能力"。目前 MCP 支持三种:tools(工具调用)、resources(资源读取)、prompts(提示词模板)。我们这里只用 tools

第二步:注册工具列表(ListTools)

这一步是告诉 Client:"我有哪些工具可以用"。

javascript 复制代码
server.setRequestHandler(ListToolsRequestSchema, async () => ({
    tools: [
        {
            name: 'read_file',
            description: '读取指定路径的本地文本文件内容',
            inputSchema: {
                type: 'object',
                properties: {
                    path: {
                        type: 'string',
                        description: '要读取的文件路径,支持绝对或相对路径'
                    },
                },
                required: ['path'],
            }
        }
    ],
}));

💡 inputSchema 就是告诉 AI:"调用这个工具需要传什么参数"。AI 会根据这个 Schema 自动生成调用参数。这个 Schema 本质上和 Function Call 的 tools 定义是一样的。

第三步:处理工具调用(CallTool)

当 AI 决定要用 read_file 工具时,Client 会把请求发过来,我们在 Server 里处理:

javascript 复制代码
server.setRequestHandler(CallToolRequestSchema, async (request) => {
    const { name, arguments: args } = request.params;
    if (name === 'read_file') {
        try {
            const content = await fs.readFile(args.path, 'utf-8');
            return { content: [{ type: 'text', text: content }] };
        } catch (err) {
            return { isError: true, content: [{ type: 'text', text: `读取文件失败: ${err.message}` }] };
        }
    }
    throw new Error(`工具 ${name} 不存在`);
});

💡 返回的 content 数组就是给 AI 的上下文。AI 拿到文件内容后,就能基于内容来回答问题了。

第四步:启动服务
javascript 复制代码
async function main() {
    const transport = new StdioServerTransport();
    await server.connect(transport);
}
main();

💡 StdioServerTransport 表示通过标准输入输出(stdin/stdout)通信。本地 MCP Server 最常用这种方式。

就这么多! 完整代码 50 行左右,一个 MCP Server 就写好了。


🧠 搞懂:MCP 调用的完整流程

写完代码,我把整个调用流程画出来,这样理解更清晰:

text 复制代码
用户: "帮我读取 readme.md"
    ↓
Claude Code (Host)
    ↓  AI 推理:需要用 read_file 工具
MCP Client
    ↓  ① AI 模型推理决定调用哪个工具及参数
    ↓  ② 把工具名和参数通过 stdio 发给 Server
MCP Server
    ↓  ③ 执行 read_file,读取文件内容
    ↓  ④ 返回文件内容给 Client
MCP Client
    ↓  ⑤ 把结果塞进 messages,再次调用 AI
Claude Code
    ↓  ⑥ AI 基于文件内容生成最终回答
用户看到结果

关键点 :MCP Server 是一个常驻进程,启动后一直在运行,随时等待 Client 的请求。不是一次性的 API 调用。


🚀 在 Claude Code 中使用 MCP

配置方式

在项目根目录创建 .mcp.json

json 复制代码
{
    "mcpServers": {
        "simple-read": {
            "type": "stdio",
            "command": "node",
            "args": ["./simple-read-mcp/server.js"]
        }
    }
}

全局配置 (对所有项目生效)------编辑 ~/.claude.json,或通过命令添加:

bash 复制代码
claude mcp add <server-name> --scope user <command> [args...]

💡 输入 /mcp 命令可以查看当前所有 MCP Server 的连接状态和工具数量。

测试效果

配置好后,对 Claude Code 说:

复制代码
帮我读取 D:\workspace\project\readme.md 的内容

Claude Code 会自动:发现 read_file 工具 → 生成参数 → 通过 MCP 调用 Server → 拿到文件内容 → 基于内容回答你。

整个过程对用户是透明的,你只需要提问就行。

💡 建议截图保留实际运行效果------掘金文章有视觉内容的推荐权重更高。你可以在终端里跑一遍,截一张 Claude Code 调用工具的截图放在文章里。

我在用的 MCP Server 推荐

写完自己的 Server 之后,我也装了一些社区 MCP Server 来用,效果提升明显:

MCP Server 功能 使用场景
filesystem 本地文件读写、搜索 AI 直接操作项目文件,不用手动复制粘贴
web-reader 网页转 Markdown AI 读取在线文档、参考文章
playwright 浏览器自动化 AI 操作网页、截图、测试
neural-memory 持久化记忆 跨会话记住项目上下文和决策

配置方式和上面类似,都是在 .mcp.json 里添加对应的 mcpServers 配置即可,不展开赘述。


🐛 踩坑记录

学习过程中踩了几个坑,分享出来帮大家避坑:

坑 1:ESM 模块报错

现象SyntaxError: Cannot use import statement outside a module

原因:Node.js 默认使用 CommonJS,MCP SDK 用的是 ESM

解决 :在 package.json 中加 "type": "module"

坑 2:zod 版本不兼容

现象:启动时报 zod 相关的类型错误

原因:MCP SDK 依赖 zod 3.x,但 npm 可能装到不兼容的版本

解决 :明确安装 zod@^3.23.0,或按 SDK 文档的版本要求来

坑 3:stdio 通信看不到日志

现象:console.log 的输出干扰了 MCP 协议通信,Server 直接报错

原因:stdio 模式下 stdout 是协议通道,不能用来打日志

解决 :日志输出到 stderr:console.error('debug info')

💡 调试 MCP Server 时,推荐用官方的 MCP Inspector 工具,可以可视化查看请求和响应,比 console.log 好用得多。安装方式:npx @modelcontextprotocol/inspector node server.js

你踩过哪些 MCP 的坑?评论区补充,我更新到文章里! 💬


⚠️ MCP 目前的局限性

学完之后我也思考了一下 MCP 的不足,客观看待才能用好它:

局限性 说明
stdio 模式不支持远程 本地通信没问题,但要远程调用得用 SSE 模式,配置更复杂
工具多了影响选择准确性 装太多 MCP Server,AI 的工具列表变长,可能选错工具
安全权限控制不完善 目前权限控制主要靠 Server 自己实现,没有统一的权限模型
调试相对困难 stdio 通信不像 HTTP 那样方便抓包,排查问题需要专门工具
没有提升模型能力 MCP 只是协议标准化,工具调用的准确性还是取决于模型本身

📚 我学到的几个关键认知

1. MCP 是协议,不是产品

它不是一个新的框架或工具,而是一套通信标准。就像 HTTP 不是浏览器,但浏览器之间能互相通信靠的就是 HTTP。

2. MCP 没有提升模型的工具调用能力

很多人以为用了 MCP 模型就能更好地调用工具,其实不是。MCP 底层还是 Function Call,工具调用的准确性取决于模型本身的能力,和协议无关。

3. MCP 的价值在于标准化

举个例子:以前你想让 AI 读文件,得自己写一套 Function Call + 文件读取的代码。有了 MCP,直接装一个 @modelcontextprotocol/server-filesystem,任何 MCP Client 都能用。写一次,到处用,这就是标准化的力量。

4. 学 MCP 最好的方式就是动手写

光看文档很容易云里雾里,但自己写一个 Server 之后,整个流程就清楚了。推荐大家也试试。


💡 进阶方向

掌握了基础版之后,还可以继续探索:

方向 说明
多工具注册 一个 Server 提供多个工具(读文件 + 写文件 + 列目录)
Resources 让 AI 能浏览资源列表(如数据库表结构)
Prompts 预定义提示词模板
远程 Server 用 SSE 替代 stdio,实现远程调用
权限控制 限制可访问的目录范围

🔗 参考资料

💬 交流讨论

你现在用的是 Claude Code 还是 Cursor?MCP Server 装了几个?评论区聊聊你的真实体验!


觉得有用?点个 👍 收藏 ⭐ 关注 👆,作为 AI 新人会持续分享学习笔记!

📌 标签:MCP、Claude Code、AI 开发、Node.js、Function Call

相关推荐
梦帮科技1 小时前
基于EVM架构的Web3音频确权与RNS Token智能合约设计深度解析
人工智能·python·架构·web3·区块链·音视频·智能合约
无糖可可果1 小时前
LLM 如何预测下一个词?—— 深入 Transformer 内部
人工智能
chaowwwww1 小时前
从0开始实现个人最简harness项目
人工智能·agent
项目治理之道1 小时前
项目管理中的“附件黑洞”:VisualProject如何让附件从“信息孤岛”蜕变为核心资产
人工智能·项目管理工具·项目管理软件·金融科技管理
大流星1 小时前
LangChainJs之Prompt提示词(二)
javascript·langchain
东风破_1 小时前
Tool Use 的核心:模型只负责判断,Runtime 才真正执行
人工智能
To_OC1 小时前
我写了个 10 行的加法函数,终于搞懂了什么是 Harness Engineering
人工智能·llm·claude
是烨笙啊1 小时前
从Loop Engineering看AI圈的循环营销术:万物皆可“工程”
人工智能
大模型最新论文速读1 小时前
RL 的 token 选择之争:高熵词到底重不重要?
论文阅读·人工智能·深度学习·神经网络·机器学习