feat(codex): support user-configured MCP servers in remote sessions (#1789)

* feat(codex): load user MCP servers in HAPI sessions

* feat(codex): proxy Windows MCP command shims

* fix(codex): address MCP review feedback

* fix(codex): use HAPI-owned MCP proxy

* fix(codex): preserve Windows MCP argv boundaries

* fix(codex): keep remote MCP placement unchanged
This commit is contained in:
Ananovo
2026-09-09 09:26:43 +08:00
committed by GitHub
parent 66ea35963e
commit 5c5c8b3a9f
18 changed files with 941 additions and 13 deletions
+71
View File
@@ -1560,6 +1560,77 @@ describe('codexRemoteLauncher', () => {
});
});
it('forwards user-configured MCP servers into fresh and resumed threads', async () => {
harness.configReadResponse = {
config: {
mcp_servers: {
'package-manager': {
command: 'uvx',
args: ['example-mcp', 'serve'],
environment_id: 'local',
enabled: true,
tool_timeout_sec: 60
},
remote: {
url: 'https://example.test/mcp',
bearer_token_env_var: 'REMOTE_MCP_TOKEN'
}
}
}
};
const fresh = createSessionStub();
await codexRemoteLauncher(fresh.session as never);
const freshConfig = harness.startThreadParams[0]?.config as Record<string, unknown> | undefined;
const freshPackageManager = freshConfig?.['mcp_servers.package-manager'] as {
command?: string;
args?: string[];
} | undefined;
expect(harness.startThreadParams[0]?.config).toMatchObject({
'mcp_servers.remote': {
url: 'https://example.test/mcp',
bearer_token_env_var: 'REMOTE_MCP_TOKEN'
}
});
expect(freshPackageManager).toEqual(expect.objectContaining({
environment_id: 'local',
enabled: true,
tool_timeout_sec: 60
}));
if (process.platform === 'win32') {
expect(freshPackageManager?.args).toContain('mcp-proxy');
} else {
expect(freshPackageManager).toMatchObject({
command: 'uvx',
args: ['example-mcp', 'serve']
});
}
harness.startThreadParams = [];
const resumed = createSessionStub();
resumed.session.sessionId = 'thread-existing';
await codexRemoteLauncher(resumed.session as never);
const resumedConfig = harness.resumeThreadParams[0]?.config as Record<string, unknown> | undefined;
const resumedPackageManager = resumedConfig?.['mcp_servers.package-manager'] as {
command?: string;
args?: string[];
} | undefined;
expect(harness.resumeThreadParams[0]?.config).toMatchObject({
'mcp_servers.remote': {
url: 'https://example.test/mcp'
}
});
expect(resumedPackageManager).toBeDefined();
if (process.platform === 'win32') {
expect(resumedPackageManager?.args).toContain('mcp-proxy');
} else {
expect(resumedPackageManager).toMatchObject({
command: 'uvx',
args: ['example-mcp', 'serve']
});
}
});
it('keeps remote sessions working when config/read is unavailable', async () => {
harness.failConfigRead = true;
const { session } = createSessionStub();