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

Open 💬 1 comment Opened Jul 15, 2026 by Lin-Jiong-HDU

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 ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