Codex 的强大之处不仅在于它能理解代码、读写文件和执行 Shell 命令,更在于它可以通过 MCP(Model Context Protocol,模型上下文协议) 接入外部工具和数据源,突破自身能力的边界。MCP 是由 Anthropic 发起的一项开放标准,旨在为 AI 应用提供统一的"USB-C 接口",让不同的 MCP 客户端(如 Codex、Claude Desktop)能够无缝连接各类 MCP 服务器。
本文将带你深入理解 Codex 的 MCP 集成机制,从配置到实战,一步步教你如何为 Codex 扩展自定义工具能力。
在深入 Codex 之前,先简单理解 MCP 的核心概念。
MCP 采用客户端-服务器架构:
- Tools:可被 AI 调用的函数,例如查询数据库、调用第三方 API。
- Resources:可被 AI 读取的结构化数据,类似于文件或 API 响应。
- Prompts:预定义的提示词模板,帮助 AI 完成特定任务。
当 Codex 连接到 MCP 服务器后,服务器注册的所有 Tool 会自动出现在 Codex 的工具列表中,AI 可以在需要时主动调用它们——就像调用内置的 read、write、bash 工具一样自然。
Codex 在 Rust 代码库中有一个专门的 codex-mcp 核心 crate,负责 MCP 服务器的连接管理、工具目录缓存、资源读取和认证编排。它的关键模块包括:
| 模块 | 职责 |
|------|------|
| mcp_connection_manager | 管理所有 MCP 服务器的生命周期,负责连接建立、心跳检测、重连和优雅关闭 |
| catalog | 构建和解析 MCP 服务器目录,处理服务器注册与冲突解决 |
| tools | 管理通过 MCP 注册的工具,提供工具发现和调用能力 |
| resource_client | 封装 MCP Resource 的读取操作,支持分页和缓存 |
| auth_elicitation | 处理 Codex Apps 的 OAuth 认证流程,包括授权引导 (elicitation) |
| rmcp_client | 基于 rmcp 库封装的底层 MCP 协议通信客户端 |
Codex 支持通过两种传输方式连接 MCP 服务器:
STDIO 传输:MCP 服务器作为子进程启动,通过标准输入输出交换 JSON-RPC 消息。这是最常用、最安全的方式,因为服务器进程完全由 Codex 管理。
HTTP(Streamable HTTP)传输:MCP 服务器作为独立 HTTP 服务运行,Codex 通过 HTTP 请求与之交互。适合需要跨网络访问的场景。
MCP 服务器通过 Codex 的 config.toml 文件来注册。该文件通常位于以下路径之一:
~/.codex/config.toml<project>/.codex/config.toml下面是一个典型的 MCP 配置示例,注册一个基于 Node.js 的天气查询服务器:
[mcp_servers.weather] command = "node" args = ["/path/to/weather-mcp-server/build/index.js"] description = "查询全球天气信息的 MCP 服务器" enabled = true [mcp_servers.database] command = "python" args = ["-m", "db_mcp_server"] description = "数据库查询 MCP 服务器" enabled = true
每个 MCP 服务器配置包含以下关键字段:
command:启动服务器的可执行文件路径。args:传递给可执行文件的命令行参数列表。description:服务器的描述信息,会展示在 Codex 的工具列表中。enabled:是否启用该服务器(默认 true)。如果 MCP 服务器需要环境变量(例如 API 密钥),可以通过 env 字段传递:
[mcp_servers.weather]
command = "node"
args = ["/path/to/weather-mcp-server/build/index.js"]
env = { API_KEY = "sk-xxxx", LOG_LEVEL = "debug" }
Codex 支持为 MCP 工具设置细粒度的权限控制。在 config.toml 中可以配置工具级别的自动审批规则:
[approvals.mcp_tools]
# 对特定 MCP 工具始终自动批准
auto_approve = [
"weather.get_forecast",
"database.run_select",
]
# 对特定模式的工具需要每次确认
require_approval = [
"database.run_mutation",
"filesystem.rm",
]
这与 Codex 的执行策略(Execution Policy)机制一致,确保你在享受 MCP 扩展能力的同时,保持对危险操作的控制。
下面我们使用 Python 和 mcp SDK 构建一个简单的 "待办事项管理" MCP 服务器,让 Codex 能够通过它来管理项目中的 TODO 列表。
mkdir todo-mcp-server && cd todo-mcp-server uv init uv venv source .venv/bin/activate # Windows: .venv\Scripts\activate uv add "mcp[cli]"
创建 server.py:
import json
import os
from pathlib import Path
from mcp.server import MCPServer
mcp = MCPServer("todo-manager")
TODO_FILE = Path.home() / ".codex" / "todo_storage.json"
def load_todos() -> list[dict]:
"""从 JSON 文件加载待办事项列表"""
if not TODO_FILE.exists():
return []
with open(TODO_FILE, "r", encoding="utf-8") as f:
return json.load(f)
def save_todos(todos: list[dict]) -> None:
"""将待办事项列表写入 JSON 文件"""
TODO_FILE.parent.mkdir(parents=True, exist_ok=True)
with open(TODO_FILE, "w", encoding="utf-8") as f:
json.dump(todos, f, ensure_ascii=False, indent=2)
@mcp.tool()
async def add_todo(title: str, priority: str = "medium") -> str:
"""
添加一条新的待办事项。
Args:
title: 待办事项标题
priority: 优先级,可选 low/medium/high,默认 medium
"""
todos = load_todos()
import uuid
new_item = {
"id": str(uuid.uuid4())[:8],
"title": title,
"priority": priority,
"done": False,
}
todos.append(new_item)
save_todos(todos)
return f"已添加待办事项 [{new_item['id']}]: {title} (优先级: {priority})"
@mcp.tool()
async def list_todos(status: str = "all") -> str:
"""
列出所有待办事项。
Args:
status: 筛选状态,可选 all/active/done,默认 all
"""
todos = load_todos()
if status == "active":
todos = [t for t in todos if not t["done"]]
elif status == "done":
todos = [t for t in todos if t["done"]]
if not todos:
return "暂无待办事项。"
lines = []
for t in todos:
mark = "✓" if t["done"] else "○"
lines.append(f" {mark} [{t['id']}] ({t['priority']}) {t['title']}")
return "\n".join(lines)
@mcp.tool()
async def mark_done(todo_id: str) -> str:
"""
将指定待办事项标记为已完成。
Args:
todo_id: 待办事项的 ID
"""
todos = load_todos()
for t in todos:
if t["id"] == todo_id:
t["done"] = True
save_todos(todos)
return f"已将 [{todo_id}] 标记为完成: {t['title']}"
return f"未找到 ID 为 [{todo_id}] 的待办事项。"
@mcp.tool()
async def delete_todo(todo_id: str) -> str:
"""
删除指定的待办事项。
Args:
todo_id: 待办事项的 ID
"""
todos = load_todos()
original_len = len(todos)
todos = [t for t in todos if t["id"] != todo_id]
if len(todos) == original_len:
return f"未找到 ID 为 [{todo_id}] 的待办事项。"
save_todos(todos)
return f"已删除待办事项 [{todo_id}]。"
if __name__ == "__main__":
mcp.run(transport="stdio")
在 config.toml 中添加:
[mcp_servers.todo]
command = "uv"
args = [
"--directory",
"/path/to/todo-mcp-server",
"run",
"server.py"
]
description = "项目待办事项管理"
重启 Codex 后,你可以直接在对话中这样使用:
> 帮我添加一个待办事项:重构用户认证模块,优先级 high > 列出当前所有的待办事项 > 将 ID 为 abc12345 的任务标记为完成
Codex 会自动识别对应的 MCP 工具并调用,就像操作本地文件一样流畅。
MCP 服务器的 STDIO 标准输出被用于 JSON-RPC 通信,所以绝对不能使用 print() 或 console.log() 输出调试信息——这会破坏消息协议。正确的做法是使用 stderr 输出日志:
import logging
logger = logging.getLogger(__name__)
logger.info("Processing request") # 写入 stderr,不会干扰协议
console.error("Server started"); // stderr 安全
mcp SDK 自带的 Inspector 工具可以帮助你独立测试服务器:
npx @modelcontextprotocol/inspector uv run server.py
这会打开一个 Web 界面,让你在不依赖 Codex 的情况下测试工具调用。
在 Codex 会话中可以通过以下方式验证 MCP 服务器是否正常运行:
> 列出当前可用的 MCP 工具
如果某个服务器未出现在工具列表中,检查启动命令是否正确、依赖是否已安装。
| 问题 | 可能原因 | 解决方案 |
|------|---------|---------|
| MCP 服务器未出现在工具列表中 | command 路径错误或依赖缺失 | 在终端中手动执行 command + args,确保能正常启动 |
| 工具调用返回空结果 | 服务器崩溃或超时 | 检查 stderr 输出的错误日志 |
| STDIO 连接断开 | 服务器进程意外退出 | 确保服务器没有调用 print(),仅使用 stderr 输出日志 |
| 权限被拒绝 | 执行策略阻止了 MCP 工具调用 | 检查 config.toml 中的 approvals 配置 |
MCP 和 Skills 是 Codex 两种互补的扩展机制:
你可以在 AGENTS.md 中结合使用两者——用 Skills 描述何时调用 MCP 工具,用 MCP 服务器提供实际的数据和计算能力。
MCP 为 Codex 打开了一扇通往外界的门。通过编写 MCP 服务器,你可以让 AI 编程助手接入数据库、调用第三方 API、操作云端资源,甚至控制 IoT 设备。Codex 对 MCP 的深度集成——从连接管理、工具缓存到权限控制和认证编排——让这一切变得安全且高效。
本文从架构原理、配置方法到实战示例,完整覆盖了 Codex 的 MCP 集成流程。建议从简单的工具入手,逐步构建符合自己团队需求的 MCP 服务器,让 AI 编程助手真正成为技术栈的一部分。