From 911a371c03cdc74c787e7375a814340db5c3d40f Mon Sep 17 00:00:00 2001 From: tt-a1i <53142663+tt-a1i@users.noreply.github.com> Date: Sat, 20 Jun 2026 12:33:41 +0800 Subject: [PATCH] fix(acp): tolerate missing MCP optional fields --- .changeset/acp-mcp-optional-fields.md | 5 ++ packages/acp-adapter/src/mcp.ts | 8 ++-- packages/acp-adapter/test/mcp-forward.test.ts | 47 +++++++++++++++++++ 3 files changed, 56 insertions(+), 4 deletions(-) create mode 100644 .changeset/acp-mcp-optional-fields.md diff --git a/.changeset/acp-mcp-optional-fields.md b/.changeset/acp-mcp-optional-fields.md new file mode 100644 index 000000000..1e3c8b190 --- /dev/null +++ b/.changeset/acp-mcp-optional-fields.md @@ -0,0 +1,5 @@ +--- +"@moonshot-ai/kimi-code": patch +--- + +Fix ACP MCP server conversion when optional headers or environment variables are omitted. diff --git a/packages/acp-adapter/src/mcp.ts b/packages/acp-adapter/src/mcp.ts index 44d419fa0..445da86e4 100644 --- a/packages/acp-adapter/src/mcp.ts +++ b/packages/acp-adapter/src/mcp.ts @@ -102,17 +102,17 @@ function acpMcpServerToConfig( } function headersArrayToRecord( - headers: ReadonlyArray<{ readonly name: string; readonly value: string }>, + headers: ReadonlyArray<{ readonly name: string; readonly value: string }> | undefined, ): Record { const out: Record = {}; - for (const h of headers) out[h.name] = h.value; + for (const h of headers ?? []) out[h.name] = h.value; return out; } function envArrayToRecord( - env: ReadonlyArray<{ readonly name: string; readonly value: string }>, + env: ReadonlyArray<{ readonly name: string; readonly value: string }> | undefined, ): Record { const out: Record = {}; - for (const e of env) out[e.name] = e.value; + for (const e of env ?? []) out[e.name] = e.value; return out; } diff --git a/packages/acp-adapter/test/mcp-forward.test.ts b/packages/acp-adapter/test/mcp-forward.test.ts index 862301b0e..6ad207a59 100644 --- a/packages/acp-adapter/test/mcp-forward.test.ts +++ b/packages/acp-adapter/test/mcp-forward.test.ts @@ -169,6 +169,34 @@ describe('acpMcpServersToConfigs', () => { expect(warnSpy).not.toHaveBeenCalled(); }); + it('converts HTTP and SSE servers without headers', () => { + const out = acpMcpServersToConfigs([ + { + type: 'http', + name: 'docs', + url: 'https://mcp.example.com', + } as unknown as McpServer, + { + type: 'sse', + name: 'events', + url: 'https://stream.example.com', + } as unknown as McpServer, + ]); + expect(out).toEqual({ + docs: { + transport: 'http', + url: 'https://mcp.example.com', + headers: {}, + }, + events: { + transport: 'sse', + url: 'https://stream.example.com', + headers: {}, + }, + }); + expect(warnSpy).not.toHaveBeenCalled(); + }); + it('converts a stdio server with args + env to a Record keyed by name', () => { const out = acpMcpServersToConfigs([ stdioServer( @@ -192,6 +220,25 @@ describe('acpMcpServersToConfigs', () => { expect(warnSpy).not.toHaveBeenCalled(); }); + it('converts a stdio server without env', () => { + const out = acpMcpServersToConfigs([ + { + name: 'fs', + command: '/usr/local/bin/mcp-fs', + args: ['--root', '/tmp'], + } as unknown as McpServer, + ]); + expect(out).toEqual({ + fs: { + transport: 'stdio', + command: '/usr/local/bin/mcp-fs', + args: ['--root', '/tmp'], + env: {}, + }, + }); + expect(warnSpy).not.toHaveBeenCalled(); + }); + it('converts an SSE server with headers to a Record keyed by name', () => { const out = acpMcpServersToConfigs([ sseServer('events', 'https://stream.example.com', [{ name: 'X-K', value: 'V' }]),