Repository navigation
MCP tools silently unavailable with llama-server backends — add option to flatten namespace tool specs to functions #36942
Description
What variant of Codex are you using?
CLI
What feature would you like to see?
A per-provider compatibility option to expose MCP tools as plain type: "function" entries
(with prefixed names) instead of a type: "namespace" entry, for Responses API backends that
do not implement the namespace tool type.
Why: since ~0.144, codex sends each configured MCP server's tools as ONE type: "namespace"
entry in the Responses tools array (create_tool_spec in codex-rs/core/src/tools/handlers/mcp.rs,
unconditional). Backends that implement /v1/responses by translating to chat completions — e.g.
llama.cpp's llama-server — skip every non-function tool type (tools/server/server-chat.cpp:
"unsupported Responses tool type ... skipped"). The result is fully silent: built-in tools work,
codex mcp list shows the server enabled, the server's stdio handshake and tools/list succeed,
yet the model never sees any MCP tool, in the TUI and in ACP clients alike. There is no config
escape today: ProviderCapabilities::namespace_tools is hard-true for custom TOML providers,
and no flatten path exists.
Ask (either would work):
- honor a
namespace_tools = falsekey on[model_providers.<id>], falling back to the
pre-namespace behavior (flat prefixedfunctionspecs); or - a
[features]/provider-levelmcp_tool_compat = "flatten"option with the same effect.
Feasibility evidence: we run a proxy in front of llama-server that does exactly this
translation and codex works end to end with local models: expand each namespace entry to flat
functions named <namespace>__<tool> on the request, and rewrite the model's function_call
items back to the split {"namespace": ..., "name": ...} form on the response (codex's tool
router resolves calls by exact (namespace, name) lookup, so both directions are needed).
Having codex emit the flat form natively would remove the need for that middleware for every
local-model user.
Additional information
- Reproduction: any custom
model_providerwithwire_api = "responses"pointed at
llama-server (current master), one stdio MCP server under[mcp_servers.*], new session →
ask the model to list its callable tools: built-ins only, nomcp__*tools, no error anywhere
(llama-server logs the skip at WARN). - Verified against codex 0.146.0 (npm) and main d1fb77d (2026-08-04); llama.cpp master as of the
same date still skips non-function Responses tool types, so the gap is real on both sides today. - Possibly the same underlying mechanism as MCP tools silently unavailable with DeepSeek official setup (custom models.json: supports_search_tool=true + tool_mode=null) #36382 ("MCP tools silently unavailable with DeepSeek
official setup" — custom provider,wire_api = "responses", resources visible but no callable
MCP tools): if DeepSeek's Responses implementation also drops unknown tool types, that report's
models.jsontheory would be a red herring and this option would fix it too. - Happy to test a build; the middleware workaround above is in production for us, so this is a
compatibility improvement for the local/OSS-backend ecosystem rather than an urgent blocker.
Activity
github-actions commentedon Aug 4, 2026
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
madmax24-ubuntu commentedon Aug 18, 2026
I built and published a compatibility bridge that implements the exact workaround described here for Codex + llama.cpp: https://github.com/madmax24-ubuntu/codex-proxy-llama.cpp
It expands each Codex MCP
namespacetool into flat functions before forwarding to llama.cpp, then reconstructs the namespace/name mapping on the response path so Codex can route the call correctly. The bridge also handles nativeapply_patch, Responses SSE/event normalization, replayable tool history, reasoning handling, and compaction.It is currently working end-to-end with local Qwen + llama.cpp, and the installer/doctor are included. Sharing it here both as a workaround for users who need MCP now and as a concrete reference implementation for a native Codex flatten option.
madmax24-ubuntu commentedon Aug 18, 2026
I have a working middleware implementation of the exact two-way flatten/restore path described here: https://github.com/madmax24-ubuntu/codex-proxy-llama.cpp
It expands Codex
type:"namespace"MCP specs into flat function tools for llama.cpp, then rewrites returned function calls back into the namespace/name form Codex expects. I’m using it end-to-end with local Qwen throughwire_api = "responses".The bridge also normalizes Codex-specific Responses behavior that local backends commonly miss (
apply_patch, tool-history replay, SSE framing, reasoning events, compaction), but the namespace translation itself is isolated and may be useful as a reference implementation for a nativenamespace_tools = falseoption.There is an installer and
doctor.pyin the repo if anyone wants to test the workaround without patching Codex/llama.cpp.chrisbrittan commentedon Aug 23, 2026
Confirming this against a second backend — Ollama (the report cites llama.cpp/llama-server), and adding the failure mode, which I think matters for prioritisation.
Setup:
codex-cli 0.149.0, Ollama 0.32.15, macOS 27,qwen3-coder:30b(Q4, ~3B active MoE), via--oss --local-provider ollama(wire_api = "responses").Captured request (proxy between Codex and Ollama, 100,655 bytes):
So none of the MCP tools reach the model as callable functions — matching this report exactly.
How it fails is the problem. Ollama doesn't reject the namespace entry; it surfaces it to the model flattened into prompt text, so the model sees
mcp__atlassian.getVisibleJiraProjectsand tries to call it with that dotted name — which Codex won't accept. In its own words mid-session: "the previous attempt failed". After that it degrades into emitting raw Qwen XML as assistant text:Note
mcp__jira_mcp— an invented server name; the real one isatlassian. Asked to check Jira, it then explored the repo with shell tools and produced a confident, wrong diagnosis (that a credential was missing, with remediation steps naming a real script). Verified outside the sandbox, that credential was fine. So the user-visible symptom isn't "tool unavailable" — it's a plausible answer to a problem that doesn't exist.Positive control — this is not model capability. Same model, same endpoint, tools sent as flat
type:"function":tool_calls, 0.4smcp__atlassian({"operation":"getVisibleJiraProjects"})And end-to-end through a harness that sends flat function tools, the same model chained
atlassianUserInfo→getAccessibleAtlassianResources→getVisibleJiraProjectsagainst live Jira and returned the right projects. The encoding is the only difference.One related note:
supports_search_tool = truein a custom model catalog makes this worse, not better. Ollama silently drops{"type":"tool_search"}too, so the deferral hides every MCP tool behind a tool the model can never see. Confirmed with a control — offered atool_searchentry alongside a normal function namedmarker_tool_alpha, the model listed the function and never mentionedtool_search.+1 for the per-provider flatten option proposed here. Thanks @madmax24-ubuntu for the bridge — useful to know there's a working path in the meantime.
oelmekki commentedon Sep 2, 2026
Hello, fellow humans!
oelmekki commentedon Sep 2, 2026
For the record, an other way to fix it: I used the patch here: https://github.com/xiaomangold/llama.cpp/tree/namespace-tool-fix
I merged the branch with latest llama.cpp, still applies without issue. I've been using llama.cpp/Qwen-3.8 with Codex since this week-end, having it using Firefox MCP and other namespaced tools. This is obviously a hack, but if anyone want a llama.cpp workaround before it's fixed upstream, that's one possibility.
An other thing I had to change specifically for Qwen is their Jinja template. Codex is sending system/developer messages in the middle of the messages list, while Qwen only accept them at the top of the list. The template I used for Qwen (3.8, 27b, UDQ4 from Unsloth):
The one change I did in Unsloth's template is replacing the line that raise exception on system message with: