<!-- title: MCP工具调试闭环实战 -->

# 用 MCP Inspector 打通自造工具的调试闭环

## 概述

本教程教你用官方调试工具 **MCP Inspector**，把"写一个 MCP 工具"和"在 Claude Code 里跑通它"之间的黑箱彻底打开。学完后你能：独立启动 Inspector 连接任意 MCP 服务器、在可视化界面里逐个调用工具并观察通知流、读懂 stderr 与日志通知的区别、导出配置一键接入 Claude Code 验证。适合已经写过或准备写第一个 MCP 服务器的开发者——尤其是那些"服务器接进 Claude Code 后莫名报错、却完全不知道哪一步出问题"的人。

## 前置条件

- **Node.js ≥ 22.7.5**（Inspector 的硬性要求，低版本会启动失败）
- 一个能跑的 MCP 服务器（Python 的 `fastmcp`，或 TypeScript 的 `@modelcontextprotocol/sdk`）。本教程用 Python 演示，但方法与语言无关
- 一个终端 + 一个浏览器（Inspector 是浏览器 UI）
- Claude Code 已安装（用于最后的接入验证环节）

---

## 一、为什么 Inspector 是你的"第一站"

很多人写完 MCP 服务器后，直接把它塞进 `claude_desktop_config.json` 或 Claude Code 的 MCP 配置里，然后在聊天框里问："帮我调用一下那个工具"。如果没反应，就陷入了盲调试——**你既看不到握手是否成功，也看不到工具列表是否被识别，更看不到调用时传了什么参数、服务端报了什么错**。

官方文档对这一点的态度非常明确，把 Inspector 列为调试工具体系的第一项，原话是："This should be your first stop"（这应该是你的第一站）。

MCP 的调试工具栈分三层，Inspector 占据最底层也最关键的位置：

| 层级 | 工具 | 解决什么问题 |
|------|------|-------------|
| 交互式测试 | **MCP Inspector** | 连服务器、调工具、看通知流——全程可视化 |
| 结构化日志 | Server logging（stderr / `notifications/message`） | 服务器自己暴露内部状态 |
| 客户端开发工具 | 客户端的日志面板（如 Claude Desktop 日志） | 看客户端这一侧发生了什么

关键认知是：**在把服务器交给 Claude Code 之前，先用 Inspector 把它彻底跑通**。一旦 Inspector 里能正常调用、返回正确、日志清晰，再接入客户端时 90% 的问题就不存在了。

## 二、看懂 Inspector 的双组件架构

Inspector 不是一个单体程序，而是两个进程协同工作。理解这个架构，后面所有的端口、认证、传输问题都能对号入座。

```mermaid
flowchart LR
    subgraph Browser["浏览器"]
        UI["MCPI Client UI<br/>React 应用"]
    end
    subgraph Local["本地进程"]
        Proxy["MCPP Proxy Server<br/>Node.js :6277"]
        Server["你的 MCP Server<br/>stdio 进程"]
    end
    UI -- "HTTP / WebSocket" --> Proxy
    Proxy -- "stdio 管道<br/>(spawn 子进程)" --> Server
    Server -. "stderr 日志 +<br/>notifications/message" .-> Proxy
    Proxy -. "通知流转发" .-> UI

    style UI fill:#e1f5ff,stroke:#0288d1
    style Proxy fill:#fff3e0,stroke:#ef6c00
    style Server fill:#e8f5e9,stroke:#388e3c
```

两个组件各司其职：

- **MCPI（MCP Inspector Client）**：一个 React 写的浏览器界面，默认跑在 `http://localhost:6274`。你在里面点按钮、填参数、看返回值。
- **MCPP（MCP Proxy）**：一个 Node.js 进程，默认端口 `6277`。它同时是"客户端"（连你的 MCP 服务器）和"HTTP 服务器"（给浏览器 UI 提供数据），是浏览器和服务器之间的协议桥。

> **小知识**：`6274` 和 `6277` 这两个端口不是随便选的，它们来自手机的 T9 九宫格键盘——`6274` 对应字母 `MCPI`，`6277` 对应 `MCPP`，是个好记的助记符。

