配方

社区译文;来源: MCP 80041ab0

连接 stdio 与 HTTP 服务器

stdio

stdio 服务器是由 Inspector 启动的进程。所有位置参数合起来就是命令行:

mcp-inspector node build/index.js -- --verbose --config /etc/myserver.conf

请在任何要传给你自己的服务器的参数之前放 --。没有该分隔符时,--verbose 会被 Inspector 解析,永远到不了服务器。

用 -e 为进程提供环境变量,用 --cwd 提供工作目录:

mcp-inspector -e API_KEY=abc123 -e REGION=us-east-1 --cwd ~/projects/my-server \
  node build/index.js

服务器的 stderr 会进入 Console 标签页(web)或 Console 标签页(o,TUI),大多数 stdio 服务器都把诊断信息放在那里,因此当连接莫名失败时先检查那里。

HTTP 与 SSE

mcp-inspector --server-url https://api.example.com/mcp --transport http \
  --header "X-Tenant: acme"

--transport 接受 http(Streamable HTTP)和 sse。如果服务器受保护,参见授权 :无需事先做任何设置,因为当服务器返回 401 时,Inspector 会执行那里描述的 OAuth 流程并重试连接。

对于 HTTP 服务器,还要决定其协议世代 。默认是 legacy;在 Server Settings 中(或 catalog 文件中的 protocolEra)设为 modern 或 auto 即可演练 2026-07-28 的行为。对于临时目标,在启动时用 --protocol-era legacy|auto|modern 传入。

导入现有客户端配置

在 Servers 界面,Add Servers 可以导入你已在别处配置好的 MCP 服务器,而无需重新输入。它直接解析 Claude Desktop、Cursor、Cline 和 VS Code 的客户端配置,也能读取服务器自己的 MCP Registry server.json。

导入会合并进活动的 catalog (Inspector 的可写服务器列表)。当导入服务器所用的 id 已被占用时,你可以选择覆盖、跳过或重命名它。如果你完全不想动自己的 catalog,可以改为针对外部文件以只读方式启动:

mcp-inspector --config ~/Library/Application\ Support/Claude/claude_desktop_config.json

--config 保证文件按原样提供,永不写入、填充或迁移。

审查 MCP App

MCP Apps 是携带 UI 组件的工具。对于自动化审查者(CI 或代理),凡是返回 JSON 的检查都使用 CLI,只有为了查看渲染后的组件才打开浏览器。

不调用工具即可探测安全态势

mcp-inspector --cli --transport http --server-url https://example.com/mcp \
  --method tools/call --tool-name <tool> --app-info --advertise-apps

--advertise-apps 让 CLI 在 initialize 时声明支持 MCP Apps。它默认关闭,因为 CLI 无法渲染 app;但否则那些只向支持 app 的客户端展示其 app 工具的服务器会被报告为"没有 app"。

stdout 上输出一行 JSON;若工具有 app 则退出 0,否则退出 2,因此 && 链会短路:

{
  "hasApp": true,
  "toolName": "get_pros",
  "resourceUri": "ui://pros/view.html",
  "csp": { "connectDomains": ["https://api.example.com"] },
  "permissions": { "clipboard": false },
  "prefersBorder": true,
  "resourceMimeType": "text/html"
}

csp 和 permissions(以及资源声明时的 domain)位于 UI 资源上而不是工具上,因此 --app-info 读取的是那个资源。工具永远不会被调用。

获取完整结果负载,仍然不需要浏览器

mcp-inspector --cli --transport http --server-url https://example.com/mcp \
  --method tools/call --tool-name <tool> --tool-args-json '{"zip":"10001"}' --format json

启动一次仅限 loopback 的 web Inspector

TOKEN="$(openssl rand -hex 24)"
HOST=127.0.0.1 CLIENT_PORT=6274 MCP_SANDBOX_PORT=6275 \
MCP_AUTO_OPEN_ENABLED=false MCP_INSPECTOR_API_TOKEN="$TOKEN" \
mcp-inspector --web &

