当前位置: 首页
AI教程
手把手搭建MCP文件读取服务 让AI能读文件

手把手搭建MCP文件读取服务 让AI能读文件

热心网友 时间:2026-07-07
转载

1. 开篇:AI 为何需要具备读取文件的能力?

想象一个典型场景:当你借助 AI 进行代码编写,希望它协助重构一个项目时,你尝试将代码片段粘贴进去,AI 却回应“这个函数看起来存在一些问题,但我需要查看完整的上下文”。你继续粘贴更多代码,AI 又提示“我还需要相关的配置文件”。在如此来回反复粘贴十几轮后,你已疲惫不堪,而 AI 始终未能建立起完整的项目视角。

从零搭建文件读取 MCP 服务——让 AI 拥有读文件的能力

这不正是“盲人摸象”的困境吗?AI 好比那个摸象的盲人,每次只能触碰到局部,永远无法窥得全貌。

是否存在一种方法,能让 AI 自主“走过去”审视整个项目?如同为它赋予一双眼睛,使其能够自行读取文件?

答案是肯定的:那就是 MCP(模型上下文协议,Model Context Protocol)。

2. MCP 究竟是什么?—— 助力 AI 突破认知边界

在阐述 MCP 之前,先分享一个小故事。

你是否还记得小时候玩过的“传话游戏”?第一个人说出一句话,依次传递给第二个人、第三个人……最终,最初那句话往往已面目全非。AI 与外部世界的交互,本质上也是一个类似的传话过程——用户将信息传递给 AI,AI 再通过用户去执行具体操作。

MCP 的使命正是打破这个冗长的传话链条。它让 AI 能够直接与工具对话,省去中间“传话人”的角色。

MCP 的核心设计思想

MCP 是一个协议,它定义了 AI 如何调用外部工具。简单来说,它解决了三个关键问题:

  1. “我能做什么?” —— 工具向 AI 声明其自身能力(包括工具名称、描述及参数)
  2. “如何调用我?” —— AI 通过标准协议发送调用请求
  3. “结果是什么?” —— 工具返回结构化的执行结果

MCP 架构概览

┌─────────────────────────────────────────────────────────────────┐
│ AI / LLM                                                       │
│   ┌─────────────┐                                              │
│   │   思考模块    │ ← 决定是否调用工具                           │
│   └──────┬──────┘                                              │
│          │                                                     │
│          ▼                                                     │
│   ┌─────────────┐                                              │
│   │   工具调用    │ ← 生成工具调用请求                           │
│   └──────┬──────┘                                              │
│          │                                                     │
│          ▼                                                     │
├────────┼────────────────────────────────────────────────────────┤
│        │   CC (Client Coordinator)                             │
│        │   ┌──────────────────────────────────────┐            │
│        │   │ 工具发现 → 参数校验 → 协议转换 → 分发 │            │
│        │   └──────────────────────────────────────┘            │
│        │                                                       │
├────────┼────────────────────────────────────────────────────────┤
│        │                                                       │
│        ▼                                                       │
│   ┌─────────────────┐   ┌─────────────────┐   ┌───────────┐   │
│   │ MCP Server 1    │   │ MCP Server 2    │   │   ...     │   │
│   │ (文件读取)       │   │ (网络搜索)       │   │           │   │
│   └────────┬────────┘   └────────┬────────┘   └─────┬─────┘   │
│            │                     │                 │          │
│            ▼                     ▼                 ▼          │
│      ┌─────────┐          ┌─────────┐       ┌─────────┐      │
│      │文件系统  │          │  网络   │       │   其他   │      │
│      └─────────┘          └─────────┘       └─────────┘      │
└─────────────────────────────────────────────────────────────────┘

此架构的精妙之处在于实现了高度解耦:

  • AI 无需了解工具的具体实现细节 —— 它只需清楚“有哪些工具可用”以及“如何进行调用”
  • 工具也无需知晓 AI 的身份 —— 只要遵循 MCP 协议,便能够被任何兼容的 AI 调用
  • CC 作为中间协调层 —— 负责协调与路由,确保整个系统具有良好的可扩展性

