Skip to content

12.5 MCP Inspector 工具链 ​

🎯 学习目标:把 Inspector 用成"能交互调试、也能进 CI 校验"的日常工具 ⏱️ 预计时间:40 分钟 📊 难度等级:⭐⭐⭐(动手为主)

📌 一句话结论:Inspector 是官方调试器,有三种形态——CLI(脚本/CI)、TUI(终端交互)、Web(图形界面)。它最独特的设计是 protocol era:同一个 HTTP 地址,可以按"旧版"或"新版"协议去连,方便你对比行为差异。配套的 npm 包还附带一个实验性的 mcpdo 连接客户端(一次连上、保持长连接跨多条命令)以及一份 Security 威胁模型文档。

🧰 三种形态,怎么选 ​

形态适合特点
CLICI 流水线、shell 一行命令、编码 agent 即时验证服务器改动每次运行只发一个 --method 指定的请求,打印结果后退出
TUI在终端里交互式调试全键盘操作,不依赖浏览器
Web图形化查看请求/响应/流标签页式界面,最直观

💡 三者的服务器连接解析逻辑完全一致:配置文件里的每服务器设置(headers、超时、OAuth、protocol era、roots)在三种形态下都按同样规则生效。

🕰️ 核心概念:Protocol Era ​

2026-07-28 对协议做了大改,所以 Inspector 把 protocol era(协议时代:legacy 旧 / modern 新,以 2026-07-28 为界)当作一等公民的"每服务器设置",与传输方式正交——同一个 HTTP URL 既能当 legacy 服务器查,也能当 modern 服务器查。

每个服务器带一个 protocolEra,取值 legacy / auto / modern:

Era连接时 Inspector 做什么
legacy默认值。 直接 initialize,不做任何探测
auto先探测 server/discover,遇到任何"非 modern"结果就回退到 initialize
modern精确钉住 2026-07-28,不回退,遇到非 modern 服务器就直接失败

配置位置:Web 客户端在 Server Settings;catalog/配置文件里是 protocolEra 字段;CLI 与 TUI 从同一份文件读取,也可以用 --protocol-era <legacy|auto|modern> 在单次运行时覆盖该设置。

💡 为什么默认是 legacy 而不是 auto? 调试工具不应该自动探测。对一个沉默的 legacy stdio 服务器发 server/discover 探测会卡住,还会污染你本就要看的那份 transcript。选 auto 或 modern 是一个主动动作,这样你在 Protocol 标签里看到的,就是"你的服务器面对一个按你配置行事的客户端时"会看到的东西。

连接成功后,协商出的 era 会作为徽章显示在 Protocol 标签的 Messages 头部,并出现在 Connection Info 里。modern 连接下,server/discover 还会提供 capabilities(含 extensions)、instructions 与 supportedVersions;服务器名与版本在结果 _meta 的 io.modelcontextprotocol/serverInfo 里。

era 差异速览(同一功能在两种 era 下的不同表现):

功能LegacyModern
日志会话级:logging/setLevel 设一次,服务器持续推 notifications/messagelogging/setLevel 已移除;改为每请求在 _meta["io.modelcontextprotocol/logLevel"] 上 opt-in
资源订阅resources/subscribe,会话级标记subscriptions/listen 长连接流,带 stream-status 徽章(Connecting → Listening;断流时 Reconnecting...,彻底关闭 Stream ended,服务器不确认时 Not acknowledged)
Tasks靠 capabilities.tasks 显示标签页;tasks/list + 阻塞式 tasks/result靠协商的扩展 io.modelcontextprotocol/tasks;只轮询 tasks/get,完成即内联结果
MRTR服务器主动发请求(server.elicitInput 等)手动逐轮驱动:input_required 在 pending-request 弹窗里等你回答
会话可能有 Mcp-Session-Id,断开时发 DELETE;legacy 还会在 initialize 后开一条独立的 GET 通知流(Server Settings 里的 Suppress Notification Stream 可跳过它)无会话、按请求;不向服务器发 DELETE,断开纯属本地
取消发 notifications/cancelledmodern Streamable HTTP 改为关闭该请求自己的 SSE 响应流(2026-07-28 的取消信号);stdio 仍走 notifications/cancelled

