Codex CLI: MCP tools return "unsupported call" or are never used with a custom model provider
You point Codex at DeepSeek, GLM, Qwen or another open model through a custom provider. The shell tool works, the MCP servers show as connected in codex mcp list, and yet the model never uses them, or every MCP call comes back as unsupported call. Nothing is wrong with your MCP server. The tools are lost between Codex and the model.
Last verified September 17, 2026 against openai/codex issue #26234, Community proxy workaround · 6 min read
| Tool | OpenAI Codex CLI and Codex Desktop, with wire_api = "responses" and a provider other than OpenAI or Azure |
| Symptom | MCP tool calls fail with unsupported call, or the model never calls an MCP tool and works around it with shell commands |
| Not affected | Built-in tools (shell, apply_patch) and MCP resources. The same MCP servers work on OpenAI models |
| Cause | Codex sends each MCP server as one {"type":"namespace"} tool. That wrapper is an OpenAI extension; other backends drop it or pass it through as a tool that cannot be called |
| When it happens | Any MCP server, stdio or HTTP, as soon as the model provider is not OpenAI or Azure |
| Can you wait it out? | No. It fails the same way on every request until the request format or the provider changes |
- Cause: Codex groups each MCP server's tools in a
namespacewrapper that only OpenAI's and Azure's backends expand. - Fixes: run a patched Codex, run a local proxy that flattens the request and restores the namespace on the way back, or use a provider that does this on its side.
- Codex's own sub-agent tools are wrapped the same way, so they are lost too.
What happens on the wire
We captured the requests Codex 0.154.0 sends. Built-in tools are ordinary function tools. Each MCP server, and Codex's own sub-agent tool set, arrives as a single tool of type namespace with the real tools nested inside:
{
"tools": [
{ "type": "function", "name": "exec_command", "parameters": { "...": "..." } },
{
"type": "namespace",
"name": "mcp__orders",
"tools": [
{ "type": "function", "name": "lookup_order", "parameters": { "...": "..." } },
{ "type": "function", "name": "add_note", "parameters": { "...": "..." } }
]
}
]
}The Responses API specification has no namespace tool type. A backend that follows the specification does one of three things: rejects the request, drops the entry, or hands the model one uncallable tool named mcp__orders. In the first two cases the model never learns the tools exist. In the third it guesses a flat name such as mcp__orders__lookup_order, and Codex's router answers unsupported call, because it routes MCP calls by a separate namespace field:
{
"type": "function_call",
"namespace": "mcp__orders",
"name": "lookup_order",
"call_id": "call_abc",
"arguments": "{\"order_id\":\"A-1042\"}"
}How to confirm this is your problem
- The same MCP server works when you switch Codex back to an OpenAI model.
list_mcp_resourcesandread_mcp_resourcestill work. They are plain function tools, so they survive.- The model reaches for the shell to do what the MCP tool would have done. In our first capture the model found the MCP server's script on disk and piped JSON into it by hand. The answer was right and the MCP server logged zero calls.
Fix 1: a patched Codex
The issue's author maintains a branch that adds a namespace_tools provider setting and flattens the tools when it is off. It works, and it means building Codex from source and rebasing on every release. As of September 17, 2026, the fix is not in a Codex release.
Fix 2: a local proxy
A commenter published a small Python proxy that sits between Codex and your provider. It rewrites each namespace into flat functions named namespace__tool, rewrites the calls Codex replays in the conversation history the same way, and splits the name back into namespace and name in the response. It is linked at the top of this page. It buffers the whole response before passing it on, so you lose streaming, and it is one more process to keep running.
Fix 3: a provider that does it on its side
The translation can live in the provider's Responses endpoint, which is where we put it. Nothing changes in your Codex config beyond the provider block:
model = "zai-org/GLM-5.3"
model_provider = "inferenceapis"
[model_providers.inferenceapis]
name = "Inference APIs"
base_url = "https://api.inferenceapis.com/v1"
env_key = "INFERENCE_API_KEY"
wire_api = "responses"
# your MCP servers stay exactly as they are
[mcp_servers.orders]
command = "python3"
args = ["/path/to/your_mcp_server.py"]What the endpoint does with a Codex request:
- Expands every namespace into flat function tools. Names longer than the 64-character limit are shortened and mapped back.
- Rewrites namespaced calls in the replayed history, so a multi-step session stays consistent.
- Returns each call with
namespaceandnamerestored, in streamed events as well as in the final response. - Accepts a call the model made by the bare tool name when only one namespace has a tool of that name.
- Reports what it did in a response header:
curl -s -D - -o /dev/null https://api.inferenceapis.com/v1/responses \
-H "Authorization: Bearer $INFERENCE_API_KEY" -H "Content-Type: application/json" \
-d '{"model":"deepseek-ai/DeepSeek-V4-Flash","input":"hi",
"tools":[{"type":"namespace","name":"mcp__demo","tools":[
{"type":"function","name":"ping","parameters":{"type":"object","properties":{}}}]}]}' | grep -i x-namespace
# X-Namespace-Tools: 1 flattenedWhat we tested
| Model | MCP tools | Sub-agents | Sub-agents, Multi-Agent V2 | Note |
|---|---|---|---|---|
deepseek-ai/DeepSeek-V4-Flash | Pass | Pass | Pass | |
deepseek-ai/DeepSeek-V4.1-Flash | Pass | Pass | Pass | |
deepseek-ai/DeepSeek-V4-Pro | Pass | Pass | Pass | |
zai-org/GLM-5.3 | Pass | Pass | Pass | |
zai-org/GLM-5.3-Flash | Pass | Pass | Pass | |
MiniMaxAI/MiniMax-M3 | Pass | Pass | Pass | |
moonshotai/Kimi-K2.7-Code | Pass | Pass | Pass | In one V1 run the parent delegated and also computed the answer itself. |
openai/gpt-oss-120b | Pass | Pass | Unreliable | V2: passed 1 of 3 runs. GPT-OSS writes tool names in its own style (collaboration.spawn_agent) and sometimes calls sub-agent tools from inside the sub-agent, where they are not offered; the endpoint normalises the names, the second problem is the model. |
Qwen/Qwen3-VL-235B-A22B-Instruct | Pass | Pass | Pass | |
meta-llama/Llama-3.3-70B-Instruct-Turbo | Fail | Not run | Not run | Called the MCP tool, then announced the second step without doing it. Not suited to multi-step agent work. |
Run with codex-cli 0.154.0 on September 17, 2026. MCP test: a local MCP server holds order data the model cannot know; pass means the server logged both tool calls and the final answer contained the release code. Sub-agent test: the parent must delegate a SHA-256 of a random string; pass means the parent ran no shell command itself, a sub-agent was spawned, and the correct digest came back.
Llama 3.3 70B is listed for completeness. Its MCP call went through, and then it announced the second step without doing it. That is the model, not the wire format; use a larger model for agent work.
Frequently asked questions
Does this affect HTTP MCP servers with OAuth, such as Figma?
Yes. The wrapper is applied to every MCP server regardless of transport. Because the fix is in how tools are presented to the model, it works for them as well; Codex still makes the MCP connection itself, with your credentials, on your machine.
Does the provider see my MCP server or its data?
The provider sees the tool definitions and whatever tool results Codex puts in the conversation, as any model provider does. It never connects to your MCP server. Here, none of it is stored after the response is returned; see the trust page.
Will this break when Codex ships its own fix?
No. If Codex starts sending flat function tools to non-OpenAI providers, there is nothing to expand and the request passes through as it is.
Is the OpenAI web search tool covered?
No. That tool runs on OpenAI's servers and is dropped here, with an X-Tools-Dropped header saying so. Use a search MCP server instead; that works.
Where Inference APIs fits
Fix 3 above is what we built: the Responses endpoint here expands Codex's namespace tools and puts the namespace back on every call, so MCP servers and sub-agents work on open models with no proxy and no patched Codex. Billing is per token from a prepaid balance.
Something changed or wrong? Tell us and we will re-verify the entry.
