跳到主内容

用 TypeScript 写 MCP Server:从零到可发布的完整路径

100%
用 TypeScript 写 MCP Server:从零到可发布的完整路径

结论先说:写 MCP Server 最容易翻车的不是代码,是两个工程细节

用 TypeScript 写一个能跑的 MCP Server 只要几十行,但绝大多数「连不上」的故障来自两个非代码问题:package.json 里没开 type: "module",以及忘跑 npm run build。官方快速上手里把「记得构建」单列成一条强调事项,就是因为这一步被跳过的概率极高。

本文按「能连上 → 能用 → 能发布」三段讲,把每一步的坑标出来。

环境准备

  • Node.js 20 及以上:官方明确要求,低版本会在依赖安装或运行时报错。
  • 依赖:运行时依赖 MCP SDK 与 zod(用于入参校验),开发依赖 typescript 与 @types/node 。
  • 目录:src/index.ts 作为入口,构建产物输出到 build/。

三段配置:每段都有必填项


  1. 配置 package.json:必须加 "type": "module";加 bin 指向 ./build/index.js;build 脚本写成 tsc && chmod 755 build/index.js。缺了 type: module,ESM 语法会直接报模块错误。

  2. 配置 tsconfig.json:target 设 ES2022,module 与 moduleResolution 设 Node16,outDir 指向 ./build,rootDir 指向 ./src,开启 strict 。这套配置是官方示例验证过的,自己改容易踩模块解析的坑。

  3. 写服务端:创建 McpServer 实例并声明 name 与 version,用 zod 定义入参 schema 注册工具,最后用 StdioServerTransport 连接。注册工具时描述要写清楚「这个工具做什么、什么情况下用」,模型靠这段文本决定要不要调。

  4. 构建:跑 npm run build。这一步不做,客户端永远连不上你的服务——这是最常见的故障原因。

  5. 接入宿主验证:在客户端配置文件的 mcpServers 键下加一条,指向构建后的绝对路径。 UI 里能出现 MCP 相关入口,才说明至少有一个服务配置成功。

  6. 发布(可选):先发到 npm 包仓库,再用官方发布工具把元数据提交到 MCP Registry 。 Registry 只托管元数据不托管产物,所以 npm 发布是前置步骤。

日志是 TypeScript MCP Server 的隐形杀手:stdio 传输下 绝对不要用 console.log(),它写的是标准输出,会和 JSON-RPC 消息混在一起直接把协议搞坏。要打日志一律用 console.error()(写 stderr)或写文件。 HTTP 传输下标准输出才安全。

工具设计:三条决定好不好用

要点反例正例
名称do、tool1get_weather、list_orders:动词+对象
描述「获取信息」「按城市名或邮编查询当前天气,返回温度与天气状况」
入参一个大 JSON每个字段都写 description,必填项标进 required

模型唯一能看到的就是工具名、描述和 JSON Schema 。描述写得含糊,模型要么不调用,要么瞎传参数。

错误处理要分两种

协议错误与执行错误是两套机制,别混在一起:

  • 协议错误:未知工具名、参数格式不对、服务端异常。用 JSON-RPC 标准错误返回。
  • 执行错误:外部 API 失败、限流、业务校验不通过。结果里带 isError: true 返回,同时把可读的原因放进文本内容。

第二种尤其重要:把失败原因写清楚,模型才有可能自己换个参数重试;只返回一个 null,它只会原地打转。

发布到 MCP Registry 的要点
  • 先发 npm:Registry 只存元数据,包本身必须在 npm 上可获取。
  • 加 mcpName:在 package.json 里加这个字段用于校验,命名形如 io.github.<用户名>/<服务名>。
  • 两处名字要一致:server.json 的 name 必须与 package.json 的 mcpName 完全匹配。
  • 版本号同步:改版本时两个文件里的版本号都要改,否则校验失败。
  • 预览期注意:Registry 目前处于预览阶段,可能出现破坏性变更或数据重置,别把关键流程强依赖在上面。

SDK 怎么选

官方 SDK 按功能完整度和维护承诺分了档:TypeScript 、 Python 、 C#、 Go 是第一档(Tier 1),协议支持与维护最完整;Java 、 Rust 是第二档;Swift 、 Ruby 、 PHP 、 Kotlin 相对靠后。选型的现实标准是生态与团队语言栈,不是协议能力——所有 SDK 都支持创建服务端、连接任意服务、本地与远程传输。


客户端连不上我的服务,最可能是什么原因?
先查两件事:package.json 有没有 type: "module",以及有没有跑过 npm run build。这两条占了绝大多数「连不上」的故障。

为什么服务启动后协议报错?
大概率是在 stdio 传输下用了 console.log()。标准输出被日志占用会破坏 JSON-RPC 消息,改成 console.error() 即可。

一定要用 TypeScript 吗?
不。 Python 、 C#、 Go 都是同档支持的 SDK 。选你团队最熟的语言,协议能力与传输方式没有差别。

工具描述可以随便写吗?
不能。规范明确要求客户端把工具注解视为不可信输入,而对模型来说描述是唯一的调用依据。写清楚用途、入参含义和返回结构,比堆砌形容词有用得多。

相关阅读

这篇有帮助吗?
云上的幻象
云上的幻象查看主页

七彩云博客,分享 WordPress 建站实战与 AI 工具测评,覆盖服务器运维、站长工具、软件资源与电商运营干货,专注原创实用的主题插件、网站加速与安全优化教程。

921文章4评论

相关文章

评论 (0)

欢迎你,新朋友,感谢参与互动!文明发言,理性交流 · 首次评论将在审核后展示