全部 Agent 工程

规范文件

Codex MCP 配置:config.toml 里 stdio 与 HTTP 服务的字段自查

按官方字段表区分 stdio 与 HTTP 服务,用自写 lint 检查字段混用、工具生效范围与密钥写法。

AI Nav City撰写 / 更新

Codex 把 MCP 服务器写在 config.toml 的 [mcp_servers.<名称>] 表里,CLI、IDE 扩展和桌面应用共用这份配置。字段分两组:stdio 服务用 command 启动本地进程,HTTP 服务用 url 访问。把两组混在一张表里,或在文件里写下静态令牌,是最容易出的错。本文依据官方文档整理字段分组,并附一个自写的 Python lint,用来在提交配置前做初步检查。它不是官方校验器,没有启动 Codex,也没有连接任何 MCP 服务。

核验日期
· 未实测
适用于
  • Codex 适用于 Codex CLI、IDE 扩展与桌面应用共享的 MCP 配置;页面没有统一版本门槛,字段以 2026-11-05 的官方页面为准。[1]
涉及工具

文件位置

路径适用范围读取方式
~/.codex/config.toml用户级 MCP 服务器配置,CLI、IDE 扩展和桌面应用共享。Codex 读取其中的 [mcp_servers.<名称>] 表。[1]
./.codex/config.toml项目级 MCP 服务器配置,仅受信项目生效。与用户级配置并用;项目目录下的内容应当可审查,避免写入密钥。[1]

什么时候用

  • 要手写或审阅 [mcp_servers.*] 配置,并想在连接前先排除字段混用和明文密钥。[1]

什么时候不用

  • 需要验证服务器能否连接、OAuth 是否可登录,或工具返回内容是否可信,这些必须在真实环境里检查。[1]

两组字段:stdio 与 HTTP

官方把可配置字段分成三块。stdio 服务器以本地进程运行,HTTP 服务器通过地址访问;两者的必填项不同,其余选项是共用的。[1]

表:官方字段分组
分组字段说明
stdiocommand(必填)、args、env、env_vars、cwdcommand 启动服务器;env_vars 是允许并转发的环境变量名
HTTPurl(必填)、auth、bearer_token_env_var、http_headers、env_http_headers、http_headers_helperbearer 令牌通过环境变量名引用;http_headers 是静态值;helper 只用于本地环境发起的 HTTP 连接,不用于 stdio
共用enabled、required、enabled_tools、disabled_tools、default_tools_approval_mode、tools.<工具>.approval_mode、startup_timeout_sec、tool_timeout_secdisabled_tools 在 enabled_tools 之后生效;超时默认 10 秒启动、60 秒工具调用

[1]

default_tools_approval_mode 官方列出的取值是 auto、prompt、writes 和 approve,其中 writes 会对未标记为只读的工具提示确认。凭据方面:bearer_token_env_var 只写环境变量名;通过 codex mcp add 配置预注册 OAuth 客户端的密钥时,Codex 会把它以 mcp_servers.<名称>.oauth.client_secret 保存到 config.toml,官方要求保持该文件私有并且不进版本控制。[1]

自写 lint:提交配置前的初步检查

这个脚本读取一份 TOML,逐个服务器检查:command 与 url 是否恰好出现一个、是否混入另一组字段、enabled_tools 减去 disabled_tools 之后实际可用的工具、approval 模式是否在官方取值内、http_headers 里是否有疑似静态密钥的头、文件里是否保存了 OAuth client_secret。它依赖 Python 3.11 及以上的 tomllib,建议用 python3 -I 运行,传入你要检查的文件路径。检查规则是我按官方字段表自己写的,不是官方校验,也没有覆盖全部字段。[1]

示例:原创 lint 脚本;离线运行,不是官方校验器
import sys
import tomllib

STDIO_ONLY = {"args", "env", "env_vars", "cwd"}
HTTP_ONLY = {"auth", "bearer_token_env_var", "http_headers", "env_http_headers", "http_headers_helper"}
MODES = {"auto", "prompt", "writes", "approve"}

