Skip to content

unsupported call: mcp__<server>__<tool> for MCP tools when used with custom Responses API provider (Apps SDK + custom model) #31354

Open

Description

@crazyi

Summary

When Codex is configured with a custom model provider that uses wire_api = "responses" (for example, MiniMax-M3 against api.minimaxi.com/v1, or llama.cpp with the Responses adapter), calls to MCP tools — including Apps SDK / UI widget MCP servers like Cowart — are rejected by Codex's tool dispatcher with the literal error:

error=unsupported call: mcp__<server>__<tool>

The MCP servers themselves are healthy and respond normally when invoked directly outside Codex. The failure happens before the MCP child process is reached. This blocks every MCP-driven plugin for non-OpenAI providers.

Environment

  • Codex Desktop: 26.623.141536 (Codex CLI 0.142.5)

  • OS: macOS 26.5.1

  • Model provider (custom):

    model_provider = "custom"
    model = "MiniMax-M3"
    
    [model_providers.custom]
    name          = "minimax"
    base_url      = "https://api.minimaxi.com/v1"
    wire_api      = "responses"
    requires_openai_auth = true
    experimental_bearer_token = "<token>"
  • MCP server used in the reproduction: Cowart v0.1.11 (Apps SDK / UI widget MCP, registerAppTool + _meta.ui.resourceUri + openai/outputTemplate = ui://widget/cowart/canvas.html).

Reproduction

  1. Install and enable the Cowart plugin in ~/.codex/config.toml:
    [plugins."cowart@personal"]
    enabled = true
  2. Run the Cowart MCP probe outside Codex to confirm the server is healthy:
    $ cd ~/.codex/plugins/cache/personal/cowart/0.1.11
    $ node ./scripts/probe-mcp.mjs
    OK: Cowart MCP tools and native widget resource are available.
    
  3. Start a Codex thread on MiniMax-M3. Send: "Open the Cowart canvas for this project."
  4. Model emits function_call for mcp__cowart_mcp__render_cowart_canvas_widget. Codex logs show:
    dispatch_tool_call_with_code_mode_result{
      otel.name = mcp__cowart_mcp__render_cowart_canvas_widget
      tool_name = mcp__cowart_mcp__render_cowart_canvas_widget
      call_id   = "call_019f3a99811d726095284eed"
      aborted   = false
    }:dispatch_tool_call_with_terminal_outcome:
      error = unsupported call: mcp__cowart_mcp__render_cowart_canvas_widget
    
  5. The same error is produced for non-widget Cowart tools too (get_cowart_canvas_state, save_cowart_canvas_state, etc.). Every call to any mcp__cowart_mcp__* tool fails identically.

Expected

  • The widget tool should render the native Cowart canvas UI (openai/outputTemplate resolution), and
  • Non-widget Cowart tools should execute against the MCP child process and return their structuredContent.

Actual

  • Every mcp__cowart_mcp__* call is rejected at the Codex dispatcher with unsupported call: <tool>, regardless of whether the tool is an Apps SDK widget tool or a regular MCP tool.
  • Searching ~/.codex/logs_2.sqlite shows all 7 hits for this error string come from the MiniMax-M3 provider; no occurrences under gpt-* models in the same time window.

Root cause (best guess, from Codex CLI binary strings)

The error is emitted by the dispatcher at core/src/tools/router.rs:185 (dispatch_tool_call_with_terminal_outcome). The function only knows how to route tool calls that match a known tool-kind arm; for everything else it falls through to unsupported call: <name>.

For Apps SDK widget MCP servers, Codex has a separate context type McpToolCallAppContext (with mcpAppResourceUri) and a corresponding dispatch path. When the model provider is OpenAI-native, the framework rewrites / re-emits Apps SDK tool calls into that path. With custom wire_api = "responses" providers, that rewrite is skipped — the model returns a plain function_call payload (e.g. call_id = "call_019f3a99..."), which the dispatcher does not recognize and rejects with unsupported call.

This explains why:

  • Widget MCP tools (Cowart, Canva, Computer Use, …) all fail.
  • Non-widget MCP tools on the same server also fail when registered by an Apps MCP server, because the whole server is treated as an Apps namespace.
  • The probe script (node scripts/probe-mcp.mjs) succeeds — the failure is purely on the Codex dispatch side, not on the MCP child process.
  • The error is provider-specific: switching back to the default OpenAI model in the same Codex build makes identical MCP calls succeed.

Related symptoms observed in ~/.codex/logs_2.sqlite under the same provider:

tool_name provider
mcp__cowart_mcp__render_cowart_canvas_widget MiniMax-M3
mcp__cowart_mcp__get_cowart_canvas_state MiniMax-M3
mcp__node_repl__js MiniMax-M3
mcp__codex_apps__github___search MiniMax-M3
multi_agent_v1__close_agent MiniMax-M3

Suggested fix (in order of preference)

  1. In core/src/tools/router.rs:185, fall through to the Apps SDK dispatch path whenever the MCP server exposes an Apps SDK resource (openai/outputTemplate on any of its tools) instead of returning unsupported call.
  2. When a custom provider returns a function_call for a server whose tools are Apps SDK widget tools, wrap the call into the McpToolCallAppContext / mcpAppResourceUri shape so the existing widget rendering pipeline can run.
  3. As a longer-term fix, distinguish "Apps SDK MCP server" from "regular MCP server" at server-spawn time (not per-tool), so the dispatcher picks the right arm up front.

Workarounds

  • Switch the thread model back to a default OpenAI model: Cowart renders correctly.
  • Patch Cowart locally to register render_cowart_canvas_widget via mcpServer.registerTool (without _meta.ui.*) instead of registerAppTool. This loses the native widget UI but restores function-call dispatch.

Related

Activity

  1. added
    bugSomething isn't working
    appIssues related to the Codex desktop app
    mcpIssues related to the use of model context protocol (MCP) servers
    custom-modelIssues related to custom model providers (including local models)
    tool-callsIssues related to tool calling
    on Jul 7, 2026
  2. github-actions commented on Jul 7, 2026

    @github-actions
    Contributor

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

    Powered by Codex Action

  3. fcoalcantarajr commented on Aug 11, 2026

    @fcoalcantarajr

    I can reproduce this on a newer Codex CLI with a different custom Responses provider path, and the evidence points to the same dispatcher-side failure.

    Environment

    • Codex CLI: 0.147.0
    • macOS on Apple Silicon
    • Custom provider: local 9Router OpenAI-compatible gateway
    • Model exposed by the gateway: github-copilot

    Sanitized profile:

    model = "github-copilot"
    model_provider = "9router"
    model_reasoning_effort = "high"
    approval_policy = "on-request"
    approvals_reviewer = "user"
    
    [model_providers.9router]
    name = "9Router"
    base_url = "http://127.0.0.1:20128/v1"
    env_key = "NINEROUTER_API_KEY_CODEX"
    wire_api = "responses"

    What is interesting in this repro

    The MCP servers are not merely configured — Codex itself enumerates their tools successfully via /mcp.

    For example, /mcp shows:

    computer-use
      Tools: click, drag, get_app_state, list_apps,
             perform_secondary_action, press_key, scroll,
             select_text, set_value, type_text
    
    node_repl
      Tools: js, js_add_node_module_dir, js_reset
    
    playwright
      Tools: browser_click, browser_navigate, browser_snapshot,
             browser_take_screenshot, browser_type, ...
    

    It also lists toolsets from codex_apps, notion, and openaiDeveloperDocs.

    I additionally probed the Computer Use MCP binary directly over stdio JSON-RPC. initialize succeeds and tools/list returns the expected tool schemas:

    serverInfo.name = "Computer Use"
    capabilities.tools.listChanged = false
    

    So the MCP server binary is healthy and tool discovery works outside the model-dispatch path.

    Failure

    When the custom-provider thread is asked to use Computer Use (for example, open Calculator and type 123 + 456), the model attempts MCP calls such as the mcp__... tools, but Codex rejects them as unsupported call before the requested UI action reaches the MCP server.

    The same session also encountered this with mcp__node_repl__js; earlier attempts involving Browser/Playwright hit the same class of unsupported call behavior for dynamic MCP tools.

    This is especially notable because /mcp in the same running thread lists those servers and their tools correctly. In other words:

    MCP startup / tool enumeration          ✅
    /mcp shows concrete tool schemas        ✅
    direct MCP initialize + tools/list      ✅
    built-in shell/tooling                   ✅
    model attempts dynamic MCP invocation   ✅
    Codex dispatch of mcp__... call          ❌ unsupported call
    

    That makes this look much more like the flat/dynamic MCP call is not being resolved back to the registered MCP runtime under the custom responses provider, rather than an MCP startup/configuration issue.

    Computer Use-specific details

    I also corrected an unrelated path issue first: the Computer Use MCP command now points to the absolute SkyComputerUseClient path, after which its startup error disappeared and /mcp began exposing its tools normally. The remaining failure is the dispatcher rejecting the tool invocation.

    Native-provider comparison limitation

    I have not been able to run the final A/B comparison against the native OpenAI provider on this machine because my current Codex/OpenAI usage quota is exhausted, and I am not adding credit right now. So I am not claiming a fresh native-vs-custom comparison from this environment; the useful signal here is that the custom-provider thread has healthy MCP discovery and a healthy MCP server, but invocation still dies at dispatch.

    This seems consistent with the issue's current RCA and also broadens the repro beyond MiniMax/llama.cpp to an OpenAI-compatible local gateway (9Router) on Codex CLI 0.147.0.

  4. madmax24-ubuntu commented on Aug 18, 2026

    @madmax24-ubuntu

    For the llama.cpp/custom-Responses side of this class of failure, I published a compatibility bridge here: https://github.com/madmax24-ubuntu/codex-proxy-llama.cpp

    The important part is that it does not only flatten mcp__... tools on the request. It also restores the namespace/name mapping on returned function calls before Codex sees them, which avoids the unsupported call routing failure for regular MCP tools in local-model sessions.

    It is currently tested with Codex + llama.cpp + Qwen and also bridges native apply_patch, tool-history replay, SSE framing, reasoning events, and compaction. This will not fix Apps SDK widget rendering semantics by itself, but it may help people hitting the same dispatcher failure with ordinary MCP tools under a custom Responses provider.

  5. madmax24-ubuntu commented on Aug 23, 2026

    @madmax24-ubuntu

    Thanks for reproducing this — that matches what I was seeing on my side.

    I also have a working proxy implementation that may be useful for debugging or as a possible reference for the per-provider flattening approach:

    https://github.com/madmax24-ubuntu/codex-proxy-llama.cpp

    The main relevant part is flattening Codex/MCP namespace tools into regular function-style tools for llama.cpp, then mapping the tool calls back on the Responses API side.

    I built this specifically to make Codex work reliably with local llama.cpp backends. It may be useful to compare the tool transformation there with what is currently happening in the Ollama/llama.cpp provider path.

    Thanks again for digging into this!

  6. mikkihugo commented on Sep 8, 2026

    @mikkihugo

    Additional repro (2026-09-08): codex-cli 0.154.0-alpha.3 (npm), custom provider against a self-hosted OpenAI-compatible gateway (CentralCloud llm-gateway, wire_api = "responses"), MCP server via streamable HTTP (http://mcp-gateway.svc/mcp, a Go router advertising 88 tools).

    Same signature: MCP startup + tools/list succeed (the model sees the mcp____ names in its tool list), the MCP server itself is healthy (direct curl initialize + tools/list + tools/call all work, protocol 2025-11-25), but every dispatch fails with error=unsupported call: mcp__<server>__<tool> from codex_core::tools::router — before the request reaches the MCP server. Affects all gateway tools including the routed mcp_tool_call. No chat-completions fallback available on this gateway (only /responses). Happy to provide more logs or test a patch.

  7. NghiaNguyenTT commented on Sep 24, 2026

    @NghiaNguyenTT

    Fixed this end-to-end on Windows 11 with a company gateway (LiteLLM-style Responses API in front of Qwen chat-completions models). Sharing the recipe — no code changes needed, the fix is entirely in which hop you put between Codex and the gateway.

    Tested: Codex CLI 0.158.0-alpha.3 · ChatGPT Desktop 26.917.62051 · CC Switch 3.20.4

    Root cause (matches @ericyanpek's datapoint in #20652)

    The gateway's Responses shim returns function_call items without the namespace field (or flattened into name). Codex's router does strict ToolName{namespace, name} matching → unsupported call: mcp__…. Pointing base_url directly at any gateway that mutates the Responses function-call shape fails the same way, regardless of MCP server type.

    Fix: let a responses→chat bridge flatten AND restore the mapping

    CC Switch's built-in local proxy does exactly what @madmax24-ubuntu's bridge does — flattens namespaced tools on the request and re-maps function_call items back on the response. The key is routing through the proxy, not the gateway, and letting it own auth:

    model_provider = "custom"
    model = "your-model"
    model_catalog_json = "cc-switch-model-catalog.json"   # relative to ~/.codex
    web_search = "disabled"                              # CRITICAL — hosted web_search_20250305
                                                         # schema 400s on every gateway we tried
    
    [model_providers.custom]
    name = "MyGateway"
    wire_api = "responses"
    requires_openai_auth = false                         # CRITICAL — otherwise the proxy's
                                                         # auth handoff silently breaks dispatch
    base_url = "http://127.0.0.1:15721/v1"               # CRITICAL — CC Switch proxy, NOT the gateway
    experimental_bearer_token = "PROXY_MANAGED"          # CRITICAL — proxy owns the real key
    supports_standalone_web_search = false

    Two gotchas that cost me a day

    1. web_search = "disabled" is not optional for gateways — the built-in hosted tool is a web_search_20250305 schema and third-party gateways reject it (tools[...].input_schema Field required), which kills the whole request before any MCP tool can land.
    2. In the model catalog, supports_search_tool and [features] tool_search = true must move together. true in the catalog without the feature = every mcp__* tool silently vanishes from the session (this is the "tools disappear" symptom in Codex Desktop starts configured MCP server successfully, but does not expose its tools to the model #21881 / Codex App shows MCP servers enabled, but current session only receives a subset of MCP tools #21654, so it may explain several of those reports). With both flags on, MCP tool defs are deferred behind tool_search and discovered on demand — measured 115k → 45k tokens for a trivial Jira lookup on a Qwen-class backend.

    Verify

    # in a TRUSTED project dir (untrusted dirs show a different tool list)
    codex exec --skip-git-repo-check "Call mcp__<server>__<tool> … and report the raw output"
    # Windows: cmd /c 'codex exec --skip-git-repo-check "…" < NUL'

    After changing anything: fully quit + relaunch the Desktop app (the embedded app-server keeps the old provider/session config alive — restarting the window is not enough).

    If the fix sticks, the remaining upstream ask is what #20652 already proposes: have the router un-flatten mcp__<server>__<tool> (and tolerate a missing namespace) instead of requiring a middlebox to restore shape that Codex itself defined.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    appIssues related to the Codex desktop appbugSomething isn't workingcustom-modelIssues related to custom model providers (including local models)mcpIssues related to the use of model context protocol (MCP) serverstool-callsIssues related to tool calling

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions