@wonderwhy-er/desktop-commander 0.2.4 → 0.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +49 -3
- package/dist/config-manager.d.ts +0 -4
- package/dist/config-manager.js +0 -6
- package/dist/custom-stdio.d.ts +23 -2
- package/dist/custom-stdio.js +167 -12
- package/dist/index.js +2 -0
- package/dist/server.js +2 -0
- package/dist/types.d.ts +4 -0
- package/dist/utils/capture.js +1 -1
- package/dist/utils/usageTracker.js +8 -5
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -22,6 +22,7 @@ Work with code and text, run processes, and automate tasks, going far beyond oth
|
|
|
22
22
|
- [Features](#features)
|
|
23
23
|
- [Installation](#installation)
|
|
24
24
|
- [Usage](#usage)
|
|
25
|
+
- [Docker Support](#docker-support)
|
|
25
26
|
- [Handling Long-Running Commands](#handling-long-running-commands)
|
|
26
27
|
- [Work in Progress and TODOs](#work-in-progress-and-todos)
|
|
27
28
|
- [Sponsors and Supporters](#sponsors-and-supporters)
|
|
@@ -203,6 +204,8 @@ The server provides a comprehensive set of tools organized into several categori
|
|
|
203
204
|
| | `search_code` | Search for text/code patterns within file contents using ripgrep |
|
|
204
205
|
| | `get_file_info` | Retrieve detailed metadata about a file or directory |
|
|
205
206
|
| **Text Editing** | `edit_block` | Apply targeted text replacements with enhanced prompting for smaller edits (includes character-level diff feedback) |
|
|
207
|
+
| **Analytics** | `get_usage_stats` | Get usage statistics for your own insight |
|
|
208
|
+
| | `give_feedback_to_desktop_commander` | Open feedback form in browser to provide feedback to Desktop Commander Team |
|
|
206
209
|
|
|
207
210
|
### Quick Examples
|
|
208
211
|
|
|
@@ -255,7 +258,34 @@ The `edit_block` tool includes several enhancements for better reliability:
|
|
|
255
258
|
|
|
256
259
|
When a search fails, you'll see detailed information about the closest match found, including similarity percentage, execution time, and character differences. All these details are automatically logged for later analysis using the fuzzy search log tools.
|
|
257
260
|
|
|
258
|
-
###
|
|
261
|
+
### Docker Support
|
|
262
|
+
|
|
263
|
+
### 🐳 Isolated Environment Usage
|
|
264
|
+
|
|
265
|
+
Desktop Commander can be run in Docker containers for **complete isolation from your host system**, providing **zero risk to your computer**. This is perfect for testing, development, or when you want complete sandboxing.
|
|
266
|
+
|
|
267
|
+
### Installation Instructions
|
|
268
|
+
|
|
269
|
+
1. **Install Docker for Windows/Mac**
|
|
270
|
+
- Download and install Docker Desktop from [docker.com](https://www.docker.com/products/docker-desktop/)
|
|
271
|
+
|
|
272
|
+
2. **Get Desktop Commander Docker Configuration**
|
|
273
|
+
- Visit: https://hub.docker.com/mcp/server/desktop-commander/manual
|
|
274
|
+
- **Option A:** Use the provided terminal command for automated setup
|
|
275
|
+
- **Option B:** Click "Standalone" to get the config JSON and add it manually to your Claude Desktop config
|
|
276
|
+

|
|
277
|
+
|
|
278
|
+
3. **Mount Your Machine Folders (Coming Soon)**
|
|
279
|
+
- Instructions on how to mount your local directories into the Docker container will be provided soon
|
|
280
|
+
- This will allow you to work with your files while maintaining complete isolation
|
|
281
|
+
|
|
282
|
+
### Benefits of Docker Usage
|
|
283
|
+
- **Complete isolation** from your host system
|
|
284
|
+
- **Consistent environment** across different machines
|
|
285
|
+
- **Easy cleanup** - just remove the container when done
|
|
286
|
+
- **Perfect for testing** new features or configurations
|
|
287
|
+
|
|
288
|
+
## URL Support
|
|
259
289
|
- `read_file` can now fetch content from both local files and URLs
|
|
260
290
|
- Example: `read_file` with `isUrl: true` parameter to read from web resources
|
|
261
291
|
- Handles both text and image content from remote sources
|
|
@@ -627,14 +657,30 @@ Join our [Discord server](https://discord.gg/kQ27sNnZr7) for community support,
|
|
|
627
657
|
|
|
628
658
|
Desktop Commander collects limited anonymous telemetry data to help improve the tool. No personal information, file contents, file paths, or command arguments are collected.
|
|
629
659
|
|
|
630
|
-
|
|
660
|
+
### Usage Analytics (Local Only)
|
|
661
|
+
- **Local usage statistics** are always collected and stored locally on your machine for functionality and the `get_usage_stats` tool
|
|
662
|
+
- Use the `get_usage_stats` tool to view your personal usage patterns, success rates, and performance metrics
|
|
663
|
+
- **This data is NOT sent anywhere** - it remains on your computer for your personal insights
|
|
664
|
+
|
|
665
|
+
### Feedback System
|
|
666
|
+
- Use the `give_feedback_to_desktop_commander` tool to provide feedback about Desktop Commander
|
|
667
|
+
- Opens a browser-based feedback form to send suggestions and feedback to the development team
|
|
668
|
+
- Only basic usage statistics (tool call count, days using, platform) are pre-filled to provide context but you can remove them
|
|
669
|
+
|
|
670
|
+
### External Telemetry Opt-Out
|
|
671
|
+
External telemetry (sent to analytics services) is enabled by default but can be disabled:
|
|
631
672
|
|
|
632
673
|
1. Open the chat and simply ask:
|
|
633
674
|
**"Disable telemetry"**
|
|
634
675
|
2. The chatbot will update your settings automatically.
|
|
635
676
|
|
|
677
|
+
**Note:** This only disables external telemetry. Local usage analytics remain active for tool functionality but is not share externally
|
|
678
|
+
|
|
636
679
|
For complete details about data collection, please see our [Privacy Policy](PRIVACY.md).
|
|
637
680
|
|
|
681
|
+
## Verifications
|
|
682
|
+
[](https://mseep.ai/app/25ff7a06-58bc-40b8-bd79-ebb715140f1a)
|
|
683
|
+
|
|
638
684
|
## License
|
|
639
685
|
|
|
640
|
-
MIT
|
|
686
|
+
MIT
|
package/dist/config-manager.d.ts
CHANGED
|
@@ -57,10 +57,6 @@ declare class ConfigManager {
|
|
|
57
57
|
* Reset configuration to defaults
|
|
58
58
|
*/
|
|
59
59
|
resetConfig(): Promise<ServerConfig>;
|
|
60
|
-
/**
|
|
61
|
-
* Get current client information for analytics
|
|
62
|
-
*/
|
|
63
|
-
getCurrentClientInfo(): ClientInfo | null;
|
|
64
60
|
}
|
|
65
61
|
export declare const configManager: ConfigManager;
|
|
66
62
|
export {};
|
package/dist/config-manager.js
CHANGED
|
@@ -176,12 +176,6 @@ class ConfigManager {
|
|
|
176
176
|
await this.saveConfig();
|
|
177
177
|
return { ...this.config };
|
|
178
178
|
}
|
|
179
|
-
/**
|
|
180
|
-
* Get current client information for analytics
|
|
181
|
-
*/
|
|
182
|
-
getCurrentClientInfo() {
|
|
183
|
-
return this.config.currentClient || null;
|
|
184
|
-
}
|
|
185
179
|
}
|
|
186
180
|
// Export singleton instance
|
|
187
181
|
export const configManager = new ConfigManager();
|
package/dist/custom-stdio.d.ts
CHANGED
|
@@ -1,8 +1,29 @@
|
|
|
1
1
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
3
|
+
* Enhanced StdioServerTransport that wraps console output in valid JSON-RPC structures
|
|
4
|
+
* instead of filtering them out. This prevents crashes while maintaining debug visibility.
|
|
5
5
|
*/
|
|
6
6
|
export declare class FilteredStdioServerTransport extends StdioServerTransport {
|
|
7
|
+
private originalConsole;
|
|
8
|
+
private originalStdoutWrite;
|
|
7
9
|
constructor();
|
|
10
|
+
private setupConsoleRedirection;
|
|
11
|
+
private setupStdoutFiltering;
|
|
12
|
+
private sendLogNotification;
|
|
13
|
+
/**
|
|
14
|
+
* Public method to send log notifications from anywhere in the application
|
|
15
|
+
*/
|
|
16
|
+
sendLog(level: "emergency" | "alert" | "critical" | "error" | "warning" | "notice" | "info" | "debug", message: string, data?: any): void;
|
|
17
|
+
/**
|
|
18
|
+
* Send a progress notification (useful for long-running operations)
|
|
19
|
+
*/
|
|
20
|
+
sendProgress(token: string, value: number, total?: number): void;
|
|
21
|
+
/**
|
|
22
|
+
* Send a custom notification with any method name
|
|
23
|
+
*/
|
|
24
|
+
sendCustomNotification(method: string, params: any): void;
|
|
25
|
+
/**
|
|
26
|
+
* Cleanup method to restore original console methods if needed
|
|
27
|
+
*/
|
|
28
|
+
cleanup(): void;
|
|
8
29
|
}
|
package/dist/custom-stdio.js
CHANGED
|
@@ -1,22 +1,177 @@
|
|
|
1
1
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
2
2
|
import process from "node:process";
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Enhanced StdioServerTransport that wraps console output in valid JSON-RPC structures
|
|
5
|
+
* instead of filtering them out. This prevents crashes while maintaining debug visibility.
|
|
6
6
|
*/
|
|
7
7
|
export class FilteredStdioServerTransport extends StdioServerTransport {
|
|
8
8
|
constructor() {
|
|
9
|
-
// Create a proxy for stdout that only allows valid JSON to pass through
|
|
10
|
-
const originalStdoutWrite = process.stdout.write;
|
|
11
|
-
process.stdout.write = function (buffer) {
|
|
12
|
-
// Only intercept string output that doesn't look like JSON
|
|
13
|
-
if (typeof buffer === 'string' && !buffer.trim().startsWith('{')) {
|
|
14
|
-
return true; //process.stderr.write(buffer);
|
|
15
|
-
}
|
|
16
|
-
return originalStdoutWrite.apply(process.stdout, arguments);
|
|
17
|
-
};
|
|
18
9
|
super();
|
|
10
|
+
// Store original methods
|
|
11
|
+
this.originalConsole = {
|
|
12
|
+
log: console.log,
|
|
13
|
+
warn: console.warn,
|
|
14
|
+
error: console.error,
|
|
15
|
+
debug: console.debug,
|
|
16
|
+
info: console.info,
|
|
17
|
+
};
|
|
18
|
+
this.originalStdoutWrite = process.stdout.write;
|
|
19
|
+
// Setup console redirection
|
|
20
|
+
this.setupConsoleRedirection();
|
|
21
|
+
// Setup stdout filtering for any other output
|
|
22
|
+
this.setupStdoutFiltering();
|
|
19
23
|
// Log initialization to stderr to avoid polluting the JSON stream
|
|
20
|
-
process.stderr.write(`[desktop-commander]
|
|
24
|
+
process.stderr.write(`[desktop-commander] Enhanced FilteredStdioServerTransport initialized\n`);
|
|
25
|
+
}
|
|
26
|
+
setupConsoleRedirection() {
|
|
27
|
+
console.log = (...args) => {
|
|
28
|
+
this.sendLogNotification("info", args);
|
|
29
|
+
};
|
|
30
|
+
console.info = (...args) => {
|
|
31
|
+
this.sendLogNotification("info", args);
|
|
32
|
+
};
|
|
33
|
+
console.warn = (...args) => {
|
|
34
|
+
this.sendLogNotification("warning", args);
|
|
35
|
+
};
|
|
36
|
+
console.error = (...args) => {
|
|
37
|
+
this.sendLogNotification("error", args);
|
|
38
|
+
};
|
|
39
|
+
console.debug = (...args) => {
|
|
40
|
+
this.sendLogNotification("debug", args);
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
setupStdoutFiltering() {
|
|
44
|
+
process.stdout.write = (buffer, encoding, callback) => {
|
|
45
|
+
// Handle different call signatures
|
|
46
|
+
if (typeof buffer === 'string') {
|
|
47
|
+
const trimmed = buffer.trim();
|
|
48
|
+
// Check if this looks like a valid JSON-RPC message
|
|
49
|
+
if (trimmed.startsWith('{') && (trimmed.includes('"jsonrpc"') ||
|
|
50
|
+
trimmed.includes('"method"') ||
|
|
51
|
+
trimmed.includes('"id"'))) {
|
|
52
|
+
// This looks like a valid JSON-RPC message, allow it
|
|
53
|
+
return this.originalStdoutWrite.call(process.stdout, buffer, encoding, callback);
|
|
54
|
+
}
|
|
55
|
+
else if (trimmed.length > 0) {
|
|
56
|
+
// Non-JSON-RPC output, wrap it in a log notification
|
|
57
|
+
this.sendLogNotification("info", [buffer.replace(/\n$/, '')]);
|
|
58
|
+
if (callback)
|
|
59
|
+
callback();
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// For non-string buffers or empty strings, let them through
|
|
64
|
+
return this.originalStdoutWrite.call(process.stdout, buffer, encoding, callback);
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
sendLogNotification(level, args) {
|
|
68
|
+
try {
|
|
69
|
+
// For data, we can send structured data or string according to MCP spec
|
|
70
|
+
let data;
|
|
71
|
+
if (args.length === 1 && typeof args[0] === 'object' && args[0] !== null) {
|
|
72
|
+
// Single object - send as structured data
|
|
73
|
+
data = args[0];
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
// Multiple args or primitives - convert to string
|
|
77
|
+
data = args.map(arg => {
|
|
78
|
+
if (typeof arg === 'object') {
|
|
79
|
+
try {
|
|
80
|
+
return JSON.stringify(arg, null, 2);
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return String(arg);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return String(arg);
|
|
87
|
+
}).join(' ');
|
|
88
|
+
}
|
|
89
|
+
const notification = {
|
|
90
|
+
jsonrpc: "2.0",
|
|
91
|
+
method: "notifications/message",
|
|
92
|
+
params: {
|
|
93
|
+
level: level,
|
|
94
|
+
logger: "desktop-commander",
|
|
95
|
+
data: data
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
// Send as valid JSON-RPC notification
|
|
99
|
+
this.originalStdoutWrite.call(process.stdout, JSON.stringify(notification) + '\n');
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
// Fallback to stderr if JSON serialization fails
|
|
103
|
+
process.stderr.write(`[${level.toUpperCase()}] ${args.join(' ')}\n`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Public method to send log notifications from anywhere in the application
|
|
108
|
+
*/
|
|
109
|
+
sendLog(level, message, data) {
|
|
110
|
+
try {
|
|
111
|
+
const notification = {
|
|
112
|
+
jsonrpc: "2.0",
|
|
113
|
+
method: "notifications/message",
|
|
114
|
+
params: {
|
|
115
|
+
level: level,
|
|
116
|
+
logger: "desktop-commander",
|
|
117
|
+
data: data ? { message, ...data } : message
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
this.originalStdoutWrite.call(process.stdout, JSON.stringify(notification) + '\n');
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
process.stderr.write(`[${level.toUpperCase()}] ${message}\n`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Send a progress notification (useful for long-running operations)
|
|
128
|
+
*/
|
|
129
|
+
sendProgress(token, value, total) {
|
|
130
|
+
try {
|
|
131
|
+
const notification = {
|
|
132
|
+
jsonrpc: "2.0",
|
|
133
|
+
method: "notifications/progress",
|
|
134
|
+
params: {
|
|
135
|
+
progressToken: token,
|
|
136
|
+
value: value,
|
|
137
|
+
...(total && { total })
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
this.originalStdoutWrite.call(process.stdout, JSON.stringify(notification) + '\n');
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
process.stderr.write(`[PROGRESS] ${token}: ${value}${total ? `/${total}` : ''}\n`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Send a custom notification with any method name
|
|
148
|
+
*/
|
|
149
|
+
sendCustomNotification(method, params) {
|
|
150
|
+
try {
|
|
151
|
+
const notification = {
|
|
152
|
+
jsonrpc: "2.0",
|
|
153
|
+
method: method,
|
|
154
|
+
params: params
|
|
155
|
+
};
|
|
156
|
+
this.originalStdoutWrite.call(process.stdout, JSON.stringify(notification) + '\n');
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
process.stderr.write(`[NOTIFICATION] ${method}: ${JSON.stringify(params)}\n`);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Cleanup method to restore original console methods if needed
|
|
164
|
+
*/
|
|
165
|
+
cleanup() {
|
|
166
|
+
if (this.originalConsole) {
|
|
167
|
+
console.log = this.originalConsole.log;
|
|
168
|
+
console.warn = this.originalConsole.warn;
|
|
169
|
+
console.error = this.originalConsole.error;
|
|
170
|
+
console.debug = this.originalConsole.debug;
|
|
171
|
+
console.info = this.originalConsole.info;
|
|
172
|
+
}
|
|
173
|
+
if (this.originalStdoutWrite) {
|
|
174
|
+
process.stdout.write = this.originalStdoutWrite;
|
|
175
|
+
}
|
|
21
176
|
}
|
|
22
177
|
}
|
package/dist/index.js
CHANGED
|
@@ -51,6 +51,8 @@ async function runServer() {
|
|
|
51
51
|
return;
|
|
52
52
|
}
|
|
53
53
|
const transport = new FilteredStdioServerTransport();
|
|
54
|
+
// Export transport for use throughout the application
|
|
55
|
+
global.mcpTransport = transport;
|
|
54
56
|
// Handle uncaught exceptions
|
|
55
57
|
process.on('uncaughtException', async (error) => {
|
|
56
58
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
package/dist/server.js
CHANGED
|
@@ -25,6 +25,7 @@ export const server = new Server({
|
|
|
25
25
|
tools: {},
|
|
26
26
|
resources: {}, // Add empty resources capability
|
|
27
27
|
prompts: {}, // Add empty prompts capability
|
|
28
|
+
logging: {}, // Add logging capability for console redirection
|
|
28
29
|
},
|
|
29
30
|
});
|
|
30
31
|
// Add handler for resources/list method
|
|
@@ -62,6 +63,7 @@ server.setRequestHandler(InitializeRequestSchema, async (request) => {
|
|
|
62
63
|
tools: {},
|
|
63
64
|
resources: {},
|
|
64
65
|
prompts: {},
|
|
66
|
+
logging: {},
|
|
65
67
|
},
|
|
66
68
|
serverInfo: {
|
|
67
69
|
name: "desktop-commander",
|
package/dist/types.d.ts
CHANGED
package/dist/utils/capture.js
CHANGED
|
@@ -2,6 +2,7 @@ import { platform } from 'os';
|
|
|
2
2
|
import { randomUUID } from 'crypto';
|
|
3
3
|
import * as https from 'https';
|
|
4
4
|
import { configManager } from '../config-manager.js';
|
|
5
|
+
import { currentClient } from '../server.js';
|
|
5
6
|
let VERSION = 'unknown';
|
|
6
7
|
try {
|
|
7
8
|
const versionModule = await import('../version.js');
|
|
@@ -78,7 +79,6 @@ export const captureBase = async (captureURL, event, properties) => {
|
|
|
78
79
|
uniqueUserId = await getOrCreateUUID();
|
|
79
80
|
}
|
|
80
81
|
// Get current client information for all events
|
|
81
|
-
const currentClient = configManager.getCurrentClientInfo();
|
|
82
82
|
let clientContext = {};
|
|
83
83
|
if (currentClient) {
|
|
84
84
|
clientContext = {
|
|
@@ -149,18 +149,21 @@ class UsageTracker {
|
|
|
149
149
|
* Check if user should be prompted for feedback based on usage patterns
|
|
150
150
|
*/
|
|
151
151
|
async shouldPromptForFeedback() {
|
|
152
|
+
return false;
|
|
153
|
+
/* TODO Turn off feedback requests until further issue investigation
|
|
152
154
|
const stats = await this.getStats();
|
|
155
|
+
|
|
153
156
|
// Don't prompt if feedback already given (check top-level config)
|
|
154
157
|
const feedbackGiven = await configManager.getValue('feedbackGiven');
|
|
155
|
-
if (feedbackGiven === true)
|
|
156
|
-
|
|
158
|
+
if (feedbackGiven === true) return false;
|
|
159
|
+
|
|
157
160
|
// Check if enough time has passed since last prompt (2 hours minimum)
|
|
158
161
|
const now = Date.now();
|
|
159
162
|
const hoursSinceLastPrompt = (now - stats.lastFeedbackPrompt) / (1000 * 60 * 60);
|
|
160
|
-
if (stats.lastFeedbackPrompt > 0 && hoursSinceLastPrompt < 2)
|
|
161
|
-
|
|
163
|
+
if (stats.lastFeedbackPrompt > 0 && hoursSinceLastPrompt < 2) return false;
|
|
164
|
+
|
|
162
165
|
// MAIN TRIGGER: 25+ total tool calls (earlier trigger for faster feedback)
|
|
163
|
-
return stats.totalToolCalls >= 25
|
|
166
|
+
return stats.totalToolCalls >= 25;*/
|
|
164
167
|
}
|
|
165
168
|
/**
|
|
166
169
|
* Get a random feedback prompt message with strong CTAs and clear actions
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const VERSION = "0.2.
|
|
1
|
+
export declare const VERSION = "0.2.6";
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = '0.2.
|
|
1
|
+
export const VERSION = '0.2.6';
|