diff --git a/open-sse/executors/codex.ts b/open-sse/executors/codex.ts index fcbcbab1cd..294c4e6ba5 100644 --- a/open-sse/executors/codex.ts +++ b/open-sse/executors/codex.ts @@ -336,6 +336,20 @@ function normalizeCodexTools(body: Record): void { } const tool = toolValue as Record; + + // Preserve namespace tools (MCP tool groups used by Codex/OpenAI Responses API). + // Codex API supports them natively; register sub-tool names for tool_choice validation. + if (tool.type === "namespace" && Array.isArray(tool.tools)) { + for (const st of tool.tools as unknown[]) { + if (st && typeof st === "object" && !Array.isArray(st)) { + const subTool = st as Record; + const name = typeof subTool.name === "string" ? subTool.name.trim() : ""; + if (name) validToolNames.add(name); + } + } + return true; + } + if (tool.type !== "function") { return false; } diff --git a/open-sse/translator/request/openai-responses.ts b/open-sse/translator/request/openai-responses.ts index e4f3a4b4c8..5cd5da520d 100644 --- a/open-sse/translator/request/openai-responses.ts +++ b/open-sse/translator/request/openai-responses.ts @@ -55,12 +55,14 @@ export function openaiResponsesToOpenAIRequest( for (const toolValue of tools) { const tool = toRecord(toolValue); const toolType = toString(tool.type); - // Allow: function tools, tools already in Chat format (have .function property), and CLI subagent tools + // Allow: function tools, tools already in Chat format (have .function property), CLI subagent tools, + // and namespace tools (MCP tool groups used by Codex/OpenAI Responses API). if ( toolType && toolType !== "function" && toolType !== "custom" && toolType !== "command" && + toolType !== "namespace" && !tool.function ) { throw unsupportedFeature(