@wonderwhy-er/desktop-commander 0.2.4 → 0.2.5

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 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
- ### URL Support
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
+ ![docker-config.png](screenshots/docker-config.png)
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
- Telemetry is enabled by default. To opt out:
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
+ [![Verified on MseeP](https://mseep.ai/badge.svg)](https://mseep.ai/app/25ff7a06-58bc-40b8-bd79-ebb715140f1a)
683
+
638
684
  ## License
639
685
 
640
- MIT
686
+ MIT
@@ -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 {};
@@ -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();
@@ -1,8 +1,29 @@
1
1
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
2
2
  /**
3
- * Extended StdioServerTransport that filters out non-JSON messages.
4
- * This prevents the "Watching /" error from crashing the server.
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
  }
@@ -1,22 +1,177 @@
1
1
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
2
2
  import process from "node:process";
3
3
  /**
4
- * Extended StdioServerTransport that filters out non-JSON messages.
5
- * This prevents the "Watching /" error from crashing the server.
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] Initialized FilteredStdioServerTransport\n`);
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
@@ -1,4 +1,8 @@
1
1
  import { ChildProcess } from 'child_process';
2
+ import { FilteredStdioServerTransport } from './custom-stdio.js';
3
+ declare global {
4
+ var mcpTransport: FilteredStdioServerTransport | undefined;
5
+ }
2
6
  export interface ProcessInfo {
3
7
  pid: number;
4
8
  command: string;
@@ -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 = {
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const VERSION = "0.2.4";
1
+ export declare const VERSION = "0.2.5";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const VERSION = '0.2.4';
1
+ export const VERSION = '0.2.5';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wonderwhy-er/desktop-commander",
3
- "version": "0.2.4",
3
+ "version": "0.2.5",
4
4
  "description": "MCP server for terminal operations and file editing",
5
5
  "license": "MIT",
6
6
  "author": "Eduards Ruzga",