From 9cb0c5ce24ba087047e4edb75242887f52441b1f Mon Sep 17 00:00:00 2001 From: Michal Date: Tue, 24 Feb 2026 19:09:47 +0000 Subject: [PATCH] fix: STDIO transport stdout flush and MCP notification handling - Wait for stdout.write callback before process.exit in STDIO transport to prevent truncation of large responses (e.g. grafana tools/list) - Handle MCP notification methods (notifications/initialized, etc.) in router instead of returning "Method not found" error - Use -p shorthand in config claude output Co-Authored-By: Claude Opus 4.6 --- src/cli/src/commands/config.ts | 2 +- src/cli/tests/commands/claude.test.ts | 8 ++++---- src/mcpd/src/services/transport/stdio-client.ts | 7 ++++--- src/mcplocal/src/router.ts | 11 +++++++++++ 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/cli/src/commands/config.ts b/src/cli/src/commands/config.ts index b57f114..7524776 100644 --- a/src/cli/src/commands/config.ts +++ b/src/cli/src/commands/config.ts @@ -98,7 +98,7 @@ export function createConfigCommand(deps?: Partial, apiDeps?: mcpServers: { [opts.project]: { command: 'mcpctl', - args: ['mcp', '--project', opts.project], + args: ['mcp', '-p', opts.project], }, }, }; diff --git a/src/cli/tests/commands/claude.test.ts b/src/cli/tests/commands/claude.test.ts index 4b55e56..ef9ae93 100644 --- a/src/cli/tests/commands/claude.test.ts +++ b/src/cli/tests/commands/claude.test.ts @@ -45,7 +45,7 @@ describe('config claude', () => { const written = JSON.parse(readFileSync(outPath, 'utf-8')); expect(written.mcpServers['homeautomation']).toEqual({ command: 'mcpctl', - args: ['mcp', '--project', 'homeautomation'], + args: ['mcp', '-p', 'homeautomation'], }); expect(output.join('\n')).toContain('1 server(s)'); }); @@ -60,7 +60,7 @@ describe('config claude', () => { const parsed = JSON.parse(output[0]); expect(parsed.mcpServers['myproj']).toEqual({ command: 'mcpctl', - args: ['mcp', '--project', 'myproj'], + args: ['mcp', '-p', 'myproj'], }); }); @@ -80,7 +80,7 @@ describe('config claude', () => { expect(written.mcpServers['existing--server']).toBeDefined(); expect(written.mcpServers['proj-1']).toEqual({ command: 'mcpctl', - args: ['mcp', '--project', 'proj-1'], + args: ['mcp', '-p', 'proj-1'], }); expect(output.join('\n')).toContain('2 server(s)'); }); @@ -96,7 +96,7 @@ describe('config claude', () => { const written = JSON.parse(readFileSync(outPath, 'utf-8')); expect(written.mcpServers['proj-1']).toEqual({ command: 'mcpctl', - args: ['mcp', '--project', 'proj-1'], + args: ['mcp', '-p', 'proj-1'], }); }); diff --git a/src/mcpd/src/services/transport/stdio-client.ts b/src/mcpd/src/services/transport/stdio-client.ts index bebb9ec..949e018 100644 --- a/src/mcpd/src/services/transport/stdio-client.ts +++ b/src/mcpd/src/services/transport/stdio-client.ts @@ -61,9 +61,10 @@ proc.stdout.on('data', d => { const msg = JSON.parse(line); if (msg.id === 2) { responded = true; - process.stdout.write(JSON.stringify(msg)); - proc.kill(); - process.exit(0); + process.stdout.write(JSON.stringify(msg), () => { + proc.kill(); + process.exit(0); + }); } } catch {} } diff --git a/src/mcplocal/src/router.ts b/src/mcplocal/src/router.ts index 681569c..1e60d1b 100644 --- a/src/mcplocal/src/router.ts +++ b/src/mcplocal/src/router.ts @@ -367,6 +367,17 @@ export class McpRouter { case 'prompts/get': return this.routeNamespacedCall(request, 'name', this.promptToServer); + // Handle MCP notifications (no response expected, but return empty result if called as request) + case 'notifications/initialized': + case 'notifications/cancelled': + case 'notifications/progress': + case 'notifications/roots/list_changed': + return { + jsonrpc: '2.0', + id: request.id, + result: {}, + }; + default: return { jsonrpc: '2.0',