MCP tools wrapped as "namespace" tool type are ignored by non-OpenAI endpoints

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

What version of Codex CLI is running?

codex-cli 0.144.4 (pnpm install, linux-x64)

What subscription do you have?

N/A — using a third-party OpenAI-compatible endpoint (not an OpenAI plan).

Which model were you using?

deepseek-v4-pro via an OpenAI-compatible endpoint with wire_api = "responses"

What platform is your computer?

Linux 7.1.3-200.fc44.x86_64 x86_64 unknown

What terminal emulator and version are you using (if applicable)?

Ghostty (not relevant to the bug).

Codex doctor report

{
  "schemaVersion": 1,
  "generatedAt": "1784093430s since unix epoch",
  "overallStatus": "ok",
  "codexVersion": "0.144.4",
  "checks": {
    "app_server.status": {
      "id": "app_server.status",
      "category": "app-server",
      "status": "ok",
      "summary": "background server is running",
      "details": {
        "app-server version": "0.144.4",
        "control socket": "/home/johnlin/.codex/app-server-control/app-server-control.sock",
        "daemon state dir": "/home/johnlin/.codex/app-server-daemon",
        "mode": "ephemeral",
        "pid file": "/home/johnlin/.codex/app-server-daemon/app-server.pid (missing)",
        "settings": "/home/johnlin/.codex/app-server-daemon/settings.json (missing)",
        "status": "running",
        "update-loop pid file": "/home/johnlin/.codex/app-server-daemon/app-server-updater.pid (missing)"
      },
      "remediation": null,
      "durationMs": 22
    },
    "auth.credentials": {
      "id": "auth.credentials",
      "category": "auth",
      "status": "ok",
      "summary": "OpenAI auth is not required for the active model provider",
      "details": {
        "auth file": "/home/johnlin/.codex/auth.json",
        "auth storage mode": "File",
        "model provider requires OpenAI auth": "false"
      },
      "remediation": null,
      "durationMs": 0
    },
    "config.load": {
      "id": "config.load",
      "category": "config",
      "status": "ok",
      "summary": "config loaded",
      "details": {
        "CODEX_HOME": "/home/johnlin/.codex",
        "config.toml": "/home/johnlin/.codex/config.toml",
        "config.toml parse": "ok",
        "cwd": "/home/johnlin/Dev",
        "enabled feature flags": "shell_tool, unified_exec, shell_snapshot, code_mode_host, terminal_resize_reflow, sqlite, hooks, enable_request_compression, multi_agent, apps, tool_search_always_defer_mcp_tools, tool_suggest, plugins, in_app_browser, browser_use, browser_use_full_cdp_access, browser_use_external, computer_use, remote_plugin, plugin_sharing, image_generation, resize_all_images, skill_mcp_dependency_install, mentions_v2, steer, guardian_approval, goals, collaboration_modes, tool_call_mcp_elicitation, auth_elicitation, personality, fast_mode, tui_app_server, remote_compaction_v2, workspace_dependencies",
        "feature flag overrides": "none",
        "feature flags enabled": "35",
        "log dir": "/home/johnlin/.codex/log",
        "mcp servers": "1",
        "model": "deepseek-v4-pro",
        "model provider": "openai_https",
        "sqlite home": "/home/johnlin/.codex"
      },
      "remediation": null,
      "durationMs": 0
    },
    "git.environment": {
      "id": "git.environment",
      "category": "git",
      "status": "ok",
      "summary": "git version 2.55.0",
      "details": {
        "PATH git #1": "/usr/bin/git",
        "PATH git #2": "/bin/git",
        "PATH git entries": "2",
        "git build options": "git version 2.55.0; cpu: x86_64; no commit associated with this build; sizeof-long: 8; sizeof-size_t: 8; shell-path: /bin/sh; rust: disabled; feature: fsmonitor--daemon; gettext: enabled; libcurl: 8.18.0; OpenSSL: OpenSSL 3.5.7 9 Jun 2026; zlib-ng: 2.3.3; SHA-1: SHA1_DC; SHA-256: SHA256_BLK; default-ref-format: files; default-hash: sha1",
        "git exec path": "/usr/libexec/git-core",
        "git version": "git version 2.55.0",
        "repo detected": "false",
        "selected git": "/usr/bin/git"
      },
      "remediation": null,
      "durationMs": 14
    },
    "installation": {
      "id": "installation",
      "category": "install",
      "status": "ok",
      "summary": "installation looks consistent",
      "details": {
        "PATH codex #1": "/home/johnlin/.local/share/pnpm/bin/codex",
        "current executable": "/home/johnlin/.local/share/pnpm/store/v11/links/@openai/codex/0.144.4-linux-x64/e2f4350a54440b272fea7a52f370f55f3b7455586f2cd0233874d5f3cdd99864/node_modules/@openai/codex/vendor/x86_64-unknown-linux-musl/bin/codex",
        "install context": "pnpm (package /home/johnlin/.local/share/pnpm/store/v11/links/@openai/codex/0.144.4-linux-x64/e2f4350a54440b272fea7a52f370f55f3b7455586f2cd0233874d5f3cdd99864/node_modules/@openai/codex/vendor/x86_64-unknown-linux-musl, bin /home/johnlin/.local/share/pnpm/store/v11/links/@openai/codex/0.144.4-linux-x64/e2f4350a54440b272fea7a52f370f55f3b7455586f2cd0233874d5f3cdd99864/node_modules/@openai/codex/vendor/x86_64-unknown-linux-musl/bin, resources /home/johnlin/.local/share/pnpm/store/v11/links/@openai/codex/0.144.4-linux-x64/e2f4350a54440b272fea7a52f370f55f3b7455586f2cd0233874d5f3cdd99864/node_modules/@openai/codex/vendor/x86_64-unknown-linux-musl/codex-resources, path /home/johnlin/.local/share/pnpm/store/v11/links/@openai/codex/0.144.4-linux-x64/e2f4350a54440b272fea7a52f370f55f3b7455586f2cd0233874d5f3cdd99864/node_modules/@openai/codex/vendor/x86_64-unknown-linux-musl/codex-path)",
        "managed by bun": "false",
        "managed by npm": "false",
        "managed by pnpm": "true",
        "managed package root": "/home/johnlin/.local/share/pnpm/store/v11/links/@openai/codex/0.144.4/1242d2d9b5029e994a46ca69cecef51530e5ff5c0b6064d5ed75850443bdcd45/node_modules/@openai/codex"
      },
      "remediation": null,
      "durationMs": 0
    },
    "mcp.config": {
      "id": "mcp.config",
      "category": "mcp",
      "status": "ok",
      "summary": "MCP configuration is locally consistent",
      "details": {
        "configured servers": "1",
        "disabled servers": "0",
        "stdio servers": "1"
      },
      "remediation": null,
      "durationMs": 0
    },
    "network.env": {
      "id": "network.env",
      "category": "network",
      "status": "ok",
      "summary": "network-related environment looks readable",
      "details": {
        "proxy env vars": "none"
      },
      "remediation": null,
      "durationMs": 0
    },
    "network.provider_reachability": {
      "id": "network.provider_reachability",
      "category": "reachability",
      "status": "ok",
      "summary": "active provider endpoints are reachable over HTTP",
      "details": {
        "openai_https API base URL": "http://<endpoint-host>:<endpoint-port>/v1 reachable (HTTP 404)",
        "openai_https API route probe": "http://<endpoint-host>:<endpoint-port>/v1/<redacted> route exists (HTTP 200)",
        "reachability mode": "provider auth"
      },
      "remediation": null,
      "durationMs": 361
    },
    "network.websocket_reachability": {
      "id": "network.websocket_reachability",
      "category": "websocket",
      "status": "ok",
      "summary": "Responses WebSocket is not enabled for the active provider",
      "details": {
        "model provider": "openai_https",
        "provider name": "OpenAI",
        "proxy env vars": "none",
        "supports websockets": "false",
        "wire API": "responses"
      },
      "remediation": null,
      "durationMs": 0
    },
    "runtime.provenance": {
      "id": "runtime.provenance",
      "category": "runtime",
      "status": "ok",
      "summary": "running pnpm on linux-x86_64",
      "details": {
        "commit": "unknown",
        "current executable": "/home/johnlin/.local/share/pnpm/store/v11/links/@openai/codex/0.144.4-linux-x64/e2f4350a54440b272fea7a52f370f55f3b7455586f2cd0233874d5f3cdd99864/node_modules/@openai/codex/vendor/x86_64-unknown-linux-musl/bin/codex",
        "install method": "pnpm (package /home/johnlin/.local/share/pnpm/store/v11/links/@openai/codex/0.144.4-linux-x64/e2f4350a54440b272fea7a52f370f55f3b7455586f2cd0233874d5f3cdd99864/node_modules/@openai/codex/vendor/x86_64-unknown-linux-musl, bin /home/johnlin/.local/share/pnpm/store/v11/links/@openai/codex/0.144.4-linux-x64/e2f4350a54440b272fea7a52f370f55f3b7455586f2cd0233874d5f3cdd99864/node_modules/@openai/codex/vendor/x86_64-unknown-linux-musl/bin, resources /home/johnlin/.local/share/pnpm/store/v11/links/@openai/codex/0.144.4-linux-x64/e2f4350a54440b272fea7a52f370f55f3b7455586f2cd0233874d5f3cdd99864/node_modules/@openai/codex/vendor/x86_64-unknown-linux-musl/codex-resources, path /home/johnlin/.local/share/pnpm/store/v11/links/@openai/codex/0.144.4-linux-x64/e2f4350a54440b272fea7a52f370f55f3b7455586f2cd0233874d5f3cdd99864/node_modules/@openai/codex/vendor/x86_64-unknown-linux-musl/codex-path)",
        "platform": "linux-x86_64",
        "version": "0.144.4"
      },
      "remediation": null,
      "durationMs": 0
    },
    "runtime.search": {
      "id": "runtime.search",
      "category": "search",
      "status": "ok",
      "summary": "search is OK (bundled)",
      "details": {
        "search command": "/home/johnlin/.local/share/pnpm/store/v11/links/@openai/codex/0.144.4-linux-x64/e2f4350a54440b272fea7a52f370f55f3b7455586f2cd0233874d5f3cdd99864/node_modules/@openai/codex/vendor/x86_64-unknown-linux-musl/codex-path/rg",
        "search command readiness": "file exists",
        "search provider": "bundled"
      },
      "remediation": null,
      "durationMs": 0
    },
    "sandbox.helpers": {
      "id": "sandbox.helpers",
      "category": "sandbox",
      "status": "ok",
      "summary": "sandbox configuration is readable",
      "details": {
        "approval policy": "Never",
        "codex-linux-sandbox helper": "/home/johnlin/.codex/tmp/arg0/codex-arg0okULxf/codex-linux-sandbox",
        "execve wrapper helper": "/home/johnlin/.codex/tmp/arg0/codex-arg0okULxf/codex-execve-wrapper",
        "filesystem sandbox": "unrestricted",
        "network sandbox": "enabled"
      },
      "remediation": null,
      "durationMs": 0
    },
    "state.paths": {
      "id": "state.paths",
      "category": "state",
      "status": "ok",
      "summary": "state paths and databases are inspectable",
      "details": {
        "CODEX_HOME": "/home/johnlin/.codex (dir)",
        "active rollout files": "54 files, 10921537 total bytes, 202250 average bytes",
        "archived rollout files": "0 files, 0 total bytes, 0 average bytes",
        "goals DB": "/home/johnlin/.codex/goals_1.sqlite (file)",
        "goals DB integrity": "ok",
        "log DB": "/home/johnlin/.codex/logs_2.sqlite (file)",
        "log DB integrity": "ok",
        "log dir": "/home/johnlin/.codex/log (missing)",
        "memories DB": "/home/johnlin/.codex/memories_1.sqlite (file)",
        "memories DB integrity": "ok",
        "sqlite home": "/home/johnlin/.codex (dir)",
        "state DB": "/home/johnlin/.codex/state_5.sqlite (file)",
        "state DB integrity": "ok"
      },
      "remediation": null,
      "durationMs": 67
    },
    "state.rollout_db_parity": {
      "id": "state.rollout_db_parity",
      "category": "threads",
      "status": "ok",
      "summary": "rollout files and state DB thread inventory agree",
      "details": {
        "default model provider": "openai_https",
        "rollout DB active files": "54",
        "rollout DB active rows": "54",
        "rollout DB archive mismatches": "0",
        "rollout DB archived files": "0",
        "rollout DB archived rows": "0",
        "rollout DB duplicate DB paths": "0",
        "rollout DB duplicate rollout thread ids": "0",
        "rollout DB malformed file names": "0",
        "rollout DB missing active rows": "0",
        "rollout DB missing archived rows": "0",
        "rollout DB model providers": "openai_https=54",
        "rollout DB rows": "54",
        "rollout DB scan cap reached": "false",
        "rollout DB scan errors": "0",
        "rollout DB sources": "exec=24, cli=21, vscode=9",
        "rollout DB stale rows": "0"
      },
      "remediation": null,
      "durationMs": 127
    },
    "system.environment": {
      "id": "system.environment",
      "category": "system",
      "status": "ok",
      "summary": "OS language zh-CN",
      "details": {
        "EDITOR": "set",
        "LANG": "zh_CN.UTF-8",
        "VISUAL": "not set",
        "os": "Fedora 44.0.0 [64-bit]",
        "os language": "zh-CN",
        "os type": "Fedora",
        "os version": "44.0.0"
      },
      "remediation": null,
      "durationMs": 4
    },
    "terminal.env": {
      "id": "terminal.env",
      "category": "terminal",
      "status": "ok",
      "summary": "terminal metadata was detected",
      "details": {
        "COLORTERM": "truecolor",
        "DISPLAY": "present",
        "TERMINFO": "/usr/share/terminfo (dir)",
        "TERM_PROGRAM": "ghostty",
        "WAYLAND_DISPLAY": "present",
        "color output": "enabled",
        "effective locale": "zh_CN.UTF-8",
        "stderr is terminal": "true",
        "stdin is terminal": "true",
        "stdout is terminal": "true",
        "terminal": "Ghostty",
        "terminal size": "179x46",
        "terminal version": "1.3.1-2.fc44"
      },
      "remediation": null,
      "durationMs": 0
    },
    "terminal.title": {
      "id": "terminal.title",
      "category": "title",
      "status": "ok",
      "summary": "terminal title default",
      "details": {
        "terminal title activity": "true",
        "terminal title items": "activity, project-name",
        "terminal title project source": "cwd",
        "terminal title project value": "Dev",
        "terminal title source": "default"
      },
      "remediation": null,
      "durationMs": 0
    },
    "updates.status": {
      "id": "updates.status",
      "category": "updates",
      "status": "ok",
      "summary": "update configuration is locally consistent",
      "details": {
        "cached latest version": "0.144.4",
        "check for update on startup": "true",
        "dismissed version": "0.144.4",
        "last checked at": "2026-07-15T05:28:03.442999221Z",
        "latest version": "0.144.4",
        "latest version status": "current version is not older",
        "update action": "pnpm add -g @openai/codex",
        "version cache": "/home/johnlin/.codex/version.json"
      },
      "remediation": null,
      "durationMs": 916
    }
  }
}

