> ## Documentation Index
> Fetch the complete documentation index at: https://learn.orbexa.io/llms.txt
> Use this file to discover all available pages before exploring further.

# MCP Server测试与调试完整指南

> MCP Server测试与调试完整实践指南，详细介绍MCP Inspector交互式调试工具使用方法、Vitest单元测试编写及端到端集成测试方案。包含JSON-RPC协议合规性验证、inputSchema与outputSchema校验技巧，确保MCP Server在生产环境中稳定可靠运行

# 测试与调试

## 8.1 MCP Inspector

官方提供的交互式调试工具，让你在浏览器中测试Server的每个能力。

```bash theme={null}
npx @modelcontextprotocol/inspector node dist/index.js
```

Inspector会打开一个Web界面，你可以：

* 查看Server暴露的所有工具、资源、提示词
* 手动输入参数执行工具
* 查看JSON-RPC请求和响应的原始数据
* 验证inputSchema和outputSchema
* 测试资源读取和订阅
* 测试提示词获取（含参数填充）
* 查看Server声明的能力（capabilities）

### Inspector测试远程Server

Inspector也支持测试Streamable HTTP Server：

```bash theme={null}
npx @modelcontextprotocol/inspector --url https://my-server.example.com/mcp
```

可以附带认证头：

```bash theme={null}
npx @modelcontextprotocol/inspector --url https://my-server.example.com/mcp --header "Authorization: Bearer YOUR_TOKEN"
```

## 8.2 单元测试

测试每个工具的执行逻辑（不涉及MCP协议层）：

```typescript theme={null}
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);
    });
  });
});
```

### 测试工具的错误处理

```typescript theme={null}
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协议交互，包括初始化、能力协商、工具调用：

```typescript theme={null}
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：

```typescript theme={null}
// 正确 — 输出到stderr
console.error("[INFO] Server started");
console.error(`[DEBUG] Processing request: ${method}`);

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

### 日志级别

MCP规范定义了标准日志级别，Server可以通过 `notifications/message` 发送日志给Client：

```typescript theme={null}
// Server → Client 发送日志通知
server.sendLoggingMessage({
  level: "warning",
  logger: "inventory",
  data: "SKU SHOE-001 库存低于安全阈值"
});
```

Client可以通过 `logging/setLevel` 设置期望接收的最低日志级别。

### 调试Streamable HTTP

对于远程Server，可以使用 `curl` 手动发送JSON-RPC请求：

```bash theme={null}
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":{}}'
```

***

**下一章**: [部署指南](/zh/book-5/ch9-deployment) — 本地部署vs远程部署
