MCP tools silently unavailable with DeepSeek official setup (custom models.json: supports_search_tool=true + tool_mode=null)

Open 💬 5 comments Opened Jul 31, 2026 by qum1nzhennan
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

Summary

Following DeepSeek's official one-command setup (irm https://cdn.deepseek.com/api-docs/codex-deepseek-setup-en.ps1 | iex), the generated ~/.codex/models.json registers deepseek-v4-flash / deepseek-v4-pro with "supports_search_tool": true and "tool_mode": null. On Codex 0.145.0 this combination makes all configured MCP tools silently unavailable: the session exposes web search and MCP resources, but no mcp__* callable tools and no tool_search.

Native OpenAI catalog models (e.g. gpt-5.6-sol, tool_mode: code_mode) can call the same MCP servers fine, so this only shows up with custom models registered through model_catalog_json.

Environment

  • Codex CLI 0.145.0 on Windows
  • Custom provider in config.toml: [model_providers.deepseek] with base_url = "https://api.deepseek.com/" and wire_api = "responses"
  • model = "deepseek-v4-flash", model_catalog_json = "C:/Users/<user>/.codex/models.json" (written by the DeepSeek setup script)
  • MCP servers: codegraph (stdio), idalib-mcp (stdio), universal-memory (stdio) — all enabled; codex mcp list shows Auth: Unsupported (expected for stdio)
  • The MCP server handshake succeeds: idalib-mcp --stdio initialize + tools/list works, and in-session list_mcp_resources returns the server's resources

Reproduction

  1. Run the DeepSeek setup script and choose model 1.
  2. Add any stdio server under [mcp_servers.*] in config.toml.
  3. Start a new session with deepseek-v4-flash.
  4. Ask the model to call any MCP tool (e.g. mcp__idalib__server_health).

Actual: the model reports the tool is not in its available tool set. The session's tool surface contains web search and the MCP resource tools but neither mcp__* tools nor tool_search.

Expected: MCP tools registered in [mcp_servers.*] should be directly callable (or at least discoverable via tool_search) for custom models, matching catalog models.

Root cause (client-side)

  • codex-rs/core/src/tools/spec_plan.rs: search_tool_enabled = model_info.supports_search_tool && provider.capabilities().namespace_tools
  • codex-rs/core/src/mcp_tool_exposure.rs: when search_tool_enabled is true, every MCP tool is registered with ToolExposure::Deferred
  • build_model_visible_specs* only emits tools whose exposure is_direct(); Deferred tools are excluded from the model-visible list
  • effective_tool_mode: tool_mode: null plus no code-mode feature flags resolves to ToolMode::Direct, so there is no code-mode exec tool into which the deferred tools could be nested
  • The DeepSeek setup script hardcodes "supports_search_tool": true (and "tool_mode": null) in the models.json it writes

Net effect: official DeepSeek setup + any MCP server = MCP tools silently invisible, with the tool_search escape hatch also absent from the session.

Workaround

Set "supports_search_tool": false in both entries of ~/.codex/models.json. MCP tools are then registered with ToolExposure::Direct and appear in a new session. Hosted web search is unaffected (it is driven by provider capabilities + web_search_tool_type, not supports_search_tool).

Related

  • #33609 (closed): gpt-5.6-sol hides MCP tools without exposing tool_search
  • #31750: custom model_provider, no tool_search / dynamic tool discovery
  • #32101: Code Mode omits tool_search from exec, degrading deferred MCP discovery
  • #34018: Windows Desktop custom stdio MCP tools pass discovery but are not exposed

View original on GitHub ↗

5 Comments

github-actions[bot] contributor · 27 days ago

Potential duplicates detected. Please review them and close your issue if it is a duplicate.

  • #35153

Powered by Codex Action

TunaTung · 27 days ago

Confirmed on Windows 11 + Codex 0.146.0 with the same DeepSeek official one-command setup. Same fingerprint: models.json registers deepseek-v4-flash/-pro/-free with supports_search_tool=true + tool_mode=null, and MCP tools are silently hidden in exec mode (and intermittently in other modes).

