理解 MCP 最快的方式不是读协议文档,而是亲手写一个 Server 并在 Claude Code 或 Cursor 里用起来。这篇教程带你实现一个「本地文件搜索」Server:给 AI 助手一个能力——在你指定的目录里按关键词搜索文件内容,返回命中的文件和上下文片段。全程 100 来行 TypeScript,不需要任何外部服务。
选文件搜索做例子有两个原因:一是足够真实(「让 AI 搜我本机/内网的资料」是高频需求),二是刚好覆盖 MCP 开发的所有关键点——工具定义、参数校验、安全边界、结果格式。如果你还没想清楚要不要用 MCP,先读《MCP vs Function Calling:AI 工具调用到底该选哪个》。
一个最小可用的 MCP Server:一个入口文件、一个工具、一条 stdio 传输通道。
准备工作与项目初始化
环境要求:Node.js 18+,以及任意一个 MCP Host(本文用 Claude Code 和 Cursor 演示)。初始化项目:
mkdir mcp-file-search && cd mcp-file-search
npm init -y
npm install @modelcontextprotocol/sdk zod
npm install -D typescript @types/node
npx tsc --init@modelcontextprotocol/sdk 是官方 TypeScript SDK,封装了 JSON-RPC 协议细节;zod 用来声明和校验工具参数。SDK 的接口在持续演进,本文写法以发布时的官方文档为准,跑不通时先对照 SDK 的 README 检查 API 名称。
核心代码:定义 Server 和搜索工具
创建 src/index.ts:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import fs from "node:fs/promises";
import path from "node:path";
// 允许搜索的根目录:从启动参数读取,这是本 Server 的安全边界
const ROOT = path.resolve(process.argv[2] ?? ".");
const server = new McpServer({ name: "file-search", version: "1.0.0" });
server.registerTool(
"search_files",
{
description:
"在指定目录中按关键词搜索文本文件内容,返回命中文件路径和上下文片段。" +
"适用于查找文档、笔记、代码中的关键词。",
inputSchema: {
query: z.string().min(1).describe("要搜索的关键词"),
subdir: z.string().default(".").describe("相对根目录的子目录,默认全部"),
maxResults: z.number().int().min(1).max(50).default(10),
},
},
async ({ query, subdir, maxResults }) => {
const base = path.resolve(ROOT, subdir);
// 防目录穿越:解析后的路径必须仍在 ROOT 之内
if (!base.startsWith(ROOT)) {
return {
content: [{ type: "text", text: "错误:目录超出允许范围" }],
isError: true,
};
}
const hits = await searchDir(base, query, maxResults);
return {
content: [{
type: "text",
text: hits.length
? hits.map(h => `${h.file}:${h.line}\n ${h.snippet}`).join("\n\n")
: `未找到包含「${query}」的文件`,
}],
};
}
);
const transport = new StdioServerTransport();
await server.connect(transport);搜索函数本身是普通的 Node 代码——递归遍历目录、逐行匹配、截取上下文,跳过 node_modules、二进制文件和超大文件即可,完整实现不到 50 行,这里不赘述。三个设计点值得说明:
- 描述写给模型看:
description决定模型什么时候、怎么用这个工具。写清楚「适用于什么」比写技术细节重要。 - 参数上限:
maxResults封顶 50,防止模型请求超大结果撑爆上下文。工具返回的内容最终都会进入模型的上下文窗口,节制是美德。 - 路径安全:
ROOT白名单 + 穿越检查是底线。Server 的权限就是模型的权限,你把根目录设为/,就等于允许 AI 读你整块硬盘。
编译并试运行:
npx tsc && node dist/index.js ~/Documents/notes进程静默等待 stdin 输入即为正常——stdio 传输模式下,Host 会以子进程方式拉起它并通过标准输入输出通信。
接入 Claude Code 和 Cursor
Claude Code:在项目里执行一条命令即可注册:
claude mcp add file-search -- node /绝对路径/dist/index.js ~/Documents/notes或者在项目根目录的 .mcp.json 里声明(适合团队共享):
{
"mcpServers": {
"file-search": {
"command": "node",
"args": ["/绝对路径/dist/index.js", "/Users/you/Documents/notes"]
}
}
}Cursor:在 ~/.cursor/mcp.json(全局)或项目 .cursor/mcp.json 写入同样格式的配置,重启后在 MCP 设置里能看到 Server 状态变绿。
接入后直接对助手说:「用 file-search 搜一下笔记里关于报销流程的内容」,就能看到模型发起 search_files 调用、拿到结果并整理回答。
stdio 模式下 Server 由 Host 拉起,注册配置只需指定启动命令和参数。
调试:Inspector 是你的第一工具
MCP 官方提供了调试利器 Inspector,不经过任何 AI 应用直接连接你的 Server:
npx @modelcontextprotocol/inspector node dist/index.js ~/Documents/notes浏览器里会打开一个调试界面,能看到 Server 声明的工具列表、手动填参数发起调用、查看原始返回。先用 Inspector 验证工具行为正确,再接入 Host,能省掉大量「不知道是 Server 错还是模型错」的排查时间。另外两个高频排查点:
- stdio 模式下不要往 stdout 打日志(会污染协议通道),调试信息一律走
console.error; - Host 里工具没出现,通常是配置路径不是绝对路径、或 Node 版本不符,看 Host 的 MCP 日志(Claude Code 里
claude mcp list可查状态)。
先让 Inspector 里每个工具都手动跑通,再谈模型调用——分层排查永远最快。
常见坑
| 坑 | 表现 | 修法 |
|---|---|---|
| stdout 打了日志 | Host 报协议解析错误 | 日志全部改走 stderr |
| 返回内容过大 | 上下文被撑爆、回答变慢变差 | 限制结果条数和片段长度,提供分页参数 |
| 工具描述含糊 | 模型不用或乱用工具 | 描述里写清适用场景和参数含义 |
| 根目录给太宽 | 敏感文件可被读取 | 白名单目录 + 穿越检查,默认最小范围 |
| 相对路径配置 | Server 启动失败 | Host 配置中一律用绝对路径 |
适用人群与下一步
适合想把内部数据、私有工具接入 Claude Code / Cursor 等 AI 应用的开发者。跑通本文后有三个自然的延伸方向:把搜索升级为 Embedding 语义检索(配合向量数据库,就是一个私有 RAG Server);加上 resources 能力让 Host 直接读取文件全文;以及改用 Streamable HTTP 传输部署成远程 Server 供团队共用——远程部署后鉴权和权限控制就成了必修课,参考《AI Agent 为什么容易失控》里的工具权限设计。
如果只是想用现成能力,不必自己写:文件系统、GitHub、数据库等官方和社区 Server 已经很丰富,先搜后写。
常见问题
Q:Python 写法差别大吗? A:思路完全一致。官方 Python SDK(FastMCP 风格)用装饰器定义工具,代码量相当,团队用什么语言顺手就用什么。
Q:一个 Server 该放多少个工具? A:按职责内聚,一般 3–10 个。工具太多会占用大量上下文并干扰模型选择;不相关的能力拆成不同 Server,让用户按需挂载。
Q:怎么控制哪些操作需要确认? A:确认机制在 Host 侧:Claude Code、Cursor 都会对工具调用请求用户批准(可配置白名单)。Server 侧的责任是把读和写拆成不同工具,别把危险操作藏在无害名字里。
小结
一个 MCP Server 的最小构成就三件事:声明身份、注册工具、接上传输。把参数校验和路径安全做扎实,用 Inspector 验证后接入 Host,你的 AI 助手就多了一个真实可用的能力。从这个 100 来行的骨架出发,替换掉搜索逻辑,就能长出数据库查询、内部 API、工单系统等任何你需要的 Server。