用 TypeScript 从零搓一个 MCP server,比你想的简单
一开始我真以为搓个 MCP server 得学一堆新概念。
看完规范以后预期是:至少要写个 transport 层、处理 JSON-RPC 路由、搞 capability handshake……结果打开 @modelcontextprotocol/sdk,发现核心代码也就十几行。40 行能出一个能用的 server。
这篇我按”我第一次做的时候希望有人告诉我”的顺序写一遍。不讲原理(原理看上一篇 MCP 协议从头撸),就讲动手。
初始化:脚手架比想的还轻
1 | mkdir weather-mcp && cd weather-mcp |
我第一次装的时候 SDK 版本是 1.0.4,现在 2026 年 4 月最新是 1.18.2,API 变化不大,主要是 transport 那边多了 streamable HTTP。
package.json 里加一行:
1 | "bin": { |
这个 bin 字段很关键,Claude Desktop 启动你 server 的时候就是走这个入口。我第一次漏了,配置里 command 怎么写都找不到可执行文件,折腾了 40 多分钟才反应过来。
40 行的 weather server 完整代码
1 |
|
第一行 shebang 别忘。第一次我忘了加 #!/usr/bin/env node,Linux 下直接报 exec format error。
zod 用来校验 tool 参数。Anthropic 官方文档里其实没强调 zod,但生产环境我强烈建议加——LLM 偶尔会传奇葩参数,没校验直接炸 runtime。
在 Claude Desktop 里挂上
config 文件位置我踩过坑——Windows 和 Mac 不一样:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
内容长这样:
1 | { |
注意两个坑:
- 路径在 Windows 下最好用正斜杠,反斜杠要转义
- 如果你 server 依赖环境变量(比如 API key),要在这里用
"env": {"OPENWEATHER_KEY": "xxx"}显式传,Claude Desktop 启动子进程是干净环境
改完配置要完全退出 Claude Desktop 再打开,不是关窗口。右下角托盘点退出,不然旧进程还抓着老配置。这点 Anthropic 官方文档也没写清楚,我当时以为配置没生效,反复改了 5 遍。
调试:inspector 救命
@modelcontextprotocol/inspector 是官方调试工具,装上:
1 | npx @modelcontextprotocol/inspector node dist/index.js |
会弹出一个 web 界面,localhost:5173。可以手动触发 initialize、tools/list、tools/call,看到完整 JSON-RPC 请求响应。不用开 Claude Desktop 就能测。
我现在每次改完 server 先用 inspector 过一遍,确认正常再接 Claude Desktop。节省至少 70% 的调试时间。
常见 Method not found 错误原因排名:
- 第一,你忘了在 server 构造函数里声明对应 capability(
{ capabilities: { tools: {} } }这句少了tools就 GG) - 第二,handler 注册漏了
- 第三,method 名拼错了(比如写成
tool/call少个 s)
一个 Jira MCP 的 token 优化故事
2025 年 10 月给一个 SaaS 客户搓过一个内部 Jira MCP server,8 个 tool:创建 issue、查询、改状态、加评论、分配、搜索、列 sprint、拉看板。
第一版直接出了。Claude 每次接 initialize 之后拿 tools/list,返回的 JSON 压缩完 3247 token。客户跑了几天发现月底 token 账单涨了 40%,问我怎么回事。
我一看——tool description 写得像技术文档,每个 tool 都有 200+ 字。inputSchema 里字段的 description 也写得巨长,还带了例子。8 个 tool 加起来 3K token 每次对话起步就吃掉。
优化思路:
- tool description 砍到一句话,只讲”它做什么”,不讲”怎么用”、”什么场景”
- inputSchema 里每个字段 description 控制在 15 字以内
- 非必要字段放到 “optional”,LLM 自己决定要不要用
- 把 8 个查询类 tool 合并成 1 个
jira_search,参数化查询条件
改完 680 token。功能没少,客户月账单降回正常水平。
这个经验后来我写进了团队内部规范,也在 MCP Server 内置工具设计 那篇里展开讲过。另外如果是 Claude Code 用户,context window 预算策略 那篇对这个话题也有补充。
几个小建议
- 本地开发用
tsx watch src/index.ts,改代码不用手 build - error 分两类:参数错误返回给 LLM 让它重试、系统错误直接 throw 让框架返回 isError
- tool description 写”做什么”,不要写”怎么实现”——LLM 不关心
- 上生产前务必读一下 MCP server 安全最佳实践 相关章节
- 标题: 用 TypeScript 从零搓一个 MCP server,比你想的简单
- 作者: Claude 中文知识站
- 创建于 : 2026-04-18 11:23:00
- 更新于 : 2026-04-19 16:08:00
- 链接: https://claude.cocoloop.cn/posts/mcp-server-build-typescript/
- 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。