Chapter 03

搭建第一个 MCP Server

从零开始,用 TypeScript 创建可运行的 MCP Server 并在 Claude Desktop 中完成测试

环境准备

开发 MCP Server 需要以下环境。本章以 TypeScript + Node.js 为主,同时介绍 Python SDK 的对应写法。

Node.js 环境要求

# 检查 Node.js 版本
node --version   # 应显示 v18.x 或更高
npm --version

# 安装 pnpm(可选,但推荐)
npm install -g pnpm

# 安装 tsx(用于直接运行 TypeScript)
npm install -g tsx

Python 环境要求

# 安装 uv(Python 包管理器)
curl -LsSf https://astral.sh/uv/install.sh | sh

# 检查 Python 版本
python3 --version  # 应显示 3.10 或更高

@modelcontextprotocol/sdk 用法

Anthropic 官方提供了 TypeScript 和 Python 两个 SDK,封装了所有底层协议细节,让开发者只需关注业务逻辑。

@modelcontextprotocol/sdk(TypeScript)
官方 TypeScript/Node.js SDK,提供 McpServerStdioServerTransport 等核心类,内置 Zod 类型验证。
mcp(Python)
官方 Python SDK,提供 FastMCP 高级接口和低级 Server 类,支持装饰器风格定义 Tools。

SDK 核心类概览

TypeScript SDK 核心类

  • McpServer — 高级 Server 封装
  • Server — 低级 Server 类
  • StdioServerTransport — stdio 传输
  • SSEServerTransport — SSE 传输(旧)
  • StreamableHTTPServerTransport — 新 HTTP 传输
  • CallToolResult — 工具调用结果类型

Python SDK 核心类

  • FastMCP — 高级声明式接口
  • Server — 低级 Server 类
  • stdio_server() — stdio 上下文管理器
  • @mcp.tool() — 工具装饰器
  • @mcp.resource() — 资源装饰器
  • @mcp.prompt() — 提示词装饰器

创建最简 MCP Server

步骤一:初始化项目

  1. 创建项目目录并初始化 npm
  2. 安装 MCP SDK 和必要依赖
  3. 配置 TypeScript
  4. 编写 Server 代码
  5. 测试运行
# 创建项目目录
mkdir my-first-mcp-server
cd my-first-mcp-server

# 初始化 package.json
npm init -y

# 安装 MCP SDK 和 Zod(参数验证)
npm install @modelcontextprotocol/sdk zod

# 安装 TypeScript 开发工具
npm install -D typescript @types/node tsx

步骤二:配置 tsconfig.json

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "Node16",
    "moduleResolution": "Node16",
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true
  },
  "include": ["src/**/*"]
}

步骤三:编写 Server 主文件

创建 src/index.ts,这是一个包含两个工具的最简 MCP Server:

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";

// 创建 MCP Server 实例
const server = new McpServer({
  name: "my-first-server",        // Server 名称
  version: "1.0.0",               // Server 版本
});

// ─── 工具一:加法计算器 ───────────────────────────────────────
server.tool(
  "add",                          // 工具名称
  "将两个数字相加并返回结果",        // 描述(AI 据此决定何时调用)
  {
    a: z.number().describe("第一个数字"),
    b: z.number().describe("第二个数字"),
  },
  async ({ a, b }) => ({
    content: [{ type: "text", text: `${a} + ${b} = ${a + b}` }],
  })
);

// ─── 工具二:问候语生成器 ──────────────────────────────────────
server.tool(
  "greet",
  "生成一条个性化问候语",
  {
    name: z.string().describe("要问候的人名"),
    language: z.enum(["zh", "en", "ja"])
      .default("zh")
      .describe("语言:zh=中文 en=英文 ja=日文"),
  },
  async ({ name, language }) => {
    const greetings = {
      zh: `你好,${name}!欢迎使用 MCP 协议。`,
      en: `Hello, ${name}! Welcome to MCP protocol.`,
      ja: `こんにちは、${name}さん!MCPプロトコルへようこそ。`,
    };
    return {
      content: [{ type: "text", text: greetings[language] }],
    };
  }
);