What issue are you seeing?

MCP server tools are invisible to the model when using a non-OpenAI Responses API endpoint. codex packages each MCP server's tools into a single OpenAI Responses API namespace tool ({"type":"namespace","name":"mcp__<server>","tools":[{"type":"function",...}]}). Non-OpenAI-compatible endpoints only accept {"type":"function"} tools and silently ignore the namespace container, so the model never sees any of the MCP server's tools — only the built-in tools (exec_command, write_stdin, list_mcp_resources, list_mcp_resource_templates, read_mcp_resource, update_plan, view_image, update_goal, …).

What steps can reproduce the bug?

  1. Configure codex with a non-OpenAI OpenAI-compatible endpoint (e.g. deepseek-v4-pro via a third-party proxy, wire_api = "responses") and any stdio MCP server that exposes tools (e.g. a local MCP server with tools/list returning N tools).
  2. Run: codex exec "List the exact names of all tools available to you, one per line."
  3. Observe the model lists only built-in tools. None of the MCP server's tools appear.
  4. RUST_LOG=trace codex exec "hi" shows the MCP tools are fetched: listed MCP server tools server_name=<server> tool_count=N (e.g. 33), but build_model_visible reports them merged into a namespace container (e.g. final specs=14 namespace=2) that the endpoint ignores.

