从零构建 MCP 文件服务:50 行代码让 AI 读懂你的文件

手把手教你写一个 MCP Server:从零构建文件读取服务

前言

上篇我们聊了 MCP(Model Context Protocol)是什么,这次直接动手,用 Node.js 手写一个文件读取 MCP Server,并在 Trae 中实际跑起来。

目标:让 AI 能通过 MCP 协议安全地读取你指定的本地文件。

整体架构

MCP Server 的通信链路一句话概括:

c 复制代码
用户输入 → LLM 分析 → 匹配 MCP 工具 → StdioServerTransport → stdin → Server 执行 → stdout → 返回结果给 LLM

核心流程:AI 不直接操作文件系统,而是通过标准输入输出(stdio)把请求发给 Server,Server 执行后返回结果。这就是 MCP 的 "USB-C 协议" 本质。

项目初始化

bash 复制代码
mkdir simple-read-mcp && cd simple-read-mcp
pnpm init
pnpm i zod @modelcontextprotocol/sdk

两个核心依赖:

包 作用
@modelcontextprotocol/sdk MCP 协议通信层,提供 Server 和 Transport
zod 运行时 Schema 校验,定义工具的入参格式

编写 Server 代码

创建 server.js:

javascript 复制代码
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import fs from "fs/promises";

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

server.tool(
  "read_file",
  "读取指定路径的本地文件内容",
  {
    path: z.string().describe("文件的绝对或相对路径"),
  },
  async ({ path }) => {
    try {
      const content = await fs.readFile(path, "utf-8");
      return {
        content: [{ type: "text", text: content }],
      };
    } catch (err) {
      return {
        isError: true,
        content: [{ type: "text", text: `读取文件失败:${err.message}` }],
      };
    }
  },
);

async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
  console.error("MCP read_file 服务已启动(stdio模式)");
}

main().catch(console.error);

代码解读

第一步:实例化 Server

javascript 复制代码
const server = new McpServer({
  name: "simple-read-mcp",
  version: "1.0.0",
});

给 Server 起个名字和版本号,方便调试和区分。

第二步:注册 Tool

javascript 复制代码
server.tool(
  "read_file",                              // 工具名
  "读取指定路径的本地文件内容",               // 描述(LLM 靠这个判断何时调用)
  { path: z.string().describe("...") },     // 参数 Schema
  async ({ path }) => { ... }               // 执行逻辑
);

新版 SDK 的 server.tool() 用 zod 定义参数 Schema,不再需要手写 JSON Schema,非常简洁。LLM 会阅读工具描述和参数说明,自行判断何时调用这个工具。

第三步:启动 stdio 通信

javascript 复制代码
const transport = new StdioServerTransport();
await server.connect(transport);

StdioServerTransport 通过标准输入输出与客户端通信------客户端通过 stdin 发请求,Server 通过 stdout 返回结果。这是 MCP 最常用的本地通信方式。

配置 Trae 客户端

在 Trae 的 mcp.json(路径:C:\Users\你的用户名\AppData\Roaming\Trae CN\User\mcp.json)中添加:

json 复制代码
{
  "mcpServers": {
    "simple-read-mcp": {
      "command": "node",
      "args": ["文件路径名"]
    }
  }
}
  • "simple-read-mcp":给这个 Server 起一个在 Trae 中使用的名字
  • "command": "node":用 Node.js 运行
  • "args":Server 文件的绝对路径

重启 Trae 后,就可以在对话中直接调用这个工具了。

实际效果

配置生效后,在 Trae 中说:

"请使用 simple-read-mcp 读取 server.js 的内容"

AI 会自动:

  1. 分析你的意图
  2. 匹配到 read_file 工具
  3. 通过 stdio 把 { path: "d:/workspace/.../server.js" } 发给 Server
  4. Server 读取文件并返回内容
  5. AI 展示给你看

整个过程对用户完全透明,就像 AI 自己会读文件一样。

为什么不用 fs 直接读?

你可能会想:Node.js 本身就能 fs.readFile,为什么要通过 MCP?

关键在于安全边界:

  • 直接调用:AI 可以访问任何路径,风险不可控
  • MCP 方式:Server 可以加上路径白名单、权限校验等逻辑,把文件访问限制在安全范围内

在生产环境中,你还可以在 Server 里加入日志记录、限流、加密等能力,这正是 MCP 作为"中间层"的价值。

总结

一个完整的 MCP Server 只需要三步:

步骤 做的事情 关键 API
实例化 创建 Server new McpServer()
注册工具 声明功能 + Schema + 逻辑 server.tool()
启动通信 通过 stdio 连接客户端 StdioServerTransport + server.connect()

加上客户端的 mcp.json 配置,总共不到 50 行代码,就让 AI 获得了安全的文件读取能力。

掌握了这个模式,你可以轻松扩展出更多能力:写文件、搜索文件、调用 API、操作数据库......MCP 的想象空间才刚刚打开。

!

相关推荐
leobertlan7 小时前
痛苦系列 | DSP-03 跨越单位圆:Z变换与零极点分析
算法·程序员
kyriewen11 小时前
我上手了 Vite+ 1.0:Node 不用自己装,lint 却翻车了
前端·javascript·程序员
鹏多多12 小时前
前端是如何死去的?| 完整时间线
前端·javascript·程序员
webxin66613 小时前
汽水music自动看广告领取VIP
程序员·github
高晶13 小时前
Android 电子词典开发实战指南
后端·程序员
用户9224342772615 小时前
会拍你的番茄钟:我在 Rokid Glasses 上写了一个会「盯」着你的 AIUI 智能体
程序员
高晶15 小时前
Android 计算器开发实战:从界面设计到核心逻辑
程序员·设计
高晶15 小时前
ARM 嵌入式 Linux 系统 U-Boot 开发与移植实战M
设计模式·程序员
AaronMan15 小时前
ZIP 压缩包为什么有时多一层目录?——看懂 ZIP 文件名与内部目录的关系
程序员
楠风测开15 小时前
MySQL 基础 + AI SQL 优化
程序员