@wonderwhy-er/desktop-commander 0.2.14 → 0.2.16

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
@@ -86,6 +86,11 @@ For debugging mode (allows Node.js inspector connection):
86
86
  ```
87
87
  npx @wonderwhy-er/desktop-commander@latest setup --debug
88
88
  ```
89
+
90
+ **Command line options during setup:**
91
+ - `--debug`: Enable debugging mode for Node.js inspector
92
+ - `--no-onboarding`: Disable onboarding prompts for new users
93
+
89
94
  Restart Claude if running.
90
95
 
91
96
  **✅ Auto-Updates:** Yes - automatically updates when you restart Claude
@@ -652,6 +657,44 @@ set_config_value({ "key": "fileWriteLineLimit", "value": 25 })
652
657
 
653
658
  4. **Always verify configuration after changes**: Use `get_config({})` to confirm your changes were applied correctly.
654
659
 
660
+ ## Command Line Options
661
+
662
+ Desktop Commander supports several command line options for customizing behavior:
663
+
664
+ ### Disable Onboarding
665
+
666
+ By default, Desktop Commander shows helpful onboarding prompts to new users (those with fewer than 10 tool calls). You can disable this behavior:
667
+
668
+ ```bash
669
+ # Disable onboarding for this session
670
+ node dist/index.js --no-onboarding
671
+
672
+ # Or if using npm scripts
673
+ npm run start:no-onboarding
674
+
675
+ # For npx installations, modify your claude_desktop_config.json:
676
+ {
677
+ "mcpServers": {
678
+ "desktop-commander": {
679
+ "command": "npx",
680
+ "args": [
681
+ "-y",
682
+ "@wonderwhy-er/desktop-commander@latest",
683
+ "--no-onboarding"
684
+ ]
685
+ }
686
+ }
687
+ }
688
+ ```
689
+
690
+ **When onboarding is automatically disabled:**
691
+ - When the MCP client name is set to "desktop-commander"
692
+ - When using the `--no-onboarding` flag
693
+ - After users have used onboarding prompts or made 10+ tool calls
694
+
695
+ **Debug information:**
696
+ The server will log when onboarding is disabled: `"Onboarding disabled via --no-onboarding flag"`
697
+
655
698
  ## Using Different Shells
656
699
 
657
700
  You can specify which shell to use for command execution:
@@ -911,7 +954,7 @@ External telemetry (sent to analytics services) is enabled by default but can be
911
954
 
912
955
  **Note:** This only disables external telemetry. Local usage analytics remain active for tool functionality but is not share externally
913
956
 