with open(sys.argv[1], "rb") as handle:
    config = tomllib.load(handle)

for name, server in config.get("mcp_servers", {}).items():
    keys, notes = set(server), []
    if ("command" in keys) == ("url" in keys):
        notes.append("need exactly one of command / url")
    if "url" in keys and keys & STDIO_ONLY:
        notes.append("stdio-only keys on an HTTP server")
    if "command" in keys and keys & HTTP_ONLY:
        notes.append("HTTP-only keys on a stdio server")
    enabled, disabled = server.get("enabled_tools"), set(server.get("disabled_tools", []))
    if enabled is not None:
        notes.append("effective tools: " + str([t for t in enabled if t not in disabled]))
    if server.get("default_tools_approval_mode", "auto") not in MODES:
        notes.append("unknown default_tools_approval_mode")
    for header in server.get("http_headers", {}):
        if any(word in header.lower() for word in ("authorization", "token", "key")):
            notes.append(f"static secret-like header in file: {header}")
    if "client_secret" in server.get("oauth", {}):
        notes.append("oauth.client_secret is stored in this file")
    print(name, "->", notes or ["no findings"])

[1]

下面的样例配置里,docs_search 是带 bearer 环境变量的 HTTP 服务,local_files 是 stdio 服务,bad_mix 故意把 command 与 url 同时写进一张表。输出里的 effective tools 来自 enabled_tools 减去 disabled_tools。样例是原创内容,没有用于连接任何真实服务。[1]

示例:原创检查样例;未让 Codex 读取
[mcp_servers.docs_search]
url = "https://mcp.example.com/docs"
bearer_token_env_var = "DOCS_MCP_TOKEN"
enabled_tools = ["search", "fetch", "export"]
disabled_tools = ["export"]
default_tools_approval_mode = "writes"
tool_timeout_sec = 30

[mcp_servers.local_files]
command = "node"
args = ["./tools/files-server.mjs"]
env_vars = ["FILES_ROOT"]
startup_timeout_sec = 20
required = true

[mcp_servers.bad_mix]
url = "https://mcp.example.com/mix"
command = "node"
default_tools_approval_mode = "always"
http_headers = { "Authorization" = "Bearer demo-literal" }

[1]

示例:lint 输出(离线运行)
docs_search -> ["effective tools: ['search', 'fetch']"]
local_files -> ['no findings']
bad_mix -> ['need exactly one of command / url', 'HTTP-only keys on a stdio server', 'unknown default_tools_approval_mode', 'static secret-like header in file: Authorization']

[1]

没有覆盖的部分与停止条件

  • 官方没有写同时配置 command 与 url 时 Codex 会怎样处理;lint 把它标为冲突,只是因为两者在官方字段表里分别是各自传输方式的必填项,实际行为待核。
  • lint 检查的是静态头里的名字(含 authorization、token、key),不检查 env 里的明文值,也不判断值本身是否真的是密钥,会漏报也会误报。
  • 官方还支持 OAuth 回调、CIMD 与 DCR 注册、插件自带的 MCP 服务器、远程执行环境等配置;这些没有在 lint 里建模,需要时直接阅读官方对应段落。
  • 服务器是否启动成功、工具列表是否符合预期,需要在真实环境用 codex mcp list 或 TUI 的 /mcp 观察;本文没有运行它们。

[1]

在真实采用前,先让 lint 对你的配置没有「need exactly one of command / url」和静态密钥提示,再确认你信任服务器来源,并核对工具名单与审批模式;任何一项不清楚就先把 enabled 设为 false,而不是带着默认审批直接连接。本文只核验了官方字段说明,没有启动 Codex,没有连接 MCP 服务,也不认证服务器的安全性或费用。[1]

来源

以下均为官方页面,核验日期见各条;链接指向核验时的版本。

  1. OpenAI · Model Context Protocol

发现错误?告诉我们 →