Skip to content

Commit 4ee1411

Browse files
committed
Fix MCP stdio protocol violation during startup
The MCP protocol requires the client to send the first message. Writing to stdout before the client's initialization request breaks the protocol handshake, causing 'BrokenResourceError' in clients like langchain-mcp-adapters. Root cause: Transport was created AFTER config/feature flags initialization, so logger.* calls during startup wrote directly to stdout (bypassing buffering) or called sendLog() which also wrote to stdout without checking isInitialized. Fixes: 1. Move FilteredStdioServerTransport creation before config loading 2. Buffer sendLog() messages when isInitialized is false 3. Guard sendProgress/sendCustomNotification when not initialized Fixes #issue-reported-by-ever1022
1 parent d3f5824 commit 4ee1411

File tree

2 files changed

+29
-4
lines changed

2 files changed

+29
-4
lines changed

src/custom-stdio.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,13 +277,25 @@ export class FilteredStdioServerTransport extends StdioServerTransport {
277277

278278
/**
279279
* Public method to send log notifications from anywhere in the application
280+
* Now properly buffers messages before MCP initialization to avoid breaking stdio protocol
280281
*/
281282
public sendLog(level: "emergency" | "alert" | "critical" | "error" | "warning" | "notice" | "info" | "debug", message: string, data?: any) {
282283
// Skip if notifications are disabled (e.g., for Cline)
283284
if (this.disableNotifications) {
284285
return;
285286
}
286287

288+
// Buffer messages before initialization to avoid breaking MCP protocol
289+
// MCP requires client to send first message - server cannot write to stdout before that
290+
if (!this.isInitialized) {
291+
this.messageBuffer.push({
292+
level,
293+
args: [data ? { message, ...data } : message],
294+
timestamp: Date.now()
295+
});
296+
return;
297+
}
298+
287299
try {
288300
const notification: LogNotification = {
289301
jsonrpc: "2.0",
@@ -315,6 +327,11 @@ export class FilteredStdioServerTransport extends StdioServerTransport {
315327
* Send a progress notification (useful for long-running operations)
316328
*/
317329
public sendProgress(token: string, value: number, total?: number) {
330+
// Don't send progress before initialization - would break MCP protocol
331+
if (!this.isInitialized) {
332+
return;
333+
}
334+
318335
try {
319336
const notification = {
320337
jsonrpc: "2.0" as const,
@@ -346,6 +363,11 @@ export class FilteredStdioServerTransport extends StdioServerTransport {
346363
* Send a custom notification with any method name
347364
*/
348365
public sendCustomNotification(method: string, params: any) {
366+
// Don't send custom notifications before initialization - would break MCP protocol
367+
if (!this.isInitialized) {
368+
return;
369+
}
370+
349371
try {
350372
const notification = {
351373
jsonrpc: "2.0" as const,

src/index.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,13 @@ async function runServer() {
3939
// Set global flag for onboarding control
4040
(global as any).disableOnboarding = DISABLE_ONBOARDING;
4141

42+
// Create transport FIRST so all logging gets properly buffered
43+
// This must happen before any code that might use logger.*
44+
const transport = new FilteredStdioServerTransport();
45+
46+
// Export transport for use throughout the application
47+
global.mcpTransport = transport;
48+
4249
try {
4350
deferLog('info', 'Loading configuration...');
4451
await configManager.loadConfig();
@@ -56,10 +63,6 @@ async function runServer() {
5663
// Continue anyway - we'll use an in-memory config
5764
}
5865

59-
const transport = new FilteredStdioServerTransport();
60-
61-
// Export transport for use throughout the application
62-
global.mcpTransport = transport;
6366
// Handle uncaught exceptions
6467
process.on('uncaughtException', async (error) => {
6568
const errorMessage = error instanceof Error ? error.message : String(error);

0 commit comments

Comments
 (0)