本项目的定位

我们今天要深入拆解的 simple-read-mcp,正是这个架构中的一个“关键组件”——一个专门负责读取本地文件的 MCP Server。它虽然小巧,但功能完备,是理解 MCP 协议的一个绝佳切入点。

3. 项目骨架解析:package.json 与 mcp.json

在分析核心代码之前,先来审视项目的基础设施。一个 Node.js 项目好比一栋建筑,package.json 是其“房产证”,而 mcp.json 则是“门牌标识”。

package.json —— 项目的身份档案

{
  "name": "simple-read-mcp",
  "version": "1.0.0",
  "type": "module",
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.29.0",
    "zod": "^4.4.3",
    "zod-to-json-schema": "^3.25.2"
  }
}

这里有三个关键的依赖项:

  1. @modelcontextprotocol/sdk —— MCP 协议的官方 SDK,提供了 McpServerStdioServerTransport 等核心类
  2. zod —— 新一代数据验证库,用于定义工具参数的校验规则
  3. zod-to-json-schema —— 可将 zod schema 自动转换成 JSON Schema,便于 AI 理解参数结构

注意 "type": "module" 这一行配置,它指示 Node.js 使用 ESM 模块系统,因此我们可以在代码中使用 import 语法。

mcp.json —— 面向客户端的“使用说明书”

{
  "mcpServers": {
    "simple-read-mcp": {
      "version": "1.0.0",
      "description": "一个简单的读取文件的工具",
      "command": "node",
      "args": [
        "C:\Users\DMYX\Desktop\workspace\lesson_zp\ai\mcp\simple-read-mcp\server.js"
      ]
    }
  }
}

这个文件是提供给 CC(客户端协调器,Client Coordinator) 查阅的,它向 CC 明确了:

  • 服务名称:simple-read-mcp
  • 版本:1.0.0
  • 描述:一个简单的读取文件的工具(AI 将依据此描述来理解工具的用途)
  • 启动方式:使用 node 命令执行 server.js

为什么需要这个文件?

试想一下,你的电脑上可能安装了众多 MCP 服务——文件读取、网络搜索、数据库查询……CC 需要清楚地知道它们位于何处、如何启动以及能够执行什么任务。mcp.json 正扮演着这个“服务注册表”的角色。

4. 核心实现:server.js 逐行深度剖析

终于来到了最核心的部分。这短短 40 行代码,浓缩了一个完整 MCP 服务的所有精华。让我们逐行进行深入解析。

4.1 导入依赖(第 1-4 行)

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';

这四个 import 语句各司其职:

模块 职责
McpServer MCP 服务的核心类,负责注册工具并处理请求
StdioServerTransport 标准输入输出传输层,负责进程间通信
z zod 验证库,用于定义参数校验规则
fs/promises Node.js 文件系统的 Promise API,支持异步读取文件

为何选择 Stdio?

进程间通信有多种方式:网络 socket、命名管道、共享内存……为什么最终选择了最简单的标准输入输出?

  • 零配置:无需端口号,也无需 IP 地址
  • 跨平台:在 Windows 和 Unix 系统中均能良好支持
  • 安全性高:不会暴露在网络上,只有父进程可以访问

4.2 创建服务器实例(第 7-10 行)

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

这一步看似简单,但背后有讲究:

  • nameversion 必须与 mcp.json 中的定义保持一致,否则 CC 将无法成功匹配
  • 这是服务的“身份标识”,AI 通过这些信息来识别并选择合适的工具