⚠️ legacy 的 collect_elicitation 模式在 2026-07-28 连接上会报错,因为该协议版本不允许服务器向客户端发请求。MRTR 是它的现代替代。

密钥存储与明文风险 ​

Inspector 把凭据移出 mcp.json / client.json / oauth.json,改存进一个密钥存储:获取的 OAuth 令牌(access / refresh / IdP session)、每个服务器的 OAuth client secret、以及每个 stdio 服务器的 env: 值(env 键留在文件里、值清空,真实值进存储)。headers 不会被移走,仍原样写在 mcp.json 里。

⚠️ 没有密钥环时,密钥会落进明文文件,而且你没被询问。 在密钥环探测失败的机器上(没装 libsecret 或没有运行中的 Secret Service 的 Linux、headless / SSH 会话、Termux、挂载卷的容器),Inspector 自动把密钥存到 ~/.mcp-inspector/secrets.json,未加密(除非你提供密钥)。文件权限 0600 能挡住其他非 root 用户,但挡不住 root、挡不住你家目录的备份或副本,也挡不住以你身份运行的程序——包括 Inspector 启动的 stdio 服务器。

三选一:

  • 找回密钥环:装 libsecret 并运行一个 Secret Service(如 gnome-keyring),或在桌面会话里跑 Inspector;下次启动会把文件里的密钥迁进密钥环并删除文件。
  • 加密文件:用 MCP_INSPECTOR_SECRET_KEY_FILE(推荐)或 MCP_INSPECTOR_SECRET_KEY 提供生成的密钥。
  • 完全不落盘:MCP_INSPECTOR_SECRET_STORE=memory,每次会话重新输入。

💻 CLI 用法详解 ​

每次 CLI 运行:连上服务器 → 用 --method 发一个请求 → 打印结果 → 退出。这正是 CI 与一行命令需要的形态。

bash
npx @modelcontextprotocol/inspector --cli node build/index.js --method tools/list

安装后可直接用 mcp-inspector 二进制;未全局安装则前面加 npx @modelcontextprotocol/inspector。

选择服务器 ​

bash
# stdio:位置参数就是要 spawn 的命令
mcp-inspector --cli node build/index.js --method tools/list

# HTTP
mcp-inspector --cli https://api.example.com/mcp --transport http --method tools/list

# 从配置文件里按名取
mcp-inspector --cli --config ./mcp.json --server myserver --method tools/list

⚠️ 配置文件是给一次运行提供 roots 的唯一持久方式:没有 roots 相关的 flag,--method roots/set 只对那一条短命连接生效。为某服务器配置的 roots 会在连接时公布,所以调用 roots/list 的服务器(如 @modelcontextprotocol/server-filesystem 用来获知允许目录)能拿到。

支持的 --method ​

--method必需的配套参数说明
initialize无仅连接的探测:返回 {serverInfo, protocolVersion, capabilities, instructions}
tools/list无--strict 把 schema 可移植性检查变成一道闸门(见 CI 闸门)
tools/call--tool-name,加(可选)--tool-arg / --tool-args-json
resources/list无
resources/read--uri
resources/directory/read--uri,可选 --cursor分页读取,传回上一页的 nextCursor
resources/templates/list无
prompts/list无
prompts/get--prompt-name,加(可选)--prompt-args
logging/setLevel--log-level仅 legacy;modern 服务器改为按请求 opt-in
skills/list无--verify 校验返回的 skills(见 CI 闸门)
skills/get--uri同样支持 --verify
servers/list无不连任何服务器,只读 catalog
servers/show--server同上,针对单条条目
servers/add--server,加一个位置参数 target 或 --server-url写 catalog(不连接),见 编辑 catalog
servers/edit--server,加要改的项同上
servers/remove--server同上

流式/会话类方法(如 logging/tail)会被拒绝——一个发完就退出的进程无法保持流打开。

传参数:两种方式,语义不同 ​

