Skip to content

Commit 96e72d4

Browse files
authored
Allow disabling MCP conflict resolution (#207)
1 parent 7ac77be commit 96e72d4

2 files changed

Lines changed: 34 additions & 6 deletions

File tree

src/CodexAcpClient.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -326,21 +326,23 @@ export class CodexAcpClient {
326326
return configWithWorkspaceRoots;
327327
}
328328

329-
// Deduplicates new servers against existing config to prevent Codex from deep-merging
330-
// incompatible field types (e.g., mixing url and stdio schemas).
331-
const existingNames = await this.getConfigMcpServerNames(projectPath);
332329
const requestedServers = mcpServers.map(mcp => ({
333330
name: sanitizeMcpServerName(mcp.name),
334331
server: mcp,
335332
}));
336-
const uniqueServers = requestedServers.filter(mcp => !existingNames.has(mcp.name));
337-
if (uniqueServers.length === 0) {
333+
let serversToConfigure = requestedServers;
334+
if (shouldDeduplicateMcpConflicts()) {
335+
// Prevents Codex from deep-merging incompatible field types, such as url and stdio schemas.
336+
const existingNames = await this.getConfigMcpServerNames(projectPath);
337+
serversToConfigure = requestedServers.filter(mcp => !existingNames.has(mcp.name));
338+
}
339+
if (serversToConfigure.length === 0) {
338340
return configWithWorkspaceRoots;
339341
}
340342

341343
return {
342344
...configWithWorkspaceRoots,
343-
"mcp_servers": Object.fromEntries(uniqueServers.map(mcp => [mcp.name, this.createMcpSeverConfig(mcp.server)])),
345+
"mcp_servers": Object.fromEntries(serversToConfigure.map(mcp => [mcp.name, this.createMcpSeverConfig(mcp.server)])),
344346
};
345347
}
346348

@@ -739,6 +741,11 @@ function formatUriAsLink(name: string | null | undefined, uri: string): string {
739741
return uri;
740742
}
741743

744+
function shouldDeduplicateMcpConflicts(): boolean {
745+
const disabledByEnv = process.env["DISABLE_MCP_CONFIG_FILTERING"] === "true";
746+
return !disabledByEnv;
747+
}
748+
742749
interface GatewayConfig {
743750
modelProvider: string;
744751
config: {

src/__tests__/CodexACPAgent/mcp-config-merge.test.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ url = "https://example.com/mcp"
3535
});
3636

3737
afterEach(() => {
38+
vi.unstubAllEnvs();
3839
removeDirectoryWithRetry(codexHome);
3940
});
4041

@@ -66,4 +67,24 @@ url = "https://example.com/mcp"
6667
expect(transportDump).contain("Configured MCP servers:");
6768
expect(transportDump).contain("- shared-mcp");
6869
});
70+
71+
it('should not filter the conflicting ACP MCP when config filtering is disabled', async () => {
72+
vi.stubEnv("DISABLE_MCP_CONFIG_FILTERING", "true");
73+
const codexAcpAgent = fixture.getCodexAcpAgent();
74+
await codexAcpAgent.initialize({protocolVersion: 1});
75+
76+
fixture.getCodexAcpClient().authRequired = vi.fn().mockResolvedValue(false);
77+
78+
const conflictingMcp: McpServerStdio = {
79+
name: "shared-mcp",
80+
command: "./node_modules/.bin/mcp-hello-world",
81+
args: ["example"],
82+
env: [{name: "example", value: "example"}],
83+
};
84+
85+
await expect(codexAcpAgent.newSession({
86+
cwd: "",
87+
mcpServers: [conflictingMcp],
88+
})).rejects.toThrow("url is not supported for stdio");
89+
});
6990
});

0 commit comments

Comments
 (0)