Codex stops at `list_mcp_resources` and fails to discover/use tool-only MCP servers like Context7
What version of the IDE extension are you using?
see below
What subscription do you have?
plus
Which IDE are you using?
everyone of them.
What platform is your computer?
_No response_
What issue are you seeing?
Summary
Codex appears to treat an MCP server with no resources as effectively unavailable, instead of continuing to discover and use that server’s tools.
This breaks compatibility with tool-oriented / legacy MCP servers such as Context7, which may expose tools but no resources. In practice, Codex tries list_mcp_resources, gets an empty result, and then says it has no access to MCP servers or that the server is not installed, even though the server is configured and visible in /mcp.
This issue seems broader than Context7 specifically. The underlying bug is that Codex should not probe the resources interface once and give up when a server exposes tools but no resources.
Expected behavior
If an MCP server has no resources, Codex should still:
- recognize the server as available
- discover its tools
- use those tools when appropriate
- avoid telling the user the server is unavailable just because
resources/listis empty
At minimum, an empty resources list should not be treated as equivalent to “server not found” or “no MCP access.”
Actual behavior
Codex repeatedly attempts list_mcp_resources, gets back an empty result or related failure, and then does not proceed correctly with tool discovery / tool usage.
Observed behaviors reported in this issue family include:
codex.list_mcp_resources({})returning{"resources": []}- Codex saying Context7 or other configured MCP servers are “not installed”
- Codex saying it has “no access to MCP servers”
- server-specific resource calls failing even though the server appears enabled in
/mcp /mcpshowing the server is enabled, but Codex still not using its tools
Example reported error:
• Called context7.list_mcp_resources({"server":"context7"})
└ Error: resources/list failed: unknown MCP server 'context7'
At the same time, /mcp can show the server as enabled.
Why the prior closure did not resolve the real issue
The previous issue was effectively closed on the basis that “Context7 has no Resources.” That explains why resources/list is empty, but it does not explain or resolve the actual bug:
- Context7 has tools
- Codex should still discover and use those tools
- an empty resources interface is not a valid reason for Codex to conclude the server is unavailable
So the problem is not “user misunderstanding MCP resources.” The problem is that Codex’s MCP discovery / selection flow appears to over-index on resources and fails on tool-only servers.
Reproduction
- Configure a tool-oriented MCP server that exposes tools but no resources, such as Context7.
- Confirm the server appears enabled in
/mcp. - Ask Codex to use that MCP server or to use functionality that should route through its tools.
- Observe Codex attempting
list_mcp_resourcesfirst. - Observe that after an empty resources result or resource-related failure, Codex does not successfully use the server’s tools and may instead claim the server is unavailable.
Example config
[mcp_servers.Context7]
command = "npx"
args = ["-y", "@upstash/context7-mcp@latest"]
[mcp_servers.Context7.env]
CONTEXT7_API_KEY = "********"
Other affected tool-oriented servers have also been reported in the same discussion pattern.
Evidence suggesting this is not isolated to one server
In the linked discussion, multiple users reported that configured MCP servers appeared enabled in /mcp but Codex still failed to use them properly. Some reports also mention other servers beyond Context7.
That suggests the issue is with Codex’s MCP handling strategy, not just one specific MCP package.
Possible root cause
A likely cause is that Codex’s MCP discovery / planning flow:
- probes
resources/list - sees no resources
- does not sufficiently fall back to tool discovery / tool descriptions / direct tool invocation
If so, Codex should treat these as separate capabilities:
- resources
- resource templates
- tools
A server with any valid capability should remain eligible for use.
Requested fix
Please update Codex’s MCP handling so that:
- empty
resources/listdoes not mark a server as unavailable - tool-only MCP servers are discoverable and usable without special prompting
- Codex falls back from resources discovery to tools discovery automatically
- error messaging distinguishes:
- “server exists but has no resources”
- “server exists and has tools”
- “server is actually unknown / unavailable”
Environment
Reported examples in the prior thread include:
- Codex CLI 0.54.0
- Codex VS Code extension 0.4.35
- VS Code 1.105.1
- macOS 26.0.1
Other users in the same thread reported similar behavior with different MCP server configurations.
Related issues
- #6215
- #6217
- #6465
Impact
This makes MCP support unreliable for a class of valid MCP servers and forces users to manually “handhold” Codex into using tools that should be discoverable automatically. It also creates misleading UX because /mcp can show a server as enabled while Codex simultaneously claims it has no MCP access.
What steps can reproduce the bug?
see above!
What is the expected behavior?
_No response_
Additional information
_No response_
7 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
Additional repro on
codex-cli 0.118.0with a local tool-only MCP server (Recallium) over both direct HTTP and stdio bridge.Environment
0.118.0gpt-5.4~/.codex/config.tomlMCP config
I also tested a stdio bridge variant:
Same result.
What is verified
codex mcp listshows the server as enabledcodex mcp get recalliumshows correct transport and URLcurl http://127.0.0.1:8001/mcp/statussucceeds outside sandboxRecalliumreports tools, but no resources (resources.supported_schemes=[]/ tool-only server)Critical observation
Using
codex debug app-server send-message-v2 'recallium'shows:mcpServer/startupStatus/updated->recallium: startingrecallium: readySo Codex does start the MCP server successfully and marks it
ready.But after that, the model still behaves as if no MCP tool exists:
recalliumas plain text / repo namecodex exec ...explicitly answers:> "The
recalliumMCP tool is unavailable in this runtime."Interpretation
This looks like a failure in the layer after startup:
config -> MCP server startup -> readyworksbut
ready -> MCP tools injected into active runtime tool listfailsBecause
Recalliumis a tool-only MCP server, this seems consistent with the hypothesis in this issue: Codex is not correctly handling servers that expose tools but no resources.Extra notes
-c features.rmcp_client=truedid not helpcodex mcp remove/adddid not helpIf useful, I can provide a tighter minimal repro based on
Recallium, but the key symptom is: server reachesready, runtime still says tool unavailable.I used codex to help get to the bottom of this.
Codex CLI defaults to bundling MCP server tools as
namespace-typed entries in thetools[]of the/v1/responsesrequest. Hosted OpenAI models (gpt‑5.x) understand this Responses‑API extension; local models (e.g.gemma4:26bvia Ollama) do not. Two failures stack on top of each other:mcp__playwright__), not the inner tool schemas, so it hallucinates names.(namespace, name)pair — a flatmcp__playwright__browser_navigatewith no separatenamespacefield doesn't match. And local models often emit alternative separators (mcp:playwright:browser_navigate).Fix candidates:
namespace_toolscapability for the OSS provider so MCP tools are sent inline asfunction-typed entries.---
Symptom
Same machine, same MCP config (Playwright MCP enabled), same Codex install:
| Setup | Behavior |
|--------------------------------------------------------|---------------------------------------|
|
codex(OpenAI hosted gpt‑5.x) | Callsplaywright.browser_navigate✓ ||
codex --oss -m gemma4:26b(Ollama) | Refuses, hallucinates tool names ✗ |Failing transcript (turn 1, before any fix):
Notice the model invented
mcp:playwright:launch_browser— a name that does notexist (the real tool is
playwright.browser_navigate).---
Reproduction (against unpatched codex)
Expected: tool call. Actual: refusal + hallucinated tool names.
---
Investigation Summary
/tmp/codex_ollama_proxy.py(see below). Saw codex sending MCP tools as{"type": "namespace", "name": "mcp__playwright__", ...}instead of expanding them as individualfunctionentries. Confirmed gemma4:26b can call tool schemas correctly when sent inline (verified with a direct curl with one tool definition).name: mcp:playwright:browser_navigate"(single colon separator) and codex emitted `function_call_output: unsupported call:mcp:playwright:browser_navigate
. The router constructsToolName::new(namespace, name)and looks up by canonical(namespace, name)pair — a flat name with:` separator never matches.:,.,/,-) back to__and split flatmcp__SERVER__TOOLnames into{name: "TOOL", namespace: "mcp__SERVER__"}so they match codex's canonical form. Browser navigated successfully.---
Root cause (with code citations)
All paths relative to
codex/codex-rs/.Where the namespace tool format is defined
tools/src/responses_api.rs:43-67:Where the capability is decided per provider
model-provider/src/provider.rs:27-39:model-provider/src/amazon_bedrock/mod.rs:60: Bedrock explicitly overrides tofalse.model-provider/src/provider.rs:156(ConfiguredModelProvider): does not override, so all custom providers — including the built-in OSS provider for Ollama/LMStudio — inheritnamespace_tools: true.Where the capability gates expansion
tools/src/tool_config.rs:244(with_namespace_tools_capability):When
false, MCP tools become individualToolSpec::Functionentries instead ofbeing grouped into a
ToolSpec::Namespace.Where routing requires the (namespace, name) split
core/src/tools/router.rs:180-192:codex-mcp/src/connection_manager.rs:616-621:codex-mcp/src/tools.rs:51:So a flat name like
mcp__playwright__browser_navigatewithnamespace: Nonewill never==the canonicalToolName::namespaced("mcp__playwright__", "browser_navigate"). The router then falls through to the plain function lookup atcore/src/tools/registry.rs:321, which emits"unsupported call: <name>".Where the unsupported-call message is emitted
core/src/tools/registry.rs:579-584:This is purely a name-lookup miss — not a permissions/sandbox failure.
Working mitigation: Python proxy codex_ollama_proxy.py
What it does
127.0.0.1:11500, forwards tohttp://localhost:11434(Ollama).EXPAND_NAMESPACES=1, default on, and the path isPOST /v1/responses): walkstools[], replaces each{"type":"namespace", "name":NS, "tools":[...]}with the inner function entries flattened to top level. Inner names are prefixed with the namespace name (e.g.,browser_navigate→mcp__playwright__browser_navigate). RecomputesContent-Length.data: {...}event, walks the JSON fortype: "function_call"items and normalizes thenamefield vianormalize_function_call_name::,.,/,-arereplaced by
__.{name: "TOOL", namespace: "mcp__SERVER__"}.Connection: closeto avoid HTTP/1.1 keep-alive framing bugs (codex's reqwest client + pythonBaseHTTPRequestHandler+ chunked SSE was hanging without this)./tmp/codex_proxy.logif you start it viapython3 codex /tmp/ollama_proxy.py 2>&1 | tee /tmp/codex_proxy.log).How to run
Confirmed working:
> can you open a browser and navigate to nhl playoffs scheduleproduces a realplaywright.browser_navigate({"url":"https://www.google.com"})(🙃) call and a snapshot of the page.Source-side fix (drafted, but I'm not an approved Contributor)
Single-method override on the catch-all
ConfiguredModelProvider:codex-rs/model-provider/src/provider.rs(insert into theimpl ModelProvider forblock, afterConfiguredModelProvider
fn info):See also: https://github.com/openai/codex/issues/20574. Might actually be a dupe.
I can confirm a closely related reproduction on Codex Desktop
26.707.72221with the bundled Codex CLI/app-server0.144.2.I have two independent custom Streamable HTTP MCP servers configured globally with the current
[mcp_servers.<name>]syntax. Both servers:codex mcp listtoolscapabilitytools/listHowever, new Desktop threads receive no callable
mcp__<server>__...tools. Restarting Desktop and creating a fresh thread does not help. The servers are tool-oriented and do not advertise resources, while the thread-facing resource discovery path does not expose them.I posted the full environment and discovery-versus-exposure evidence in #19425. This looks like the intersection of this tool-only discovery issue and the Desktop thread injection/indexing problem described there. Related Desktop report: #10499. Historical thread-injection regression: #19649.
Follow-up: I traced and fixed the overlapping unreachable-tool path locally.
Under
code_mode_only, MCP tools can be discovered successfully and then deferred, buttool_searchwas also removed from the top-level model surface because it retained normal exposure. That leaves the discovered tools unreachable even thoughtools/listsucceeded. The patch makestool_searchDirectModelOnly, while keeping MCP tools deferred until search returns their namespace.The change includes planner and Responses Lite integration coverage for search -> deferred MCP tool call:
The PR is hosted in the fork because upstream documents contributions as invitation-only and GitHub rejected direct PR creation for this account. A maintainer invitation would allow the same branch to be submitted upstream.
I can confirm the namespace-routing half of this issue with Codex CLI
0.144.6, a local tool-only MCP server, and a custom Responses API provider.Minimal reproduction
websearchandwebfetch, and no resources.Observed result:
unsupported call: mcp__issue781__websearch.(namespace, name)pair works:With that envelope, Codex dispatched one search, three sequential fetches, and received the final cited synthesis.
Workaround
Our provider now recursively expands namespace children into qualified names only for internal capability selection, retains the original namespace definition, and rehydrates a selected child to separate
namespaceandnamefields in the Responses output. We also mark the MCP toolsreadOnlyHint: true; without the annotation, a non-interactive read-only Codex run cancelled the otherwise valid calls as approval-requiring operations.The complete red/green transcripts and regression are being preserved in link-assistant/formal-ai#803.
Suggested fixes
(namespace, child name)pair; a flattened fully-qualifiednameis not equivalent.unsupported callmessage.This matches the router analysis already posted above and adds a current, deterministic end-to-end reproduction against a custom Responses provider.