4.3 注册工具(第 13-32 行)—— 核心中的核心

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}` }]
      };
    }
  }
);

这是整个文件中最关键的部分,server.tool() 方法接收四个参数:

参数一:工具名称("read_file"

这是工具的唯一标识符,AI 通过这个名称来调用该工具。命名时建议使用下划线分隔的小写字母,保持语义清晰。

参数二:工具描述("读取指定路径的本地文件内容"

这是提供给 AI 的“使用说明”,AI 将根据这个描述来判断何时应该调用此工具。描述的质量将直接影响 AI 的调用决策,写得越清晰,AI 越能准确判断使用时机。

参数三:参数 Schema({ path: z.string().describe(...) }

这是新版 SDK 的一个重要特性——直接使用 zod schema 定义参数,无需再手动编写 JSON Schema。

{
  path: z.string().describe("文件的绝对或相对路径")
}

z.string() 表示 path 参数的类型必须是字符串。.describe() 方法则为参数添加了描述信息,AI 将通过这个描述来理解参数的具体含义。

zod 是如何工作的?

当 AI 调用工具时,传入的参数会经过 zod 的校验:

  • 如果 path 不是字符串类型,会自动返回错误信息
  • 如果缺少 path 参数,也会自动返回错误
  • 只有校验通过后,参数才会被传递给执行函数

参数四:执行函数(async ({ path }) => { ... }

这是工具实际执行逻辑所在,接收经过校验的参数,并返回执行结果。

执行流程解析:

AI 请求 → CC 校验参数 → server.tool() 接收 → 执行函数调用
    │                          │                    │
    │                          │                    ▼
    │                          │               zod 验证失败
    │                          │                    │
    │                          │                    ▼
    │                          │               fs.readFile()
    │                          │                    │
    │                          │                    ▼
    │                          │               返回错误信息     返回文件内容
    │                          │                    │            │
    ▼                          ▼                    ▼            ▼
CC 返回给 AI

返回值格式:

成功时返回:

{
  content: [{ type: "text", text: content }]
}

失败时返回:

{
  isError: true,
  content: [{ type: "text", text: `读取文件失败:${err.message}` }]
}

为什么要采用 content: [{ type: "text", text: ... }] 这种格式?

这是 MCP 协议定义的统一返回格式,支持多种内容类型:

类型 用途 示例
"text" 文本内容 文件内容、错误信息
"image" 图片内容 图片的 base64 编码
"file" 文件引用 文件路径和元信息

这种设计使得工具可以返回多种类型的结果,AI 能够根据不同的类型进行相应的处理。

4.4 启动服务(第 34-40 行)

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

这部分代码启动服务并等待请求:

  1. 创建 StdioServerTransport 实例 —— 建立标准输入输出通道
  2. server.connect(transport) —— 让服务器绑定到这个通道上
  3. 通过 console.error 输出启动信息 —— 为什么不用 console.log? 因为 console.log 的输出会被 CC 当作协议数据读取,所以日志信息必须写到 stderr

完整数据流图:

┌─────────────────────────────────────────────────────────────────────┐
│ 用户提问                                                           │
│ "帮我读一下 package.json"                                          │
└─────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ AI / LLM                                                            │
│ 分析问题 → 决定调用 read_file 工具 → 生成调用请求                   │
│ { name: "read_file", arguments: { path: "package.json" } }        │
└─────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ CC                                                                  │
│ 读取 mcp.json → 找到 simple-read-mcp → 启动进程 → 发送请求到 stdin │
└─────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ simple-read-mcp                                                    │
│ StdioServerTransport 接收请求 → McpServer 处理                     │
│ → 执行 read_file 函数 → fs.readFile("package.json")               │
│ → 返回结果到 stdout                                                 │
└─────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ CC                                                                  │
│ 接收 stdout 输出 → 解析结果 → 返回给 AI                           │
└─────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ AI / LLM                                                            │
│ 接收工具返回结果 → 整合到回答中 → 返回给用户                       │
└─────────────────────────────────────────────────────────────────────┘

5. 安全性思考:一个“危险”的玩具

现在我们已经完成了代码的拆解,但有一个问题必须严肃对待:这个工具安全吗?

当前实现的安全隐患

让我们仔细审视一下这个工具的实现:

async ({ path }) => {
  const content = await fs.readFile(path, 'utf-8');
  return { content: [{ type: "text", text: content }] };
};

这里没有进行任何路径检查,这意味着用户(或者说 AI)可以读取系统上的任意文件:

  • read_file("/etc/passwd") —— 读取系统用户信息
  • read_file("C:\Windows\System32\config\SAM") —— 读取 Windows 密码哈希
  • read_file("~/.ssh/id_rsa") —— 读取 SSH 私钥

这是一个巨大的安全漏洞! 如果这个工具被恶意利用,后果将不堪设想。

如何修复?

一个生产级的文件读取工具,至少应该包含以下安全措施:

方案一:路径白名单

只允许读取特定目录下的文件:

const ALLOWED_DIRS = [
  process.cwd(),
  '/path/to/project'
];
async ({ path }) => {
  const resolvedPath = path.resolve(path);
  const isAllowed = ALLOWED_DIRS.some(dir => resolvedPath.startsWith(path.resolve(dir)));
  if (!isAllowed) {
    return {
      isError: true,
      content: [{ type: "text", text: `访问被拒绝:${path}` }]
    };
  }
  // ... 后续逻辑
};

方案二:文件大小限制

防止读取超大文件导致内存溢出:

const MAX_FILE_SIZE = 1024 * 1024; // 1MB
async ({ path }) => {
  const stat = await fs.stat(path);
  if (stat.size > MAX_FILE_SIZE) {
    return {
      isError: true,
      content: [{ type: "text", text: `文件过大:${stat.size} bytes` }]
    };
  }
  // ... 后续逻辑
};

方案三:文件类型限制

只允许读取特定类型的文件:

const ALLOWED_EXTENSIONS = ['.txt', '.md', '.json', '.js', '.ts'];
async ({ path }) => {
  const ext = path.extname(path);
  if (!ALLOWED_EXTENSIONS.includes(ext)) {
    return {
      isError: true,
      content: [{ type: "text", text: `不支持的文件类型:${ext}` }]
    };
  }
  // ... 后续逻辑
};

为什么这个项目没有安全措施?

这个项目是一个教学示例,其目的在于展示 MCP 的核心概念,而非提供一个生产环境可用的工具。在实际使用时,你必须根据自己的具体需求添加适当的安全防护机制。

6. 进阶方向:从玩具到生产

如果你希望将这个简单的示例转变为一个真正可用的工具,可以考虑以下改进方向:

6.1 添加更多工具

一个文件读取服务不应该仅限于 read_file

  • list_files —— 列出目录内容
  • write_file —— 写入文件(需要更严格的安全控制)
  • read_binary_file —— 读取二进制文件
  • get_file_info —— 获取文件元信息

6.2 添加流式支持

对于大文件,可以返回流式结果,避免一次性读取整个文件占用过多内存:

async ({ path }) => {
  const stream = fs.createReadStream(path);
  return {
    content: [{ type: "stream", stream }]
  };
};

6.3 添加缓存机制

对于频繁读取的文件,可以引入缓存机制,减少磁盘 I/O 开销:

const cache = new Map();
const CACHE_TTL = 60 * 1000; // 1分钟
async ({ path }) => {
  const now = Date.now();
  const cached = cache.get(path);
  if (cached && now - cached.timestamp < CACHE_TTL) {
    return { content: [{ type: "text", text: cached.content }] };
  }
  const content = await fs.readFile(path, 'utf-8');
  cache.set(path, { content, timestamp: now });
  return { content: [{ type: "text", text: content }] };
};

结尾:AI 的第三只手

回到开头那个“盲人摸象”的故事。MCP 就像是为盲人赋予了第三只手——不仅能触摸,还能“拿取”、能“观察”、能“操作”。

今天,我们用 40 行代码搭建了一个简单的文件读取工具,但这仅仅是 MCP 能力的冰山一角。请想象一下:

  • 一个能查询数据库的 MCP 服务
  • 一个能调用 API 的 MCP 服务
  • 一个能控制智能家居的 MCP 服务
  • 一个能操作 Excel 的 MCP 服务

当这些服务组合在一起,就能让 AI 拥有近乎无限的能力。唯一的限制,就是你的想象力。

最后,留给你一个问题:如果你要为 AI 打造一个“超级工具箱”,你会添加哪些工具?又是什么原因呢?

来源:https://juejin.cn/post/7659027978949328939

游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。

同类文章
更多
科研人员必读:多肽、蛋白质、重组蛋白区别及定制指南

科研人员必读:多肽、蛋白质、重组蛋白区别及定制指南

Section 01 多肽 VS 蛋白质 VS 重组蛋白 多肽、蛋白质和重组蛋白,本质上是同宗同源的东西——都是氨基酸串起来的生物大分子。三者的核心区别,说到底无非是三个维度:分子大小、折叠形态,以及生产方式。 接下来是一张清晰的对比图,帮你快速建立直觉: ![对比图1](https:

时间:2026-07-07 17:55
知识图谱与本体语义建模的核心区别解析

知识图谱与本体语义建模的核心区别解析

谈到人工智能如何“理解”知识,有两个概念常被放在一起讨论:知识图谱与本体语义建模。不少人以为它们是同一事物,或者认为后者是前者的进化版。实际上,两者的分工完全不同——打个比方,一个是“记事的本子”,另一个是“写本子之前先定好的规矩”。 1 本体语义建模:先绘制一张“通用分类蓝图” 设想一下,你要整

时间:2026-07-07 17:55
强烈推荐工作搭子WorkBuddy

强烈推荐工作搭子WorkBuddy

一次偶然的机会,从朋友那里了解到WorkBuddy这个工具。说实话,在AI产品扎堆的今天,能遇到一个下载即用的助手,确实值得推荐给每一个被日常琐事缠身的人。 安装过程没什么难度,双击安装包默认安装即可。需要留意的是,如果在Windows7上折腾了半天没反应,别慌——这工具在高版本Windows下运行

时间:2026-07-07 17:55
跨境电商系统自动化测试与CI/CD流水线构建指南

跨境电商系统自动化测试与CI/CD流水线构建指南

技术方向:自动化测试与DevOps实践关键词:日本代购、一站式日淘、雅虎代拍系统、煤炉自动代拍 一、测试分层策略详解 不少人刚开始就想直接搞E2E测试,觉得跑通完整流程才够“真实”。然而,测试金字塔这么多年仍不过时,原因很简单——不同层级的测试各有分工,缺少任何一层都会不稳。来看看这张金字塔图: ┌

时间:2026-07-07 17:54
中小企业AI营销矩阵工具推荐:赛诺贝斯智域蒲公英

中小企业AI营销矩阵工具推荐:赛诺贝斯智域蒲公英

天天刷着别人的爆款内容,自己却“有心无力”——这才是2026年绝大多数中小企业运营社交媒体的真实写照。说白了,社交媒体如今早已不是“要不要做”的选择题,而是“怎么做才能真正见效”的生存考验。现实情况是,团队人力就那么几个,预算也紧巴巴,却要同时运营抖音、小红书、知乎、头条、百家号等多个阵地……文案、

时间:2026-07-07 17:54
热门专题
更多
刀塔传奇破解版无限钻石下载大全 刀塔传奇破解版无限钻石下载大全
洛克王国正式正版手游下载安装大全 洛克王国正式正版手游下载安装大全
思美人手游下载专区 思美人手游下载专区
好玩的阿拉德之怒游戏下载合集 好玩的阿拉德之怒游戏下载合集
不思议迷宫手游下载合集 不思议迷宫手游下载合集
百宝袋汉化组游戏最新合集 百宝袋汉化组游戏最新合集
jsk游戏合集30款游戏大全 jsk游戏合集30款游戏大全
宾果消消消原版下载大全 宾果消消消原版下载大全