Repro summary: codex successfully fetches the MCP server's tools (the MCP/stdio layer is fine), but they are serialized as a namespace tool type that the endpoint doesn't understand, so they never reach the model.

What is the expected behavior?

MCP server tools should be callable by the model on any Responses-API-compatible endpoint. When the endpoint doesn't support the namespace tool type, codex should degrade gracefully — either emit the MCP tools as plain {"type":"function"} tools, or expose a way to opt out of the namespace grouping.

Additional information

Root cause (codex-rs, current main):

  • core/src/tools/handlers/mcp.rs create_tool_spec wraps every MCP tool in ToolSpec::Namespace(ResponsesApiNamespace{...}), which serializes to {"type":"namespace",...} (an OpenAI Responses API tool type; tools/src/responses_api.rs, tool_spec.rs).
  • merge_into_namespaces (core/src/tools/spec_plan.rs) coalesces same-namespace tools into one container (intended for prompt-size / grouping optimization).
  • The namespace_tools provider capability (model-provider/src/provider.rs) looks like the intended escape hatch, but when false the namespace tools are filtered out entirely (spec_plan.rs: namespace_tools_enabled || !Namespace) rather than expanded — so the model still can't use them. Note: every provider currently defaults to namespace_tools = true (including Bedrock, which only disables image_generation/web_search), so the false branch is never exercised in practice — the escape hatch exists but is not wired up to a working degradation path.
  • Non-OpenAI endpoints don't implement the namespace tool type (nor the defer_loading field), so the container is silently dropped.

