
一个LiteLLM,允许OpenAI Codex CLI与不理解Codex的MCP工具格式的模型提供者合作。
概览
一个LiteLLM,允许OpenAI Codex CLI与不理解Codex的MCP工具格式的模型提供者合作。 Codex CLI 通过非标准类型“命名空间”工具对象将其 MCP(模型上下文协议)发送给模型。OpenAI 的 /v1/responses 端点能够理解该功能。而像 OpenRouter 这样的提供者则不理解。这些工具被静默投放,永远无法到达模型,因此助手的行为就像没有工具一样。 该保护栏将每个命名空间平整化为普通类型:在请求离开代理前,“函数”工具命名为mcp__: 模型看到一个标准工具可以调用,工具调用则以同样的扁平名称返回。 因为LiteLLM自定义护栏可以从你已经运行的代理内部重写原始的/v1/response请求主体。在async_pre_call_hook中变异数据会传播到上游请求。这一点已经通过litellm 1.105进行了实证验证。
概述(AI 翻译)
一个LiteLLM 自定义护栏,使OpenAI Codex CLI能够与不理解Codex的MCP工具格式的模型提供者合作。
问题所在
Codex CLI 通过非标准的 type: "namespace" 工具对象将其 MCP(模型上下文协议)服务器发送给模型。OpenAI 的 /v1/responses 端点能够理解该对象。而像 OpenRouter 这样的提供者则不理解。这些工具会被无声投放,永远无法到达模型,因此助手的行为就像没有工具一样。
一份法典请求如下:
修复
该护栏将每个命名空间平整成普通的type: "function"工具,名为mcp__ __ ,然后请求离开代理:
模型看到一个标准工具可以调用,工具调用则以同样的扁平名称返回。
为什么是护栏而不是第二个代理? 因为 LiteLLM 自定义护栏可以从你已经运行的代理内部重写原始的
/v1/responses请求主体。在async_pre_call_hook中变异data会一直传播到上游请求。这一点已在 litellm 1.105 的实验中得到验证。
本页由 AI 翻译,技术细节请以原文为准。
README
以下为原文 README 节选,完整内容请查看原文。
litellm-guardrail-codex-mcp
A LiteLLM Custom Guardrail that lets OpenAI Codex CLI work with model providers that do not understand Codex’s MCP tool format.
The problem
Codex CLI sends its MCP (Model Context Protocol) servers to the model using a non-standard type: "namespace" tool object. OpenAI’s /v1/responses endpoint understands it. Providers such as OpenRouter do not. The tools are silently dropped and never reach the model, so the assistant behaves as though it has no tools at all.
A Codex request looks like this:
{
"model": "claude-sonnet-4.5",
"tools": [
{
"type": "namespace",
"name": "mcp__filesystem",
"tools": [
{ "name": "read_file", "description": "Read a file", "parameters": { "...": "..." } }
]
}
]
}
The fix
This guardrail flattens each namespace into ordinary type: "function" tools named mcp____ before the request leaves the proxy:
{
"tools": [
{
"type": "function",
"name": "mcp__filesystem__read_file",
"description": "Read a file",
"parameters": { "...": "..." }
}
]
}
The model sees a standard tool it can call, and its tool call comes back under that same flattened name.
Why a guardrail and not a second proxy? Because a LiteLLM custom guardrail can rewrite the raw
/v1/responsesrequest body from inside the proxy you already run. Mutatingdatainasync_pre_call_hookpropagates all the way to the upstream request. This was verified empirically against litellm 1.105.1 for both streaming and non-streaming requests: the mock upstream receivedmcp__filesystem__read_fileas atype: "function"tool. No extra hop, no extra process, and/chat/completionstraffic stays untouched.
Installation
uv pip install litellm-guardrail-codex-mcp
The guardrail module path is litellm_guardrail_codex_mcp.CodexMCPNamespaceGuardrail.
Configuration
Merge this into your LiteLLM proxy config (see examples/config.example.yaml):
guardrails:
- guardrail_name: codex-mcp-namespace-compat
litellm_params:
guardrail: litellm_guardrail_codex_mcp.CodexMCPNamespaceGuardrail
mode: [pre_call, post_call]
default_on: true
Two settings here are mandatory:
| Setting | Why |
|---|---|
default_on: true |
Without it LiteLLM’s should_run_guardrail() returns False and the hook never runs. The guardrail silently does nothing. |
mode includes post_call |
The streaming iterator hook only fires when post_call is present. With mode: pre_call alone, has_iterator_override stays false and SSE responses are never observed. |
Optional settings
| Field | Default | Meaning |
|---|---|---|
enabled |
true |
Set false to install without rewriting anything. |
target_models |
null |
Restrict to matching models, e.g. ["openrouter/*"]. Null applies to every attached route. |
安装
This server does not publish a one-line install command.
Open the repository installation guide 新手?先看 mcpskill.org 教程 →