--tool-arg key=value:会尝试把每个值按 JSON 解析,能解析就转成对应类型,所以 count=1 和 zip=10001 都会发成数字;解析不了的(zip=012、裸词)按字符串发送,再由工具 input schema 声明的类型转换,因此数值型 zip 下 zip=012 仍会发成 12:

bash
mcp-inspector --cli <server> --method tools/call --tool-name mytool \
  --tool-arg key=value --tool-arg count=1 --tool-arg 'options={"format":"json"}'

--tool-args-json:一次传入整个参数对象,跳过 key=value 解析,所以 {"zip":"10001"} 发的是字符串 "10001" 而非数字;schema 转换仍会作用于字符串值,只有该属性被声明为字符串(或未声明)时才保留原字符串。两者互斥:

bash
mcp-inspector --cli <server> --method tools/call --tool-name mytool \
  --tool-args-json '{"zip":"10001"}'

输出格式 ​

  • --format text(默认):给人看的美化输出
  • --format json:stdout 上输出单个 JSON 对象、无横幅,便于整段管道处理:
bash
mcp-inspector --cli <server> --method tools/list --format json | jq '.result.tools[].name'

退出码与错误信封(CI 关键) ​

每个非零退出都映射到一个稳定的失败类别,调用方可以据此分支,而不必去猜文案:

码含义
0成功
1用法或意外错误(兜底),也包括读不到的密钥存储或 OAuth 状态文件
2该工具没有 MCP App(--app-info 探测)
3服务器要求认证(HTTP 401/403,或 SDK 授权错误)
4服务器不可达(DNS、连接被拒、超时、fetch failed)
5工具错误:tools/call 返回 isError: true,或工具不存在
6--strict 发现 error 级工具 schema 可移植性问题
7--verify 发现违反 SEP-2640 的 skill(一致性、摘要或大小不符)
8--verify 未能在读取边界内检查完所有 skill
9--verify --require-digests 发现未声明摘要的 skill

任何非零退出,CLI 还会向 stderr 写一行 JSON:

json
{
  "error": {
    "code": "auth_required",
    "message": "Unauthorized",
    "status": 401,
    "url": "https://api.example/mcp"
  }
}

信封始终带 code 与 message,已知时还会带 cause(底层错误,如 DNS 失败)、status(HTTP 状态码)与 url;URL 中的 query 密钥会被脱敏。code 是一组稳定的失败名:auth_required、unreachable、tool_not_found、tool_is_error、no_app、schema_unportable、store_unavailable、oauth_state_unrecognized,或兜底 error 等。因为只有一行,可以用 2>&1 | tail -1 | jq .error 解析。

💡 tools/call 即使返回 isError: true 仍会打印其载荷,但退出码为 5——所以 && 链不会在失败调用后继续。

脚本里的授权 ​

默认情况下 CLI 会跑和 TUI 一样的 loopback OAuth 流程:开浏览器、等一个 localhost 回调——CI 跑不通。两个 flag 让非交互运行可预测:

flag行为
--stored-auth-only从不启动交互式 OAuth 或 step-up,从不自动开浏览器;有令牌就用共享存储里的,没有就立即以 auth_required 失败。CI 要的就是它
--use-stored-auth复用本机 Web Inspector 已取得的令牌,有刷新令牌时先刷新。按 URL 查找令牌,因此需配合 --server-url

两者都不给、且 stdin/stderr 没有 TTY 时,CLI 会快速失败为 auth_required,而不是傻等十五分钟的回调。

CI 闸门 ​

三个 flag 能把一次检查变成非零退出,各自有独立退出码,便于在 job 里区分失败类型:

  • --strict(配合 tools/list):把工具 schema 可移植性问题完整打印到 stderr(路径、问题、建议修复),出现 error 级问题时退出 6;不加则 tools/list 只打印一行计数。
  • --verify(配合 skills/list 或 skills/get):跑 SEP-2640 一致性与摘要校验,每个 skill 在 stdout 打印一份 JSON 报告;出现违规退出 7,读取边界提前终止退出 8。--skill-catalog-max-skills / --skill-catalog-max-bytes 设定边界。
  • --require-digests(配合 --verify):遇到未声明摘要的 skill 退出 9,而不是当作不可校验并退出 0。
