MCP tool invocation regressed for custom/local providers (Ollama Responses API) in v0.117.0+
What version of Codex CLI is running?
Bisected with the same Codex config and MCP server set:
0.115.0: good0.116.0: good0.117.0through0.125.0: MCP tool invocation becomes unreliable for custom/local model providers0.126.0-alphavariants tested during investigation: still reproduced
I am currently pinning 0.116.0 only as a workaround for the custom/local provider path.
What subscription do you have?
Not applicable to the repro. This is a custom model provider path using a remote Ollama/OpenAI-compatible endpoint, not an OpenAI-hosted model invocation.
Which model were you using?
Primary repro: gpt-oss:120b served by Ollama through an OpenAI-compatible /v1 endpoint with wire_api = "responses".
I also reproduced the same class of failure with other Ollama-served models through the same Codex provider/config shape, including qwen3.5:122b, qwen3.6:35b-a3b-bf16, and gemma4:31b. One vision model profile can also stall in some runs, but the simpler text-model repro is enough to show the MCP dispatch regression.
What platform is your computer?
Linux x86_64. The current deployed host reports Linux 6.8.0-100-generic.
What terminal emulator and version are you using (if applicable)?
The clearest repro is non-interactive codex exec from a shell. My local helper script is not intended to be the interesting part of the report: it renders config.toml, sets CODEX_HOME, chooses a profile, and then execs Codex.
The important command shape is equivalent to:
printf '%s\n' '<prompt>' | CODEX_HOME=/tmp/codex-local codex exec \
--profile=local-ollama \
--cd /path/to/workspace \
--skip-git-repo-check \
--dangerously-bypass-approvals-and-sandbox \
-
What issue are you seeing?
For custom/local model providers using the Responses wire API, Codex 0.117.0+ often fails to invoke registered MCP tools even when the prompt explicitly requires tool use.
The session starts, the MCP server list can show the relevant server as ready, and other tool surfaces such as shell execution may still work. The failure is that Codex does not emit the expected MCP tool call. With the same config and prompt on 0.115.0 or 0.116.0, the transcript includes a real tool call such as:
tool brave_search.brave_web_search({...})
On regressed versions, the model frequently answers without making the MCP call, or otherwise fails to dispatch the registered tool despite it being available.
This looks like a Codex custom-provider/tool-routing regression rather than a generic MCP server startup issue. The same MCP servers and same model-provider config are usable on the known-good versions, and the regression starts at 0.117.0 in my bisect.
What steps can reproduce the bug?
Use a custom provider backed by Ollama or another OpenAI-compatible endpoint, with the Responses wire API and at least one MCP server that is easy to force from the prompt.
Representative config shape:
model_provider = "remoteollama"
model = "gpt-oss:120b"
[model_providers.remoteollama]
name = "Ollama"
base_url = "http://<ollama-host>:11434/v1"
wire_api = "responses"
[profiles.local-ollama]
model_provider = "remoteollama"
model = "gpt-oss:120b"
model_context_window = 131072
model_catalog_json = "/path/to/model-catalog.json"
approval_policy = "never"
sandbox_mode = "danger-full-access"
[mcp_servers.brave_search]
startup_timeout_sec = 60
command = "<brave-search-mcp-command>"
args = ["..."]
The catalog entries mark these models as API-supported and define their context windows/reasoning metadata; they are ordinary Codex model catalog entries, not special wrapper logic.
Then run an explicit MCP-forcing prompt:
printf '%s\n' 'Use the brave_search tool, not shell, to find the special comedian guest for the 2026 White House Correspondents Dinner. Return only the name and one source URL.' \
| CODEX_HOME=/tmp/codex-local codex exec --profile=local-ollama --cd /path/to/workspace -
If MCP startup is slow on the first invocation, run a simple warm-up prompt first and judge the second run:
printf '%s\n' 'Reply with exactly READY.' \
| CODEX_HOME=/tmp/codex-local codex exec --profile=local-ollama --cd /path/to/workspace -
Expected comparison:
0.115.0/0.116.0: after MCP startup, the transcript emits a realbrave_searchMCP tool call and returns the sourced answer.0.117.0+: MCP startup may still showbrave_search ready, but Codex no longer reliably emits the MCP tool call for the same prompt/config/provider.
What is the expected behavior?
Once an MCP server is started and available, a prompt that explicitly requires brave_search or web_search should reliably produce a real MCP tool call for custom/local provider models, matching the behavior seen in 0.115.0 and 0.116.0.
The custom-provider path should not require pinning an older Codex binary solely to preserve MCP tool invocation.
Additional information
My deployment has extra scaffolding that I do not think is essential to the bug: a VM image build, envsubst/envconsul config rendering, Dockerized MCP server images, persistent CODEX_HOME, and a small wrapper script used to select profiles. Those details explain why I noticed the regression, but the wrapper is not doing tool dispatch. It ultimately renders normal Codex config and runs codex exec --profile=... --cd ... -.
The current workaround in my environment is to install two Codex binaries:
0.116.0for Ollama/custom-provider profiles because MCP calls still work there- a newer Codex build for the OpenAI
gpt-5.5profile because the older binary is no longer a practical OpenAI runtime
This is only a compatibility bridge. It is not evidence that newer versions restored custom-provider MCP tool calling.
Related upstream signals that may be relevant:
- Closed issues in the same general area: #1558, #7517, #8829
- Open issue from the same release window: #16335
- MCP/tool-routing PRs around this period: #17556 and #18413
17 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
The two pinned candidates look like sibling MCP regressions on different layers, not duplicates of this one.
chrome-devtools-mcpandcontext7-mcpvianpxon Windows. MCP servers never register at all (different from this issue, where servers register successfully andcodex mcp listshows them).gpt-5.4. Servers register, tools are visible viacodex mcp list/codex mcp get, but the tools "are not exposed inside new Codex threads" (nomcp__server__toolcalls reach the model).wire_api = "responses". Servers register, tools register, the model sees them, but dispatch fails.The bisect windows also differ. #19649 places the regression between 0.120.0 and 0.121.x; this issue places it earlier, between 0.116 and 0.117. If they share a root cause, the local-provider responses-API path was sensitive to the change ~3 versions before the hosted path noticed it.
Worth keeping #19871 distinct so the responses-API-specific repro (and the cross-model evidence:
gpt-oss:120b,qwen3.5:122b,gemma4:31b,qwen3-vl:235b) doesn't get folded into a hosted-only triage.I've been working on an agent with multiple external tools — API lookups, search, live data. When tools fail (timeouts, empty responses, bad data), the model does one of two things: surfaces the raw error to the user, or answers from memory as if the tool worked. Neither is usable.
What I actually want is specific acknowledgment of the failure plus a useful alternative. Not "Error: connection timeout." Not a fabricated answer. Something like: tool unavailable, here's what you'd need to check this yourself, here's what matters when you do.
Built training data for this exact gap. One row from DinoDS Lane 33 (Fallback Behavior):
JSONL Row (public proof):
{"sample_id": "lane_33_spec_example_03", "mode": "conversation", "tone": "friendly",
"intent_family": "qa_general", "intent_subtype": "no_guarantees",
"user_message": "Give me the exact stock price right now.",
"assistant_response": "I can't guarantee an exact real-time price in this chat.
If you tell me the ticker and your market, I can explain how to check the
current quote and what numbers matter (last trade vs. bid/ask vs. after-hours)."}
Still not sure whether this covers multi-tool cascade failures well — where the first tool fails and the model has to decide whether to try a fallback tool or degrade gracefully. Probably a separate training surface or a superset of rows. Dataset exists. Full bundle available via Discord and Platform.
I am seeing the same thing. I suspect it's related to the namespacing not looking like typical tool calls.
That transcript matches the shape: the model emits text describing MCP tools (
mcp__tilth__ — Code intelligence server) instead of producing atool_useinvocation. That points at the regression being on the responses wire path: namespaced tool names register on the server side but no longer round-trip to a real tool call between 0.116 and 0.117.Two things that would tighten the bisect:
0.116.0with the sameglm-5.1:cloudconfig restore the actual tool call? Same regression line on a second provider/model pair rules out anything model-specific ingpt-oss:120b.wire_api = "responses"? Thechatpath was unchanged across that range, so confirmingresponseslocks the scope.A redacted
config.tomlpaste would help narrow the maintainer search.I did some more investigation and suspect that this might be https://github.com/openai/codex/issues/20574.
Tracing rnett's #20574 hypothesis against current HEAD: the two surfaces are adjacent, but the dispatch endings differ.
#20574 surfaces loudly with
Fatal error: tool_search handler received unsupported payload. Percodex-rs/core/src/tools/router.rs:188-201, that's the path whereResponseItem::FunctionCall(Ollama's only call shape on Responses API) routes throughresolve_mcp_tool_info, returnsNonefortool_search, and falls toToolPayload::Functionwhich the tool_search handler rejects.#19871 reports the model emitting no tool call at all, a silent fall-through. If a namespaced MCP name (
mcp__brave_search__brave_web_search) failed to resolve throughresolve_mcp_tool_info, the sametool_search-style loud error should fire. Silence implies dispatch did succeed and the bug is downstream, somewhere in result-encoding back to the model, or in tool spec encoding so the model never emits the call shape in the first place.A quick disambiguator: grep your regressed run's
$CODEX_HOME/log/codex-tui.logforunsupported payloador any dispatch error. Loud means the root likely overlaps with #20574 and the fix shape is at the router. Silent means it's a different bug surfacing in the same provider path.Bisect note on the related-PR list at the bottom of the issue body: #17556 (Support flattened deferred MCP tool calls) merged 2026-04-12, #18413 (support namespaced deferred dynamic tools) merged 2026-04-21, both after
rust-v0.117.0was tagged on 2026-03-26. The MCP-namespace work cited (#17404 register-all-mcp-with-namespace, #16944 expand-tool-search-to-custom-MCPs, #18097 defer-all-tools-behind-flag) all merged April. They could be partial fixes that didn't fully close the regression, but they're not the commits that introduced 0.116→0.117. Narrowing torust-v0.116.0..rust-v0.117.0(232 commits, 5-day window) for changes to ResponseItem function-call shape or non-native-provider tool spec encoding would be the wedge if this still reproduces on current main.Yes. I get the same issue.
Seems like codex app is moving toward cloud native tool calling payloads breaking the traditional function calling schema. Look at my profile. You might find a temporary solution to this.
Yes. Thank you.
Confirmed on macOS with an OpenAI model served through OpenRouter, so this is not limited to local/non-OpenAI model implementations.
Environment:
0.143.00.144.0-alpha.4wire_api = "responses"/api/v1openai/gpt-5.4Observed behavior matches this issue:
tools/listsucceeds. One test server exposed 39 tools; another exposed 19.exec_commandcall executed successfully.response.completed, and Codex records a normaltask_complete/turn.completed.tool_search_call, MCPfunction_call, or MCP dispatch event occurs. There is no process crash or MCP transport error.Control comparison:
tool_search_call, receives the deferred MCP namespace, invokes the MCP function, and returns its result successfully.I also tested these overrides with both installed binaries:
Both binaries report
tool_searchandsearch_toolas removed features, and the overrides do not change behavior.The important detail is that this is an OpenAI model (
openai/gpt-5.4) still ultimately served by OpenAI, only routed through OpenRouter. Standard function tools work, but Codex’s client-executed/deferredtool_searchpath does not.This appears to overlap with:
tool_search/ dynamic discoveryfunction_callGiven the June change making searched MCP-tool flow the default, it looks like custom provider capability negotiation or the direct-tool fallback is incomplete. Ideally, Codex should support
tool_searchend-to-end for compatible custom Responses endpoints rather than only falling back to direct tools.Reproduction notes and proposed fix path
I took a pass at reducing this to an actionable code path before suggesting a fix.
Breakdown:
Proposed approach:
just test -p codex-core→ fail — package run completed with 2856 passed, 52 failed, 1 timed out; observed failures were unrelated missingcodex/test_stdio_serverhelper binaries plus oneimage_preparationtimeout.Precedent: #29189 Codex Desktop 26.616.41845 node_repl fails: codex/sandbox-state-meta missing sandboxPolicy
How I shaped this comment from prior successful threads:
This analysis was prepared with AI assistance.
<!-- fkst:codex-saga:engage:codex-triage:dup:openai/codex#19871 -->
I built an experimental user-space workaround for this exact
tool_searchpayload mismatch. You can add any custom response api provider to use it and it works as natively with all the codex tooling.back into the native Codex item expected by the app-server:
It also converts existing
tool_search_callconversation items into ordinary function calls before forwarding them to an Ollama-compatible Responses API upstream.Install:
Repository: https://github.com/bharat2808/codex-ollama-proxy
npm: https://www.npmjs.com/package/codex-ollama-proxy
Additional matching reproduction on macOS with a local OpenAI-compatible Responses provider.
Current environment:
0.144.10.5.1, endpointhttp://127.0.0.1:8011/v1wire_api = "responses"Qwen3.6-35B-A3B-oQ4e-mtp1.5.3MCP server using the Codex contextObserved with this configuration immediately before the OMLX 0.5.1 upgrade:
tools/list, exposing 22 tools. The project is automatically activated and the Dart LSP is healthy.list_mcp_resourcesinstead. Serena is a tool-only server, so the empty resource list is incorrectly interpreted as no Serena tools being available.tool_searchis interpreted as anexec_commandshell command, rather than producing a nativetool_search_call.CallToolRequestreaches Serena.web_search__searchare rejected by Codex asunsupported call.OMLX 0.5.1 is now installed and restarted successfully. I have not repeated the full model smoke turn after that upgrade yet, so the behavioral observations above are from the same configuration immediately before the upgrade.
This looks consistent with the deferred
tool_search/ custom Responses-provider path discussed here. The resource-list fallback also overlaps with #14242.Hopefully OpenAI can see this as important to their users. Yes they are so generous with the rate limit but we do get limited. It would be cool if we could mess around with a local model while we wait to be out of time out. This fix would help with that.
meanwhile you waiting for the fix on OpenAI side you can use the proxy I built to route the request to OpenAI. It fully support current codex version with any response api.
https://github.com/bharat2808/codex-ollama-proxy
I made a fix and am using t3 code instead of their app. I should look at your proxy again. Still would be nice for them to do it.