|
| 1 | +# Cline Notification Problem and Alternative Solutions |
| 2 | + |
| 3 | +## Branch Name Changed |
| 4 | +Branch renamed from `explore-stdio-server` to `do-not-show-notification-in-cline` |
| 5 | + |
| 6 | +## The Problem |
| 7 | + |
| 8 | +Desktop Commander currently uses `notifications/message` JSON-RPC method to send log messages: |
| 9 | + |
| 10 | +```json |
| 11 | +{ |
| 12 | + "jsonrpc": "2.0", |
| 13 | + "method": "notifications/message", |
| 14 | + "params": { |
| 15 | + "level": "info", |
| 16 | + "logger": "desktop-commander", |
| 17 | + "data": "Your log message here" |
| 18 | + } |
| 19 | +} |
| 20 | +``` |
| 21 | + |
| 22 | +**Issue:** Cline displays these notifications in the UI, which creates visual clutter for users. |
| 23 | + |
| 24 | +## Research Findings |
| 25 | + |
| 26 | +### What MCP Specification Says |
| 27 | + |
| 28 | +1. **stdout**: MUST only contain valid JSON-RPC messages |
| 29 | +2. **stderr**: Can be used for logging - clients MAY capture, forward, or ignore |
| 30 | +3. **notifications/message**: Official MCP logging notification method |
| 31 | + |
| 32 | +### How Different Clients Handle Logging |
| 33 | + |
| 34 | +#### Claude Desktop |
| 35 | +- Captures stderr automatically |
| 36 | +- Shows `notifications/message` in logs but doesn't overwhelm UI |
| 37 | + |
| 38 | +#### Cline (VSCode Extension) |
| 39 | +- **Problem**: Shows `notifications/message` notifications prominently in UI |
| 40 | +- **Stderr behavior**: Captures stderr but treats it as error indicator |
| 41 | + - From Issue #1287: "stderr output confuses Roo's MCP server configuration view" |
| 42 | + - From Issue #1959: "Cline spawns MCPs but hides logs that these MCPs may write to the console" |
| 43 | +- **Workaround used by others**: `--logLevel none` flag to suppress logging entirely |
| 44 | + |
| 45 | +#### VS Code Copilot |
| 46 | +- Has "Show Output" option to view server logs |
| 47 | +- Doesn't show notifications in main chat UI |
| 48 | + |
| 49 | +### Key Discovery |
| 50 | + |
| 51 | +From the research, the **only safe logging mechanism** in MCP stdio transport is: |
| 52 | + |
| 53 | +1. **STDERR** - But has limitations: |
| 54 | + - Some clients treat any stderr as error |
| 55 | + - Some clients hide stderr completely |
| 56 | + - Some clients (like Cline) capture it but don't show it well |
| 57 | + |
| 58 | +2. **No alternatives exist** - The MCP spec only defines: |
| 59 | + - Requests (require responses) |
| 60 | + - Responses (must match request) |
| 61 | + - Notifications (one-way messages) |
| 62 | + |
| 63 | +### JSON-RPC 2.0 Notification Types in MCP |
| 64 | + |
| 65 | +According to the spec, these are the **ONLY** notification methods in MCP: |
| 66 | + |
| 67 | +1. **`notifications/message`** - For logging (what we're using now) |
| 68 | +2. **`notifications/progress`** - For progress updates |
| 69 | +3. **`notifications/initialized`** - Sent after client completes initialization |
| 70 | +4. **`notifications/cancelled`** - For cancellation |
| 71 | +5. **`notifications/roots/list_changed`** - Root list changed |
| 72 | +6. **`notifications/resources/list_changed`** - Resources changed |
| 73 | +7. **`notifications/resources/updated`** - Resource content changed |
| 74 | +8. **`notifications/tools/list_changed`** - Tools list changed |
| 75 | +9. **`notifications/prompts/list_changed`** - Prompts list changed |
| 76 | + |
| 77 | +**None of these are suitable for silent logging** - they're all meant to be seen by the client. |
| 78 | + |
| 79 | +## Solutions Analysis |
| 80 | + |
| 81 | +### Option 1: Use STDERR Exclusively ❌ |
| 82 | +**Pros:** |
| 83 | +- Standard practice for logging |
| 84 | +- Doesn't interfere with JSON-RPC protocol |
| 85 | + |
| 86 | +**Cons:** |
| 87 | +- Cline might treat it as errors |
| 88 | +- Users can't see logs at all in many clients |
| 89 | +- Some MCP servers completely hide stderr |
| 90 | +- Inconsistent behavior across clients |
| 91 | + |
| 92 | +### Option 2: Make Logging Configurable ✅ (RECOMMENDED) |
| 93 | +**Pros:** |
| 94 | +- Users can choose their preference |
| 95 | +- Works for all clients |
| 96 | +- Most flexible solution |
| 97 | + |
| 98 | +**Cons:** |
| 99 | +- Requires configuration management |
| 100 | + |
| 101 | +**Implementation:** |
| 102 | +```typescript |
| 103 | +interface LoggingConfig { |
| 104 | + enabled: boolean; |
| 105 | + method: 'notifications' | 'stderr' | 'silent'; |
| 106 | + level: 'error' | 'warning' | 'info' | 'debug'; |
| 107 | +} |
| 108 | +``` |
| 109 | + |
| 110 | +### Option 3: Client Detection ✅ (RECOMMENDED) |
| 111 | +**Pros:** |
| 112 | +- Automatic - no user configuration needed |
| 113 | +- Best experience per client |
| 114 | + |
| 115 | +**Cons:** |
| 116 | +- Requires maintaining client detection logic |
| 117 | + |
| 118 | +**Implementation:** |
| 119 | +```typescript |
| 120 | +// In initialization handler |
| 121 | +const clientName = request.params?.clientInfo?.name || 'unknown'; |
| 122 | + |
| 123 | +if (clientName === 'vscode-cline' || clientName === 'cline') { |
| 124 | + // Disable notifications/message for Cline |
| 125 | + logConfig.method = 'stderr'; |
| 126 | +} else if (clientName === 'claude-desktop') { |
| 127 | + // Claude Desktop handles notifications well |
| 128 | + logConfig.method = 'notifications'; |
| 129 | +} |
| 130 | +``` |
| 131 | + |
| 132 | +### Option 4: Reduce Notification Volume ✅ |
| 133 | +**Pros:** |
| 134 | +- Simple to implement |
| 135 | +- Works with all clients |
| 136 | +- Still provides important info |
| 137 | + |
| 138 | +**Cons:** |
| 139 | +- Still shows some notifications in Cline |
| 140 | + |
| 141 | +**Implementation:** |
| 142 | +- Only send notifications for `error` and `warning` levels |
| 143 | +- Use stderr for `info` and `debug` |
| 144 | +- Batch startup messages instead of sending individually |
| 145 | + |
| 146 | +### Option 5: Custom Notification Method ❌ |
| 147 | +**Pros:** |
| 148 | +- Could avoid Cline's notification display |
| 149 | + |
| 150 | +**Cons:** |
| 151 | +- **Violates MCP spec** - clients expect specific notification methods |
| 152 | +- Clients will likely ignore unknown notification methods |
| 153 | +- Not a long-term solution |
| 154 | + |
| 155 | +## Recommended Solution |
| 156 | + |
| 157 | +### **Hybrid Approach (Best for Desktop Commander)** |
| 158 | + |
| 159 | +```typescript |
| 160 | +class FilteredStdioServerTransport extends StdioServerTransport { |
| 161 | + private logConfig: { |
| 162 | + clientName: string; |
| 163 | + useNotifications: boolean; |
| 164 | + minLevel: 'error' | 'warning' | 'info' | 'debug'; |
| 165 | + }; |
| 166 | + |
| 167 | + constructor() { |
| 168 | + super(); |
| 169 | + // Default to safe logging |
| 170 | + this.logConfig = { |
| 171 | + clientName: 'unknown', |
| 172 | + useNotifications: false, // Start with stderr only |
| 173 | + minLevel: 'info' |
| 174 | + }; |
| 175 | + } |
| 176 | + |
| 177 | + public configureLogging(clientInfo: { name: string; version: string }) { |
| 178 | + this.logConfig.clientName = clientInfo.name; |
| 179 | + |
| 180 | + // Client-specific behavior |
| 181 | + if (clientInfo.name === 'claude-desktop') { |
| 182 | + this.logConfig.useNotifications = true; |
| 183 | + this.logConfig.minLevel = 'info'; |
| 184 | + } else if (clientInfo.name?.includes('cline') || clientInfo.name?.includes('vscode')) { |
| 185 | + this.logConfig.useNotifications = false; // Use stderr for Cline |
| 186 | + this.logConfig.minLevel = 'warning'; |
| 187 | + } else { |
| 188 | + // For unknown clients, be conservative |
| 189 | + this.logConfig.useNotifications = false; |
| 190 | + this.logConfig.minLevel = 'error'; |
| 191 | + } |
| 192 | + } |
| 193 | + |
| 194 | + private sendLogNotification(level: string, args: any[]) { |
| 195 | + // Check if we should send based on level |
| 196 | + const levelPriority = { debug: 0, info: 1, warning: 2, error: 3 }; |
| 197 | + const minPriority = levelPriority[this.logConfig.minLevel]; |
| 198 | + const msgPriority = levelPriority[level] || 0; |
| 199 | + |
| 200 | + if (msgPriority < minPriority) { |
| 201 | + return; // Skip this message |
| 202 | + } |
| 203 | + |
| 204 | + if (this.logConfig.useNotifications) { |
| 205 | + // Send JSON-RPC notification (for Claude Desktop) |
| 206 | + const notification = { |
| 207 | + jsonrpc: "2.0", |
| 208 | + method: "notifications/message", |
| 209 | + params: { level, logger: "desktop-commander", data: /* ... */ } |
| 210 | + }; |
| 211 | + this.originalStdoutWrite.call(process.stdout, JSON.stringify(notification) + '\n'); |
| 212 | + } else { |
| 213 | + // Send to stderr (for Cline and others) |
| 214 | + const message = args.map(arg => |
| 215 | + typeof arg === 'object' ? JSON.stringify(arg) : String(arg) |
| 216 | + ).join(' '); |
| 217 | + process.stderr.write(`[${level.toUpperCase()}] ${message}\n`); |
| 218 | + } |
| 219 | + } |
| 220 | +} |
| 221 | +``` |
| 222 | + |
| 223 | +## Implementation Steps |
| 224 | + |
| 225 | +1. **Add client detection** in initialization handler |
| 226 | +2. **Configure logging behavior** based on detected client |
| 227 | +3. **Reduce notification volume** - only send important messages |
| 228 | +4. **Add config option** for users to override (optional) |
| 229 | +5. **Document behavior** for different clients |
| 230 | + |
| 231 | +## Alternative: Feature Flag |
| 232 | + |
| 233 | +Add to config.json: |
| 234 | +```json |
| 235 | +{ |
| 236 | + "logging": { |
| 237 | + "enabled": true, |
| 238 | + "useNotifications": false, // false = stderr only |
| 239 | + "level": "info", |
| 240 | + "clients": { |
| 241 | + "cline": { "useNotifications": false, "level": "warning" }, |
| 242 | + "claude-desktop": { "useNotifications": true, "level": "info" } |
| 243 | + } |
| 244 | + } |
| 245 | +} |
| 246 | +``` |
| 247 | + |
| 248 | +## Conclusion |
| 249 | + |
| 250 | +**There is NO way to send invisible logging via JSON-RPC in MCP.** The only options are: |
| 251 | + |
| 252 | +1. ✅ **Use stderr** (standard but has client compatibility issues) |
| 253 | +2. ✅ **Client detection** (best automatic solution) |
| 254 | +3. ✅ **Reduce notification frequency** (simple and effective) |
| 255 | +4. ✅ **Make it configurable** (most flexible) |
| 256 | +5. ❌ **Custom notification method** (violates spec) |
| 257 | + |
| 258 | +The best solution is a **combination of #2, #3, and #4**: detect the client, reduce notifications, and allow configuration override. |
0 commit comments