注意：这里的 Proxy **不是**抓包用的网络代理，它不会拦截流量，它的作用是让"只能讲 stdio 的服务器"也能被"只会发 HTTP 的浏览器"操控。

## 三、5 分钟启动 Inspector

### 方式一：npx 直接拉起（最常用）

```bash
# 只启动 Inspector UI，稍后在界面里手动填服务器命令
npx @modelcontextprotocol/inspector
```

浏览器会自动打开 `http://localhost:6274`。如果你要直接调试一个本地的 MCP 服务器，把启动命令追加在后面：

```bash
# 调试一个构建产物为 build/index.js 的 TS 服务器
npx @modelcontextprotocol/inspector node build/index.js

# 调试一个 Python 服务器
npx @modelcontextprotocol/inspector python temp_tool.py
```

需要给服务器传环境变量或参数时，用 `-e` 和 `--`：

```bash
# 只传环境变量
npx @modelcontextprotocol/inspector -e API_KEY=sk-xxx -e DEBUG=true python temp_tool.py

# 同时传环境变量和参数，用 -- 分隔 Inspector 自己的标志
npx @modelcontextprotocol/inspector -e API_KEY=sk-xxx -- python temp_tool.py --port 8080
```

### 方式二：Docker 容器（隔离环境）

```bash
docker run --rm \
  -p 127.0.0.1:6274:6274 \
  -p 127.0.0.1:6277:6277 \
  -e HOST=0.0.0.0 \
  -e MCP_AUTO_OPEN_ENABLED=false \
  ghcr.io/modelcontextprotocol/inspector:latest
```

注意 `127.0.0.1:` 前缀——这是有意只绑定本地回环，不暴露到局域网（见后文安全章节）。

### 自定义端口

如果默认端口被占用：

```bash
CLIENT_PORT=8080 SERVER_PORT=9000 npx @modelcontextprotocol/inspector python temp_tool.py
```

**预期结果**：终端打印一行 `🔑 Session token: <一长串十六进制>`，并给出一个带 token 的 URL，浏览器自动打开。这就是你的调试入口。

## 四、实战：调试一个"有 bug"的温度工具

光看文档没用，我们来调试一个真实的小项目。这个服务器做温度换算，里面故意埋了一个常见 bug——空列表除零。

### 第 1 步：写服务器

```python
# temp_tool.py —— 温度换算 MCP 服务器（故意留了一个 bug）
import sys
from fastmcp import FastMCP

mcp = FastMCP("temp-converter")


@mcp.tool()
def celsius_to_fahrenheit(celsius: float) -> str:
    """把摄氏度换算成华氏度"""
    # 关键：调试日志写到 stderr，而不是 stdout
    print(f"[debug] 收到输入 celsius={celsius}", file=sys.stderr)
    fahrenheit = celsius * 9 / 5 + 32
    return f"{celsius}°C = {fahrenheit:.1f}°F"


@mcp.tool()
def average_temps(temps: list[float]) -> str:
    """计算多个温度的平均值"""
    total = sum(temps)
    avg = total / len(temps)   # bug：temps 为空列表时会抛 ZeroDivisionError
    return f"平均温度 {avg:.1f}°C"


if __name__ == "__main__":
    mcp.run(transport="stdio")
```

### 第 2 步：用 Inspector 连接它

```bash
npx @modelcontextprotocol/inspector python temp_tool.py
```

浏览器打开后，左侧已经自动填好了 Command=`python`、Args=`temp_tool.py`。点击 **Connect**，Inspector 通过 Proxy 拉起你的服务器进程并完成 MCP 握手。

### 第 3 步：在 UI 里逐项验证

连接成功后，顶部会看到三个标签页，对应 MCP 的三原语：

- **Tools**：列出 `celsius_to_fahrenheit` 和 `average_temps`。如果这里为空，说明服务器没正确注册工具——这是最常见的"接进去没反应"的根因。
- **Resources** / **Prompts**：本例没有，正常应为空。

