Skip to content

Commit 2b785ed

Browse files
authored
Fix: Disable notifications for Cline to prevent UI clutter (#269)
* v0.2.20 * Add documentation explaining custom stdio server implementation * Research: Cline notification problem and alternative logging solutions * EXPERIMENTAL: Test custom log/info notification method to avoid Cline UI notifications * Change custom notification method from 'log/info' to simpler 'log' * Update test documentation with results - 'log' method works in Claude Desktop * Add summary document for custom log method solution * Revert "EXPERIMENTAL: Test custom log/info notification method to avoid Cline UI notifications" This reverts commit 1c03e97. * Implement client detection to disable notifications for Cline * Add documentation for client detection solution * Add PR summary document * remove md files
1 parent e3afba4 commit 2b785ed

File tree

9 files changed

+629
-6
lines changed

9 files changed

+629
-6
lines changed

.claude/settings.local.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"permissions": {
3+
"allow": [
4+
"Bash(git log:*)",
5+
"Bash(npm run build:*)",
6+
"Bash(chmod:*)",
7+
"Bash(node:*)"
8+
],
9+
"deny": [],
10+
"ask": []
11+
}
12+
}

CLINE_NOTIFICATION_PROBLEM.md

Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
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

Comments
 (0)