Skip to content

Commit a5c998a

Browse files
committed
fix: replace console.log with console.error in handlers
console.log() writes to stdout, but on stdio MCP servers stdout is reserved for JSON-RPC protocol messages. Any debug output on stdout corrupts the framing, which Claude Code surfaces as "MCP error -32000: Connection closed" — the entire MCP becomes unavailable until /mcp reconnect. This explains: - Issue #11: "Running getLabels() always seems to crash the mcp server" (likely same class — getLabels handler may have similar logs) - Issue #8: "'Client Closed' error when installing Linear MCP Server with Smithery in Cursor" (Cursor likely surfaces the same corruption with a different error string) - Reproducible deterministic crash on linear_searchIssues for any workspace with non-trivial issue counts The fix is one-line per call: console.log → console.error. stderr is never read by the MCP transport and is the standard Node.js channel for diagnostic logs. Affected handlers: - src/tools/handlers/initiative-handlers.ts (20 calls) - src/tools/handlers/issue-handlers.ts (2 calls — both inside searchIssues, which is why that path crashes deterministically) Verified: post-patch, the linear_searchIssues call that crashed every time previously now returns 124KB of data cleanly. Multiple sequential heavy queries succeed in a row. This is the official Node.js MCP guidance for stdio servers — see the modelcontextprotocol/typescript-sdk README and the MCP spec discussion on stdout reservation.
1 parent 2abd224 commit a5c998a

2 files changed

Lines changed: 22 additions & 22 deletions

File tree

src/tools/handlers/initiative-handlers.ts

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ export function getInitiativesHandler(linearService: LinearService) {
1717
throw new Error('Invalid input for getInitiatives');
1818
}
1919

20-
console.log('[getInitiatives] Fetching initiatives with options:', args);
20+
console.error('[getInitiatives] Fetching initiatives with options:', args);
2121
const initiatives = await linearService.getInitiatives(args);
22-
console.log(`[getInitiatives] Retrieved ${initiatives.length} initiatives`);
22+
console.error(`[getInitiatives] Retrieved ${initiatives.length} initiatives`);
2323
return initiatives;
2424
};
2525
}
@@ -30,12 +30,12 @@ export function getInitiativeByIdHandler(linearService: LinearService) {
3030
throw new Error('Invalid input for getInitiativeById');
3131
}
3232

33-
console.log(`[getInitiativeById] Fetching initiative: ${args.initiativeId}`);
33+
console.error(`[getInitiativeById] Fetching initiative: ${args.initiativeId}`);
3434
const initiative = await linearService.getInitiativeById(
3535
args.initiativeId,
3636
args.includeProjects,
3737
);
38-
console.log(`[getInitiativeById] Retrieved initiative: ${initiative.name}`);
38+
console.error(`[getInitiativeById] Retrieved initiative: ${initiative.name}`);
3939
return initiative;
4040
};
4141
}
@@ -46,9 +46,9 @@ export function createInitiativeHandler(linearService: LinearService) {
4646
throw new Error('Invalid input for createInitiative');
4747
}
4848

49-
console.log('[createInitiative] Creating new initiative:', args.name);
49+
console.error('[createInitiative] Creating new initiative:', args.name);
5050
const result = await linearService.createInitiative(args);
51-
console.log(`[createInitiative] Initiative created successfully`);
51+
console.error(`[createInitiative] Initiative created successfully`);
5252
return result;
5353
};
5454
}
@@ -60,9 +60,9 @@ export function updateInitiativeHandler(linearService: LinearService) {
6060
}
6161

6262
const { initiativeId, ...updateData } = args;
63-
console.log(`[updateInitiative] Updating initiative: ${initiativeId}`);
63+
console.error(`[updateInitiative] Updating initiative: ${initiativeId}`);
6464
const result = await linearService.updateInitiative(initiativeId, updateData);
65-
console.log(`[updateInitiative] Initiative updated successfully`);
65+
console.error(`[updateInitiative] Initiative updated successfully`);
6666
return result;
6767
};
6868
}
@@ -73,9 +73,9 @@ export function archiveInitiativeHandler(linearService: LinearService) {
7373
throw new Error('Invalid input for archiveInitiative');
7474
}
7575