点击 `celsius_to_fahrenheit`，右侧弹出参数表单（由 JSON Schema 自动生成），填入 `celsius: 25`，点 **Run Tool**：

```
预期返回：25°C = 77.0°F
```

同时，下方的 **Notifications** 面板会刷出一条 `[debug] 收到输入 celsius=25`——这就是你那条 `print(..., file=sys.stderr)` 被捕获的证据。

### 第 4 步：触发 bug 并捕获

现在调用 `average_temps`，把 `temps` 留空（或填 `[]`），点 Run。UI 会直接展示一个错误响应，而 Notification 面板里能看到服务端的 traceback。**这就是 Inspector 的核心价值**：在接入 Claude Code 之前，你已经定位到了"空列表除零"这个 bug，而不是等到用户在聊天里踩坑。

修复很简单：

```python
    if not temps:
        return "错误：温度列表不能为空"
    avg = total / len(temps)
```

## 五、日志：调试的命脉（以及一个致命陷阱）

调试 MCP 服务器，本质是"让服务器说话"。但**怎么说、往哪说**有严格规矩，踩错了会让整个协议崩溃。

### 致命陷阱：stdout 是协议通道

MCP 的 stdio 传输，把**标准输出（stdout）整个当成了协议消息通道**。这意味着：

> ⚠️ 本地 MCP 服务器**绝不能**往 stdout 打印任何日志，否则会污染协议、导致客户端解析失败、握手直接断开。

这是新手最常犯的错误。一个无辜的 `print("server started")`（默认走 stdout）就足以让 Claude Code 报"服务器未响应"。正确做法是写 stderr：

```python
# 错误：会破坏协议
print("server started")

# 正确：stderr 是安全的
print("server started", file=sys.stderr)
import sys  # 别忘了导入
```

官方文档对此专门用黄色警告框强调。**记住这条规则，能帮你省掉几个小时的盲调。**

### 协议级日志：`notifications/message`

除了 stderr，MCP 还定义了一条正式的日志通知通道，适用于**所有传输方式**（不只 stdio）：

```python
@mcp.tool()
async def my_tool(ctx) -> str:
    await ctx.session.send_log_message(
        level="info",
        data="开始处理请求",
    )
    return "done"
```

这条消息会作为 `notifications/message` 推送给客户端（包括 Inspector），和 stderr 日志的区别在于：

| 维度 | stderr 日志 | notifications/message |
|------|------------|----------------------|
| 适用传输 | 仅 stdio（被客户端捕获） | 所有传输 |
| 语义 | 非正式调试输出 | 协议正式日志 |
| 日志级别 | 无结构 | RFC 5424 八级（debug→emergency） |
| 客户端可调级别 | 否 | 是，运行时用 `logging/setLevel` |

MCP 定义了八个 RFC 5424 严重级别（`debug`、`info`、`notice`、`warning`、`error`、`critical`、`alert`、`emergency`）。客户端可以在运行时通过 `logging/setLevel` 请求动态调整最低级别——这意味着用户能在不重启服务器的前提下，把日志从"只看 error"切换到"全量 debug"。Inspector 的 UI 里就有这个级别开关，调高后能看到服务器内部每一步的细粒度通知。

实战建议：**stderr 用于开发期的临时调试（最快），`notifications/message` 用于生产期的正式日志（结构化、可被任何客户端消费）**。

## 六、闭环关键：导出配置，接入 Claude Code

Inspector 真正的设计巧思在于——它不只是个调试器，还是**接入客户端的跳板**。调通之后，你不用手写 JSON 配置。

Inspector UI 里有两个导出按钮（配置好服务器后出现）：

- **Server Entry**：复制单个服务器条目，可直接粘进你已有的 `mcp.json`。
- **Servers File**：复制一份完整的 `mcp.json`，含 `default-server`。

stdio 服务器导出长这样：