固定 MCP_SANDBOX_PORT 可以让地址保持明确:app 的 UI 由单独的沙箱端口提供,而你的自动化需要知道它在哪。

用一个深层链接导航到渲染后的组件

http://127.0.0.1:6274/?serverUrl=<encoded url>&transport=http&autoConnect=<TOKEN>&openApp=<tool>&appArgs=<base64url(JSON)>&autoOpen=<TOKEN>

appArgs 是该工具的参数,以 base64url 编码的 JSON 表示,每个深层链接参数都在深层链接 中说明。autoConnect 和 autoOpen 都必须等于会话 token,因为 autoOpen 会直接从 URL 触发工具调用,需要与 autoConnect 相同的门禁。

等待确定性信号而不是睡眠

Apps 界面提供了一套稳定的自动化约定。请轮询这些属性而不是 sleep:

选择器属性取值
[data-testid="apps-form"]data-app-statusidle,然后是 loading,然后是 ready 或 error(为 error 时 data-app-error 携带原因)
[data-testid="connection-status"]data-statusdisconnected、connecting,然后是 connected 或 error(data-error-message 含详情)
[data-testid="connection-status"]data-deeplinkparsed、rejected 或 none(none 表示未给出深层链接,rejected 表示某个链接被拒绝)

Docker

已向 GitHub Container Registry 发布适用于 linux/amd64 和 linux/arm64 的容器镜像:

docker run --rm -p 127.0.0.1:6274:6274 ghcr.io/modelcontextprotocol/inspector

从容器日志中读取会话 token ,或用 -e MCP_INSPECTOR_API_TOKEN=<value> 固定它。

镜像默认使用 --web,绑定到 0.0.0.0:6274 且关闭浏览器自动打开,并以非 root 的 node 用户(uid 1000)运行。它设置了 DANGEROUSLY_BIND_ALL_INTERFACES=true,因为容器必须绑定通配地址才能通过 -p 访问。

每个发布的端口都要保留 127.0.0.1: 前缀。 裸写 -p 6274:6274 会在所有主机接口上发布,把一个会启动进程的后端,以及那个披露其 token 的页面,暴露在你的本地网络上。镜像的 DANGEROUSLY_BIND_ALL_INTERFACES 只覆盖容器的接口,不覆盖主机的。参见只在 loopback 上发布端口 。

要使用 Apps 标签页,还需发布 MCP Apps 沙箱端口 6275,以及为声明 _meta.ui.domain 的 app 发布 6278。请让容器内外使用相同的端口号发布,因为浏览器拿到的就是容器内端口:

docker run --rm -p 127.0.0.1:6274:6274 -p 127.0.0.1:6275:6275 \
  ghcr.io/modelcontextprotocol/inspector

保留你的服务器与机密

服务器列表、OAuth 状态和机密位于 /home/node/.mcp-inspector,即在容器的可写层中,因此 --rm 会丢弃它们,每次运行都从空开始。在那里挂载一个卷即可保留它们:

docker run --rm -p 127.0.0.1:6274:6274 \
  -v mcp-inspector-data:/home/node/.mcp-inspector \
  ghcr.io/modelcontextprotocol/inspector

容器没有操作系统钥匙串,因此机密的存放位置取决于该卷:

情形机密存储重启后是否保留?
/home/node/.mcp-inspector 未挂载卷内存否,仅限本次会话
挂载了该卷卷上的 secrets.json,权限 0600是

如果你改用绑定挂载主机目录而不是具名卷,它会保留主机的所有权,因此在 Linux 上需加上 --user "$(id -u):$(id -g)",或把它 chown 给 uid 1000,否则保存会因 EACCES 失败。不要单独绑定挂载机密文件:它不会被识别为持久存储,也无法被原子替换。

挂载该卷会启用机密的文件存储,而没有密钥时该文件是明文。 每个获取到的 OAuth token,以及你保存的每个客户端 secret 和 stdio env: 值,都会被写入卷上的 secrets.json。root 以及主机 docker 组的每个成员都能读取它,任何拿到该卷的备份、快照或副本的人也能读取。