76-
console.log(`[archiveInitiative] Archiving initiative: ${args.initiativeId}`);
76+
console.error(`[archiveInitiative] Archiving initiative: ${args.initiativeId}`);
7777
const result = await linearService.archiveInitiative(args.initiativeId);
78-
console.log(`[archiveInitiative] Initiative archived successfully`);
78+
console.error(`[archiveInitiative] Initiative archived successfully`);
7979
return result;
8080
};
8181
}
@@ -86,9 +86,9 @@ export function unarchiveInitiativeHandler(linearService: LinearService) {
8686
throw new Error('Invalid input for unarchiveInitiative');
8787
}
8888

89-
console.log(`[unarchiveInitiative] Unarchiving initiative: ${args.initiativeId}`);
89+
console.error(`[unarchiveInitiative] Unarchiving initiative: ${args.initiativeId}`);
9090
const result = await linearService.unarchiveInitiative(args.initiativeId);
91-
console.log(`[unarchiveInitiative] Initiative unarchived successfully`);
91+
console.error(`[unarchiveInitiative] Initiative unarchived successfully`);
9292
return result;
9393
};
9494
}
@@ -99,9 +99,9 @@ export function deleteInitiativeHandler(linearService: LinearService) {
9999
throw new Error('Invalid input for deleteInitiative');
100100
}
101101

102-
console.log(`[deleteInitiative] Deleting initiative: ${args.initiativeId}`);
102+
console.error(`[deleteInitiative] Deleting initiative: ${args.initiativeId}`);
103103
const result = await linearService.deleteInitiative(args.initiativeId);
104-
console.log(`[deleteInitiative] Initiative deleted successfully`);
104+
console.error(`[deleteInitiative] Initiative deleted successfully`);
105105
return result;
106106
};
107107
}
@@ -112,12 +112,12 @@ export function getInitiativeProjectsHandler(linearService: LinearService) {
112112
throw new Error('Invalid input for getInitiativeProjects');
113113
}
114114

115-
console.log(`[getInitiativeProjects] Fetching projects for initiative: ${args.initiativeId}`);
115+
console.error(`[getInitiativeProjects] Fetching projects for initiative: ${args.initiativeId}`);
116116
const projects = await linearService.getInitiativeProjects(
117117
args.initiativeId,
118118
args.includeArchived,
119119
);
120-
console.log(`[getInitiativeProjects] Retrieved ${projects.length} projects`);
120+
console.error(`[getInitiativeProjects] Retrieved ${projects.length} projects`);
121121
return projects;
122122
};
123123
}
@@ -128,11 +128,11 @@ export function addProjectToInitiativeHandler(linearService: LinearService) {
128128
throw new Error('Invalid input for addProjectToInitiative');
129129
}
130130

131-
console.log(
131+
console.error(
132132
`[addProjectToInitiative] Adding project ${args.projectId} to initiative ${args.initiativeId}`,
133133
);
134134
const result = await linearService.addProjectToInitiative(args.initiativeId, args.projectId);
135-
console.log(`[addProjectToInitiative] Project added successfully`);
135+
console.error(`[addProjectToInitiative] Project added successfully`);
136136
return result;
137137
};
138138
}
@@ -143,14 +143,14 @@ export function removeProjectFromInitiativeHandler(linearService: LinearService)
143143
throw new Error('Invalid input for removeProjectFromInitiative');
144144
}
145145

146-
console.log(
146+
console.error(
147147
`[removeProjectFromInitiative] Removing project ${args.projectId} from initiative ${args.initiativeId}`,
148148
);
149149
const result = await linearService.removeProjectFromInitiative(
150150
args.initiativeId,
151151
args.projectId,
152152
);
153-
console.log(`[removeProjectFromInitiative] Project removed successfully`);
153+
console.error(`[removeProjectFromInitiative] Project removed successfully`);
154154
return result;
155155
};
156156
}

src/tools/handlers/issue-handlers.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,14 +63,14 @@ export function handleGetIssueById(linearService: LinearService) {
6363
export function handleSearchIssues(linearService: LinearService) {
6464
return async (args: unknown) => {
6565
try {
66-
console.log('searchIssues args:', JSON.stringify(args, null, 2));
66+
console.error('searchIssues args:', JSON.stringify(args, null, 2));
6767

6868
if (!isSearchIssuesArgs(args)) {
6969
console.error('Invalid arguments for searchIssues');
7070
throw new Error('Invalid arguments for searchIssues');
7171
}
7272

73-
console.log('Arguments validated successfully');
73+
console.error('Arguments validated successfully');
7474
return await linearService.searchIssues(args);
7575
} catch (error) {
7676
logError('Error searching issues', error);

0 commit comments

Comments
 (0)