Local workaround that fully fixes it (verified live): set supports_search_tool: false for the DeepSeek models in ~/.codex/models.json. After that all configured MCP tools (10 hermes-team tools in our case) are exposed and calls succeed in exec mode. No codex upgrade needed.

This is worth fixing in the official setup script (deepseek setup ps1) and/or docs — every DeepSeek-onboarding user hits it.

CCanxue · 25 days ago

Additional data point for this family, also from DeepSeek's official catalog (supports_search_tool: true, tool_mode: null):

With multi_agent_version: "v1", the V1 multi-agent tools are registered as Deferred behind tool_search. The tool_search tool is present in the request's tools array, but deepseek-v4-flash does not recognize or call it, so the V1 tools are unreachable. Tool discovery is silently broken for this model in general, not just for MCP tools, which also explains why the documented V1 workaround for subagent delivery fails with DeepSeek.

More detail in the write-up: https://github.com/CCanxue/codex-deepseek-subagent-fix

drsnxt · 16 days ago

Confirmed on Linux + Codex 0.146.0 + custom router (LiteLLM → sglang), with additional findings:

Same symptom: MCP tools silently invisible for opencode-zen/deepseek-v4-flash-free via a custom model_providers entry, while native GPT models call the same MCP servers fine.

Additional data points from debugging the full chain (codex → router → LiteLLM → sglang):

  1. tool_search is unusable for custom providers, not just absent. When supports_search_tool: true, Codex emits the tool with type: "tool_search" (no function field). LiteLLM's Responses→chat translation rejects it (400: Field required ... tools[13].function), and if you rewrite it to a plain function, Codex's own handler fails with tool_search handler received unsupported payload (core/src/tools/handlers/tool_search.rs). So the "escape hatch" is doubly broken on the custom-provider path.
  1. Tool elicitation works for native models but not DeepSeek. With tool_call_mcp_elicitation, naming the tool in the prompt makes native models (gpt-5.6-terra/luna) call mcp__* directly. DeepSeek instead calls list_mcp_resources, sees only codex_apps, concludes nothing exists, and gives up — it never attempts the deferred call, so elicitation never fires.
  1. The supports_search_tool: false workaround did not expose tools in our setup (Linux, custom provider via LiteLLM). Tools stayed invisible; the model's tool surface contained only codex host functions + codex_apps resources. Possibly the exposure decision also depends on provider.capabilities().namespace_tools / tool_mode as noted in spec_plan.rs.
  1. Separate bug found while testing: the router's message coalescing dropped DeepSeek's reasoning_content on follow-up requests, causing 400: The reasoning_content in the thinking mode must be passed back to the API from sglang whenever a tool call happened. (Fixed locally by preserving the field.)

Net: MCP tools for custom/non-OpenAI models need a real fix in mcp_tool_exposure.rs / spec_plan.rs — either expose deferred tools directly when tool_search can't work on the provider path, or make tool_search a plain function tool that custom providers can pass through. Upvoting; happy to provide logs/config.

kyambuthia · 8 days ago

Confirmed on Linux (x86_64), codex-cli 0.147.0, running the official DeepSeek deepseek-v4-flash setup (custom models.json with supports_search_tool: true, tool_mode: null, provider [model_providers.deepseek] -> https://api.deepseek.com/, wire_api = \"responses\").

Same symptom: codex mcp list shows the stdio server enabled and the handshake works (MCP stdio tools/list returns all tools), but codex exec never injects mcp__* tools into the model-visible schema — only the built-ins plus the list_mcp_resources/resource functions.

Fixing ~/.codex/models.json by setting "supports_search_tool": false in the deepseek-v4-flash (and deepseek-v4-pro) entries makes every MCP tool registered with direct exposure and the tools immediately appear in a fresh session. Verified end-to-end: the model then successfully called an stdio MCP *_health tool and returned its real payload.

Hosted web search still works (driven by provider capabilities + web_search_tool_type, not supports_search_tool), so the workaround is minimal. Thanks for the clean root-cause writeup.