// ─── 启动 Server ──────────────────────────────────────────────
async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
  // 注意:Server 运行时不要向 stdout 输出任何内容
  // 所有日志应写入 stderr
  process.stderr.write("MCP Server started\n");
}

main().catch(console.error);
重要:stdio 模式下禁止写入 stdout 使用 stdio 传输时,stdout 是 MCP 协议消息的专用通道。任何非 JSON-RPC 消息的输出(包括 console.log)都会污染协议流,导致 Client 解析失败。调试日志必须写入 stderr 或文件。

Python 版本对比

同样的功能用 Python FastMCP 实现更加简洁:

from mcp.server.fastmcp import FastMCP

mcp = FastMCP("my-first-server")

@mcp.tool()
def add(a: float, b: float) -> str:
    """将两个数字相加并返回结果"""
    return f"{a} + {b} = {a + b}"

@mcp.tool()
def greet(name: str, language: str = "zh") -> str:
    """生成一条个性化问候语"""
    greetings = {
        "zh": f"你好,{name}!",
        "en": f"Hello, {name}!",
    }
    return greetings.get(language, greetings["zh"])

if __name__ == "__main__":
    mcp.run()  # 默认使用 stdio 传输

在 Claude Desktop 中注册测试

Claude Desktop 通过一个 JSON 配置文件来管理所有已注册的 MCP Server。

配置文件位置

macOS
~/Library/Application Support/Claude/claude_desktop_config.json
Windows
%APPDATA%\Claude\claude_desktop_config.json
Linux
~/.config/Claude/claude_desktop_config.json

配置文件格式

{
  "mcpServers": {
    "my-first-server": {
      "command": "node",
      "args": [
        "/absolute/path/to/my-first-mcp-server/dist/index.js"
      ],
      "env": {
        "NODE_ENV": "production"
      }
    },
    "another-server": {
      "command": "python3",
      "args": ["/path/to/server.py"]
    }
  }
}
开发模式使用 tsx 开发阶段不需要先编译,可以直接使用 tsx 运行 TypeScript 文件: "command": "npx""args": ["tsx", "/path/to/src/index.ts"]

配置后重启 Claude Desktop

修改配置文件后,需要完全退出 Claude Desktop 并重新启动(不是最小化,是完全退出)。成功加载后,你会在 Claude 对话界面的输入框附近看到工具图标,表示 MCP Server 已就绪。

调试技巧

查看 MCP 日志

Claude Desktop 会将 MCP Server 的 stderr 输出记录到日志文件:

# macOS
tail -f ~/Library/Logs/Claude/mcp-server-my-first-server.log

# 查看所有 MCP 相关日志
ls ~/Library/Logs/Claude/

使用 MCP Inspector(最佳调试工具)

MCP Inspector 是官方提供的可视化调试工具,无需 Claude Desktop 就能直接测试 Server:

# 启动 MCP Inspector,连接到你的 Server
npx @modelcontextprotocol/inspector node dist/index.js

# 如果使用 tsx 直接运行
npx @modelcontextprotocol/inspector tsx src/index.ts

Inspector 会在浏览器打开一个 Web 界面,可以可视化查看 Server 的所有能力,并手动触发工具调用来测试返回值。

添加结构化日志

// 封装一个安全的日志函数,写入 stderr
function log(level: string, message: string, data?: unknown) {
  const entry = {
    timestamp: new Date().toISOString(),
    level,
    message,
    data,
  };
  process.stderr.write(JSON.stringify(entry) + "\n");
}

// 使用示例
log("info", "Tool called", { tool: "add", params: { a, b } });
log("error", "File not found", { path: filePath });

常见问题排查

Server 未出现在 Claude 界面
检查配置文件 JSON 格式是否正确;确认命令路径使用绝对路径;查看 Claude 日志文件是否有启动错误。
工具调用无响应
确认没有向 stdout 输出非 JSON-RPC 内容;检查工具处理函数是否有未捕获的异常;使用 Inspector 直接测试工具。
JSON 解析错误
通常是 stdout 被污染导致。搜索代码中所有 console.log 调用,改为写入 stderr。