diff --git a/packages/agent/src/adapters/codex/codex-agent.ts b/packages/agent/src/adapters/codex/codex-agent.ts index f52194b3d..77b618a64 100644 --- a/packages/agent/src/adapters/codex/codex-agent.ts +++ b/packages/agent/src/adapters/codex/codex-agent.ts @@ -108,9 +108,15 @@ export class CodexAcpAgent extends BaseAcpAgent { super(client); this.logger = new Logger({ debug: true, prefix: "[CodexAcpAgent]" }); + // Load user codex settings before spawning so spawnCodexProcess can + // filter out any [mcp_servers.*] entries from ~/.codex/config.toml. + const cwd = options.codexProcessOptions.cwd ?? process.cwd(); + const settingsManager = new CodexSettingsManager(cwd); + // Spawn the codex-acp subprocess this.codexProcess = spawnCodexProcess({ ...options.codexProcessOptions, + settings: settingsManager.getSettings(), logger: this.logger, processCallbacks: options.processCallbacks, }); @@ -120,9 +126,6 @@ export class CodexAcpAgent extends BaseAcpAgent { const codexWritable = nodeWritableToWebWritable(this.codexProcess.stdin); const codexStream = ndJsonStream(codexWritable, codexReadable); - // Set up session with CodexSettingsManager - const cwd = options.codexProcessOptions.cwd ?? process.cwd(); - const settingsManager = new CodexSettingsManager(cwd); const abortController = new AbortController(); this.session = { abortController, diff --git a/packages/agent/src/adapters/codex/settings.ts b/packages/agent/src/adapters/codex/settings.ts index 7493df02e..5d1e0665c 100644 --- a/packages/agent/src/adapters/codex/settings.ts +++ b/packages/agent/src/adapters/codex/settings.ts @@ -13,6 +13,8 @@ export interface CodexSettings { personality?: string; modelReasoningEffort?: string; trustLevel?: string; + // Names of every `[mcp_servers.]` section declared in the user's config.toml + mcpServerNames: string[]; } /** @@ -24,32 +26,29 @@ export interface CodexSettings { */ export class CodexSettingsManager { private cwd: string; - private settings: CodexSettings = {}; - private initialized = false; + private settings: CodexSettings = { mcpServerNames: [] }; constructor(cwd: string) { this.cwd = cwd; + this.loadSettings(); } async initialize(): Promise { - if (this.initialized) { - return; - } - await this.loadSettings(); - this.initialized = true; + // No-op: settings are loaded in the constructor. Kept async to + // satisfy the BaseSettingsManager interface. } private getConfigPath(): string { return path.join(os.homedir(), ".codex", "config.toml"); } - private async loadSettings(): Promise { + private loadSettings(): void { const configPath = this.getConfigPath(); try { - const content = await fs.promises.readFile(configPath, "utf-8"); + const content = fs.readFileSync(configPath, "utf-8"); this.settings = parseCodexToml(content, this.cwd); } catch { - this.settings = {}; + this.settings = { mcpServerNames: [] }; } } @@ -62,17 +61,13 @@ export class CodexSettingsManager { } async setCwd(cwd: string): Promise { - if (this.cwd === cwd) { - return; - } - this.dispose(); + if (this.cwd === cwd) return; this.cwd = cwd; - this.initialized = false; - await this.initialize(); + this.loadSettings(); } dispose(): void { - this.initialized = false; + // No-op: no resources to release. Kept to satisfy the BaseSettingsManager interface. } } @@ -82,7 +77,8 @@ export class CodexSettingsManager { * Does NOT handle full TOML spec — only what codex config uses. */ function parseCodexToml(content: string, cwd: string): CodexSettings { - const settings: CodexSettings = {}; + const settings: CodexSettings = { mcpServerNames: [] }; + const mcpServerNames = new Set(); let currentSection = ""; for (const line of content.split("\n")) { @@ -93,6 +89,9 @@ function parseCodexToml(content: string, cwd: string): CodexSettings { const sectionMatch = trimmed.match(/^\[(.+)\]$/); if (sectionMatch) { currentSection = sectionMatch[1] ?? ""; + if (currentSection.startsWith("mcp_servers.")) { + mcpServerNames.add(currentSection.slice("mcp_servers.".length)); + } continue; } @@ -123,5 +122,6 @@ function parseCodexToml(content: string, cwd: string): CodexSettings { } } + settings.mcpServerNames = Array.from(mcpServerNames); return settings; } diff --git a/packages/agent/src/adapters/codex/spawn.ts b/packages/agent/src/adapters/codex/spawn.ts index 3bc0b53e6..fd4777572 100644 --- a/packages/agent/src/adapters/codex/spawn.ts +++ b/packages/agent/src/adapters/codex/spawn.ts @@ -4,6 +4,7 @@ import { delimiter, dirname } from "node:path"; import type { Readable, Writable } from "node:stream"; import type { ProcessSpawnedCallback } from "../../types"; import { Logger } from "../../utils/logger"; +import type { CodexSettings } from "./settings"; export interface CodexProcessOptions { cwd?: string; @@ -14,6 +15,7 @@ export interface CodexProcessOptions { binaryPath?: string; logger?: Logger; processCallbacks?: ProcessSpawnedCallback; + settings?: CodexSettings; } export interface CodexProcess { @@ -28,6 +30,13 @@ function buildConfigArgs(options: CodexProcessOptions): string[] { args.push("-c", `features.remote_models=false`); + // Disable the user's local MCPs one-by-one so Codex only uses the MCPs we + // provide via ACP. We can't use `-c mcp_servers={}` because that makes Codex + // ignore MCPs entirely, including the ones we inject later. + for (const name of options.settings?.mcpServerNames ?? []) { + args.push("-c", `mcp_servers.${name}.enabled=false`); + } + if (options.apiBaseUrl) { args.push("-c", `model_provider="posthog"`); args.push("-c", `model_providers.posthog.name="PostHog Gateway"`);