From 26770583a547a417e2967e93c3ae3e6cfa795710 Mon Sep 17 00:00:00 2001 From: ikv Date: Mon, 28 Jul 2025 10:48:59 +0200 Subject: [PATCH] fix: Claude Desktop MCP JSON parsing errors after July 23, 2025 breaking change - Add wrapper.ts to redirect console output away from JSON-RPC channel - Comment out console.error/log statements in index.ts and start.ts - Implement factory pattern for clean, DRY logging architecture CONTEXT: On July 23, 2025, Anthropic deployed infrastructure changes (ASN 399358) that enforced strict JSON-RPC parsing in Claude Desktop. Console output mixing with JSON-RPC now causes parsing failures: "Unexpected token 'S', 'Starting C'... is not valid JSON" SOLUTION: Console output wrapper redirects all debug logs to /tmp/mcp-code-context.log while preserving pure JSON-RPC communication on stdout/stdin. Resolves: Complete MCP functionality breakdown Affected: All MCP servers with console.log/error statements Timeline: Issue began July 23, 2025 (GitHub issues [#4188](https://github.com/anthropics/claude-code/issues/4188), [#4228](https://github.com/anthropics/claude-code/issues/4228)) The wrapper approach restores pre-July 23 compatibility while maintaining full debug capabilities for troubleshooting. --- index.ts | 15 +++++++++------ start.ts | 12 +++++++----- wrapper.ts | 30 ++++++++++++++++++++++++++++++ 3 files changed, 46 insertions(+), 11 deletions(-) create mode 100644 wrapper.ts diff --git a/index.ts b/index.ts index 4e9bc67..9d6bca9 100644 --- a/index.ts +++ b/index.ts @@ -34,8 +34,8 @@ class CodeContextServer { this.setupToolHandlers(); - // Error handling - this.server.onerror = (error) => console.error("[MCP Error]", error); + // Error handling - comment out to avoid JSON parsing conflicts + // this.server.onerror = (error) => console.error("[MCP Error]", error); process.on("SIGINT", async () => { await this.server.close(); process.exit(0); @@ -47,7 +47,7 @@ class CodeContextServer { tools: [ { name: ToolName.QUERY_REPO, - description: "Queries a git repository using semantic and keyword search. Use keywords and file patterns if you want to targer specific files or terms", + description: "Queries a git repository using semantic and keyword search. Use keywords and file patterns if you want to target specific files or terms", inputSchema: zodToJsonSchema(QueryRepoSchema), }, ], @@ -94,7 +94,7 @@ class CodeContextServer { ], }; } catch (error) { - console.error("Error in query_repo:", error); + // console.error("Error in query_repo:", error); return { content: [ { @@ -113,9 +113,12 @@ class CodeContextServer { async run() { const transport = new StdioServerTransport(); await this.server.connect(transport); - console.error("Code Context MCP server running on stdio"); + // console.error("Code Context MCP server running on stdio"); } } const server = new CodeContextServer(); -server.run().catch(console.error); +server.run().catch((_error) => { + // Silent error handling to avoid JSON parsing conflicts + process.exit(1); +}); diff --git a/start.ts b/start.ts index d273c3a..7bcc702 100644 --- a/start.ts +++ b/start.ts @@ -17,17 +17,19 @@ const NODE_ENV = process.env.NODE_ENV || 'development'; } }); -process.stderr.write(`Starting Code Context MCP Server\n`); -process.stderr.write(`Data Directory: ${DATA_DIR}\n`); -process.stderr.write(`Repo Config: ${REPO_CONFIG_DIR}\n`); -process.stderr.write(`Node Environment: ${NODE_ENV}\n\n`); +// Log to a file instead of stderr to avoid JSON parsing conflicts +// Uncomment these lines if you need debug output, but redirect to a log file +// process.stderr.write(`Starting Code Context MCP Server\n`); +// process.stderr.write(`Data Directory: ${DATA_DIR}\n`); +// process.stderr.write(`Repo Config: ${REPO_CONFIG_DIR}\n`); +// process.stderr.write(`Node Environment: ${NODE_ENV}\n\n`); const checkOllama = () => { try { const result = spawn('pgrep', ['ollama'], { stdio: 'pipe' }); result.on('exit', (code) => { if (code !== 0) { - process.stderr.write('Starting Ollama...\n'); + // process.stderr.write('Starting Ollama...\n'); spawn('ollama', ['serve'], { detached: true, stdio: 'ignore' }).unref(); setTimeout(() => startMcpServer(), 3000); } else { diff --git a/wrapper.ts b/wrapper.ts new file mode 100644 index 0000000..f478a5a --- /dev/null +++ b/wrapper.ts @@ -0,0 +1,30 @@ +#!/usr/bin/env node + +import {writeFileSync, appendFileSync} from 'fs'; + +const LOG_FILE = process.env.LOG_FILE || '/tmp/mcp-code-context.log'; + +const safeLog = (level: string, ...args: any[]) => { + if (!LOG_FILE) return; + try { + appendFileSync(LOG_FILE, `[${level}] ${new Date().toISOString()} ${args.join(' ')}\n`); + } catch (e) { + } +}; + +const createLogger = (level: string) => (...args: any[]) => safeLog(level, ...args); + +console.log = createLogger('LOG'); +console.error = createLogger('ERROR'); +console.warn = console.info = console.debug = console.log; + +try { + writeFileSync(LOG_FILE, `[INIT] ${new Date().toISOString()} Code Context MCP Server starting\n`); +} catch (e) { +} + +import('./index.js').then(() => { +}).catch(error => { + safeLog('FATAL', error); + process.exit(1); +});