914
- For complete details about data collection, please see our [Privacy Policy](PRIVACY.md).
957
+ For complete details about data collection, please see our [Privacy Policy](https://legal.desktopcommander.app/privacy_desktop_commander_mcp).
915
958
 
916
959
  ## Verifications
917
960
  [![Verified on MseeP](https://mseep.ai/badge.svg)](https://mseep.ai/app/25ff7a06-58bc-40b8-bd79-ebb715140f1a)
@@ -22,14 +22,16 @@ export class FilteredStdioServerTransport extends StdioServerTransport {
22
22
  this.setupConsoleRedirection();
23
23
  // Setup stdout filtering for any other output
24
24
  this.setupStdoutFiltering();
25
- // Send initialization notification
26
- this.sendLogNotification('info', ['Enhanced FilteredStdioServerTransport initialized']);
25
+ // Note: We defer the initialization notification until enableNotifications() is called
26
+ // to ensure MCP protocol compliance - notifications must not be sent before initialization
27
27
  }
28
28
  /**
29
29
  * Call this method after MCP initialization is complete to enable JSON-RPC notifications
30
30
  */
31
31
  enableNotifications() {
32
32
  this.isInitialized = true;
33
+ // Send the deferred initialization notification first
34
+ this.sendLogNotification('info', ['Enhanced FilteredStdioServerTransport initialized']);
33
35
  // Replay all buffered messages in chronological order
34
36
  if (this.messageBuffer.length > 0) {
35
37
  this.sendLogNotification('info', [`Replaying ${this.messageBuffer.length} buffered initialization messages`]);
@@ -132,6 +132,10 @@ export async function handleGetMoreSearchResults(args) {
132
132
  }
133
133
  if (results.isComplete) {
134
134
  output += `\n✅ Search completed.`;
135
+ // Warn users if search was incomplete due to permission issues
136
+ if (results.wasIncomplete) {
137
+ output += `\n⚠️ Warning: Some files were inaccessible due to permissions. Results may be incomplete.`;
138
+ }
135
139
  }
136
140
  return {
137
141
  content: [{ type: "text", text: output }],
package/dist/index.js CHANGED
@@ -1,11 +1,16 @@
1
1
  #!/usr/bin/env node
2
2
  import { FilteredStdioServerTransport } from './custom-stdio.js';
3
- import { server } from './server.js';
3
+ import { server, flushDeferredMessages } from './server.js';
4
4
  import { configManager } from './config-manager.js';
5
5
  import { runSetup } from './npm-scripts/setup.js';
6
6
  import { runUninstall } from './npm-scripts/uninstall.js';
7
7
  import { capture } from './utils/capture.js';
8
8
  import { logToStderr, logger } from './utils/logger.js';
9
+ // Store messages to defer until after initialization
10
+ const deferredMessages = [];
11
+ function deferLog(level, message) {
12
+ deferredMessages.push({ level, message });
13
+ }
9
14
  async function runServer() {
10
15
  try {
11
16
  // Check if first argument is "setup"
@@ -18,17 +23,24 @@ async function runServer() {
18
23
  await runUninstall();
19
24
  return;
20
25
  }
26
+ // Parse command line arguments for onboarding control
27
+ const DISABLE_ONBOARDING = process.argv.includes('--no-onboarding');
28
+ if (DISABLE_ONBOARDING) {
29
+ logToStderr('info', 'Onboarding disabled via --no-onboarding flag');
30
+ }
31
+ // Set global flag for onboarding control
32
+ global.disableOnboarding = DISABLE_ONBOARDING;
21
33
  try {
22
- logToStderr('info', 'Loading configuration...');
34
+ deferLog('info', 'Loading configuration...');
23
35
  await configManager.loadConfig();
24
- logToStderr('info', 'Configuration loaded successfully');
36
+ deferLog('info', 'Configuration loaded successfully');
25
37
  }
26
38
  catch (configError) {
27
- logToStderr('error', `Failed to load configuration: ${configError instanceof Error ? configError.message : String(configError)}`);
39
+ deferLog('error', `Failed to load configuration: ${configError instanceof Error ? configError.message : String(configError)}`);
28
40
  if (configError instanceof Error && configError.stack) {
29
- logToStderr('debug', `Stack trace: ${configError.stack}`);
41
+ deferLog('debug', `Stack trace: ${configError.stack}`);
30
42
  }
31
- logToStderr('warning', 'Continuing with in-memory configuration only');
43
+ deferLog('warning', 'Continuing with in-memory configuration only');
32
44
  // Continue anyway - we'll use an in-memory config
33
45
  }
34
46
  const transport = new FilteredStdioServerTransport();
@@ -63,17 +75,23 @@ async function runServer() {
63
75
  process.exit(1);
64
76
  });
65
77
  capture('run_server_start');
66
- logToStderr('info', 'Connecting server...');
78
+ deferLog('info', 'Connecting server...');
67
79
  // Set up event-driven initialization completion handler
68
80
  server.oninitialized = () => {
69
81
  // This callback is triggered after the client sends the "initialized" notification
70
82
  // At this point, the MCP protocol handshake is fully complete
71
83
  transport.enableNotifications();
72
- // Use the transport to send a proper JSON-RPC notification
73
- transport.sendLog('info', 'MCP fully initialized, notifications enabled');
84
+ // Flush all deferred messages from both index.ts and server.ts
85
+ while (deferredMessages.length > 0) {
86
+ const msg = deferredMessages.shift();
87
+ transport.sendLog('info', msg.message);
88
+ }
89
+ flushDeferredMessages();
90
+ // Now we can send regular logging messages
91
+ transport.sendLog('info', 'Server connected successfully');
92
+ transport.sendLog('info', 'MCP fully initialized, all startup messages sent');
74
93
  };
75
94
  await server.connect(transport);
76
- logToStderr('info', 'Server connected successfully');
77
95
  }
78
96
  catch (error) {
79
97
  const errorMessage = error instanceof Error ? error.message : String(error);
@@ -18,6 +18,7 @@ export interface SearchSession {
18
18
  buffer: string;
19
19
  totalMatches: number;
20
20
  totalContextLines: number;
21
+ wasIncomplete?: boolean;
21
22
  }
22
23
  export interface SearchSessionOptions {
23
24
  rootPath: string;
@@ -64,6 +65,7 @@ export interface SearchSessionOptions {
64
65
  error?: string;
65
66
  hasMoreResults: boolean;
66
67
  runtime: number;
68
+ wasIncomplete?: boolean;
67
69
  };
68
70
  /**
69
71
  * Terminate a search session (like force_terminate)
@@ -105,4 +107,3 @@ export interface SearchSessionOptions {
105
107
  private parseLine;
106
108
  }
107
109
  export declare const searchManager: SearchManager;
108
- export declare function stopSearchManagerCleanup(): void;
@@ -120,7 +120,8 @@ import { capture } from './utils/capture.js';
120
120
  isError: session.isError && !!session.error?.trim(), // Only error if we have actual errors
121
121
  error: session.error?.trim() || undefined,
122
122
  hasMoreResults: false, // Tail always returns what's available
123
- runtime: Date.now() - session.startTime
123
+ runtime: Date.now() - session.startTime,
124
+ wasIncomplete: session.wasIncomplete
124
125
  };
125
126
  }
126
127
  // Handle positive offsets (range behavior) - like file reading
@@ -136,7 +137,8 @@ import { capture } from './utils/capture.js';
136
137
  isError: session.isError && !!session.error?.trim(), // Only error if we have actual errors
137
138
  error: session.error?.trim() || undefined,
138
139
  hasMoreResults,
139
- runtime: Date.now() - session.startTime
140
+ runtime: Date.now() - session.startTime,
141
+ wasIncomplete: session.wasIncomplete
140
142
  };
141
143
  }
142
144
  /**
@@ -149,7 +151,6 @@ import { capture } from './utils/capture.js';
149
151
  }
150
152
  if (!session.process.killed) {
151
153
  session.process.kill('SIGTERM');
152
- capture('search_session_terminated', { sessionId });
153
154
  }
154
155
  // Don't delete session immediately - let user read final results
155
156
  // It will be cleaned up by cleanup process
@@ -178,7 +179,6 @@ import { capture } from './utils/capture.js';
178
179
  for (const [sessionId, session] of this.sessions) {
179
180
  if (session.isComplete && session.lastReadTime < cutoffTime) {
180
181
  this.sessions.delete(sessionId);
181
- capture('search_session_cleaned_up', { sessionId });
182
182
  }
183
183
  }
184
184
  }
@@ -289,6 +289,9 @@ import { capture } from './utils/capture.js';
289
289
  });
290
290
  process.stderr?.on('data', (data) => {
291
291
  const errorText = data.toString();
292
+ // Store error text for potential user display, but don't capture individual errors
293
+ // We'll capture incomplete search status in the completion event instead
294
+ session.error = (session.error || '') + errorText;
292
295
  // Filter meaningful errors
293
296
  const filteredErrors = errorText
294
297
  .split('\n')
@@ -320,6 +323,11 @@ import { capture } from './utils/capture.js';
320
323
  this.processBufferedOutput(session, true);
321
324
  }
322
325
  session.isComplete = true;
326
+ // Track if search was incomplete due to access issues
327
+ // Ripgrep exit code 2 means "some files couldn't be searched"
328
+ if (code === 2) {
329
+ session.wasIncomplete = true;
330
+ }
323
331
  // Only treat as error if:
324
332
  // 1. Unexpected exit code (not 0, 1, or 2) AND
325
333
  // 2. We have meaningful errors after filtering AND
@@ -340,7 +348,8 @@ import { capture } from './utils/capture.js';
340
348
  exitCode: code,
341
349
  totalResults: session.totalMatches + session.totalContextLines,
342
350
  totalMatches: session.totalMatches,
343
- runtime: Date.now() - session.startTime
351
+ runtime: Date.now() - session.startTime,
352
+ wasIncomplete: session.wasIncomplete || false // NEW: Track incomplete searches
344
353
  });
345
354
  // Rely on cleanupSessions(maxAge) only; no per-session timer
346
355
  });
@@ -348,10 +357,6 @@ import { capture } from './utils/capture.js';
348
357
  session.isComplete = true;
349
358
  session.isError = true;
350
359
  session.error = `Process error: ${error.message}`;
351
- capture('search_session_process_error', {
352
- sessionId: session.id,
353
- error: error.message
354
- });
355
360
  // Rely on cleanupSessions(maxAge) only; no per-session timer
356
361
  });
357
362
  }
@@ -462,10 +467,3 @@ function startCleanupIfNeeded() {
462
467
  }, 1000);
463
468
  }
464
469
  }
465
- // Export cleanup function for graceful shutdown
466
- export function stopSearchManagerCleanup() {
467
- if (cleanupInterval) {
468
- clearInterval(cleanupInterval);
469
- cleanupInterval = null;
470
- }
471
- }
package/dist/server.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
2
+ export declare function flushDeferredMessages(): void;
2
3
  export declare const server: Server<{
3
4
  method: string;
4
5
  params?: {
package/dist/server.js CHANGED
@@ -18,8 +18,20 @@ import { usageTracker } from './utils/usageTracker.js';
18
18
  import { processDockerPrompt } from './utils/dockerPrompt.js';
19
19
  import { VERSION } from './version.js';
20
20
  import { capture, capture_call_tool } from "./utils/capture.js";
21
- import { logToStderr } from './utils/logger.js';
22
- logToStderr('info', 'Loading server.ts');
21
+ import { logToStderr, logger } from './utils/logger.js';
22
+ // Store startup messages to send after initialization
23
+ const deferredMessages = [];
24
+ function deferLog(level, message) {
25
+ deferredMessages.push({ level, message });
26
+ }
27
+ // Function to flush deferred messages after initialization
28
+ export function flushDeferredMessages() {
29
+ while (deferredMessages.length > 0) {
30
+ const msg = deferredMessages.shift();
31
+ logger.info(msg.message);
32
+ }
33
+ }
34
+ deferLog('info', 'Loading server.ts');
23
35
  export const server = new Server({
24
36
  name: "desktop-commander",
25
37
  version: VERSION,
@@ -57,8 +69,8 @@ server.setRequestHandler(InitializeRequestSchema, async (request) => {
57
69
  name: clientInfo.name || 'unknown',
58
70
  version: clientInfo.version || 'unknown'
59
71
  };
60
- // Send JSON-RPC notification about client connection
61
- logToStderr('info', `Client connected: ${currentClient.name} v${currentClient.version}`);
72
+ // Defer client connection message until after initialization
73
+ deferLog('info', `Client connected: ${currentClient.name} v${currentClient.version}`);
62
74
  }
63
75
  // Return standard initialization response
64
76
  return {
@@ -82,7 +94,7 @@ server.setRequestHandler(InitializeRequestSchema, async (request) => {
82
94
  });
83
95
  // Export current client info for access by other modules
84
96
  export { currentClient };
85
- logToStderr('info', 'Setting up request handlers...');
97
+ deferLog('info', 'Setting up request handlers...');
86
98
  server.setRequestHandler(ListToolsRequestSchema, async () => {
87
99
  try {
88
100
  logToStderr('debug', 'Generating tools list...');
@@ -105,6 +117,10 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
105
117
  - systemInfo (operating system and environment details)
106
118
  ${CMD_PREFIX_DESCRIPTION}`,
107
119
  inputSchema: zodToJsonSchema(GetConfigArgsSchema),
120
+ annotations: {
121
+ title: "Get Configuration",
122
+ readOnlyHint: true,
123
+ },
108
124
  },
109
125
  {
110
126
  name: "set_config_value",
@@ -127,6 +143,12 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
127
143
 
128
144
  ${CMD_PREFIX_DESCRIPTION}`,
129
145
  inputSchema: zodToJsonSchema(SetConfigValueArgsSchema),
146
+ annotations: {
147
+ title: "Set Configuration Value",
148
+ readOnlyHint: false,
149
+ destructiveHint: true,
150
+ openWorldHint: false,
151
+ },
130
152
  },
131
153
  // Filesystem tools
132
154
  {
@@ -165,6 +187,11 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
165
187
  ${PATH_GUIDANCE}
166
188
  ${CMD_PREFIX_DESCRIPTION}`,
167
189
  inputSchema: zodToJsonSchema(ReadFileArgsSchema),
190
+ annotations: {
191
+ title: "Read File or URL",
192
+ readOnlyHint: true,
193
+ openWorldHint: true,
194
+ },
168
195
  },
169
196
  {
170
197
  name: "read_multiple_files",
@@ -181,6 +208,10 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
181
208
  ${PATH_GUIDANCE}
182
209
  ${CMD_PREFIX_DESCRIPTION}`,
183
210
  inputSchema: zodToJsonSchema(ReadMultipleFilesArgsSchema),
211
+ annotations: {
212
+ title: "Read Multiple Files",
213
+ readOnlyHint: true,
214
+ },
184
215
  },
185
216
  {
186
217
  name: "write_file",
@@ -214,6 +245,12 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
214
245
  ${PATH_GUIDANCE}
215
246
  ${CMD_PREFIX_DESCRIPTION}`,
216
247
  inputSchema: zodToJsonSchema(WriteFileArgsSchema),
248
+ annotations: {
249
+ title: "Write File",
250
+ readOnlyHint: false,
251
+ destructiveHint: true,
252
+ openWorldHint: false,
253
+ },
217
254
  },
218
255
  {
219
256
  name: "create_directory",
@@ -239,6 +276,10 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
239
276
  ${PATH_GUIDANCE}
240
277
  ${CMD_PREFIX_DESCRIPTION}`,
241
278
  inputSchema: zodToJsonSchema(ListDirectoryArgsSchema),
279
+ annotations: {
280
+ title: "List Directory Contents",
281
+ readOnlyHint: true,
282
+ },
242
283
  },
243
284
  {
244
285
  name: "move_file",
@@ -251,6 +292,12 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
251
292
  ${PATH_GUIDANCE}
252
293
  ${CMD_PREFIX_DESCRIPTION}`,
253
294
  inputSchema: zodToJsonSchema(MoveFileArgsSchema),
295
+ annotations: {
296
+ title: "Move/Rename File",
297
+ readOnlyHint: false,
298
+ destructiveHint: true,
299
+ openWorldHint: false,
300
+ },
254
301
  },
255
302
  {
256
303
  name: "start_search",
@@ -360,6 +407,10 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
360
407
 
361
408
  ${CMD_PREFIX_DESCRIPTION}`,
362
409
  inputSchema: zodToJsonSchema(GetMoreSearchResultsArgsSchema),
410
+ annotations: {
411
+ title: "Get Search Results",
412
+ readOnlyHint: true,
413
+ },
363
414
  },
364
415
  {
365
416
  name: "stop_search",
@@ -387,6 +438,10 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
387
438
 
388
439
  ${CMD_PREFIX_DESCRIPTION}`,
389
440
  inputSchema: zodToJsonSchema(ListSearchesArgsSchema),
441
+ annotations: {
442
+ title: "List Active Searches",
443
+ readOnlyHint: true,
444
+ },
390
445
  },
391
446
  {
392
447
  name: "get_file_info",
@@ -406,6 +461,10 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
406
461
  ${PATH_GUIDANCE}
407
462
  ${CMD_PREFIX_DESCRIPTION}`,
408
463
  inputSchema: zodToJsonSchema(GetFileInfoArgsSchema),
464
+ annotations: {
465
+ title: "Get File Information",
466
+ readOnlyHint: true,
467
+ },
409
468
  },
410
469
  // Note: list_allowed_directories removed - use get_config to check allowedDirectories
411
470
  // Text editing tools
@@ -445,6 +504,12 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
445
504
  ${PATH_GUIDANCE}
446
505
  ${CMD_PREFIX_DESCRIPTION}`,
447
506
  inputSchema: zodToJsonSchema(EditBlockArgsSchema),
507
+ annotations: {
508
+ title: "Edit Text Block",
509
+ readOnlyHint: false,
510
+ destructiveHint: true,
511
+ openWorldHint: false,
512
+ },
448
513
  },
449
514
  // Terminal tools
450
515
  {
@@ -501,6 +566,12 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
501
566
  ${PATH_GUIDANCE}
502
567
  ${CMD_PREFIX_DESCRIPTION}`,
503
568
  inputSchema: zodToJsonSchema(StartProcessArgsSchema),
569
+ annotations: {
570
+ title: "Start Terminal Process",
571
+ readOnlyHint: false,
572
+ destructiveHint: true,
573
+ openWorldHint: true,
574
+ },
504
575
  },
505
576
  {
506
577
  name: "read_process_output",
@@ -528,6 +599,10 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
528
599
 
529
600
  ${CMD_PREFIX_DESCRIPTION}`,
530
601
  inputSchema: zodToJsonSchema(ReadProcessOutputArgsSchema),
602
+ annotations: {
603
+ title: "Read Process Output",
604
+ readOnlyHint: true,
605
+ },
531
606
  },
532
607
  {
533
608
  name: "interact_with_process",
@@ -580,6 +655,12 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
580
655
 
581
656
  ${CMD_PREFIX_DESCRIPTION}`,
582
657
  inputSchema: zodToJsonSchema(InteractWithProcessArgsSchema),
658
+ annotations: {
659
+ title: "Send Input to Process",
660
+ readOnlyHint: false,
661
+ destructiveHint: true,
662
+ openWorldHint: true,
663
+ },
583
664
  },
584
665
  {
585
666
  name: "force_terminate",
@@ -588,6 +669,12 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
588
669
 
589
670
  ${CMD_PREFIX_DESCRIPTION}`,
590
671
  inputSchema: zodToJsonSchema(ForceTerminateArgsSchema),
672
+ annotations: {
673
+ title: "Force Terminate Process",
674
+ readOnlyHint: false,
675
+ destructiveHint: true,
676
+ openWorldHint: false,
677
+ },
591
678
  },
592
679
  {
593
680
  name: "list_sessions",
@@ -606,6 +693,10 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
606
693
 
607
694
  ${CMD_PREFIX_DESCRIPTION}`,
608
695
  inputSchema: zodToJsonSchema(ListSessionsArgsSchema),
696
+ annotations: {
697
+ title: "List Terminal Sessions",
698
+ readOnlyHint: true,
699
+ },
609
700
  },
610
701
  {
611
702
  name: "list_processes",
@@ -616,6 +707,10 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
616
707
 
617
708
  ${CMD_PREFIX_DESCRIPTION}`,
618
709
  inputSchema: zodToJsonSchema(ListProcessesArgsSchema),
710
+ annotations: {
711
+ title: "List Running Processes",
712
+ readOnlyHint: true,
713
+ },
619
714
  },
620
715
  {
621
716
  name: "kill_process",
@@ -626,6 +721,12 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
626
721
 
627
722
  ${CMD_PREFIX_DESCRIPTION}`,
628
723
  inputSchema: zodToJsonSchema(KillProcessArgsSchema),
724
+ annotations: {
725
+ title: "Kill Process",
726
+ readOnlyHint: false,
727
+ destructiveHint: true,
728
+ openWorldHint: false,
729
+ },
629
730
  },
630
731
  {
631
732
  name: "get_usage_stats",
@@ -636,6 +737,10 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
636
737
 
637
738
  ${CMD_PREFIX_DESCRIPTION}`,
638
739
  inputSchema: zodToJsonSchema(GetUsageStatsArgsSchema),
740
+ annotations: {
741
+ title: "Get Usage Statistics",
742
+ readOnlyHint: true,
743
+ },
639
744
  },
640
745
  {
641
746
  name: "give_feedback_to_desktop_commander",
@@ -14,17 +14,7 @@ export async function getConfig() {
14
14
  const configWithSystemInfo = {
15
15
  ...config,
16
16
  currentClient,
17
- systemInfo: {
18
- platform: systemInfo.platform,
19
- platformName: systemInfo.platformName,
20
- defaultShell: systemInfo.defaultShell,
21
- pathSeparator: systemInfo.pathSeparator,
22
- isWindows: systemInfo.isWindows,
23
- isMacOS: systemInfo.isMacOS,
24
- isLinux: systemInfo.isLinux,
25
- docker: systemInfo.docker,
26
- examplePaths: systemInfo.examplePaths
27
- }
17
+ systemInfo
28
18
  };
29
19
  console.error(`getConfig result: ${JSON.stringify(configWithSystemInfo, null, 2)}`);
30
20
  return {
package/dist/types.d.ts CHANGED
@@ -2,6 +2,7 @@ import { ChildProcess } from 'child_process';
2
2
  import { FilteredStdioServerTransport } from './custom-stdio.js';
3
3
  declare global {
4
4
  var mcpTransport: FilteredStdioServerTransport | undefined;
5
+ var disableOnboarding: boolean | undefined;
5
6
  }
6
7
  export interface ProcessInfo {
7
8
  pid: number;
@@ -29,6 +29,18 @@ export interface SystemInfo {
29
29
  isMacOS: boolean;
30
30
  isLinux: boolean;
31
31
  docker: ContainerInfo;
32
+ isDXT: boolean;
33
+ nodeInfo?: {
34
+ version: string;
35
+ path: string;
36
+ npmVersion?: string;
37
+ };
38
+ processInfo: {
39
+ pid: number;
40
+ arch: string;
41
+ platform: string;
42
+ versions: NodeJS.ProcessVersions;
43
+ };
32
44
  examplePaths: {
33
45
  home: string;
34
46
  temp: string;
@@ -305,6 +305,27 @@ function getContainerEnvironment(containerType) {
305
305
  }
306
306
  return Object.keys(env).length > 0 ? env : undefined;
307
307
  }
308
+ /**
309
+ * Detect Node.js installation and version from current process
310
+ */
311
+ function detectNodeInfo() {
312
+ try {
313
+ // Get Node.js version from current process
314
+ const version = process.version.replace('v', ''); // Remove 'v' prefix
315
+ // Get Node.js executable path from current process
316
+ const path = process.execPath;
317
+ // Get npm version from environment if available
318
+ const npmVersion = process.env.npm_version;
319
+ return {
320
+ version,
321
+ path,
322
+ ...(npmVersion && { npmVersion })
323
+ };
324
+ }
325
+ catch (error) {
326
+ return undefined;
327
+ }
328
+ }
308
329
  /**
309
330
  * Get comprehensive system information for tool prompts
310
331
  */
@@ -397,6 +418,15 @@ export function getSystemInfo() {
397
418
  examplePaths.accessible = mountPoints.map(mount => mount.containerPath);
398
419
  }
399
420
  }
421
+ // Detect Node.js installation from current process
422
+ const nodeInfo = detectNodeInfo();
423
+ // Get process information
424
+ const processInfo = {
425
+ pid: process.pid,
426
+ arch: process.arch,
427
+ platform: process.platform,
428
+ versions: process.versions
429
+ };
400
430
  return {
401
431
  platform,
402
432
  platformName,
@@ -415,6 +445,9 @@ export function getSystemInfo() {
415
445
  mountPoints,
416
446
  containerEnvironment: getContainerEnvironment(containerDetection.containerType)
417
447
  },
448
+ isDXT: !!process.env.MCP_DXT,
449
+ nodeInfo,
450
+ processInfo,
418
451
  examplePaths
419
452
  };
420
453
  }
@@ -545,33 +578,51 @@ LINUX-SPECIFIC NOTES:
545
578
  * Get common development tool guidance based on OS
546
579
  */
547
580
  export function getDevelopmentToolGuidance(systemInfo) {
548
- const { isWindows, isMacOS, isLinux, platformName } = systemInfo;
581
+ const { isWindows, isMacOS, isLinux, platformName, nodeInfo, processInfo } = systemInfo;
582
+ // Add detected Node.js info to guidance
583
+ const nodeGuidance = nodeInfo
584
+ ? `Node.js: v${nodeInfo.version} (${nodeInfo.path})${nodeInfo.npmVersion ? ` | npm: v${nodeInfo.npmVersion}` : ''}`
585
+ : 'Node.js: Not detected';
586
+ // Add process environment info
587
+ const envInfo = `
588
+ Current Process Environment:
589
+ - Node: v${processInfo.versions.node}
590
+ - V8: v${processInfo.versions.v8}
591
+ - Architecture: ${processInfo.arch}
592
+ - Platform: ${processInfo.platform}
593
+ - Process ID: ${processInfo.pid}`;
549
594
  if (isWindows) {
550
595
  return `
551
596
  COMMON WINDOWS DEVELOPMENT TOOLS:
552
- - Node.js: Usually installed globally, accessible from any shell
597
+ - ${nodeGuidance}
553
598
  - Python: May be 'python' or 'py' command, check both
554
599
  - Git: Git Bash provides Unix-like environment
555
600
  - WSL: Windows Subsystem for Linux available for Unix tools
556
- - Visual Studio tools: cl, msbuild for C++ compilation`;
601
+ - Visual Studio tools: cl, msbuild for C++ compilation
602
+
603
+ ${envInfo}`;
557
604
  }
558
605
  else if (isMacOS) {
559
606
  return `
560
607
  COMMON MACOS DEVELOPMENT TOOLS:
561
608
  - Xcode Command Line Tools: Required for many development tools
562
609
  - Homebrew: Primary package manager for development tools
610
+ - ${nodeGuidance}
563
611
  - Python: Usually python3, check if python points to Python 2
564
- - Node.js: Available via brew or direct installer
565
- - Ruby: System Ruby available, rbenv/rvm for version management`;
612
+ - Ruby: System Ruby available, rbenv/rvm for version management
613
+
614
+ ${envInfo}`;
566
615
  }
567
616
  else {
568
617
  return `
569
618
  COMMON LINUX DEVELOPMENT TOOLS:
570
619
  - Package managers: Install tools via distribution package manager
571
620
  - Python: Usually python3, python may point to Python 2
572
- - Node.js: Available via package manager or NodeSource repository
621
+ - ${nodeGuidance}
573
622
  - Build tools: gcc, make typically available or easily installed
574
- - Container tools: docker, podman common for development`;
623
+ - Container tools: docker, podman common for development
624
+
625
+ ${envInfo}`;
575
626
  }
576
627
  }
577
628
  /**
@@ -299,6 +299,21 @@ class UsageTracker {
299
299
  * Check if user should see onboarding invitation - SIMPLE VERSION
300
300
  */
301
301
  async shouldShowOnboarding() {
302
+ // Check if onboarding is disabled via command line argument
303
+ if (global.disableOnboarding) {
304
+ return false;
305
+ }
306
+ // Check if client is desktop-commander (disable for this client)
307
+ try {
308
+ const { currentClient } = await import('../server.js');
309
+ if (currentClient?.name === 'desktop-commander') {
310
+ return false;
311
+ }
312
+ }
313
+ catch (error) {
314
+ // If we can't import server, continue with other checks
315
+ console.log('[ONBOARDING DEBUG] Could not check client name, continuing...');
316
+ }
302
317
  const stats = await this.getStats();
303
318
  const onboardingState = await this.getOnboardingState();
304
319
  const now = Date.now();
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const VERSION = "0.2.14";
1
+ export declare const VERSION = "0.2.16";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const VERSION = '0.2.14';
1
+ export const VERSION = '0.2.16';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wonderwhy-er/desktop-commander",
3
- "version": "0.2.14",
3
+ "version": "0.2.16",
4
4
  "description": "MCP server for terminal operations and file editing",
5
5
  "mcpName": "io.github.wonderwhy-er/desktop-commander",
6
6
  "license": "MIT",
@@ -42,6 +42,7 @@
42
42
  "link:local": "npm run build && npm link",
43
43
  "unlink:local": "npm unlink",
44
44
  "inspector": "npx @modelcontextprotocol/inspector dist/index.js",
45
+ "build:mcpb": "node scripts/build-mcpb.cjs",
45
46
  "logs:view": "npm run build && node scripts/view-fuzzy-logs.js",
46
47
  "logs:analyze": "npm run build && node scripts/analyze-fuzzy-logs.js",
47
48
  "logs:clear": "npm run build && node scripts/clear-fuzzy-logs.js",