Skip to main content
Inspector 是一个会持有真实凭据并启动真实进程的开发者工具。本页汇总它保护什么、信任什么以及边界在哪里,帮助你决定哪种设置适合你的机器。配置和配方页面会链接到这里,而不是重复这些原因。 简要结论:
  • Web 后端可以启动进程。 API 令牌是该能力与所有能够访问端口的对象之间的屏障。
  • 有操作系统钥匙串时,机密信息会进入钥匙串。 没有钥匙串时,机密信息会进入一个除非提供密钥否则为明文的文件,而且这是自动发生的。
  • stdio 服务器以你的身份运行。 Inspector 能读取的内容,所启动的服务器通常也能读取。
  • mcpdo 守护进程是持有活动连接的长期进程,由令牌和同用户文件权限保护。

Web 后端及其 API 令牌

Web 客户端是由 Node 服务器支持的浏览器应用,该服务器负责 MCP 连接。它可以按请求启动 stdio 进程,因此每个 /api/* 路由都需要每次启动专用的 bearer 令牌(x-mcp-remote-auth: Bearer <token>)。浏览器如何获取令牌,请参阅会话令牌。 令牌能保护和不能保护的内容如下:
  • 令牌是非浏览器客户端的真正守门机制。 来源允许列表(ALLOWED_ORIGINS)可以阻止其他网页驱动后端,但不带 Origin 请求头的请求(curl、脚本以及任何非浏览器客户端)会完全跳过该检查。
  • GET / 会披露令牌。 后端会将令牌注入所提供的 HTML,使重新加载或书签继续工作。因此任何能够加载该页面的人都可以读取令牌,这就是绑定地址比令牌值更重要的原因。设置自定义 MCP_INSPECTOR_API_TOKEN 不会改变这一点,因为自定义令牌也会以同样方式披露。
  • DANGEROUSLY_OMIT_AUTH=true 会完全移除守门机制。 任何能够访问端口的对象都可以以你的身份启动进程,并使用 Inspector 持有的任何 OAuth 令牌。只有 true 或 1 会关闭身份验证;包括 false 在内的其他值都会保持开启。
  • 只有 /api/* 受到令牌保护。 页面本身、静态资源以及 GET /healthz 无需令牌即可提供。/healthz 只返回 {"status":"ok"},供容器和编排器探测。

后端监听位置

后端默认绑定 127.0.0.1。除非设置 DANGEROUSLY_BIND_ALL_INTERFACES=true,否则绑定所有接口(0.0.0.0、:: 以及等效写法)会被拒绝。绑定某一个特定地址无需该选择加入,因为它只暴露一个有意指定的地址,而不是一次性暴露所有地址。请参阅在网络上托管。
切勿将 DANGEROUSLY_OMIT_AUTH 与非环回绑定组合使用。如果需要让其他人访问 Inspector,请在它前面放置真正的访问控制边界:需要身份验证的反向代理、SSH 隧道或私有网络。

Docker

Docker 镜像会改变上面描述的两个方面。

仅在环回地址上发布端口

容器内的 Inspector 必须绑定 0.0.0.0 才能通过 -p 访问,因此镜像设置了 DANGEROUSLY_BIND_ALL_INTERFACES=true。这个选择加入只影响容器接口,不影响主机接口。哪些主机接口可以看到 Inspector,取决于发布端口的方式: 裸写 -p 会将能够创建进程的后端以及会披露令牌的页面暴露到本地网络。发布每个端口(6274,以及 MCP Apps 监听器的 6275 / 6278)时都应保留 127.0.0.1: 前缀。

数据卷会将机密信息放到磁盘

容器没有操作系统钥匙串。没有挂载 /home/node/.mcp-inspector 卷时,机密信息会在会话期间保留在内存中,容器退出后丢失。为了保留服务器列表而挂载该目录,会同时将机密信息切换到卷上的 secrets.json 文件;该文件除非提供密钥否则为明文。root、主机上 docker 组的每个成员(其权限等同于 root),以及获得该卷备份、快照或副本的任何人都可以读取它。 请通过 MCP_INSPECTOR_SECRET_KEY_FILE 以文件形式提供密钥(使用 Docker 或 Compose secret),不要使用环境变量:通过 -e MCP_INSPECTOR_SECRET_KEY=… 传入的密钥可被能够运行 docker inspect 或 docker exec 的任何人看到。配方页展示了两种方式。

机密信息存储

Inspector 不会将凭据写入 mcp.json、client.json 和 oauth.json,这样共享、提交或同步这些文件就不会泄露凭据。以下内容会作为机密信息存储:
  • 已获取的 OAuth 令牌(访问、刷新和 ID 令牌),受MCP_INSPECTOR_PERSIST_TOKENS限制,以及企业管理授权中的 IdP 会话令牌;
  • 每个服务器的 OAuth 客户端密钥和企业 IdP 客户端密钥;
  • 动态注册的客户端密钥及其注册访问令牌;
  • 每个 stdio 服务器的 env: 值。
headers 不是机密信息。 它们会按原样保存到 mcp.json,因此携带凭据(API key 或静态 Authorization 值)的请求头仍会保留在文件中。 存储方式如何选择以及如何更改,请参阅机密信息存储位置。本节说明相关风险。

自动回退到明文

当无法访问操作系统钥匙串时(没有 libsecret 或正在运行的 Secret Service 的 Linux、没有 D-Bus 会话的无头服务器或 SSH 会话、Android/Termux),Inspector 会自动将机密信息存储到 ~/.mcp-inspector/secrets.json。你不需要主动请求这种行为;除非提供密钥,否则该文件不会加密。唯一的提示是选择存储方式时 stderr 上的警告,以及 Web 客户端设置对话框中的页脚。
这应被视为风险,而不仅是配置事实。文件以 0600 权限写入,这只能阻止其他非 root 用户读取。要关闭这一风险,请执行以下任一操作:
  • 恢复钥匙串(安装 libsecret 并运行 Secret Service,例如 gnome-keyring,或在桌面会话中运行);
  • 使用 MCP_INSPECTOR_SECRET_KEY_FILE 中的生成密钥加密文件;
  • 设置 MCP_INSPECTOR_SECRET_STORE=memory,每次会话重新输入机密信息。

文件存储可以防范什么

文件存储用于没有钥匙串的机器,安全性弱于钥匙串。即使经过加密,也应将把机密信息保存在其中视为中等风险。 没有密钥(明文,权限 0600): 有密钥(AES-256-GCM,使用每次写入随机生成的盐通过 scrypt 拉伸密钥): 密钥存放位置决定第二行的风险。使用 MCP_INSPECTOR_SECRET_KEY 时,密钥位于 Inspector 环境中,同用户或 root 可以通过 /proc/<pid>/environ 读取;容器中可以通过 docker inspect / docker exec 读取,也可能存在于用于启动它的 shell profile、.env 文件或 Compose 文件中。使用 MCP_INSPECTOR_SECRET_KEY_FILE 会将风险缩小到能够读取密钥文件的人,但 Inspector 必须读取它,因此同一用户仍然可以读取。如果密钥与机密文件位于同一目录、备份、卷或仓库中,加密就没有实际收益。 简而言之,加密会将“文件泄露”变成“文件和密钥同时泄露”。它无法防范已经拥有机器或容器 root 权限,或已经拥有 Inspector 用户权限的人。如果这不可接受,请使用钥匙串或内存存储。 以下两种失败模式会明确报错,而不是静默处理:
  • 如果密钥文件缺失、不可读或为空,如果 MCP_INSPECTOR_SECRET_KEY_FILE 设置为空值,或者同时设置了两个密钥变量,存储方式会拒绝读写,而不是回退到明文。
  • 如果口令发生变化或丢失,现有文件将无法解密。Inspector 会将其读取为空,并拒绝覆盖,因此请恢复口令,或删除文件后重新输入值。

stdio 服务器以你的身份运行

stdio MCP 服务器是 Inspector 以你的用户权限启动的进程,与任何 MCP 主机的行为相同。
  • **环境:**Inspector 不会将自己的整个环境传给服务器。stdio 服务器只会获得短的允许列表(macOS 和 Linux 上的 HOME、LOGNAME、PATH、SHELL、TERM、USER)以及配置的 env:。因此 MCP_INSPECTOR_SECRET_KEY 中的密钥不会直接传给它。
  • **但它使用同一用户:**服务器可以自行打开 secrets.json,读取 oauth.json 和目录,通常也可以通过 /proc/<pid>/environ 读取 Inspector 的环境。环境允许列表是卫生措施,不是隔离边界。
只连接你愿意让其访问这些机密信息的 stdio 服务器。若不完全信任某个服务器,请将其命令包装在容器中,例如将 docker run -i --rm --network none <image> 作为 stdio 命令。HTTP 和 SSE 服务器不会运行本地代码,因此不需要进程隔离。

mcpdo 连接守护进程

mcpdo 通过将连接交给后台守护进程 mcpdod,在命令之间保持连接。它会在第一次需要命令时自动启动(mcpdo connect 或任何针对连接的命令),不需要单独的“启动守护进程”步骤。本节说明该进程暴露的内容。

客户端如何访问它,以及谁还能访问

没有 TCP 端口,因此机器外部无法访问守护进程。在 Unix 上,守护进程目录会创建为或收紧为权限 0700、由你拥有,并且必须是真实目录而不能是符号链接。里面的 socket 和锁文件权限为 0600,因此其他非 root 用户无法访问;root 以及以你的身份运行的进程可以访问。 目录按以下顺序选择:MCP_INSPECTOR_DAEMON_DIR、MCP_STORAGE_DIR、~/.mcp-inspector。如果 socket 路径超过平台限制(macOS 约 104 字节、Linux 108 字节),会提前拒绝,并在错误中指出要缩短的变量。

如何验证命令

每个请求都必须携带 bearer 令牌,不存在未授权请求路径。
  • **共享模式(默认):**启动守护进程的 mcpdo 命令生成随机的 256 位令牌,并通过环境传给守护进程。守护进程将其发布到目录中权限为 0600 的 mcpdod.token,因此同一用户运行的任意 mcpdo 命令都可以读取。该文件系统权限边界与 socket 的同用户边界相同。
  • 私有模式:eval "$(mcpdo private)" 会在 $TMPDIR/mcp-conn-<uid>/ 下创建新的 0700 目录,并向当前 shell 导出 MCP_INSPECTOR_DAEMON_DIR 和 MCP_INSPECTOR_DAEMON_TOKEN,使它拥有自己的守护进程和连接。由于 $TMPDIR 可能共享,使用前会检查父目录的所有权和符号链接。
令牌比较采用常量时间算法。超过 1 MiB 的请求行会被拒绝。向运行中的守护进程提交错误令牌的命令会明确失败,且不会替换该守护进程。
私有模式可以隔离不同 shell 之间的连接和守护进程状态,但它不是针对同一用户其他进程的安全边界:任何拥有你的 UID 且知道守护进程目录的进程都可以读取令牌。需要硬边界时,请使用单独的用户账户或容器。

它在内存中持有什么

在守护进程运行期间,每个打开的连接都会持有:
  • 活动中的 MCP 连接,以及对于 stdio 服务器由守护进程作为父进程启动的子进程;
  • 连接解析后的配置,包括从机密信息存储中读取的 stdio env: 值;
  • HTTP 连接正在使用的 OAuth 令牌;重新拨号断开的传输时,它还会从存储中重新读取令牌;
  • 非交互式命令停放的 elicitation,直到回答或 10 分钟后过期。
守护进程使用启动它的 mcpdo 命令的环境启动。它会继承该 shell 的变量(包括设置时的 MCP_INSPECTOR_SECRET_KEY 和自己的 IPC 令牌 MCP_INSPECTOR_DAEMON_TOKEN),并在整个生命周期内保留,即使你之后在 shell 中修改了这些变量。它启动的 stdio 服务器仍然只会收到上文所述的允许列表,以及从运行 mcpdo connect 的 shell 快照出的 env:。 在没有钥匙串的主机上,mcpdo 不会自动回退到内存存储。它的前端命令和守护进程是不同进程,按进程存储无法在二者之间传递令牌,因此 mcpdo 使用共享的 secrets.json 文件,并遵循上文“除非提供密钥否则为明文”的规则。显式设置 MCP_INSPECTOR_SECRET_STORE=memory 仍然优先。mcpdo 每次 connect 打印一次存储警告,而不是每条命令都打印。

生命周期

  • **启动:**第一次需要命令时启动。如果两个命令同时启动,O_EXCL 锁(mcpdod.lock)保证只有一个成功。已退出守护进程留下的锁会被回收,运行中的守护进程永远不会被接管。
  • **停止:**运行 mcpdo daemon stop 或发送 SIGINT / SIGTERM。最后一个连接关闭约 60 秒后也会自动退出。没有最大生命周期:只要有连接打开,守护进程就会保持运行。
  • **正常停止:**拒绝新工作,为正在执行的请求提供短暂宽限期,关闭每个连接,并删除 socket、令牌文件和锁。
  • **崩溃:**它持有的所有连接都会丢失,stdio 服务器会失去 stdin,通常随即退出。已存储的 OAuth 令牌和机密信息不受影响。下一条 mcpdo 命令会启动新的守护进程,清理旧 socket、回收锁并写入新令牌;必须重新运行 mcpdo connect 建立连接。
使用 pgrep mcpdod 查找遗留的守护进程。它会将进程标题设置为 mcpdod。

写入磁盘的内容

所有内容都位于权限为 0700 的守护进程目录中: 守护进程通过与其他客户端相同的 oauth.json 和机密信息存储写入 OAuth 状态和机密信息,因此机密信息存储中的所有内容同样适用。