fix(bridge): retire unsupported Codex MCP backend

This commit is contained in:
Rudy Celekli committed 2026-09-28 09:40:35 -04:00
1 parent b14c79e6f7
commit a356350815
6 files changed
+100 -69

No files matched your search

+11 -13
View File
@@ -1,4 +1,4 @@
# ADR-034: Optional MCP Backends — Claude Code, Gemini, Codex
# ADR-034: Optional MCP Backends — Claude Code and Gemini
**Status:** Accepted
**Date:** 2026-03-05
@@ -16,7 +16,7 @@ These require their own API keys and have different resource profiles, so they s
## Decision
Add three optional MCP backends that can be enabled via environment variables. Unlike ruvector/ruflo (enabled by default), these are **disabled by default** and require explicit API keys.
Claude Code and Gemini remain optional MCP backends. Codex was originally listed here, but current Codex CLI has no MCP server mode. As of #3509, the bridge leaves that group disabled and warns when `MCP_GROUP_CODEX=true` is requested. Ruflo's separate Codex dual-mode workers use `codex exec`; a future MCP adapter needs a separate execution and security design.
### Backend Configuration
@@ -26,7 +26,7 @@ Add three optional MCP backends that can be enabled via environment variables. U
| ruflo | `ENABLE_RUFLO` | None | `npx ruflo mcp start` | **enabled** |
| Claude Code | `ENABLE_CLAUDE_CODE` | `ANTHROPIC_API_KEY` | `claude mcp serve` | disabled |
| Gemini MCP | `ENABLE_GEMINI_MCP` | `GOOGLE_API_KEY` | `npx gemini-mcp-server` | disabled |
| Codex | `ENABLE_CODEX` | `OPENAI_API_KEY` | `npx @openai/codex mcp serve` | disabled |
| Codex | `MCP_GROUP_CODEX` | N/A | No supported MCP server mode | unavailable; opt-in warns |
### Architecture
@@ -37,7 +37,6 @@ ruvector__hooks_route → ruvector MCP
ruflo__agent_spawn → ruflo MCP
claude__Read → Claude Code MCP
gemini__chat → Gemini MCP
codex__execute → Codex MCP
```
```
@@ -52,10 +51,10 @@ codex__execute → Codex MCP
│ └─────────────┘ └──────────────┘ │
│ │
│ Optional backends (API key required): │
│ ┌──────────────┐ ┌───────────┐ ┌───────────────┐ │
│ │ Claude Code │ │ Gemini │ │ OpenAI Codex │ │
│ │ (opt-in) │ │ (opt-in) │ │ (opt-in) │ │
│ └──────────────┘ └───────────┘ └───────────────┘ │
│ ┌──────────────┐ ┌───────────┐ │
│ │ Claude Code │ │ Gemini │ │
│ │ (opt-in) │ │ (opt-in) │ │
│ └──────────────┘ └───────────┘ │
└───────────────────────────────────────────────────────┘
```
@@ -69,10 +68,10 @@ ANTHROPIC_API_KEY=sk-ant-...
ENABLE_GEMINI_MCP=true
GOOGLE_API_KEY=AIzaSy... # already set for Gemini models
ENABLE_CODEX=true
OPENAI_API_KEY=sk-... # already set for OpenAI models
```
`MCP_GROUP_CODEX=true` is not an enablement path: it produces an explicit warning and does not start a Codex child process.
### Security Considerations
1. **API keys stay server-side** — keys are only in the bridge container's env vars, never exposed to the browser
@@ -88,14 +87,13 @@ OPENAI_API_KEY=sk-... # already set for OpenAI models
| ruflo | ~50MB | Low | ~5s |
| Claude Code | ~100MB | Medium | ~5s |
| Gemini MCP | ~40MB | Low | ~4s |
| Codex | ~80MB | Medium | ~5s |
With all 5 backends enabled, the bridge container needs ~800MB memory.
Resource estimates for the remaining backends require current deployment measurement.
## Consequences
### Positive
- Users can access Claude, Gemini, and Codex capabilities directly from HF Chat UI
- Users can access Claude and Gemini capabilities directly from HF Chat UI
- Single `/mcp` endpoint — no client-side config changes
- Opt-in model keeps default resource usage low
- API keys shared with the chat proxy (no additional secrets needed for Gemini/OpenAI)
+8 -17
View File
@@ -106,14 +106,18 @@ const TOOL_GROUPS = {
source: "gemini-mcp",
},
// --- OpenAI Codex ---
// --- OpenAI Codex (MCP server mode removed from current CLI; #3509) ---
codex: {
enabled: process.env.MCP_GROUP_CODEX === "true",
description: "OpenAI Codex coding agent — code generation and execution (requires OPENAI_API_KEY)",
enabled: false,
description: "Unavailable: Codex CLI has no MCP server mode. Use Codex dual-mode workers instead.",
source: "codex",
},
};
if (process.env.MCP_GROUP_CODEX === "true") {
console.warn("[codex] Codex CLI has no MCP server mode; MCP_GROUP_CODEX is disabled. Use Codex dual-mode workers (codex exec) instead.");
}
// =============================================================================
// STDIO MCP CLIENT — Connects to external MCP servers via child process
// =============================================================================
@@ -257,7 +261,6 @@ const BACKEND_DEFS = [
{ name: "agentic-flow", command: "npx", args: ["-y", "agentic-flow@alpha", "mcp", "start"], groups: ["agentic-flow"] },
{ name: "claude", command: "claude", args: ["mcp", "serve"], groups: ["claude-code"] },
{ name: "gemini-mcp", command: "npx", args: ["-y", "gemini-mcp-server"], groups: ["gemini"] },
{ name: "codex", command: "npx", args: ["-y", "@openai/codex", "mcp-server"], groups: ["codex"] },
];
const mcpBackends = new Map();
@@ -602,19 +605,7 @@ Requires: GOOGLE_API_KEY environment variable (already set for Gemini models).
codex: `# Codex Group
OpenAI Codex coding agent.
Requires: OPENAI_API_KEY environment variable (already set for OpenAI models).
## Capabilities
- Code generation and execution
- Code completion
- Code explanation
## When to Use
- Code generation tasks
- Quick code completions
- Code explanation and documentation`,
Unavailable: current Codex CLI has no MCP server mode (#3509). This group is disabled even if MCP_GROUP_CODEX=true, so it exposes no tools. Use the separate Codex dual-mode worker through codex exec. A future MCP adapter requires its own execution and security design.`,
};
if (topic === "tool" && toolName) {
@@ -1,4 +1,4 @@
# ADR-034: Optional MCP Backends — Claude Code, Gemini, Codex
# ADR-034: Optional MCP Backends — Claude Code and Gemini
**Status:** Accepted
**Date:** 2026-03-05
@@ -16,7 +16,7 @@ These require their own API keys and have different resource profiles, so they s
## Decision
Add three optional MCP backends that can be enabled via environment variables. Unlike ruvector/ruflo (enabled by default), these are **disabled by default** and require explicit API keys.
Claude Code and Gemini remain optional MCP backends. Codex was originally listed here, but current Codex CLI has no MCP server mode. As of #3509, the bridge leaves that group disabled and warns when `MCP_GROUP_CODEX=true` is requested. Ruflo's separate Codex dual-mode workers use `codex exec`; a future MCP adapter needs a separate execution and security design.
### Backend Configuration
@@ -26,7 +26,7 @@ Add three optional MCP backends that can be enabled via environment variables. U
| ruflo | `ENABLE_RUFLO` | None | `npx ruflo mcp start` | **enabled** |
| Claude Code | `ENABLE_CLAUDE_CODE` | `ANTHROPIC_API_KEY` | `claude mcp serve` | disabled |
| Gemini MCP | `ENABLE_GEMINI_MCP` | `GOOGLE_API_KEY` | `npx gemini-mcp-server` | disabled |
| Codex | `ENABLE_CODEX` | `OPENAI_API_KEY` | `npx @openai/codex mcp serve` | disabled |
| Codex | `MCP_GROUP_CODEX` | N/A | No supported MCP server mode | unavailable; opt-in warns |
### Architecture
@@ -37,7 +37,6 @@ ruvector__hooks_route → ruvector MCP
ruflo__agent_spawn → ruflo MCP
claude__Read → Claude Code MCP
gemini__chat → Gemini MCP
codex__execute → Codex MCP
```
```
@@ -52,10 +51,10 @@ codex__execute → Codex MCP
│ └─────────────┘ └──────────────┘ │
│ │
│ Optional backends (API key required): │
│ ┌──────────────┐ ┌───────────┐ ┌───────────────┐ │
│ │ Claude Code │ │ Gemini │ │ OpenAI Codex │ │
│ │ (opt-in) │ │ (opt-in) │ │ (opt-in) │ │
│ └──────────────┘ └───────────┘ └───────────────┘ │
│ ┌──────────────┐ ┌───────────┐ │
│ │ Claude Code │ │ Gemini │ │
│ │ (opt-in) │ │ (opt-in) │ │
│ └──────────────┘ └───────────┘ │
└───────────────────────────────────────────────────────┘
```
@@ -69,10 +68,10 @@ ANTHROPIC_API_KEY=sk-ant-...
ENABLE_GEMINI_MCP=true
GOOGLE_API_KEY=AIzaSy... # already set for Gemini models
ENABLE_CODEX=true
OPENAI_API_KEY=sk-... # already set for OpenAI models
```
`MCP_GROUP_CODEX=true` is not an enablement path: it produces an explicit warning and does not start a Codex child process.
### Security Considerations
1. **API keys stay server-side** — keys are only in the bridge container's env vars, never exposed to the browser
@@ -88,14 +87,13 @@ OPENAI_API_KEY=sk-... # already set for OpenAI models
| ruflo | ~50MB | Low | ~5s |
| Claude Code | ~100MB | Medium | ~5s |
| Gemini MCP | ~40MB | Low | ~4s |
| Codex | ~80MB | Medium | ~5s |
With all 5 backends enabled, the bridge container needs ~800MB memory.
Resource estimates for the remaining backends require current deployment measurement.
## Consequences
### Positive
- Users can access Claude, Gemini, and Codex capabilities directly from HF Chat UI
- Users can access Claude and Gemini capabilities directly from HF Chat UI
- Single `/mcp` endpoint — no client-side config changes
- Opt-in model keeps default resource usage low
- API keys shared with the chat proxy (no additional secrets needed for Gemini/OpenAI)
+8 -17
View File
@@ -106,14 +106,18 @@ const TOOL_GROUPS = {
source: "gemini-mcp",
},
// --- OpenAI Codex ---
// --- OpenAI Codex (MCP server mode removed from current CLI; #3509) ---
codex: {
enabled: process.env.MCP_GROUP_CODEX === "true",
description: "OpenAI Codex coding agent — code generation and execution (requires OPENAI_API_KEY)",
enabled: false,
description: "Unavailable: Codex CLI has no MCP server mode. Use Codex dual-mode workers instead.",
source: "codex",
},
};
if (process.env.MCP_GROUP_CODEX === "true") {
console.warn("[codex] Codex CLI has no MCP server mode; MCP_GROUP_CODEX is disabled. Use Codex dual-mode workers (codex exec) instead.");
}
// =============================================================================
// STDIO MCP CLIENT — Connects to external MCP servers via child process
// =============================================================================
@@ -262,7 +266,6 @@ const BACKEND_DEFS = [
{ name: "agentic-flow", command: "npx", args: ["-y", "agentic-flow@alpha", "mcp", "start"], groups: ["agentic-flow"] },
{ name: "claude", command: "claude", args: ["mcp", "serve"], groups: ["claude-code"] },
{ name: "gemini-mcp", command: "npx", args: ["-y", "gemini-mcp-server"], groups: ["gemini"] },
{ name: "codex", command: "npx", args: ["-y", "@openai/codex", "mcp-server"], groups: ["codex"] },
];
const mcpBackends = new Map();
@@ -607,19 +610,7 @@ Requires: GOOGLE_API_KEY environment variable (already set for Gemini models).
codex: `# Codex Group
OpenAI Codex coding agent.
Requires: OPENAI_API_KEY environment variable (already set for OpenAI models).
## Capabilities
- Code generation and execution
- Code completion
- Code explanation
## When to Use
- Code generation tasks
- Quick code completions
- Code explanation and documentation`,
Unavailable: current Codex CLI has no MCP server mode (#3509). This group is disabled even if MCP_GROUP_CODEX=true, so it exposes no tools. Use the separate Codex dual-mode worker through codex exec. A future MCP adapter requires its own execution and security design.`,
};
if (topic === "tool" && toolName) {
@@ -0,0 +1,53 @@
import assert from 'node:assert/strict';
import { spawn } from 'node:child_process';
import { chmodSync, existsSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { fileURLToPath } from 'node:url';
import test from 'node:test';
const bridges = [
'../src/mcp-bridge/index.js',
'../src/ruvocal/mcp-bridge/index.js',
];
for (const relativePath of bridges) {
test(`${relativePath}: removed Codex MCP mode does not delay other groups`, async t => {
const dir = mkdtempSync(join(tmpdir(), 'ruflo-codex-mcp-'));
const marker = join(dir, 'npx-spawned');
const fakeNpx = join(dir, 'npx');
writeFileSync(fakeNpx, '#!/bin/sh\nprintf launched > "$RUFLO_CODEX_SPAWN_MARKER"\nexit 1\n');
chmodSync(fakeNpx, 0o755);
const child = spawn(process.execPath, [fileURLToPath(new URL(relativePath, import.meta.url))], {
env: {
...process.env,
PATH: `${dir}:${process.env.PATH ?? ''}`,
PORT: '0',
MCP_GROUP_INTELLIGENCE: 'false',
MCP_GROUP_AGENTS: 'false',
MCP_GROUP_MEMORY: 'false',
MCP_GROUP_DEVTOOLS: 'false',
MCP_GROUP_CODEX: 'true',
RUFLO_CODEX_SPAWN_MARKER: marker,
},
stdio: ['ignore', 'pipe', 'pipe'],
});
let stdout = '';
let stderr = '';
child.stdout.on('data', chunk => { stdout += chunk; });
child.stderr.on('data', chunk => { stderr += chunk; });
t.after(() => {
child.kill();
rmSync(dir, { recursive: true, force: true });
});
const deadline = Date.now() + 3000;
while (Date.now() < deadline && !stderr.includes('Codex CLI has no MCP server mode')) {
await new Promise(resolve => setTimeout(resolve, 25));
}
assert.match(stdout, /Active groups: core\s*$/m, `stdout: ${stdout}\nstderr: ${stderr}`);
assert.match(stderr, /Codex CLI has no MCP server mode/, stderr);
assert.equal(existsSync(marker), false, 'removed backend must not spawn npx');
});
}
+9 -9
View File
@@ -26,18 +26,18 @@ const section = (t) => console.log(`\n${t}`);
console.log('Codex ↔ Ruflo integration audit (#1909)\n' + '─'.repeat(48));
// ── 1. Codex MCP backend uses the real `mcp-server` subcommand ──────────────
section('MCP backend registration (`codex` group):');
// ── 1. The removed Codex MCP mode must not return as a dead backend ──────────
section('Codex MCP backend retirement (#3509):');
for (const p of ['ruflo/src/mcp-bridge/index.js', 'ruflo/src/ruvocal/mcp-bridge/index.js']) {
if (!existsSync(resolve(ROOT, p))) { fail(`${p}: file missing`); continue; }
const src = read(p);
const codexLine = (src.match(/.*@openai\/codex.*/g) ?? [])[0]?.trim() ?? '(no @openai/codex entry)';
check(/@openai\/codex"\s*,\s*"mcp-server"/.test(src),
`${p}: codex backend uses "mcp-server"`,
`${p}: codex backend must use "mcp-server" — found: ${codexLine}`);
check(!/@openai\/codex"\s*,\s*"mcp"\s*,\s*"serve"/.test(src),
`${p}: no invalid "mcp serve" subcommand`,
`${p}: still uses "mcp serve" (not a valid \`codex\` subcommand) — ${codexLine}`);
const codexGroup = src.match(/codex:\s*\{([^}]+)\}/)?.[1] ?? '';
check(/enabled:\s*false/.test(codexGroup),
`${p}: unsupported Codex MCP group stays disabled`);
check(!/\{\s*name:\s*"codex"\s*,\s*command:/.test(src),
`${p}: no backend launches the removed Codex MCP server`);
check(/MCP_GROUP_CODEX === "true"/.test(src) && /Codex CLI has no MCP server mode/.test(src),
`${p}: opt-in users receive a specific unsupported-mode diagnostic`);
}
// ── 2. @claude-flow/codex VERSION const tracks package.json ─────────────────