跳转到主要内容

测试与调试

8.1 MCP Inspector

官方提供的交互式调试工具,让你在浏览器中测试Server的每个能力。
npx @modelcontextprotocol/inspector node dist/index.js
Inspector会打开一个Web界面,你可以:
  • 查看Server暴露的所有工具、资源、提示词
  • 手动输入参数执行工具
  • 查看JSON-RPC请求和响应的原始数据
  • 验证inputSchema和outputSchema
  • 测试资源读取和订阅
  • 测试提示词获取(含参数填充)
  • 查看Server声明的能力(capabilities)

Inspector测试远程Server

Inspector也支持测试Streamable HTTP Server:
npx @modelcontextprotocol/inspector --url https://my-server.example.com/mcp
可以附带认证头:
npx @modelcontextprotocol/inspector --url https://my-server.example.com/mcp --header "Authorization: Bearer YOUR_TOKEN"

8.2 单元测试

测试每个工具的执行逻辑(不涉及MCP协议层):
import { describe, it, expect } from "vitest";

describe("search_products tool", () => {
  it("返回匹配的商品", async () => {
    const result = await searchProducts("跑步鞋", { limit: 5 });
    expect(result.results).toHaveLength(5);
    expect(result.results[0]).toHaveProperty("name");
    expect(result.results[0]).toHaveProperty("price");
  });

  it("空搜索返回空数组", async () => {
    const result = await searchProducts("不存在的商品xyz", { limit: 5 });
    expect(result.results).toHaveLength(0);
  });

  it("价格范围筛选有效", async () => {
    const result = await searchProducts("鞋", {
      minPrice: 500,
      maxPrice: 1000,
      limit: 10
    });
    result.results.forEach(p => {
      expect(p.price).toBeGreaterThanOrEqual(500);
      expect(p.price).toBeLessThanOrEqual(1000);
    });
  });
});

测试工具的错误处理

describe("get_order tool", () => {
  it("返回isError当订单不存在", async () => {
    const result = await getOrderTool({ orderId: "INVALID-001" });
    expect(result.isError).toBe(true);
    expect(result.content[0].text).toContain("不存在");
  });
});

8.3 集成测试

测试完整的MCP协议交互,包括初始化、能力协商、工具调用:
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";

describe("MCP Server集成测试", () => {
  let client: Client;

  beforeAll(async () => {
    const transport = new StdioClientTransport({
      command: "node",
      args: ["dist/index.js"]
    });
    client = new Client({
      name: "test-client",
      version: "1.0.0"
    });
    await client.connect(transport);
  });

  afterAll(async () => {
    await client.close();
  });

  it("初始化成功并返回Server信息", () => {
    const info = client.getServerInfo();
    expect(info.name).toBe("my-commerce-server");
  });

  it("工具列表非空", async () => {
    const tools = await client.listTools();
    expect(tools.tools.length).toBeGreaterThan(0);
  });

  it("工具执行返回正确格式", async () => {
    const result = await client.callTool("search_products", {
      query: "鞋"
    });
    expect(result.content[0].type).toBe("text");
    const data = JSON.parse(result.content[0].text);
    expect(data).toHaveProperty("results");
  });

  it("资源列表可获取", async () => {
    const resources = await client.listResources();
    expect(resources.resources.length).toBeGreaterThan(0);
  });

  it("资源可读取", async () => {
    const result = await client.readResource(
      "commerce://catalog/categories"
    );
    expect(result.contents[0].mimeType).toBe("application/json");
  });

  it("提示词可获取", async () => {
    const prompts = await client.listPrompts();
    expect(prompts.prompts.length).toBeGreaterThan(0);
  });
});

8.4 协议合规验证

确保Server符合MCP规范的关键检查点:
检查项验证方法
initialize 正确响应Inspector查看初始化握手
能力声明完整检查capabilities中是否声明了所有提供的Primitive
tools/list 分页正确测试大量工具时的cursor分页
isError 正确使用业务错误用isError,不用JSON-RPC error
通知格式正确通知消息没有id字段
content类型正确text/image/audio类型字段齐全
structuredContent与outputSchema匹配有outputSchema时返回structuredContent

8.5 常见调试技巧

问题诊断方法解决
Server不响应检查stderr输出确认进程启动成功
工具不出现在列表Inspector查看tools/list检查工具注册代码
参数类型错误Inspector查看inputSchema修正JSON Schema
返回数据格式不对Inspector查看原始响应确保content数组格式正确
Claude看不到Server检查 claude_desktop_config.json确认路径和命令正确
初始化超时检查Server启动时间减少启动时的阻塞操作
Streamable HTTP 401检查OAuth配置确认token有效且未过期
会话丢失检查 Mcp-Session-Id确保Client在每次请求中携带

stderr日志

stdio Server的日志必须输出到stderr:
// 正确 — 输出到stderr
console.error("[INFO] Server started");
console.error(`[DEBUG] Processing request: ${method}`);

// 错误 — 会破坏协议通信
console.log("Server started");  // stdout是协议通道

日志级别

MCP规范定义了标准日志级别,Server可以通过 notifications/message 发送日志给Client:
// Server → Client 发送日志通知
server.sendLoggingMessage({
  level: "warning",
  logger: "inventory",
  data: "SKU SHOE-001 库存低于安全阈值"
});
Client可以通过 logging/setLevel 设置期望接收的最低日志级别。

调试Streamable HTTP

对于远程Server,可以使用 curl 手动发送JSON-RPC请求:
curl -X POST https://my-server.example.com/mcp \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "MCP-Protocol-Version: 2025-11-25" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'

下一章: 部署指南 — 本地部署vs远程部署