Proposed fix :

Extend the namespace_tools = false path to expand namespace containers into individual {"type":"function"} tools (instead of dropping them), plus a dispatch fallback in ToolRegistry::tool (core/src/tools/registry.rs) so tool calls arriving with namespace=None (plain functions carry no namespace field; build_tool_call in router.rs reads ToolName::new(namespace, name)) still match the registered McpHandler by name. The OpenAI path (namespace_tools = true) stays unchanged, preserving the namespace/grouping + deferred-loading optimization. A provider that doesn't support the namespace tool type would declare namespace_tools = false.

I've validated this direction with a patched build: on a deepseek-v4-pro endpoint, the model lists and successfully calls MCP tools (e.g. search_semantic + entry_get returning real results). Happy to open a PR by invitation if this aligns with the team's intended direction. Pointers to the relevant code are above; happy to refine the approach based on maintainer feedback.

View original on GitHub ↗

4 Comments

github-actions[bot] contributor · 1 month ago

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

  • #32318

Powered by Codex Action

mengyu-arch · 1 month ago

Workaround: Use Claude Code's MCP server for web search when using a custom provider

For those using Codex with a custom model provider (e.g., DeepSeek via wire_api = "responses"), the built-in web_search tool is unavailable and MCP tools wrapped as namespace type are ignored by non-OpenAI endpoints.

