# 用官方 SDK 写一个 TypeScript MCP 服务器

> 要用 TypeScript 写 MCP 服务器，先安装 `@modelcontextprotocol/server` 和 `zod`，创建一个 `McpServer`，再用 `registerTool` 和 Zod 输入参数结构注册每个工具。用 `serveStdio` 提供服务，宿主就能把这个文件当作本地进程启动。运行 `npx tsx src/index.ts` 即可，不需要构建，然后用 MCP Inspector 调用工具。

Mindket 编辑部 · 发布于 2026-11-04 · 更新于 2026-11-05 · https://mindket.com/cn/learn/mcp/build-mcp-server-typescript

## 要点

- 你会在一个 TypeScript 文件里写出 stdio 服务器，带一个查询美国天气预警的工具 `get-alerts`。
- Zod 结构是你唯一要写的参数结构。其余的都由 SDK 据此生成，包括在处理函数运行之前的参数校验。
- 当前版本的 SDK 包叫 `@modelcontextprotocol/server`。旧教程装的 `@modelcontextprotocol/sdk` 是 1.x 系列。
- stdio 服务器里只要有一个 `console.log`，协议就会被弄坏。日志请用 `console.error`。

## 开始之前

这一课照着 TypeScript SDK 自己仓库里的第一个服务器教程来做，它用 `tsx` 直接运行 TypeScript。[MCP 构建指南](https://modelcontextprotocol.io/docs/develop/build-server)写的是一个相似的天气服务器，多一个工具，并用 `tsc` 编译。想要有构建步骤的话，可以选那一份。

你需要 Node.js 20 或更高、npm，还要能上网，因为工具调用的是美国国家气象局的 API。最后一步的 Inspector 需要打过补丁的 Node：22.23.4、24.22.1 或 26.12.1，或者各自系列里更新的版本，第 3 课会解释。装任何东西之前先看清包名，因为 TypeScript 的 MCP 教程分成了两代。

| 包 | SDK 系列 | 在哪里会遇到 |
| --- | --- | --- |
| `@modelcontextprotocol/server` | v2，当前的稳定系列 | 官方文档和这一课 |
| `@modelcontextprotocol/client` | v2，用于连接服务器的代码 | 客户端和测试脚本 |
| `@modelcontextprotocol/sdk` | v1.x，较早的系列，仍在维护 | 旧教程和已有代码 |

## 六步写出服务器

代码分两段：工具和启动调用。两段都放进同一个文件 `src/index.ts`。

````typescript
import { McpServer } from '@modelcontextprotocol/server';
import { serveStdio } from '@modelcontextprotocol/server/stdio';
import * as z from 'zod/v4';

const NWS_API = 'https://api.weather.gov';

interface AlertsResponse {
    features: { properties: { event?: string; headline?: string } }[];
}

function createServer(): McpServer {
    const server = new McpServer({ name: 'weather', version: '1.0.0' });

    server.registerTool(
        'get-alerts',
        {
            description: 'Get the active weather alerts for a US state',
            inputSchema: z.object({
                state: z.string().length(2).describe('Two-letter US state code, e.g. CA')
            })
        },
        async ({ state }) => {
            const code = state.toUpperCase();
            const url = `${NWS_API}/alerts/active?area=${code}`;
            const res = await fetch(url, { headers: { 'User-Agent': 'mcp-weather-tutorial/1.0' } });
            if (!res.ok) {
                return { content: [{ type: 'text', text: `NWS API error: HTTP ${res.status}` }], isError: true };
            }
            const { features } = (await res.json()) as AlertsResponse;
            if (features.length === 0) {
                return { content: [{ type: 'text', text: `No active alerts for ${code}.` }] };
            }
            const lines = features.map(f => f.properties.headline ?? f.properties.event ?? 'Unnamed alert');
            return { content: [{ type: 'text', text: lines.join('\n') }] };
        }
    );

    return server;
}

void serveStdio(createServer);
console.error('weather MCP server running on stdio');

````

1. 用 `node --version` 检查 Node，版本必须是 20 或更高。
2. 用 `mkdir weather && cd weather` 创建项目目录，再运行 `npm init -y`，然后运行 `npm pkg set type=module`。`type=module` 这个设置不能省，因为 SDK 只提供 ES 模块。
3. 用 `npm install @modelcontextprotocol/server zod tsx` 安装 SDK、Zod 和 tsx，再用 `mkdir src` 建源码目录。
4. 新建 `src/index.ts`，把上面的代码粘进去。
5. 在项目根目录用 `npx tsx src/index.ts` 启动服务器。它会往 stderr 打印 `weather MCP server running on stdio`，然后停在那里等待。按 Ctrl+C 停止。
6. 运行 `npx @modelcontextprotocol/inspector npx tsx src/index.ts` 调用工具。在它打开的浏览器标签页里点 Connect，打开 Tools 标签页，选中 `get-alerts`，输入像 `TX` 这样的州代码，然后运行。

## 代码在做什么

`createServer` 是一个工厂函数，每次调用都新建一个 `McpServer` 并在上面注册一个工具。`registerTool` 接收名称、配置和一个异步处理函数。`inputSchema` 是一个 Zod 结构，SDK 就靠这一份结构给模型生成 JSON Schema，在处理函数运行之前校验参数，并推断出处理函数的参数类型。

处理函数返回 `content`，也就是一组带类型的内容块，这里只有一个 `text` 块。设置 `isError: true` 表示这次结果是失败，模型能读到并作出反应。用 `{ "state": "California" }` 调用 `get-alerts`，SDK 会在处理函数运行之前就拒绝，模型看到的是这条消息：

````text
Input validation error: Invalid arguments for tool get-alerts: state: Too big: expected string to have <=2 characters

````

> 工厂写法是有意为之的。MCP 构建指南说，`serveStdio` 每次需要实例时都会调用它，所以每次调用都必须返回新的 `McpServer`。SDK README 里更短的示例是用 `server.connect(new StdioServerTransport())` 手动接线的。构建指南说，这样启动的服务器只能服务用 2025 年版旧握手开头的客户端，所以 `serveStdio` 更稳妥。

## 模型调用工具时发生了什么

模型看不到你的代码。它看到的是工具的名称、说明，以及由 `inputSchema` 生成的 JSON Schema，所以工具会不会被调用，全看这三样。把服务器接到宿主上之后，一次回答要经过六步。

**一次工具调用，从提问到回答**

1. 宿主发送问题。SDK 示例里的宿主是 VS Code。它把问题连同每个可用工具的名称、说明和输入参数结构一起发给模型。
2. 模型挑选工具。它判断 `get-alerts` 能回答这个问题，就发出一次调用，参数由它自己决定。
3. 客户端调用你的服务器。宿主内部的 MCP 客户端通过 stdio 发出一个 `tools/call` 请求。
4. SDK 运行你的处理函数。它先按 `inputSchema` 校验参数，再运行处理函数。
5. 你的内容被送回去。处理函数的 `content` 通过 stdout 作为 `tools/call` 的结果返回。
6. 模型写出回答。它读取文本块，写出你在聊天里看到的回复。

## David Soria Parra 谈无状态规范和 v2 SDK

David Soria Parra 是 Anthropic 的技术人员，也是 MCP 的联合创建者。2026-07-28 版规范发布当天，他[在 X 上](https://x.com/dsp_/status/2082173429399142616)宣布：MCP 变成了无状态协议，Python 和 TypeScript SDK 也为它发布了 v2.0.0。

这和本课的工厂函数对得上：没有会话把客户端绑在某个对象上，`serveStdio` 需要时就能新建一个 `McpServer`。

同一条帖子还把扩展列为另一项重点：MCP Apps、Tasks、Enterprise Managed Auth 等等，都是在特定领域使用 MCP 的方式。TypeScript SDK 在 10 月 23 日的 [2.4.0 版](https://github.com/modelcontextprotocol/typescript-sdk/releases/tag/v2.4.0)又加了 Skills over MCP。自己动手写这类功能之前，先翻翻 [SDK 的发布记录](https://github.com/modelcontextprotocol/typescript-sdk/releases)。

当天晚些时候，他又[在 X 上](https://x.com/dsp_/status/2082225962402402384)感谢各 SDK 的维护者，并列出支持新规范的版本：TypeScript、Python 和 C# SDK 的 v2.0.0，以及 Go SDK 的 v1.7.0。协议都是同一套，按团队写的语言挑 SDK 就行。

> 部署 MCP 服务一下子简单多了，也更容易扩展。
>
> David Soria Parra, Anthropic 技术人员，MCP 联合创建者，引文为译文, [X](https://x.com/dsp_/status/2082173429399142616) (2026-11-05 查阅)

[![David Soria Parra（@dsp_）2026 年 7 月 28 日在 X 上发帖：很高兴宣布 MCP 2026-07-28 版发布。MCP 现在是无状态的，并为多轮往返的请求定义了语义，部署 MCP 服务一下子简单多了，也更容易扩展。MCP 现在有了扩展。Python、Typescript 和 C#（即将推出）SDK 都为新规范发布了 v2.0.0，用起来更顺手](https://mindket.com/blog/shots/x-dsp-mcp-2026-07-28-release.webp?v=cf60293d4a62)](https://x.com/dsp_/status/2082173429399142616)

David Soria Parra 在 X 上，2026 年 7 月 28 日

[![David Soria Parra（@dsp_）2026 年 7 月 28 日晚些时候在 X 上发帖：特别感谢各 SDK 的维护者，赶在发布时备好了带根本性升级的 v2.0.0。去拿 Typescript、Python 和 C# SDK 的 v2.0.0，以及支持新规范的 Go SDK v1.7.0](https://mindket.com/blog/shots/x-dsp-sdk-v2-maintainers.webp?v=248b22307d6b)](https://x.com/dsp_/status/2082225962402402384)

David Soria Parra 在 X 上，2026 年 7 月 28 日

> X 官方嵌入卡片的截图，拍摄于 2026 年 11 月 5 日，时间为 UTC。两条帖子都被 X 用“Show more”截断了。点击截图可到 X 上打开原帖。

## 常见错误

- 使用 `console.log`。在 stdio 上，stdout 就是协议通道，宿主会报 `SyntaxError: Unexpected token ... is not valid JSON`，引号里的那个字符是多出来那一行的第一个字符。改用 `console.error`。
- 漏掉第 2 步里 package.json 的 `type=module` 那一行。
- 依赖树里有两份 zod。TypeScript 可能报 `TS2589: Type instantiation is excessively deep`。运行 `npm ls zod`，让它只列出一个版本，某个依赖固定了别的版本时，在 package.json 里加一条 `overrides`。
- 用 Node 18 或更旧的版本。SDK 的每个包都要求更新的运行时，故障排查页说 `ReferenceError: crypto is not defined` 就表示运行时太旧。
- 照搬 1.x 的教程。如果它安装的是 `@modelcontextprotocol/sdk`，它的导入写法和这一课对不上。

## 动手试试

- 在 Inspector 里用 `{ "state": "California" }` 调用 `get-alerts`，把得到的消息和上面那条对比一下。
- 在文件开头加一行 `console.log("hello")`，再运行一次 Inspector 命令，看看什么坏了，然后把它改成 `console.error`。
- 在 `createServer` 里紧挨着第一个工具再加一个工具，确认 Tools 标签页里列出了两个。[MCP 构建指南](https://modelcontextprotocol.io/docs/develop/build-server)里的 `get_forecast` 就是现成的例子。

## 下一课：用 MCP Inspector 好好测一遍

你在 Inspector 里调用过一次工具了。[第 3 课](https://mindket.com/cn/learn/mcp/mcp-inspector)会把每个标签页、命令行模式，以及服务器出问题时该查什么都过一遍。想要这个服务器的 Python 版本，[第 1 课](https://mindket.com/cn/learn/mcp/build-mcp-server-python)就是。

想了解全貌，可以看[什么是 MCP](https://mindket.com/cn/blog/what-is-mcp)，里面讲了宿主、客户端和服务器是怎样配合的。

## 自测一下

1. 当前版本的 TypeScript SDK 用哪个包来构建服务器？

   A. `@modelcontextprotocol/sdk`
   B. `@modelcontextprotocol/client`
   C. `@modelcontextprotocol/server`

   答案：C。 当前的 SDK 是 `@modelcontextprotocol/server`，而 `@modelcontextprotocol/sdk` 是老教程里装的 1.x 版本。

2. stdio 服务器的宿主报出 `SyntaxError: Unexpected token ... is not valid JSON`，最可能的原因是什么？

   A. 有一处 `console.log` 往 stdout 写了内容，而 stdout 是协议通道
   B. 服务器是用 `npx tsx` 启动的，没有经过构建步骤
   C. 写的是 Zod schema，而不是 JSON Schema

   答案：A。 在 stdio 上，stdout 传的是协议数据，多出来的一行会把它破坏掉，所以日志要用 `console.error`。

3. 模型用 `{ "state": "California" }` 调用 `get-alerts`，会发生什么？

   A. 你的处理函数会运行，并把它转成 `CA`
   B. Inspector 会先把它转成州代码
   C. 处理函数运行后抛出 `TypeError`
   D. SDK 在你的处理函数运行之前就拒绝了它

   答案：D。 SDK 会先按 `inputSchema` 检查参数，模型会读到校验失败的消息。

4. 是什么决定了模型会不会调用你的工具？

   A. 你的处理函数里的代码
   B. 工具的名称、描述和输入 schema
   C. 你注册工具的先后顺序

   答案：B。 模型从来看不到你的代码，只能看到名称、描述，以及由 `inputSchema` 生成的 JSON Schema。

## 常见问题

### 还能用旧的 @modelcontextprotocol/sdk 包吗？

暂时可以。它是 1.x 系列，README 说在 2026 年 7 月 27 日 v2 发布后，它至少六个月内仍会收到缺陷修复和安全更新。新代码应该从 `@modelcontextprotocol/server` 开始。

### 构建指南里的 tsc 方案多了什么？

它把 `src` 编译到 `build` 文件夹，package.json 里多出一个 `bin` 条目、一个 `files` 列表和一个运行 `tsc` 的 `build` 脚本。之后宿主对 `build/index.js` 运行 `node`，就像指南里的 Claude Desktop 配置那样。

### 输入参数结构一定要用 Zod 吗？

不一定。README 说工具和提示词的结构使用 Standard Schema，所以 Zod v4、Valibot、ArkType 或任何兼容的库都可以。教程用的是 Zod，而且同一棵依赖树里有两份 Zod 会引起类型错误。

### SDK 能在 Bun 或 Deno 上运行吗？

可以。README 说 SDK 能在 Node.js、Bun 和 Deno 上运行。用 `bun add @modelcontextprotocol/server` 或 `deno add npm:@modelcontextprotocol/server` 安装。

### 怎样让同一个服务器走 HTTP？

把同一个 `createServer` 工厂交给 SDK 的 HTTP 服务，而不是 `serveStdio`。教程说，要让一个端点服务很多客户端时就这样做。SDK 还发布了适配 Express、Fastify、Hono 和 Node 自带 HTTP 服务器的小型适配包。

### stdio 服务器怎样打日志？

用写到 stderr 的 `console.error`，或者写到 stderr 或文件的日志库。宿主会自动捕获 stdio 服务器的 stderr，不需要额外接线。

## 资料来源

- [MCP TypeScript SDK: README](https://github.com/modelcontextprotocol/typescript-sdk) (2026-11-04 查阅)
- [MCP TypeScript SDK docs: Build your first server](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/docs/get-started/first-server.md) (2026-11-04 查阅)
- [MCP TypeScript SDK docs: Plug into a real host](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/docs/get-started/real-host.md) (2026-11-04 查阅)
- [MCP TypeScript SDK docs: Troubleshooting](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/docs/troubleshooting.md) (2026-11-04 查阅)
- [MCP docs: Build an MCP server](https://modelcontextprotocol.io/docs/develop/build-server) (2026-11-04 查阅)
- [MCP docs: MCP Inspector](https://modelcontextprotocol.io/docs/tools/inspector) (2026-11-04 查阅)
- [MCP docs: Debugging](https://modelcontextprotocol.io/docs/2026-07-28/tools/debugging) (2026-11-04 查阅)
- [MCP Inspector: README](https://github.com/modelcontextprotocol/inspector) (2026-11-05 查阅)
- [MCP TypeScript SDK release v2.4.0](https://github.com/modelcontextprotocol/typescript-sdk/releases/tag/v2.4.0) (2026-11-05 查阅)
- [David Soria Parra on X: the MCP 2026-07-28 release](https://x.com/dsp_/status/2082173429399142616) (2026-11-05 查阅)
- [David Soria Parra on X: the SDK releases that support the spec](https://x.com/dsp_/status/2082225962402402384) (2026-11-05 查阅)

[浏览 MCP 服务器](https://mindket.com/cn/mcp-servers)