```json
{
  "mcpServers": {
    "default-server": {
      "command": "python",
      "args": ["temp_tool.py", "--debug"],
      "env": {
        "API_KEY": "your-api-key",
        "DEBUG": "true"
      }
    }
  }
}
```

把这段粘进 Claude Code 的项目配置（`.mcp.json`）或用户配置后，在 Claude Code 里验证：

```bash
# 查看所有 MCP 服务器连接状态
/mcp
```

`/mcp` 会列出已连接的服务器及其工具数。如果你的 `temp-converter` 显示 connected 且工具数为 2，闭环就完成了。

整个开发闭环如下：

```mermaid
flowchart TD
    A["写/改 MCP 服务器代码"] --> B["npx inspector 启动调试"]
    B --> C["Inspector UI 测试工具"]
    C --> D{"调用成功?"}
    D -- "失败/有bug" --> E["看 Notification 与 stderr 日志"]
    E --> A
    D -- "成功" --> F["点 Server Entry 导出 JSON"]
    F --> G["粘进 Claude Code 的 .mcp.json"]
    G --> H["/mcp 验证已连接"]
    H --> I["在对话里实际调用"]
    I --> J{"符合预期?"}
    J -- "否" --> A
    J -- "是" --> K["✅ 闭环完成"]

    style A fill:#e8f5e9,stroke:#388e3c
    style K fill:#c8e6c9,stroke:#2e7d32
    style D fill:#fff9c4,stroke:#f9a825
    style J fill:#fff9c4,stroke:#f9a825
```

## 七、常见问题排查表

| 现象 | 根因 | 解决 |
|------|------|------|
| Inspector 连接后 Tools 为空 | 服务器没注册工具，或握手失败 | 看 Notification 面板的初始化日志；确认 `@mcp.tool()` 装饰器写对 |
| 客户端报"服务器未响应" | 日志打到 stdout 污染了协议 | 全部改用 `print(..., file=sys.stderr)` |
| 长任务超时被取消 | 默认请求超时 60 秒 | 调大 `MCP_REQUEST_MAX_TOTAL_TIMEOUT`，或配合进度通知 |
| 工具能跑但读不到文件 | 工作目录未定义（macOS 上是 `/`） | 配置里一律用绝对路径，别依赖 cwd |
| Inspector 报 401/认证失败 | 新版默认开启 session token 认证 | 用终端打印的 `?MCP_PROXY_AUTH_TOKEN=xxx` 那个 URL 打开 |
| Streamable HTTP 服务器看不到 stderr | HTTP 传输不捕获 stderr | 改用 `notifications/message` 或 curl 抓包 |

超时相关的配置项（在 Inspector 的 Configuration 面板里改）：

| 配置项 | 默认 | 含义 |
|--------|------|------|
| `MCP_SERVER_REQUEST_TIMEOUT` | 300000ms | 单次请求客户端超时 |
| `MCP_REQUEST_TIMEOUT_RESET_ON_PROGRESS` | true | 收到进度通知就重置超时 |
| `MCP_REQUEST_MAX_TOTAL_TIMEOUT` | 60000ms | 配合进度通知的总超时上限 |
| `MCP_AUTO_OPEN_ENABLED` | true | 启动时是否自动开浏览器 |

## 八、安全警示：别让调试工具变成后门

Inspector 默认绑定了多层安全防护，理解它们能避免把开发机变成靶机：

**1. 默认 localhost 绑定 + session token 认证。** 新版 Inspector 启动时生成随机 token，必须带在 URL 里才能访问 UI。终端里那一行 `🔑 Session token:` 不是装饰，是认证凭据。

**2. CVE-2025-49596——一个真实的 RCE 漏洞。** 安全公司 Oligo 曾披露 Inspector 存在远程代码执行漏洞：攻击者只要诱导你访问一个恶意网页（甚至恶意广告），就能通过浏览器反向打穿你的 Inspector Proxy，进而在你机器上执行任意命令。所以官方强烈警告：

> 🚨 `DANGEROUSLY_OMIT_AUTH=true`（关闭认证）极其危险——不只是暴露到公网时危险，**即便只在本机，访问恶意网站也可能导致远程入侵**。除非你完全理解风险，否则不要关闭它。