请为它提供密钥,生成到只有你能读取、且位于该卷及其备份和任何仓库之外的文件中:

mkdir -p ~/.config/mcp-inspector
(umask 077 && openssl rand -base64 32 > ~/.config/mcp-inspector/secret-key)

即使加密,磁盘上的机密仍带有中等风险。参见文件存储防范哪些风险 。

请以文件方式通过 MCP_INSPECTOR_SECRET_KEY_FILE 把密钥交给容器,而不是作为环境变量。用 -e MCP_INSPECTOR_SECRET_KEY=… 传入的密钥,任何能运行 docker inspect 或 docker exec 的人都能读取。

docker run

docker run --rm -p 127.0.0.1:6274:6274 \
  -v mcp-inspector-data:/home/node/.mcp-inspector \
  -v "$HOME/.config/mcp-inspector/secret-key:/run/secrets/mcp_inspector_secret_key:ro" \
  -e MCP_INSPECTOR_SECRET_KEY_FILE=/run/secrets/mcp_inspector_secret_key \
  ghcr.io/modelcontextprotocol/inspector

Compose secrets

services:
  inspector:
    image: ghcr.io/modelcontextprotocol/inspector
    ports: ["127.0.0.1:6274:6274"]
    volumes: ["mcp-inspector-data:/home/node/.mcp-inspector"]
    environment:
      MCP_INSPECTOR_SECRET_KEY_FILE: /run/secrets/mcp_inspector_secret_key
    secrets: [mcp_inspector_secret_key]
secrets:
  mcp_inspector_secret_key:
    file: ${HOME}/.config/mcp-inspector/secret-key
volumes:
  mcp-inspector-data:

不使用 Swarm 时,Compose secrets 是保留主机文件所有者与权限的绑定挂载,因此 0600 的密钥文件必须属于 uid 1000。在你的 uid 不同的 Linux 主机上,请运行 sudo chown 1000 ~/.config/mcp-inspector/secret-key,而不是放宽其权限。每次运行都要提供同一个密钥。

如果密钥文件缺失、不可读或为空,或两个密钥变量都被设置,Inspector 会拒绝读写机密文件,而不是回退到明文,并在日志和设置对话框的页脚说明原因。关于该存储的其他一切(选择顺序、位置、权限)见机密存储于何处 。

健康检查与其他模式

镜像的 HEALTHCHECK 会探测 HOST 所绑定地址上的 web UI。--cli 和 --tui 没有 web 服务器,因此该探测会从容器参数中识别这些模式,并在它们运行期间报告健康。外部编排器(Kubernetes 探针、Compose 的 healthcheck)可以调用 web 端口上的 GET /healthz,该端点不需要 token,只返回 {"status":"ok"}。

下面的 <target> 是一个临时目标 :位置形式的 stdio 命令,或 --server-url <url> --transport http。

docker run --rm ghcr.io/modelcontextprotocol/inspector --cli <target> --method tools/list
如果你重映射了发布的端口,请设置 ALLOWED_ORIGINS。 使用 -p 127.0.0.1:8080:6274 时,浏览器的来源变成 http://localhost:8080,它不再匹配容器内端口,连接会返回 403。要么运行 -e CLIENT_PORT=8080 -p 127.0.0.1:8080:8080,要么设置 -e ALLOWED_ORIGINS=http://localhost:8080,http://127.0.0.1:8080。

在网络中托管

Inspector 默认绑定 127.0.0.1,且其后端会启动进程,因此请把暴露到网络视为一个深思熟虑的决定。

除非设置 DANGEROUSLY_BIND_ALL_INTERFACES=true,Inspector 拒绝绑定通配的全接口地址(0.0.0.0、:: 以及所有等价写法)。绑定特定地址无需选择加入即可,因为那是一次有意的暴露,而不是一次暴露所有接口——后者正是 DNS 重绑定攻击所针对的形态。

