diff --git a/src/CodexAcpClient.ts b/src/CodexAcpClient.ts index 7040057f..05c5b5b9 100644 --- a/src/CodexAcpClient.ts +++ b/src/CodexAcpClient.ts @@ -773,9 +773,16 @@ export class CodexAcpClient { return configWithWorkspaceRoots; } + const configuredMcpServers = isJsonObject(configWithWorkspaceRoots["mcp_servers"]) + ? configWithWorkspaceRoots["mcp_servers"] + : {}; + return { ...configWithWorkspaceRoots, - "mcp_servers": Object.fromEntries(serversToConfigure.map(mcp => [mcp.name, this.createMcpSeverConfig(mcp.server)])), + "mcp_servers": { + ...configuredMcpServers, + ...Object.fromEntries(serversToConfigure.map(mcp => [mcp.name, this.createMcpSeverConfig(mcp.server)])), + }, }; } diff --git a/src/__tests__/CodexACPAgent/CodexAcpClient.test.ts b/src/__tests__/CodexACPAgent/CodexAcpClient.test.ts index fe51f160..0a3d1c6f 100644 --- a/src/__tests__/CodexACPAgent/CodexAcpClient.test.ts +++ b/src/__tests__/CodexACPAgent/CodexAcpClient.test.ts @@ -943,6 +943,58 @@ describe('ACP server test', { timeout: 40_000 }, () => { }); }); + it('preserves MCP servers from CODEX_CONFIG when ACP adds another MCP server', async () => { + const mockFixture = createCodexMockTestFixture(); + const codexAcpClient = mockFixture.getCodexAcpClient(); + const codexAppServerClient = mockFixture.getCodexAppServerClient(); + + (codexAcpClient as unknown as {config: Record}).config = { + mcp_servers: { + node_repl: { + env: { + HTTPS_PROXY: "http://127.0.0.1:7890", + }, + }, + }, + }; + vi.spyOn(codexAppServerClient, "listSkills").mockResolvedValue({data: []}); + vi.spyOn(codexAppServerClient, "configRead").mockResolvedValue({config: {}} as any); + const threadStartSpy = vi.spyOn(codexAppServerClient, "threadStart").mockResolvedValue({ + thread: {id: "thread-id"} as any, + model: "gpt-5", + reasoningEffort: "medium", + serviceTier: null, + } as any); + vi.spyOn(codexAppServerClient, "listModels").mockResolvedValue({ + data: [createTestModel({id: "gpt-5"})], + nextCursor: null, + }); + + await codexAcpClient.newSession({ + cwd: "/workspace", + mcpServers: [{ + name: "docs", + command: "npx", + args: ["docs-mcp"], + env: [{name: "DOCS_TOKEN", value: "token"}], + }], + }); + + const threadStartRequest = threadStartSpy.mock.calls[0]![0]; + expect(threadStartRequest.config?.["mcp_servers"]).toEqual({ + node_repl: { + env: { + HTTPS_PROXY: "http://127.0.0.1:7890", + }, + }, + docs: { + command: "npx", + args: ["docs-mcp"], + env: {DOCS_TOKEN: "token"}, + }, + }); + }); + it('waits for typed mcp startup status updates and returns terminal states', async () => { const mockFixture = createCodexMockTestFixture(); const codexAcpClient = mockFixture.getCodexAcpClient();