If you have Claude Code installed, there's a working workaround:

  1. Claude Code's claude mcp serve exposes WebSearch and WebFetch tools via stdio MCP (plus 25 other tools)
  2. Call it from any Codex session by spawning a subprocess and communicating via MCP stdio protocol
  3. Works with any Codex model provider since it runs as a local subprocess, not through Codex's MCP infrastructure

Example usage:

import subprocess, json

claude = r"path/to/claude.exe"
proc = subprocess.Popen([claude, "mcp", "serve"], stdin=..., stdout=..., text=True)
# MCP handshake -> tools/call WebSearch -> get results

Or use a quick wrapper script that handles the MCP handshake automatically:

python web_search.py "your search query"

This approach bypasses the namespace tool type issue entirely since we communicate with Claude Code's MCP directly via stdio, not through Codex's tool routing layer.

metrofun · 18 days ago

A local proxy can work around this without patching Codex.

I tested it by flattening Codex's MCP namespace tools into ordinary function tools before sending them to the backend, then restoring the namespace on the returned call.

So the backend sees:

{"type":"function","name":"search", ...}

while Codex gets back:

{"type":"function_call","name":"search","namespace":"mcp__open_websearch__","arguments":"{...}"}

The previously failing request completes successfully.

Tested with:

  • Responses → Responses
  • Responses → Chat Completions

swobuforge/swobu now does this translation automatically.

curl -fsSL https://swobu.com/install.sh | sh

Point Codex at Swobu and retry.

summerflowersstudio-tw · 15 days ago

I can reproduce this on the latest Codex Desktop app.

Environment:

  • macOS
  • Codex Desktop 26.803.61601
  • Local model: Qwen3.6-35B-A3B(ollama)
  • MCP server: Brave Search MCP

The Brave MCP server starts correctly, and all Brave tools are visible in the Codex tool list.

The important test is:

  • GPT-5.6 + the exact same Brave MCP configuration → works
  • Qwen3.6-35B-A3B + the exact same configuration → unsupported call

The MCP server and API key therefore appear to be working correctly. The failure only occurs when using the local/custom model provider.

This seems consistent with the namespace-tool compatibility issue described here.

I hope this additional reproduction helps confirm the issue on the Desktop app.