**3. DNS 重绑定防护。** Inspector 校验请求的 `Origin` 头，默认只允许来自客户端端口（6274）的请求。需要放行额外来源时：

```bash
ALLOWED_ORIGINS=http://localhost:6274,http://localhost:8000 npm start
```

实战守则：**永远别关认证、永远别 `HOST=0.0.0.0` 绑公网、别在生产机常驻 Inspector**。它是开发期工具，用完即关。

## 延伸阅读

- **MCP 官方文档 - Debugging**：`https://modelcontextprotocol.io/docs/tools/debugging` —— 本文日志章节的权威来源，含 stdio/HTTP 差异详解
- **MCP 官方文档 - Inspector**：`https://modelcontextprotocol.io/docs/tools/inspector` —— 官方使用指南
- **Inspector GitHub 仓库**：`https://github.com/modelcontextprotocol/inspector` —— 源码、架构图、最新配置项
- **Oligo 安全博客 - CVE-2025-49596**：`https://www.oligo.security/blog/critical-rce-vulnerability-in-anthropic-mcp-inspector-cve-2025-49596` —— RCE 漏洞技术细节
- **MCP 规范 - Logging**：`https://modelcontextprotocol.io/specification/latest/server/utilities/logging` —— 八级日志与 setLevel 的规范定义

## 训练营适用性评估

| 维度 | 评估 |
|------|------|
| **适合的训练营类型** | AI 应用开发营、MCP/智能体工具开发营、企业内部"自建工具"实操营 |
| **学员基础** | 已完成至少一个 MCP 服务器 Hello World，懂 Python/TS 基础 |
| **建议教学时长** | 1.5–2 小时（含动手调 bug 环节） |
| **最佳教学顺序** | 放在"造第一个 MCP 工具"之后、"接入客户端"之前，作为承上启下的调试课 |
| **核心收获** | 学员能独立排查"工具接进去没反应"类问题，建立"先 Inspector 后客户端"的工程习惯 |
| **可拓展** | 可与 Hooks、Skills 课程联动——把调通的工具再用 Skill 封装触发 |

## 参考来源

1. [MCP Inspector - Model Context Protocol 官方文档](https://modelcontextprotocol.io/docs/tools/inspector) — Inspector 的官方使用说明，本文架构与启动章节的主要依据
2. [Debugging - Model Context Protocol 官方文档](https://modelcontextprotocol.io/docs/tools/debugging) — 调试工具栈、stderr/stdout 规则、notifications/message 日志机制的权威来源
3. [modelcontextprotocol/inspector - GitHub](https://github.com/modelcontextprotocol/inspector) — 源码与 README 全文，含端口助记符、导出配置、认证、安全章节的完整原文
4. [Critical RCE Vulnerability in Anthropic MCP Inspector - CVE-2025-49596 (Oligo)](https://www.oligo.security/blog/critical-rce-vulnerability-in-anthropic-mcp-inspector-cve-2025-49596) — Inspector RCE 漏洞技术分析，安全警示章节来源
5. [MCP Inspector: Debug and Test Your MCP Servers (2026 Guide) - Apigene](https://apigene.ai/blog/mcp-inspector) — 实战调试经验补充，stdio 限制与替代方案

> [!tip] 51学通信知识星球
> **星主：爱卫生**（微信：gprshome201101）——公众号「51学通信」创办者，19 年通信行业运维与讲师经验，著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书（各大电商平台有售）。
> **星球定位**：深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域，主打运维岗位与信令分析。
> **当前规模**：5200 篇主题 / 990 个回答，198 元/年。
> **加入福利**：加入星球后请加爱卫生微信，拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题，另有群内分享。
> **AI 加持**：51学通信知识星球已作为 skill 接入 WorkBuddy，使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例，做总结、找文档和 PPT 等场景全覆盖。

![51学通信知识星球海报-扫码加入](https://wiki.500nian.com/images/zhixingqiu_qrcode.png)
