

# MCP实战教程:零基础搭建本地工具服务器,提升开发效率
从下面这张图可以快速理解这个工具的核心使用方式——一个本地运行的 MCP 服务器,通过 stdio 协议与 AI 客户端通信,提供文件搜索和数据库查询两个安全可控的工具:

如果你最近在尝试用 AI 辅助开发,大概率已经遇到过这种情况:模型能回答代码问题,但没法直接操作你的本地文件或数据库。每次都要手动复制粘贴文件内容、手动查询数据库,效率大打折扣。
MCP(Model Context Protocol)就是来解决这个问题的。它让 AI 客户端能安全地调用你本地的工具——搜索文件、查询数据库、读取日志,而不需要把整个硬盘暴露给模型。
但问题来了:网上能找到的 MCP 教程,大部分都是“调一个天气 API”或者“返回一句 Hello World”的 demo。真放到自己的开发环境里,你会发现那些教程根本没讲清楚边界怎么处理——文件搜索能不能限制在项目目录内?SQLite 查询能不能防止模型执行 DELETE?工具超时了怎么办?
这篇文章就是来填这些坑的。我会从零搭建一个 TypeScript MCP 服务器,提供两个真实可用的工具:search_files(在指定项目目录中搜索文本)和 query_tasks(从本地 SQLite 数据库读取任务记录)。更重要的是,我会把路径白名单、参数校验、数量限制、只读模式、错误处理这些边界逐一讲清楚——这些才是 MCP 工具真正值得花时间的地方。
为什么你需要一个本地 MCP 服务器
先说说场景。假设你正在用 Claude Desktop 或 Codex 写代码,需要:
- 在项目里搜索某个关键词出现在哪些文件里
- 查看本地数据库中的任务状态
- 读取最近的 Git 提交记录
没有 MCP 时,你得手动操作终端和数据库客户端,然后把结果粘贴给 AI。有了 MCP,AI 可以直接调用你注册的工具,拿到结果后继续分析。
但这里有个关键问题:工具不是越多越好,而是越安全越好。一个能执行任意 SQL 的“灵活”工具,比没有工具更危险。一个能搜索整个硬盘的文件搜索工具,可能把不该暴露的配置信息也暴露出去。
所以这篇 MCP 实战教程的核心不是“怎么注册一个函数”,而是“怎么注册一个安全的函数”。
项目初始化:搭建 MCP 开发环境
环境检查与依赖安装
开始之前,确认你的 Node.js 版本在 18 以上。我用的环境是 Node.js 24,但 18+ 应该都能跑。
PR0
然后初始化项目并安装依赖:
PR1
这里有个容易踩的坑:SDK v2 拆了包,只写服务端装 @modelcontextprotocol/server 就够了。如果你看到旧文章从 @modelcontextprotocol/sdk/server/mcp.js 导入,那是 v1 的写法,不要混用。
TypeScript 配置
创建 tsconfig.json:
PR2
为什么建议打开 strict 和 noUncheckedIndexedAccess? MCP 工具本质上在处理外部参数,类型检查越松,越容易在“理论上有值”的地方得到 undefined。编译器提前指出问题,总比客户端调用后返回内部错误更省时间。
实现安全的文件搜索工具
推荐可能感兴趣的网站
我们为你精心挑选了更多优质网址,希望能给你带来更好的体验
定义结果结构
先创建一个 src/file-search.ts,定义返回结果的格式:
PR3
注意这里只保留三个字段:相对路径、行号、截断后的当前行。不返回绝对路径,是为了减少不必要的本机信息暴露;不返回整个文件,是为了控制结果大小。
路径校验:不能只用 startsWith
很多新手会这样写路径校验:
PR4
但这是有问题的。如果根目录是 D:codeapp,另一个目录是 D:codeapp-backup,字符串同样以 D:codeapp 开头。app-backup 的文字前缀包含 app,但它不是 app 的子目录。
更稳妥的判断方式是使用 path.relative:
PR5
除此之外,还要注意符号链接。目录表面上在项目中,但符号链接可能指向项目外。真正读取前,应尽量对根目录和目标文件执行 realpath,再做一次范围判断。
递归搜索实现
PR6
这里直接跳过符号链接。这不是所有场景的唯一答案,但对本地只读搜索工具来说,它比“跟随链接后再判断”更简单,也更容易解释。
完整的搜索函数
PR7
这段代码没有调用 shell,也没有拼接命令。性能肯定比专业搜索工具差,但边界比较清楚,适合作为第一版。项目很大时,可以在服务端内部调用 ripgrep,但必须使用参数数组,不要拼接命令字符串,同时仍然要保留根目录和数量限制。
为什么不能只依赖输入 Schema?
zod 可以保证 limit 是数字,也可以限制它在 1 到 50 之间,但它不能自动判断:
- 当前文件是不是二进制
- 符号链接是否越界
- 文件读取是否超时
- 返回结果是否过大
Schema 是第一层,业务校验是第二层,操作系统权限是第三层。 安全不是某一个 z.object() 能解决的。
SQLite 查询:不要把“任意 SQL”包装成工具
先建立一个最小任务表
Node.js 24 可以使用 node:sqlite。创建初始化脚本 scripts/init-db.mjs:
PR8
注意最后使用的是 console.error。对 stdio MCP Server 来说,标准输出承载协议消息。 调试信息写到 stdout,可能和 JSON-RPC 数据混在一起,客户端最后只会告诉你“连接断开”或“解析失败”。
一个看似灵活、实际很危险的工具
不要设计成这样:
PR9
然后在代码里判断:
PR10
这种校验挡不住复杂注释、多个语句、不同大小写和数据库特性。即使能挡住写操作,模型也可能一次读取整个数据库。
更合理的接口是让工具表达业务意图: 按状态、优先级和数量查询任务。模型不需要知道表结构,更不需要自己拼 SQL。
任务仓库实现
创建 src/task-repository.ts:
PR11
这里用了三层限制:
- 数据库以只读模式打开
- SQL 模板由服务端固定
- 条件值使用参数绑定
就算模型传入 open' OR 1=1 --,zod 枚举也不会接受;即使绕过了上层,参数绑定也不会把它当作 SQL 结构执行。
注册两个 MCP 工具
创建服务器
创建 src/index.ts:
PR12
工具名称应该稳定、清晰。 像 do_it、run、execute 这种名字几乎不能帮助模型选择。描述中也不要写空话,要说明输入、结果和限制。
注册文件搜索工具
PR13
这里故意没有把完整堆栈返回给客户端。堆栈可以写进本地日志,但工具结果只需要告诉模型:什么操作失败了、用户可以检查什么、是否可以重试。把本机绝对路径、依赖位置和内部堆栈全部返回,不但噪声大,还可能泄露不必要的信息。
注册任务查询工具
PR14
连接 stdio
PR15
至此,两个工具已经注册完成。
行业趋势:为什么 MCP 工具越来越重要
如果你关注 AI 开发工具的发展,会发现一个明显的趋势:从“对话式 AI”向“工具化 AI”转变。早期的 AI 编程助手只能回答问题,现在的 AI 客户端开始能操作本地环境——搜索代码、读取数据库、运行测试。
这个转变背后有两个驱动力:
- 效率优先:开发者不想在 AI 和终端之间来回切换。如果 AI 能直接查询数据库,为什么还要手动复制粘贴?
- 安全可控:MCP 提供了一套标准化的权限控制机制。相比让 AI 直接操作终端,MCP 的边界更清晰,风险更可控。
但这也意味着,MCP 工具的质量比数量更重要。一个没有边界检查的工具,比没有工具更危险。如果你正在筛选类似工具,可以参考「
」进行系统对比。
使用建议:什么人适合用,什么人要谨慎
适合人群
- 前端/全栈开发者:经常需要在项目中搜索代码、查看配置文件
- 后端开发者:需要频繁查询本地数据库、查看日志
- AI 工具重度用户:已经用 Claude Desktop 或 Codex 辅助开发,想进一步提升效率
不适合人群
- 对 Node.js 和 TypeScript 不熟悉的初学者:虽然代码不复杂,但调试 MCP 连接问题需要一定基础
- 对安全性要求极高的项目:如果你在开发涉及敏感数据的应用,建议先评估 MCP 工具的数据暴露范围
- 只需要简单问答的场景:如果只是问代码问题,不需要操作本地文件,MCP 反而增加了复杂度
我的判断
MCP 值得长期使用,但前提是你愿意花时间理解它的边界机制。不要把它当成“万能工具注册器”,而是当成“安全可控的能力扩展器”。工具多了反而更要写清楚:允许范围、校验证据、失败方式。
总结
写完了。两个工具——文件搜索和任务查询——代码本身不复杂,真正花时间的是边界。
- 根目录由服务端配置,不由模型决定
- 文件搜索限制扩展名、大小和返回数量
- 数据库只读打开,不接收任意 SQL
- 所有参数经过 Schema 和业务逻辑双重校验
- stdio 日志写入 stderr
- 错误结果能判断,但不暴露内部细节
MCP 最有价值的地方不是让 AI “什么都能调”,是能让我们把能力拆成一个个边界明确的工具。 后面如果继续扩展,可以增加:项目构建结果读取、Git diff 摘要、测试报告查询、只读日志分析、先生成补丁确认后再写入的配置修改。
但不管加什么工具,先想清楚三件事:
- 它能读什么?
- 它能改什么?
- 它失败后会留下什么?
这篇 MCP 实战教程覆盖了从零搭建本地工具服务器的完整流程,包括文件搜索和 SQLite 查询两个真实场景。如果你正在寻找更完整的 MCP 工具对比和替代方案,可以参考相关资源进行系统评估。





