
解密 MCP 协议如何用 Node.js 从零手写一个本地文件读取 MCP 服务器前言为什么需要 MCP它解决了什么问题开发准备核心实现手写 simple-read-mcp 服务代码深度解析落地集成接入 AI 研发环境总结前言在当下大语言模型LLM与 AI Agent 技术爆发的时代AI 助手在编写代码、解答疑问时表现得越发成熟。然而受限于自身的训练数据与运行沙箱AI 在执行“实时任务”或“触达本地生态”时常显得无能为力——它既无法感知你的本地文件也无法直接调用你的专属工具。为了打破这种“认知沙箱”Anthropic 推出了MCPModel Context Protocol模型上下文协议。它统一了 AI 与外部数据源、工具之间的通信标准。本文将跳过复杂的理论灌输通过基于 Node.js 的原生开发视角带你从零手写一个具备本地文件读取能力File System Server的 MCP 服务并将其接入到现代化 AI IDE 中。读完本文你不仅能掌握 MCP 的底层通信逻辑更能亲手为你的 AI 助手装上一双触达本地系统的“手”。为什么需要 MCP它解决了什么问题在 MCP 出现之前如果你想让 AI 读取本地项目结构或文件通常需要通过 IDE 插件或是自定义脚本来硬编码拼凑 Prompt。这种模式存在两个核心痛点接口标准不一每个应用、每个扩展都有自己的数据组织形式开发迁移成本极高。上下文孤立大模型无法以结构化、可感知的方式确认自己是否拥有某种能力难以进行精准的工具决策。MCP 就像是 AI 时代的“USB 接口协议”。它定义了一个标准的 C/SClient/Server架构MCP Host / Client宿主/客户端例如集成了 AI 能力的开发工具Trae、VS Code 或是 Claude Desktop。它们负责接收用户的自然语言指令并将指令翻译给 AI。MCP Server服务器由开发者编写的独立服务负责暴露具体的“工具Tools”或“资源Resources”。当用户发出指令时整体的工作流如下所示用户下达指令在 AI 聊天框输入“帮我看看本地config.json里写了什么”。客户端与大模型决策Host 接收 Prompt 传给 LLMLLM 分析上下文发现需要读取文件于是选中了注册在客户端内的read_file工具。协议传输Stdio 管道客户端通过标准输入stdin向 MCP Server 发送一条 JSON-RPC 请求。服务器执行MCP Server 接收请求调用底层的fs模块执行本地文件读取并将结果通过标准输出stdout返回。生成最终响应客户端Host将读取到的文件上下文喂给 LLMLLM 根据最新的完整数据生成最终回复返还给用户。通过这一层标准化的封装LLM 可以在不需要知道本地细节的情况下安全、动态地调用各类本地或者云端服务。开发准备在开始动手之前我们需要初始化一个 Node.js 项目并引入相关的依赖。这里主要依赖两个核心库modelcontextprotocol/sdk官方提供的协议 SDK帮助我们快捷实现符合 MCP 规范的底层通信与生命周期管理。zod一个强类型 Schema 声明与验证库。由于 MCP 核心需要向 LLM 声明工具的输入参数格式Zod 能够提供极简且严谨的校验能力避免手写繁琐的 JSON Schema。首先在你的终端中执行如下命令进行依赖安装pnpmi zod modelcontextprotocol/sdk核心实现手写simple-read-mcp服务接下来我们创建一个名为app.service.mjs或者其他符合 ECMAScript Modules 规范的命名的文件。以下是完整的核心代码实现import{McpServer}frommodelcontextprotocol/sdk/server/mcp.js;import{StdioServerTransport}frommodelcontextprotocol/sdk/server/stdio.js;import{z}fromzod;importfsfromfs/promises;// 1. 实例化 MCP 服务端constservernewMcpServer({name:simple-read-mcp,version:1.0.0});// 2. 利用新版极简工具注册器注册 read_file 工具server.tool(read_file,读取指定路径的本地文件内容,{path:z.string().describe(文件的绝对或相对路径)// 校验参数是否为字符串并提供描述供 LLM 理解},async({path}){try{// 执行本地异步文件读取constcontentawaitfs.readFile(path,utf-8);// 严格按照 MCP 规范返回 text 类型的上下文数据return{content:[{type:text,text:content}]};}catch(err){// 容错处理若文件不存在或无权限返回 error 标识并附带错误原因return{isError:true,content:[{type:text,text:读取文件失败${err.message}}]};}});// 3. 异步启动函数建立标准输入输出通信通道asyncfunctionmain(){consttransportnewStdioServerTransport();awaitserver.connect(transport);console.error(MCP read_file 服务已启动stdio模式);}main().catch(console.error);代码深度解析很多开发者在接触 MCP 时容易被繁琐的协议细节劝退。上面的代码利用了新版官方 SDK 的高层抽象非常优雅地完成了功能实现。我们来重点拆解它的几个关键设计new McpServer(...)初始化 MCP 服务的实例。服务名name和版本version会在 Client 与 Server 建立连接时进行握手交换便于客户端识别和管理。server.tool(...)内的 Schema 声明这是 MCP 的精髓所在。我们不需要用复杂的 JSON 格式去写定义而是直接传入read_file的名字、功能的自然语言描述以及一个 Zod 对象{ path: z.string().describe(...) }。大模型就是通过这些“描述文字Description”来感知这个工具有什么用、需要什么参数的。当大模型决定调用时Zod 会在底层自动拦截并校验入参是否合法。返回值结构体MCP 协议规定了 Server 返回给大模型的上下文格式。必须符合{ content: [{ type: text, text: ... }] }的数组嵌套结构。如果中途发生异常例如无权限、路径不存在则需提供isError: true标记告知大模型执行失败以便大模型在后续对话中进行调整或向用户报错。StdioServerTransport与console.error的设计妙处在主函数main中我们采用了 Stdio标准输入输出进程管道进行通信。这意味着客户端与服务器之间传递 JSON-RPC 数据是通过process.stdin和process.stdout完成的。因此服务器代码中绝对不能出现普通的console.log。因为任何console.log都会污染stdout管道导致客户端解析 JSON 报错。这就解释了为什么代码中提示服务启动的日志必须使用console.error输出——因为stderr不会影响主通信管道它是安全的日志输出通道。落地集成接入 AI 研发环境编写完代码后如何让我们的 AI 编辑器如 Trae / VS Code 等用上这个工具呢我们可以通过编辑编辑器支持的 MCP 配置文件通常位于项目根目录的.trae/mcp.json或全局配置中来导入该服务。配置的核心逻辑是告诉 Host 如何用 Node 启动我们写好的脚本{mcpServers:{simple-read-mcp:{command:node,args:[/你的项目绝对路径/app.service.mjs],disabled:false}}}配置完成后打开编辑器的 MCP 管理面板你会看到如图所示的界面当看到simple-read-mcp显示为启用状态且带有绿色对勾时说明宿主客户端已经成功通过 Stdio 管道拉起了我们的 Node.js 进程并完成了能力握手。现在你可以在聊天对话框或者 Agent 模式下直接对 AI 说“帮我分析一下系统里根目录下的package.json”。AI 将在后台自动识别并调用read_file工具读取内容并给出专业的解答。总结MCP 协议的出现在 LLM 与本地物理世界之间架起了一座标准化的桥梁。通过本文的实践我们仅用了不到 40 行的 Node.js 代码就实现了一个安全、规范的本地文件读取服务器。在实际的生产实践中你可以基于此架构进行更广阔的扩展安全性控制在read_file中加入沙箱路径校验防止 AI 被提示词攻击Prompt Injection进而读取系统敏感文件如.env或~/.ssh。多功能集成在同一个McpServer实例上通过server.tool继续挂载数据库查询、外部 API 联动、自动化编译等更多工具。掌握 MCP就是掌握了让 AI Agent 走向务实落地的钥匙。希望这篇文章能帮你顺利开启自定义 AI 工具库的大门