# MCP Stdio Protocol Violation Fix
**Date**: October 14, 2025
**Version**: 1.7.1
**Severity**: CRITICAL
**Status**: FIXED
## Problem Summary
The LOG_LEVEL logging improvements introduced in v1.6.0 used `console.log()` which writes to **stdout**, breaking the MCP JSON-RPC protocol with Claude Desktop.
### Error Observed
```
Unexpected token 'E', "[EmbeddingU"... is not valid JSON
```
### Root Cause
The MCP protocol requires:
- **stdout**: ONLY JSON-RPC messages (with `jsonrpc`, `id`, `result`/`error` fields)
- **stderr**: ALL logging, debugging, and diagnostic output
Two critical files were using `console.log()` in MCP stdio mode:
1. `src/services/embedding-updater.ts` - Line 49
2. `src/desktop-mcp-server.ts` - Line 60
## Fix Applied
### 1. EmbeddingUpdater Service
**File**: `src/services/embedding-updater.ts`
**Before**:
```typescript
private log(level: 'debug' | 'info' | 'warn' | 'error', message: string, ...args: any[]): void {
if (this.shouldLog(level)) {
const logFn = level === 'error' ? console.error : console.log; // ← PROBLEM
logFn(`[EmbeddingUpdater] ${message}`, ...args);
}
}
```
**After**:
```typescript
private log(level: 'debug' | 'info' | 'warn' | 'error', message: string, ...args: any[]): void {
if (this.shouldLog(level)) {
// CRITICAL: Always use stderr to avoid breaking MCP stdio protocol
// stdout is reserved for JSON-RPC messages only
console.error(`[EmbeddingUpdater] ${message}`, ...args);
}
}
```
### 2. Desktop MCP Server
**File**: `src/desktop-mcp-server.ts`
**Before**:
```typescript
constructor() {
this.debugEnabled = process.env.MCP_DEBUG === '1';
this.logLevel = process.env.LOG_LEVEL?.toLowerCase() || 'info';
if (this.debugEnabled || this.logLevel === 'debug') {
console.log(`[MCPServer] Log level: ${this.logLevel}`); // ← PROBLEM
}
}
```
**After**:
```typescript
constructor() {
this.debugEnabled = process.env.MCP_DEBUG === '1';
this.logLevel = process.env.LOG_LEVEL?.toLowerCase() || 'info';
// CRITICAL: Use stderr to avoid breaking MCP stdio protocol
// stdout is reserved for JSON-RPC messages only
if (this.debugEnabled || this.logLevel === 'debug') {
console.error(`[MCPServer] Log level: ${this.logLevel}`);
}
}
```
## Regression Prevention
Created comprehensive unit tests to prevent future violations:
**File**: `tests/mcp/stdio-pollution-prevention.test.ts`
### Test Coverage
1. **EmbeddingUpdater Logging** - Verifies no stdout pollution in MCP mode
2. **Desktop MCP Server Logging** - Verifies startup logs use stderr
3. **JSON-RPC Protocol Compliance** - Validates only JSON-RPC on stdout
4. **Regression Prevention** - Source code analysis to detect console.log usage
5. **Stream Output Validation** - Verifies proper log formatting on stderr
### Test Results
```bash
✓ tests/mcp/stdio-pollution-prevention.test.ts (9 tests) 3522ms
✓ MCP Stdio Protocol - Stdout Pollution Prevention
✓ EmbeddingUpdater logging (2 tests)
✓ Desktop MCP Server logging (1 test)
✓ JSON-RPC protocol compliance (2 tests)
✓ Regression prevention (3 tests)
✓ Stream output validation (1 test)
Test Files 1 passed (1)
Tests 9 passed (9)
```
## Verification
### Manual Testing
```bash
# 1. Build project
npm run build
# 2. Run unit tests
npm test -- stdio-pollution-prevention
# 3. Test MCP server (logs should appear on stderr, not stdout)
MCP_STDIO_MODE=1 LOG_LEVEL=debug node dist/desktop-mcp-server.js 2>&1 >/dev/null
```
### Expected Behavior
**Correct (after fix)**:
- stdout: Only JSON-RPC messages
- stderr: All logging output (EmbeddingUpdater, MCPServer, etc.)
**Incorrect (before fix)**:
- stdout: Mixed JSON-RPC + log messages → Protocol violation
- stderr: Some logging
## Impact
### Before Fix
- ❌ Claude Desktop connection failed
- ❌ JSON-RPC parsing errors
- ❌ MCP server unusable in stdio mode
### After Fix
- ✅ Claude Desktop connection works
- ✅ Clean JSON-RPC communication
- ✅ All logging properly isolated to stderr
- ✅ Unit tests prevent regression
## Related Files
### Modified
- `src/services/embedding-updater.ts` - Fixed log method
- `src/desktop-mcp-server.ts` - Fixed constructor logging
### Added
- `tests/mcp/stdio-pollution-prevention.test.ts` - Regression prevention
### Critical Paths
Files where `console.log()` is forbidden in MCP context:
- `src/services/embedding-updater.ts`
- `src/desktop-mcp-server.ts`
- Any file involved in MCP request/response handling
## Best Practices
### For Future Development
1. **NEVER use console.log() in MCP context**
```typescript
// ❌ Wrong - pollutes stdout
console.log('[MyService] Some message');
// ✅ Correct - uses stderr
console.error('[MyService] Some message');
```
2. **Always test with stdio mode**
```bash
MCP_STDIO_MODE=1 node dist/desktop-mcp-server.js
```
3. **Run regression tests before commit**
```bash
npm test -- stdio-pollution-prevention
```
4. **Document protocol requirements**
- Add comments explaining stderr usage
- Reference MCP protocol specification
- Link to this fix document
## Prevention Measures
### Automated Checks
The unit tests now automatically detect:
- Direct `console.log()` calls in critical files
- Stdout pollution during MCP operations
- Invalid JSON-RPC responses
- Missing protocol compliance comments
### CI/CD Integration
Add to pre-commit hooks:
```bash
# Run stdio pollution tests
npm test -- stdio-pollution-prevention
# Check for console.log in critical files
! grep -R "console\.log" src/services/embedding-updater.ts src/desktop-mcp-server.ts
```
## References
- **MCP Protocol**: [Model Context Protocol Specification](https://modelcontextprotocol.io/)
- **JSON-RPC 2.0**: [JSON-RPC Specification](https://www.jsonrpc.org/specification)
- **Issue**: Claude Desktop connection error with LOG_LEVEL debug mode
- **Fix Commit**: stdio-protocol-fix-v1.7.1
## Lessons Learned
1. **Protocol Compliance is Critical**: MCP stdio mode requires strict adherence to JSON-RPC
2. **Logging Must Be Isolated**: Always use stderr for diagnostics in stdio-based protocols
3. **Tests Prevent Regression**: Comprehensive unit tests caught the issue and prevent recurrence
4. **Documentation Matters**: Clear comments explain WHY stderr is used, not just HOW
## Future Improvements
1. Consider adding a logging utility that automatically routes to stderr in MCP mode
2. Add ESLint rule to detect console.log in critical paths
3. Create development guide for MCP protocol compliance
4. Add integration tests with actual Claude Desktop
---
**Fixed by**: Claude (Anthropic)
**Test Coverage**: 9/9 tests passing
**Status**: Production Ready
**Next Review**: Before v1.8.0 release