Custom stdio MCP server discovered by /mcp but tools not exposed to Desktop threads
Summary
Codex Desktop sees a custom stdio MCP server and successfully discovers its tools via tools/list, but the tools are not exposed to Desktop threads or tool_search.
This appears to be a regression or exposure-layer bug in Desktop/App Server 0.124.0-alpha.2.
Environment
- Codex Desktop client:
26.422.21637 - Codex/App Server:
0.124.0-alpha.2 - Platform: macOS
- Server type: custom stdio MCP server
- Config location: project-scoped
.codex/config.toml
MCP config
[mcp_servers.interdomestik_qa]
command = "/bin/bash"
args = ["scripts/start-repo-qa.sh"]
cwd = "."
The wrapper script resolves the repo root, changes into it, and starts a TypeScript MCP server over stdio.
Expected behavior
The following custom MCP tools should be exposed to Desktop threads and callable via names like mcp__interdomestik_qa__project_map:
project_mapread_filescode_searchcheck_healthpr_verifysecurity_guarde2e_gate
Actual behavior
- Desktop
/mcpshows the server enabled. codex mcp list --jsonshows the server enabled.- Repo preflight passes, including live MCP
tools/listdiscovery. - Desktop logs show the server starts and returns a
ListToolsResultcontaining the expected custom QA tools. tool_searchreturns 0 callableinterdomestik_qatools.- Thread request payloads do not include any
mcp__interdomestik_qa__*tools. - The current thread's
thread_dynamic_toolsonly contains generic Desktop tools such asautomation_update,read_thread_terminal,load_workspace_dependencies, andinstall_workspace_dependencies.
Evidence collected locally
Manual/repo preflight confirms live MCP discovery works:
pnpm mcp:preflight
Desktop logs show the custom server is started and queried successfully:
- custom QA server starts on stdio
- client initializes against the server
ListToolsRequestis sentListToolsResultis received with the expected custom tools
But those discovered tools are not present in the thread/deferred tool surface.
Historical comparison from local Codex state/logs:
- An Apr 20 thread on Codex
0.91.0successfully called tools such as: mcp__interdomestik_qa__read_filesmcp__interdomestik_qa__code_searchmcp__interdomestik_qa__project_map- Apr 24 threads on Codex
0.124.0-alpha.2do not expose those tools, despite successful/mcpdiscovery.
I also checked whether stale Desktop workspace state was the cause. The active workspace root and thread cwd both point to the correct repo, but the custom MCP tools are still not exposed.
Suspected failure point
The failure appears to be after successful MCP tools/list discovery and before Desktop registers/indexes those tools into the per-thread/deferred tool surface.
It does not appear to be caused by the MCP server implementation, stdio transport, project config, or CLI discovery, because live tools/list discovery succeeds outside the Desktop thread tool surface.
24 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
Update after full Codex Desktop restart:
Outcome is still: Discovery works, exposure still fails.
Fresh thread details:
/Users/arbenlila/development/interdomestik-crystal-home019dc0ed-8043-78b1-badb-e1a5aa5e6f02tool_searchfor expected QA tools returned 0 tools, both initially and after rechecking.codex mcp list --jsonshowsinterdomestik_qaenabled.pnpm mcp:preflightpassed, including livetools/listdiscovery.thread_dynamic_toolsfor the current thread contains only:automation_updateread_thread_terminalload_workspace_dependenciesinstall_workspace_dependenciesthread_dynamic_toolscontains 0interdomestik_qa,project_map,read_files, orcode_searchentries.interdomestik-qaversion1.0.0.ListToolsResultfor this same thread containing the expected QA tool names.tools/listfailure.Versions after restart:
codex --version:codex-cli 0.124.0cli_version:0.125.0-alpha.3Codex.app26.422.30944, build2080Most precise failure boundary:
Desktop/RMCP can start
interdomestik_qaand receiveListToolsResult, but those tools are not persisted/indexed into the current thread's callable dynamic tools, sotool_searchexposes none of them.Reviewed the potential duplicates suggested by the bot. I do not think this is an exact duplicate, though it is likely related to the same MCP exposure/indexing area.
Comparison:
ListToolsResultfor the same current thread containing the expected tool names, whilethread_dynamic_toolsstill contains no custom MCP entries.codex execsession schema. This issue is Desktop thread/tool_search exposure.tools/list, so OAuth is not involved.So the narrower failure here remains: Desktop/RMCP successfully starts the stdio server and receives
ListToolsResult, but the discovered tools are not persisted/indexed into the thread's callable dynamic tools.Additional cross-client comparison from the same local repo/machine:
Gemini/Antigravity is able to mount and use MCP tools successfully in this repo context. Verified working there:
mcp-context-server/project_mapmcp-context-server/read_filescontext7/resolve-library-idplaywright/browser_tabse2e-test-generator/generate_basic_testThis further suggests the MCP server/environment is not the root problem. Other MCP clients can index and call repo/context/playwright-style MCP tools on the same machine, while Codex Desktop still discovers
interdomestik_qaviatools/listbut does not expose the tools to the thread/tool_search surface.One separate note: Gemini's
repo-qawrapper hit a JSON parsing/encoding issue (invalid character 'â') for some QA commands, which looks like command-output formatting inside that wrapper. That is distinct from this Codex issue, where the tools are not mounted at all despite successfulListToolsResultdiscovery.Additional related issue with a useful version boundary: #19649
That report describes the same core failure class: MCP servers are registered and visible via
codex mcp list/codex mcp get, but their tools are not injected into Codex threads.Important detail from #19649:
codex-cli 0.120.0codex-cli 0.122.0-alpha.13and later0.120.0That lines up with this Desktop report:
0.91.0had usablemcp__interdomestik_qa__...calls0.124.x/0.125.xdiscovertools/listbut do not expose the tools totool_search/thread_dynamic_toolsSo this issue may be the Desktop/app-server manifestation of the same regression family as #19649. This issue still adds Desktop-specific evidence: the logs show
ListToolsResultfor the same current thread, whilethread_dynamic_toolsremains missing the discovered MCP tools.This may be https://github.com/openai/codex/issues/20574, are you using a custom or older model?
Thanks. I do not think this is #20574.
In this repro I am not using a custom provider like Ollama/LiteLLM/Bifrost. The failing Desktop thread is using the normal Codex Desktop / ChatGPT path with
gpt-5.5.The failure signature is also different:
tool_searchis present but aborts withunsupported payloadwhen called through a non-native provider.tool_searchexecutes successfully, but returns an empty result forinterdomestik_qa.codex mcp list --jsonshowsinterdomestik_qaenabled.pnpm mcp:preflightpasses, including livetools/listdiscovery from the stdio server.ListToolsResultcontaining the expectedinterdomestik_qatool names.mcp__interdomestik_qa__...namespace becomes callable.So this still looks like a Desktop/app-server MCP exposure/indexing issue, not the custom-provider
tool_searchpayload bug from #20574.Additional update: I also tested a repo-side hardening for a possible stale-workspace-root factor.
Originally the project-scoped MCP entry used a relative cwd:
I changed it to pin the server cwd to the absolute repository root while keeping the launcher argument unchanged:
I also added a local preflight guard so
pnpm mcp:preflightfails ifinterdomestik_qaresolves with a relative cwd or a cwd outside this repo.Verification after that hardening:
codex mcp list --jsonresolvesinterdomestik_qawith the absolute repo cwd.pnpm mcp:preflightpasses.tools/listdiscovery still passes and returns the expected QA tools.However, this did not fix Desktop thread exposure. In the running Desktop session,
tool_searchstill returns 0 results forinterdomestik_qa/project_map/read_files/code_search.So the absolute-cwd change is a useful repo-side mitigation that removes stale root ambiguity, but it is not a complete fix for this issue. The remaining failure is still in the Desktop/app-server exposure/indexing step after successful MCP discovery.
Additional update from the repo side: I hardened the
interdomestik_qaMCP configuration to remove possible local blockers, but the Desktop exposure failure remains in the current thread.Repo-side changes made and verified:
interdomestik_qais pinned to the absolute repository cwd instead ofcwd = ".".enabled_toolsallowlist for the core agent tools:project_mapread_filesread_file_rangecode_searchgit_status_compactgit_branch_infochanged_filesscope_auditcheck_healthpr_verifysecurity_guarde2e_gateadditionalProperties: false.pnpm mcp:preflightnow verifiescodex mcp get interdomestik_qa --json, including the parsedenabled_toolsallowlist.Verification results:
codex mcp get interdomestik_qa --jsonshows the server enabled with the absolute cwd and the parsedenabled_toolsallowlist.pnpm mcp:preflightpasses.tools/listdiscovery passes.tools/listreturns 35 tools, all with strict object schemas, and the core allowlisted tools are present.However, this existing Codex Desktop thread still returns 0 results from
tool_searchforinterdomestik_qa project_map read_files code_search ....So the repo/server side is now exposure-ready: no relative cwd, no unbounded Codex tool surface, and no permissive input schemas. If a fresh Desktop thread still does not surface
mcp__interdomestik_qa__..., the remaining failure is still in Desktop/app-server MCP indexing/injection after successful discovery.Additional update from the current Desktop session after restart and after testing the local-plugin/CLI distinction.
Current state:
Interdomestik QAplugin as available/enabled in the session metadata.tool_searchstill returns 0 results forinterdomestik_qa,project_map,read_files, andcode_search.mcp__interdomestik_qa__...namespace in the Desktop session.interdomestik-qa, while the MCP server id remainsinterdomestik_qa; Desktop can know about the plugin/server, but the tools still are not admitted into the callable tool index.CLI / local stdio contrast from the same repo and machine:
tools/list, and can execute tool calls.pnpm mcp:tool listreturns the expected QA tools.pnpm mcp:tool call read_file_range '{"file":"package.json","startLine":1,"endLine":8}'succeeds.interdomestik_qaas enabled with the expected command/cwd/env configuration.So the current narrowed boundary is:
initializeworks.tools/listworks.tool_search/ the callablemcp__...namespace.I also checked public issues/docs for evidence that Codex Desktop has started admitting local custom MCPs into
tool_search; I did not find a confirmed fix or documented supported path for that yet. This still looks like a Desktop/app-server MCP admission/indexing bug, not a repo MCP implementation problem.Hi OpenAI Codex team,
I’m asking for help with a Codex Desktop MCP exposure issue.
I have a repo-local MCP server named
interdomestik_qa. The server is configured, startable, and discoverable, but Codex Desktop does not expose its tools as callablemcp__interdomestik_qa__...tools throughtool_search.What works:
codex mcp get interdomestik_qa --jsonshows the server enabled.initializeworks.tools/listworks.pnpm mcp:preflightpasses.additionalProperties: false.What fails:
tool_searchreturns 0 tools forinterdomestik_qa,project_map,read_files,read_file_range, andcode_search.mcp__interdomestik_qa__project_mapappears, even after restarting Codex Desktop from the repo root.Current suspected boundary:
Codex Desktop can see/start/discover the MCP server, but the discovered tools are not admitted into the Desktop/app-server callable tool index.
Could you please clarify whether Codex Desktop currently supports exposing local custom stdio MCP servers into
tool_search? If yes, what exact config/plugin shape is required? If not, is this a known limitation or regression?This is blocking my preferred workflow because the repo MCP tools are much faster and deeper for codebase inspection than shell fallbacks.
Thank you.
I'm running into same issue, this is a huge one, I think it should have been fixed by now...
Codex CLI recognize my MCP and model can make call, but Codex app does not. /cli on codex app shown the MCP server, but model not being able to call it.
it's absurd this bug is still open after all this time. MCP is such a key feature, why is the team not fixing something like this?
I'm experiencing this issue as well.
I am seeing the same Desktop MCP discovery-to-exposure failure on a newer Codex build, with two independent custom Streamable HTTP MCP servers.
Environment:
26.707.722210.144.20.144.5~/.codex/config.tomlWhat works:
codex mcp listshows both custom servers enabled.initializesuccessfully.toolscapability.tools/listreturns their complete and valid tool definitions.What fails:
mcp__<server>__...tools are present in the thread tool registry.This appears to fail after successful server initialization and tool discovery, but before the discovered tools are admitted into the Desktop thread callable-tool index. That matches the boundary described in this issue.
The servers are primarily tool-oriented and do not advertise MCP resources, so this may overlap with:
The older #3441 appears different because its example used the obsolete
[mcp.servers]configuration shape. My configuration uses the current[mcp_servers.<name>]format and is parsed correctly by both CLI versions.So this is still reproducible on Desktop/app-server
0.144.2, well beyond the0.124.xversions originally reported here.Adding a stricter reproduction on a newer Desktop build. This server exposes both MCP resources and tools, so the failure is not limited to tool-only servers or an empty
resources/listresponse.Environment:
26.707.91948(build 5440)0.144.5~/.codex/config.tomlVerified working:
listed MCP server tools while building tool list server_name=RepoPromptCE tool_count=20Observed failure:
mcp__RepoPromptCE__*tools.tool_searchor equivalent deferred-tool retrieval bridge.ToolSearchAlwaysDeferMcpToolsenabled.Expected behavior:
When Codex defers discovered MCP tools, the task must receive
tool_search; otherwise it should inject the direct MCP tools. Successfully discovered tools must not become unreachable through both paths.This places the failure after successful
tools/listand before model-visible direct/deferred tool registration, matching the boundary described in this issue. The server configuration, process, transport, resource path, and tool discovery all remain healthy.this is the discovery-vs-registration split, and it shows up wherever tool exposure is a separate layer from tool discovery. /mcp, codex mcp list, and your preflight are all reading the same fact: a tools/list round trip succeeded. none of them read the thread's tool surface, which is the thing that actually gates a call. so every green light you have is green about the wrong layer. the useful diff is the 0.91.0 thread against the alpha one, same ListToolsResult, different thread_dynamic_tools. whatever indexes discovered tools into that surface stopped consuming the result, and nothing downstream of it is built to notice the gap.
Additional reproduction: Windows / Streamable HTTP / Codex Desktop 0.144.5
I can reproduce this with a custom Streamable HTTP MCP server for a game
I am developing. The server exposes resources and three tools for observing
and controlling the running application.
The failure does not appear to be game-specific: Codex successfully discovers
the tools but does not expose them to the thread.
Environment:
Observed behavior:
resources/list,resources/templates/list, andresources/readsucceed.tools/list.tools/callrequests to the server succeed.server_name=<custom_server> tool_count=3thread tools.
tool_searchis also unavailable in the affected thread, so the deferredtools cannot be discovered.
The discovered tools consist of:
The server exposes resources as well as tools, so this is not caused by an
empty or unsupported
resources/listimplementation.I reproduced the result in an independent fresh Codex task. It also reported
that none of the custom MCP tools was directly available, ruling out stale
tool state in an existing thread.
Additional diagnostic result:
codex features listreports:tool_search_always_defer_mcp_tools removed trueAdding the following user configuration had no effect and was removed
afterward:
tool_search_always_defer_mcp_tools = falseExpected behavior:
Either:
tool_searchmust be supplied whenever those tools are deferred.The failure appears to occur after successful
tools/listdiscovery butbefore the tools are exposed to the thread or tool-search surface.
I traced this to a concrete code-mode exposure bug and have a tested patch.
Root cause: in
code_mode_only, the planner retains only the code executor tools and tools markedDirectModelOnly. MCP tools are intentionally deferred totool_search, but the cachedtool_searchhandler itself retained normal exposure and was filtered out. The MCP server could therefore initialize and returnListToolsResult, while both access paths were unavailable: the MCP tools were deferred and the search bridge was hidden.The patch marks
tool_searchasDirectModelOnly. Deferred MCP tools remain out of the initial prompt, but the model keeps the top-level search bridge, can receive the deferred namespace, and can then call the MCP tool. I added planner coverage and an end-to-end Responses Lite test for search -> deferred MCP namespace -> tool call, and also verified the patched app-server against a real configured Streamable HTTP Jira MCP server.Review PR: https://github.com/yunusemregul/codex/pull/1
Commit: https://github.com/yunusemregul/codex/commit/dd5b8e84ca190efcd4222f2eb0460e38959a8301
OpenAI documents external Codex contributions as invitation-only, and GitHub rejected creating the PR directly against
openai/codexthrough both GraphQL and REST. The fork PR is therefore the reviewable patch; if a maintainer invites it, the same branch can be submitted upstream directly.Related reports: #14242, #10499, and #19649.
Thanks for the detailed report. I independently reproduced a closely matching Windows case and narrowed it to a specific Sol/Responses Lite tool-exposure path. I believe it belongs in this thread because MCP discovery succeeds, but the Desktop task does not receive a usable deferred tool surface.
Environment
0.144.5gpt-5.6-soluse_responses_lite: truesupports_search_tool: truetool_mode: directmulti_agent_version: v1Controlled live results
| Route | Only catalog change |
tools/list| Successful MCP call ||---|---|---:|---:|
| GPT-5.5, normal Responses | none | 1/1 | 1/1 |
| Sol, Responses Lite + deferred
tool_search| none | 3/3 | 0/3 || Sol, normal Responses + deferred
tool_search|use_responses_lite: true → false| 3/3 | 3/3 || Sol, Responses Lite + direct MCP namespace |
supports_search_tool: true → false| 2/2 | 2/2 |Every failing Sol/Lite run initialized the MCP server, received a valid one-tool
tools/listresult, and logged that tool in Codex’s inventory. None madetools/call; each returned the prompt’s exactTOOL_SEARCH_UNAVAILABLEfallback. All five matched Sol controls called the canary with the expected token.This rules out the MCP process, stdio transport, tool schema, Windows helper, Codex’s MCP invocation router, a generic Sol tool-calling limitation, and random tool choice.
Sanitized final outbound request trace
I captured the final request with the real
0.144.5executable pointed only at a loopback endpoint. No request was forwarded externally, and no raw body, header values, credentials, prompts, instructions, or tool schemas were persisted.For the affected Sol/Lite route, the request:
POST /v1/responses;x-openai-internal-codex-responses-lite;toolsfield;additional_toolsitem; andThe initial body does not contain
mcp__node_repl, but that is expected for a deferred namespace. Its metadata remains in Codex’s client-side search index and should be returned after a successfultool_searchcall.The matched wire controls further narrow the boundary:
use_responses_lite: false, the same client-executedtool_searchappears in top-leveltools, and Sol succeeds 3/3.supports_search_tool: false,additional_toolscontains the directmcp__node_replnamespace and its three child tools. Sol successfully invokes the equivalent synthetic MCP canary 2/2.Therefore, Responses Lite can expose and invoke direct MCP namespaces. The failing sub-path is specifically the client-executed
tool_searchsurface under Responses Lite.Public-source check
I checked both the installed source-equivalent commit
8c68d4cand upstreammainat9ff4786. In both versions, the Lite request builder intentionally serializes tools into a leadingAdditionalToolsitem.The existing
stdio_server_round_triptests:However, it covers only normal Responses. I added a local Lite variant that asserts the wire placement and reuses the complete search/load/invoke round trip:
Formatting also passes. This confirms that the public client serializer, client-side search index, response routing, and MCP invocation path can complete a mocked Lite exchange. It does not cover the internal service transformation that determines which tools become model-visible.
Most likely remaining boundary
Could a maintainer inspect a sanitized post-ingestion trace for one failing Sol request and confirm:
additional_toolsitem still contains{"type":"tool_search","execution":"client"}.tool_searchis registered in the model-visible tool list.AdditionalToolsconversion, or a Sol-specific allowlist.I have sanitized per-trial summaries, three matched request traces, and the focused regression-test patch available to attach.
For users, a fresh GPT-5.5 task is a reliable workaround. A fresh Sol task alone is not sufficient in the affected
direct + Responses Litecatalog cohort. Forcing non-Lite was useful as a diagnostic, not a supported configuration recommendation.Additional reproduction: official Linear app/plugin regressed immediately after Desktop update
I can reproduce the same discovery-to-exposure failure with the official curated Linear integration, rather than a custom MCP server.
Environment:
26.707.91948(build5440)0.144.5gpt-5.6-sollinear@openai-curatedpluginBefore/after boundary:
codex_appsLinearget_issuecall at2026-07-16T02:16Z, before the Desktop update.26.707.91948.Verified healthy after the update:
https://mcp.linear.app/mcpendpoint.codex mcp listandcodex mcp get linearshow the Linear MCP server enabled with OAuth.codex mcp login linearcompletes successfully.Observed failure:
codex_appsLinear tools.tool_searchbridge.codex execprobe cannot callget_issueand reports the Linear tool unavailable.unknown MCP server lineareven thoughcodex mcp list/getreport it enabled.This appears to be the same post-discovery exposure failure described above, but it also affects a first-party curated plugin whose app connection and OAuth state are healthy. The update did not merely break a custom server configuration; it made a previously working official connector unreachable from new tasks.
The current release notes say existing app connections should remain unaffected by the App Directory to Plugin Directory migration, so this behavior appears to be an unintended regression.
All identifiers, account details, local paths, OAuth data, issue content, and workspace-specific values have been intentionally omitted.
it's really crazy that a bug of this magnitude goes ignored for so long considering the development is done primarily by agents. What is going on? Don't OpenAI have auth tests for MCP in the testing harness? I have that in my own hand rolled framework!
please contribute to the discussion https://github.com/openai/codex/discussions/33682 to raise visibility with the team. The MCP bug reports are very clearly being ignored right now.
Additional reproduction: Solo orchestration MCP / resources visible, action tools unreachable
I can reproduce this with Solo's local stdio MCP server. This is especially confusing because Solo correctly reports Codex as connected, and Codex can use Solo's resource APIs, while Solo's orchestration/action tools remain unreachable.
Environment:
0.144.5gpt-5.6-sol, medium reasoning0.9.3Verified working:
codex mcp get soloshows the server enabled.server="solo".resources/listreturns Solo scratchpads and todos.tools/listround trip returns Solo's complete action-tool catalog, including:whoamilist_agent_toolsspawn_agentsend_inputget_process_outputObserved failure:
mcp__solo__...action tools.tool_searchbridge.list_agent_tools,spawn_agent,send_input, or timers even though Solo is connected and advertises them successfully.Restart/update test:
0.144.4.0.144.5.codex features listreports:tool_search_always_defer_mcp_tools = falsetoconfig.tomlhas no effect on the reported effective state.Expected behavior:
Either Solo's discovered action tools should be injected directly, or the thread must receive
tool_searchso the deferred tools can be loaded. A healthy connection plus resource access should not leave every action tool inaccessible.This matches the
code_mode_only/DirectModelOnlyroot cause and proposed patch described in the comment above.