mirror of
https://github.com/ruvnet/ruflo.git
synced 2026-10-23 03:47:00 +09:00
fix(bridge): retire unsupported Codex MCP backend
This commit is contained in:
1 parent
b14c79e6f7
commit
a356350815
6 files changed
+100
-69
No files matched your search
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
}
|
||||
@@ -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 ─────────────────
|
||||
|
||||
Reference in new issue
Block a user