Merge pull request 'fix: STDIO transport stdout flush and MCP notification handling' (#37) from fix/stdio-flush-and-notifications into main

This commit is contained in:
2026-02-24 19:10:03 +00:00
4 changed files with 20 additions and 8 deletions

View File

@@ -98,7 +98,7 @@ export function createConfigCommand(deps?: Partial<ConfigCommandDeps>, apiDeps?:
mcpServers: { mcpServers: {
[opts.project]: { [opts.project]: {
command: 'mcpctl', command: 'mcpctl',
args: ['mcp', '--project', opts.project], args: ['mcp', '-p', opts.project],
}, },
}, },
}; };

View File

@@ -45,7 +45,7 @@ describe('config claude', () => {
const written = JSON.parse(readFileSync(outPath, 'utf-8')); const written = JSON.parse(readFileSync(outPath, 'utf-8'));
expect(written.mcpServers['homeautomation']).toEqual({ expect(written.mcpServers['homeautomation']).toEqual({
command: 'mcpctl', command: 'mcpctl',
args: ['mcp', '--project', 'homeautomation'], args: ['mcp', '-p', 'homeautomation'],
}); });
expect(output.join('\n')).toContain('1 server(s)'); expect(output.join('\n')).toContain('1 server(s)');
}); });
@@ -60,7 +60,7 @@ describe('config claude', () => {
const parsed = JSON.parse(output[0]); const parsed = JSON.parse(output[0]);
expect(parsed.mcpServers['myproj']).toEqual({ expect(parsed.mcpServers['myproj']).toEqual({
command: 'mcpctl', 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['existing--server']).toBeDefined();
expect(written.mcpServers['proj-1']).toEqual({ expect(written.mcpServers['proj-1']).toEqual({
command: 'mcpctl', command: 'mcpctl',
args: ['mcp', '--project', 'proj-1'], args: ['mcp', '-p', 'proj-1'],
}); });
expect(output.join('\n')).toContain('2 server(s)'); expect(output.join('\n')).toContain('2 server(s)');
}); });
@@ -96,7 +96,7 @@ describe('config claude', () => {
const written = JSON.parse(readFileSync(outPath, 'utf-8')); const written = JSON.parse(readFileSync(outPath, 'utf-8'));
expect(written.mcpServers['proj-1']).toEqual({ expect(written.mcpServers['proj-1']).toEqual({
command: 'mcpctl', command: 'mcpctl',
args: ['mcp', '--project', 'proj-1'], args: ['mcp', '-p', 'proj-1'],
}); });
}); });

View File

@@ -61,9 +61,10 @@ proc.stdout.on('data', d => {
const msg = JSON.parse(line); const msg = JSON.parse(line);
if (msg.id === 2) { if (msg.id === 2) {
responded = true; responded = true;
process.stdout.write(JSON.stringify(msg)); process.stdout.write(JSON.stringify(msg), () => {
proc.kill(); proc.kill();
process.exit(0); process.exit(0);
});
} }
} catch {} } catch {}
} }

View File

@@ -367,6 +367,17 @@ export class McpRouter {
case 'prompts/get': case 'prompts/get':
return this.routeNamespacedCall(request, 'name', this.promptToServer); 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: default:
return { return {
jsonrpc: '2.0', jsonrpc: '2.0',