目标做法
从局域网内另一台机器访问HOST=192.168.1.50。默认来源允许列表会跟随绑定主机,因此 http://192.168.1.50:6274 无需进一步配置即可被接受。
位于 TLS 或反向代理之后浏览器的 Origin 会变成公开来源,不再匹配绑定主机。设置 ALLOWED_ORIGINS=https://inspector.example.com,对 MCP Apps 还要设置 MCP_SANDBOX_FULL_ADDRESS(见下文)。
通配绑定(容器)设置 DANGEROUSLY_BIND_ALL_INTERFACES=true。loopback 访问开箱即用;通过非 loopback 地址访问则需要 ALLOWED_ORIGINS。

ALLOWED_ORIGINS 是替换默认列表,而不是与之合并。请列出你会用到的每个来源,包括你想保留的 loopback 形式:

ALLOWED_ORIGINS=http://localhost:6274,http://127.0.0.1:6274,http://192.168.1.50:6274

每个条目都必须包含 scheme;缺少 scheme 的值会被丢弃并给出警告。空值不会禁用检查;它会回退到默认值。没有任何开关可以关闭来源校验。

离开 loopback 时还有两点注意事项:

  • MCP Apps 也需要其沙箱端口可达。 它是一个独立的监听器(默认 6275,用 MCP_SANDBOX_PORT 设置),因此要随 web 端口一起暴露或转发它。
  • 位于 TLS 或反向代理之后时,要为 MCP Apps 提供它们的公开地址。 默认情况下沙箱被声明为 http://<bind host>:6275,https:// 页面会把它作为混合内容拦截。请设置 MCP_SANDBOX_FULL_ADDRESS(例如 https://inspector-sandbox.example.com/sandbox),对于声明 _meta.ui.domain 的 app 还要设置 MCP_APP_ORIGIN_FULL_ADDRESS(例如 https://inspector-apps.example.com)。每个地址都需要自己的主机名或端口;与 Inspector 同源的地址会被拒绝。
  • MCP Apps 无法在裸 IPv6 字面量下渲染。 带方括号的 IPv6 字面量不是合法的 CSP host-source,因此请通过名称或 IPv4 地址浏览。

无论哪种形态:都要保持身份验证开启。不要在任何除你之外的人可以访问的实例上设置 DANGEROUSLY_OMIT_AUTH。理由见安全 。

用 mcpdo 保持连接打开

当一次探索跨越许多调用时(在单个会话上走多步流程、盯着一条日志、代理在会话中途使用服务器工具),为每次 --cli 调用重连会很碍事。mcpdo 会替你保持连接:

mcpdo connect my-server
mcpdo @my-server tools/call --task start_job size:=large   # task-augmented; blocks until the task finishes
mcpdo @my-server tools/call get_job_report
mcpdo @my-server logging/tail          # long-lived; Ctrl-C to stop
mcpdo disconnect my-server

一个连接一次只运行一个调用,因此来自其他 shell 对同一连接的命令会排队等待。

持有该连接的守护进程会自动启动,并在最后一个连接关闭后约一分钟退出。在共享机器上使用之前请阅读 mcpdo 连接守护进程 。

开发工作流

一套在实践中效果良好的循环:

从 CLI 开始

--method initialize 用一秒时间、以机器可读的答案确认服务器能启动、完成握手并报告你期望的能力。多数"它就是不能用"的情况都出在这里。

转向 web 客户端做探索

schema 驱动的表单、渲染后的结果,以及旁边的 Protocol 标签页,让找到工具行为异常的那个场景变得很快。

测试边界情况

无效输入、缺失的必需 prompt 参数、并发调用,以及对 HTTP 服务器而言的两种协议世代。验证错误与成功一样是有意设计的。

用 CLI 固化下来

把你发现的东西变成 CI 断言:把 CLI 的 --format json 输出连同 --stored-auth-only 一起管道给 jq -e,这样缺少 token 会快速失败,而不是启动交互式 OAuth。完整命令见在 CI 中验证服务器 。