bash
mcp-inspector --cli <server> --method tools/list --strict > /dev/null
mcp-inspector --cli <server> --method skills/list --verify --require-digests

编辑 catalog ​

servers/add、servers/edit、servers/remove 不连接任何服务器就能写 catalog,走的是与 Web 客户端服务器列表相同的代码,所以 env 值与 client secret 会落进密钥存储(就像从 UI 录入一样),正在运行的 Web 客户端也会立刻看到改动。只有可写的 catalog(--catalog、MCP_CATALOG_PATH 或默认位置)能被写入;--config 会被拒绝。

bash
# 新增:位置参数 target 或 --server-url 描述的是要登记的项,不是要连的服务器
mcp-inspector --cli node build/index.js -e API_KEY=secret --method servers/add --server my-server

# 修改:改 target、-e、--cwd、--header、--protocol-era,或重命名
mcp-inspector --cli --method servers/edit --server my-server --rename my-renamed-server

# 删除该条目及其存储的密钥
mcp-inspector --cli --method servers/remove --server my-renamed-server

⚠️ 使用内存态密钥存储时,带 -e 值(或 --rename)的写入会被拒绝,因为这些密钥会在进程退出时丢失。

🍳 常用 recipe ​

在 CI 里验证服务器 ​

bash
set -euo pipefail

# 连不上、或没暴露目标工具,就让构建失败
mcp-inspector --cli --config ./ci-servers.json --server my-server \
  --stored-auth-only --method tools/list --format json \
  | jq -e '.result.tools | map(.name) | index("get_weather")' > /dev/null

按失败类别分支 ​

bash
if out=$(mcp-inspector --cli "$URL" --transport http --method tools/list 2>err.json); then
  echo "$out"
else
  case $? in
    3) echo "需要认证:先跑一次 web inspector 登录" ;;
    4) echo "服务器不可达" ;;
    *) jq .error < err.json ;;
  esac
fi

找出所有带 UI 的工具 ​

bash
mcp-inspector --cli "$URL" --transport http --method tools/list --app-info \
  | jq -r 'select(.hasApp) | .toolName'

不连接、只查 catalog ​

bash
mcp-inspector --cli --catalog ~/.mcp-inspector/mcp.json --method servers/list
mcp-inspector --cli --catalog ~/.mcp-inspector/mcp.json --method servers/show --server my-server

⚠️ servers/show 会脱敏带密钥的字段(env 值、敏感 header、OAuth client secret),但不会清理 URL 里内嵌的凭据(userinfo 或 query token),也不会清理 stdio 的 args。贴 issue 前把原始 URL 和 detail 字段当作敏感信息。

🧭 排错范式 ​

你看到的去哪看大概率原因
请求发出但无响应Web 的 Network / Protocol传输端点、Mcp-* 头缺失
登录/授权失败Web 的 AuthorizationOAuth 配置或 iss 校验(见 12.1)
工具列表里缺了某工具Tools 标签的 Excluded 分隔区标了无效的 x-mcp-header(SEP-2243),合规客户端会丢弃它
返回 -32022Protocol 标签协议版本不受支持;data.supported 里有服务器支持的版本
返回 -32020Network 标签缺少或错误的镜像 header
日志一条都没有Logs 标签modern era 下 logLevel 为 off——这是正确行为,不是 bug

💡 代理:连远程 HTTP/SSE 服务器遵循常规代理变量(HTTPS_PROXY / HTTP_PROXY / NO_PROXY),无需 Inspector 专属 flag。

💡 不想让它自动开浏览器? 设置 MCP_AUTO_OPEN_ENABLED=false 即可关闭 Web 客户端的自动打开;在 CLI 下则不再强制自动打开、也不在无 TTY 时跑交互式 OAuth。

📖 官方资料 ​


🎉 恭喜你走完全书。 从第1章的概念到这里,你已经覆盖了从写一个服务器,到授权、治理、扩展、分发与调试的完整链路。

🏠 返回教程首页 | 📖 回看 12.1 OAuth 2.1 授权体系 | 🔁 协议版本演进

内容依据 MCP 官方规范整理 · 规范以 官方文档 为准