@wonderwhy-er/desktop-commander 0.2.3 → 0.2.4

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.
Files changed (43) hide show
  1. package/README.md +25 -3
  2. package/dist/config-manager.d.ts +10 -0
  3. package/dist/config-manager.js +7 -1
  4. package/dist/handlers/edit-search-handlers.js +25 -6
  5. package/dist/handlers/terminal-handlers.d.ts +8 -4
  6. package/dist/handlers/terminal-handlers.js +16 -10
  7. package/dist/index-dxt.d.ts +2 -0
  8. package/dist/index-dxt.js +76 -0
  9. package/dist/index-with-startup-detection.d.ts +5 -0
  10. package/dist/index-with-startup-detection.js +180 -0
  11. package/dist/server.d.ts +5 -0
  12. package/dist/server.js +343 -42
  13. package/dist/terminal-manager.d.ts +7 -0
  14. package/dist/terminal-manager.js +93 -18
  15. package/dist/tools/client.d.ts +10 -0
  16. package/dist/tools/client.js +13 -0
  17. package/dist/tools/config.d.ts +1 -1
  18. package/dist/tools/config.js +21 -3
  19. package/dist/tools/edit.js +4 -3
  20. package/dist/tools/environment.d.ts +55 -0
  21. package/dist/tools/environment.js +65 -0
  22. package/dist/tools/feedback.d.ts +8 -0
  23. package/dist/tools/feedback.js +132 -0
  24. package/dist/tools/filesystem.js +152 -57
  25. package/dist/tools/improved-process-tools.js +170 -29
  26. package/dist/tools/schemas.d.ts +20 -2
  27. package/dist/tools/schemas.js +20 -2
  28. package/dist/tools/usage.d.ts +5 -0
  29. package/dist/tools/usage.js +24 -0
  30. package/dist/utils/capture.js +23 -1
  31. package/dist/utils/early-logger.d.ts +4 -0
  32. package/dist/utils/early-logger.js +35 -0
  33. package/dist/utils/mcp-logger.d.ts +30 -0
  34. package/dist/utils/mcp-logger.js +59 -0
  35. package/dist/utils/smithery-detector.d.ts +94 -0
  36. package/dist/utils/smithery-detector.js +292 -0
  37. package/dist/utils/startup-detector.d.ts +65 -0
  38. package/dist/utils/startup-detector.js +390 -0
  39. package/dist/utils/usageTracker.d.ts +85 -0
  40. package/dist/utils/usageTracker.js +280 -0
  41. package/dist/version.d.ts +1 -1
  42. package/dist/version.js +1 -1
  43. package/package.json +3 -1
@@ -3,7 +3,8 @@ import { commandManager } from '../command-manager.js';
3
3
  import { StartProcessArgsSchema, ReadProcessOutputArgsSchema, InteractWithProcessArgsSchema, ForceTerminateArgsSchema } from './schemas.js';
4
4
  import { capture } from "../utils/capture.js";
5
5
  import { analyzeProcessState, cleanProcessOutput, formatProcessStateMessage } from '../utils/process-detection.js';
6
- import { getSystemInfo } from '../utils/system-info.js';
6
+ import * as os from 'os';
7
+ import { configManager } from '../config-manager.js';
7
8
  /**
8
9
  * Start a new process (renamed from execute_command)
9
10
  * Includes early detection of process waiting for input
@@ -36,7 +37,26 @@ export async function startProcess(args) {
36
37
  isError: true,
37
38
  };
38
39
  }
39
- const result = await terminalManager.executeCommand(parsed.data.command, parsed.data.timeout_ms, parsed.data.shell);
40
+ let shellUsed = parsed.data.shell;
41
+ if (!shellUsed) {
42
+ const config = await configManager.getConfig();
43
+ if (config.defaultShell) {
44
+ shellUsed = config.defaultShell;
45
+ }
46
+ else {
47
+ const isWindows = os.platform() === 'win32';
48
+ if (isWindows && process.env.COMSPEC) {
49
+ shellUsed = process.env.COMSPEC;
50
+ }
51
+ else if (!isWindows && process.env.SHELL) {
52
+ shellUsed = process.env.SHELL;
53
+ }
54
+ else {
55
+ shellUsed = isWindows ? 'cmd.exe' : '/bin/sh';
56
+ }
57
+ }
58
+ }
59
+ const result = await terminalManager.executeCommand(parsed.data.command, parsed.data.timeout_ms, shellUsed);
40
60
  if (result.pid === -1) {
41
61
  return {
42
62
  content: [{ type: "text", text: result.output }],
@@ -45,9 +65,6 @@ export async function startProcess(args) {
45
65
  }
46
66
  // Analyze the process state to detect if it's waiting for input
47
67
  const processState = analyzeProcessState(result.output, result.pid);
48
- // Get system info for shell information
49
- const systemInfo = getSystemInfo();
50
- const shellUsed = parsed.data.shell || systemInfo.defaultShell;
51
68
  let statusMessage = '';
52
69
  if (processState.isWaitingForInput) {
53
70
  statusMessage = `\nšŸ”„ ${formatProcessStateMessage(processState, result.pid)}`;
@@ -93,19 +110,27 @@ export async function readProcessOutput(args) {
93
110
  const outputPromise = new Promise((resolve) => {
94
111
  const initialOutput = terminalManager.getNewOutput(pid);
95
112
  if (initialOutput && initialOutput.length > 0) {
113
+ // Immediate check on existing output
114
+ const state = analyzeProcessState(initialOutput, pid);
115
+ if (state.isWaitingForInput) {
116
+ earlyExit = true;
117
+ processState = state;
118
+ }
96
119
  resolve(initialOutput);
97
120
  return;
98
121
  }
99
122
  let resolved = false;
100
123
  let interval = null;
101
124
  let timeout = null;
125
+ // Quick prompt patterns for immediate detection
126
+ const quickPromptPatterns = />>>\s*$|>\s*$|\$\s*$|#\s*$/;
102
127
  const cleanup = () => {
103
128
  if (interval)
104
129
  clearInterval(interval);
105
130
  if (timeout)
106
131
  clearTimeout(timeout);
107
132
  };
108
- const resolveOnce = (value, isTimeout = false) => {
133
+ let resolveOnce = (value, isTimeout = false) => {
109
134
  if (resolved)
110
135
  return;
111
136
  resolved = true;
@@ -113,6 +138,43 @@ export async function readProcessOutput(args) {
113
138
  timeoutReached = isTimeout;
114
139
  resolve(value);
115
140
  };
141
+ // Monitor for new output with immediate detection
142
+ const session = terminalManager.getSession(pid);
143
+ if (session && session.process && session.process.stdout && session.process.stderr) {
144
+ const immediateDetector = (data) => {
145
+ const text = data.toString();
146
+ // Immediate check for obvious prompts
147
+ if (quickPromptPatterns.test(text)) {
148
+ const newOutput = terminalManager.getNewOutput(pid) || text;
149
+ const state = analyzeProcessState(output + newOutput, pid);
150
+ if (state.isWaitingForInput) {
151
+ earlyExit = true;
152
+ processState = state;
153
+ resolveOnce(newOutput);
154
+ return;
155
+ }
156
+ }
157
+ };
158
+ session.process.stdout.on('data', immediateDetector);
159
+ session.process.stderr.on('data', immediateDetector);
160
+ // Cleanup immediate detectors when done
161
+ const originalResolveOnce = resolveOnce;
162
+ const cleanupDetectors = () => {
163
+ if (session.process.stdout) {
164
+ session.process.stdout.removeListener('data', immediateDetector);
165
+ }
166
+ if (session.process.stderr) {
167
+ session.process.stderr.removeListener('data', immediateDetector);
168
+ }
169
+ };
170
+ // Override resolveOnce to include cleanup
171
+ const resolveOnceWithCleanup = (value, isTimeout = false) => {
172
+ cleanupDetectors();
173
+ originalResolveOnce(value, isTimeout);
174
+ };
175
+ // Replace the local resolveOnce reference
176
+ resolveOnce = resolveOnceWithCleanup;
177
+ }
116
178
  interval = setInterval(() => {
117
179
  const newOutput = terminalManager.getNewOutput(pid);
118
180
  if (newOutput && newOutput.length > 0) {
@@ -209,32 +271,100 @@ export async function interactWithProcess(args) {
209
271
  }],
210
272
  };
211
273
  }
212
- // Smart waiting with process state detection
274
+ // Smart waiting with immediate and periodic detection
213
275
  let output = "";
214
- let attempts = 0;
215
- const maxAttempts = Math.ceil(timeout_ms / 200);
216
276
  let processState;
217
- while (attempts < maxAttempts) {
218
- await new Promise(resolve => setTimeout(resolve, 200));
219
- const newOutput = terminalManager.getNewOutput(pid);
220
- if (newOutput && newOutput.length > 0) {
221
- output += newOutput;
222
- // Analyze current state
223
- processState = analyzeProcessState(output, pid);
224
- // Exit early if we detect the process is waiting for input
225
- if (processState.isWaitingForInput) {
226
- break;
227
- }
228
- // Also exit if process finished
229
- if (processState.isFinished) {
230
- break;
277
+ let earlyExit = false;
278
+ // Quick prompt patterns for immediate detection
279
+ const quickPromptPatterns = />>>\s*$|>\s*$|\$\s*$|#\s*$/;
280
+ const waitForResponse = () => {
281
+ return new Promise((resolve) => {
282
+ let resolved = false;
283
+ let attempts = 0;
284
+ const maxAttempts = Math.ceil(timeout_ms / 200);
285
+ let interval = null;
286
+ let resolveOnce = () => {
287
+ if (resolved)
288
+ return;
289
+ resolved = true;
290
+ if (interval)
291
+ clearInterval(interval);
292
+ resolve();
293
+ };
294
+ // Set up immediate detection on the process streams
295
+ const session = terminalManager.getSession(pid);
296
+ if (session && session.process && session.process.stdout && session.process.stderr) {
297
+ const immediateDetector = (data) => {
298
+ const text = data.toString();
299
+ // Immediate check for obvious prompts
300
+ if (quickPromptPatterns.test(text)) {
301
+ // Get the latest output and analyze
302
+ setTimeout(() => {
303
+ const newOutput = terminalManager.getNewOutput(pid);
304
+ if (newOutput) {
305
+ output += newOutput;
306
+ const state = analyzeProcessState(output, pid);
307
+ if (state.isWaitingForInput) {
308
+ processState = state;
309
+ earlyExit = true;
310
+ resolveOnce();
311
+ }
312
+ }
313
+ }, 50); // Small delay to ensure output is captured
314
+ }
315
+ };
316
+ session.process.stdout.on('data', immediateDetector);
317
+ session.process.stderr.on('data', immediateDetector);
318
+ // Cleanup when done
319
+ const cleanupDetectors = () => {
320
+ if (session.process.stdout) {
321
+ session.process.stdout.removeListener('data', immediateDetector);
322
+ }
323
+ if (session.process.stderr) {
324
+ session.process.stderr.removeListener('data', immediateDetector);
325
+ }
326
+ };
327
+ // Override resolveOnce to include cleanup
328
+ const originalResolveOnce = resolveOnce;
329
+ const resolveOnceWithCleanup = () => {
330
+ cleanupDetectors();
331
+ originalResolveOnce();
332
+ };
333
+ // Replace the local resolveOnce reference
334
+ resolveOnce = resolveOnceWithCleanup;
231
335
  }
232
- }
233
- attempts++;
234
- }
336
+ // Periodic check as fallback
337
+ interval = setInterval(() => {
338
+ if (resolved)
339
+ return;
340
+ const newOutput = terminalManager.getNewOutput(pid);
341
+ if (newOutput && newOutput.length > 0) {
342
+ output += newOutput;
343
+ // Analyze current state
344
+ processState = analyzeProcessState(output, pid);
345
+ // Exit early if we detect the process is waiting for input
346
+ if (processState.isWaitingForInput) {
347
+ earlyExit = true;
348
+ resolveOnce();
349
+ return;
350
+ }
351
+ // Also exit if process finished
352
+ if (processState.isFinished) {
353
+ resolveOnce();
354
+ return;
355
+ }
356
+ }
357
+ attempts++;
358
+ if (attempts >= maxAttempts) {
359
+ resolveOnce();
360
+ }
361
+ }, 200);
362
+ });
363
+ };
364
+ await waitForResponse();
235
365
  // Clean and format output
236
366
  const cleanOutput = cleanProcessOutput(output, input);
237
- const timeoutReached = attempts >= maxAttempts;
367
+ const timeoutReached = !earlyExit && !processState?.isFinished && !processState?.isWaitingForInput;
238
368
  // Determine final state
239
369
  if (!processState) {
240
370
  processState = analyzeProcessState(output, pid);
@@ -253,14 +383,25 @@ export async function interactWithProcess(args) {
253
383
  return {
254
384
  content: [{
255
385
  type: "text",
256
- text: `āœ… Input executed in process ${pid}.\n(No output produced)${statusMessage}`
386
+ text: `āœ… Input executed in process ${pid}.\nšŸ“­ (No output produced)${statusMessage}`
257
387
  }],
258
388
  };
259
389
  }
390
+ // Format response with better structure and consistent emojis
391
+ let responseText = `āœ… Input executed in process ${pid}`;
392
+ if (cleanOutput && cleanOutput.trim().length > 0) {
393
+ responseText += `:\n\nšŸ“¤ Output:\n${cleanOutput}`;
394
+ }
395
+ else {
396
+ responseText += `.\nšŸ“­ (No output produced)`;
397
+ }
398
+ if (statusMessage) {
399
+ responseText += `\n\n${statusMessage}`;
400
+ }
260
401
  return {
261
402
  content: [{
262
403
  type: "text",
263
- text: `āœ… Input executed in process ${pid}:\n\n${cleanOutput}${statusMessage}`
404
+ text: responseText
264
405
  }],
265
406
  };
266
407
  }
@@ -11,7 +11,7 @@ export declare const SetConfigValueArgsSchema: z.ZodObject<{
11
11
  value?: any;
12
12
  }>;
13
13
  export declare const ListProcessesArgsSchema: z.ZodObject<{}, "strip", z.ZodTypeAny, {}, {}>;
14
- export declare const ExecuteCommandArgsSchema: z.ZodObject<{
14
+ export declare const StartProcessArgsSchema: z.ZodObject<{
15
15
  command: z.ZodString;
16
16
  timeout_ms: z.ZodNumber;
17
17
  shell: z.ZodOptional<z.ZodString>;
@@ -24,7 +24,7 @@ export declare const ExecuteCommandArgsSchema: z.ZodObject<{
24
24
  timeout_ms: number;
25
25
  shell?: string | undefined;
26
26
  }>;
27
- export declare const ReadOutputArgsSchema: z.ZodObject<{
27
+ export declare const ReadProcessOutputArgsSchema: z.ZodObject<{
28
28
  pid: z.ZodNumber;
29
29
  timeout_ms: z.ZodOptional<z.ZodNumber>;
30
30
  }, "strip", z.ZodTypeAny, {
@@ -173,3 +173,21 @@ export declare const EditBlockArgsSchema: z.ZodObject<{
173
173
  new_string: string;
174
174
  expected_replacements?: number | undefined;
175
175
  }>;
176
+ export declare const InteractWithProcessArgsSchema: z.ZodObject<{
177
+ pid: z.ZodNumber;
178
+ input: z.ZodString;
179
+ timeout_ms: z.ZodOptional<z.ZodNumber>;
180
+ wait_for_prompt: z.ZodOptional<z.ZodBoolean>;
181
+ }, "strip", z.ZodTypeAny, {
182
+ pid: number;
183
+ input: string;
184
+ timeout_ms?: number | undefined;
185
+ wait_for_prompt?: boolean | undefined;
186
+ }, {
187
+ pid: number;
188
+ input: string;
189
+ timeout_ms?: number | undefined;
190
+ wait_for_prompt?: boolean | undefined;
191
+ }>;
192
+ export declare const GetUsageStatsArgsSchema: z.ZodObject<{}, "strip", z.ZodTypeAny, {}, {}>;
193
+ export declare const GiveFeedbackArgsSchema: z.ZodObject<{}, "strip", z.ZodTypeAny, {}, {}>;
@@ -8,12 +8,12 @@ export const SetConfigValueArgsSchema = z.object({
8
8
  // Empty schemas
9
9
  export const ListProcessesArgsSchema = z.object({});
10
10
  // Terminal tools schemas
11
- export const ExecuteCommandArgsSchema = z.object({
11
+ export const StartProcessArgsSchema = z.object({
12
12
  command: z.string(),
13
13
  timeout_ms: z.number(),
14
14
  shell: z.string().optional(),
15
15
  });
16
- export const ReadOutputArgsSchema = z.object({
16
+ export const ReadProcessOutputArgsSchema = z.object({
17
17
  pid: z.number(),
18
18
  timeout_ms: z.number().optional(),
19
19
  });
@@ -75,3 +75,21 @@ export const EditBlockArgsSchema = z.object({
75
75
  new_string: z.string(),
76
76
  expected_replacements: z.number().optional().default(1),
77
77
  });
78
+ // Send input to process schema
79
+ export const InteractWithProcessArgsSchema = z.object({
80
+ pid: z.number(),
81
+ input: z.string(),
82
+ timeout_ms: z.number().optional(),
83
+ wait_for_prompt: z.boolean().optional(),
84
+ });
85
+ // Usage stats schema
86
+ export const GetUsageStatsArgsSchema = z.object({});
87
+ // Feedback tool schema - no pre-filled parameters, all user input
88
+ export const GiveFeedbackArgsSchema = z.object({
89
+ // No parameters needed - form will be filled manually by user
90
+ // Only auto-filled hidden fields remain:
91
+ // - tool_call_count (auto)
92
+ // - days_using (auto)
93
+ // - platform (auto)
94
+ // - client_id (auto)
95
+ });
@@ -0,0 +1,5 @@
1
+ import { ServerResult } from '../types.js';
2
+ /**
3
+ * Get usage statistics for debugging and analysis
4
+ */
5
+ export declare function getUsageStats(): Promise<ServerResult>;
@@ -0,0 +1,24 @@
1
+ import { usageTracker } from '../utils/usageTracker.js';
2
+ /**
3
+ * Get usage statistics for debugging and analysis
4
+ */
5
+ export async function getUsageStats() {
6
+ try {
7
+ const summary = await usageTracker.getUsageSummary();
8
+ return {
9
+ content: [{
10
+ type: "text",
11
+ text: summary
12
+ }]
13
+ };
14
+ }
15
+ catch (error) {
16
+ return {
17
+ content: [{
18
+ type: "text",
19
+ text: `Error retrieving usage stats: ${error instanceof Error ? error.message : String(error)}`
20
+ }],
21
+ isError: true
22
+ };
23
+ }
24
+ }
@@ -77,6 +77,15 @@ export const captureBase = async (captureURL, event, properties) => {
77
77
  if (uniqueUserId === 'unknown') {
78
78
  uniqueUserId = await getOrCreateUUID();
79
79
  }
80
+ // Get current client information for all events
81
+ const currentClient = configManager.getCurrentClientInfo();
82
+ let clientContext = {};
83
+ if (currentClient) {
84
+ clientContext = {
85
+ client_name: currentClient.name,
86
+ client_version: currentClient.version,
87
+ };
88
+ }
80
89
  // Create a deep copy of properties to avoid modifying the original objects
81
90
  // This ensures we don't alter error objects that are also returned to the AI
82
91
  let sanitizedProperties;
@@ -109,16 +118,29 @@ export const captureBase = async (captureURL, event, properties) => {
109
118
  delete sanitizedProperties[key];
110
119
  }
111
120
  }
121
+ // Is MCP installed with DXT
122
+ let isDXT = 'false';
123
+ if (process.env.MCP_DXT) {
124
+ isDXT = 'true';
125
+ }
126
+ // Is MCP running in a Docker container
127
+ let isDocker = 'false';
128
+ if (process.env.MCP_CLIENT_DOCKER) {
129
+ isDocker = 'true';
130
+ }
112
131
  // Prepare standard properties
113
132
  const baseProperties = {
114
133
  timestamp: new Date().toISOString(),
115
134
  platform: platform(),
135
+ isDocker,
136
+ isDXT,
116
137
  app_version: VERSION,
117
138
  engagement_time_msec: "100"
118
139
  };
119
- // Combine with sanitized properties
140
+ // Combine with sanitized properties and client context
120
141
  const eventProperties = {
121
142
  ...baseProperties,
143
+ ...clientContext,
122
144
  ...sanitizedProperties
123
145
  };
124
146
  // Prepare GA4 payload
@@ -0,0 +1,4 @@
1
+ export declare function setTransportInstance(transport: any): void;
2
+ export declare function logError(message: string, data?: any): void;
3
+ export declare function logInfo(message: string, data?: any): void;
4
+ export declare function logWarning(message: string, data?: any): void;
@@ -0,0 +1,35 @@
1
+ // Early logger that can work before FilteredStdioServerTransport is ready
2
+ let transportInstance = null;
3
+ export function setTransportInstance(transport) {
4
+ transportInstance = transport;
5
+ }
6
+ export function logError(message, data) {
7
+ if (transportInstance && transportInstance.sendLog) {
8
+ // Use transport's structured logging
9
+ transportInstance.sendLog("error", message, data);
10
+ }
11
+ else {
12
+ // Fallback to console.error which will be intercepted later
13
+ console.error(message);
14
+ }
15
+ }
16
+ export function logInfo(message, data) {
17
+ if (transportInstance && transportInstance.sendLog) {
18
+ // Use transport's structured logging
19
+ transportInstance.sendLog("info", message, data);
20
+ }
21
+ else {
22
+ // Fallback to console.log which will be intercepted later
23
+ console.log(message);
24
+ }
25
+ }
26
+ export function logWarning(message, data) {
27
+ if (transportInstance && transportInstance.sendLog) {
28
+ // Use transport's structured logging
29
+ transportInstance.sendLog("warning", message, data);
30
+ }
31
+ else {
32
+ // Fallback to console.warn which will be intercepted later
33
+ console.warn(message);
34
+ }
35
+ }
@@ -0,0 +1,30 @@
1
+ import '../types.js';
2
+ export type LogLevel = "emergency" | "alert" | "critical" | "error" | "warning" | "notice" | "info" | "debug";
3
+ /**
4
+ * MCP-compatible logger that sends log messages via JSON-RPC notifications
5
+ * Falls back to stderr if transport is not available
6
+ */
7
+ export declare class McpLogger {
8
+ private component;
9
+ constructor(component?: string);
10
+ private logMessage;
11
+ emergency(message: string, data?: any): void;
12
+ alert(message: string, data?: any): void;
13
+ critical(message: string, data?: any): void;
14
+ error(message: string, data?: any): void;
15
+ warning(message: string, data?: any): void;
16
+ notice(message: string, data?: any): void;
17
+ info(message: string, data?: any): void;
18
+ debug(message: string, data?: any): void;
19
+ warn(message: string, data?: any): void;
20
+ logInfo(message: string, data?: any): void;
21
+ toolCall(toolName: string, args: any, result?: any, error?: Error): void;
22
+ }
23
+ export declare const logger: McpLogger;
24
+ export declare const mcpConsole: {
25
+ log: (message: string, data?: any) => void;
26
+ info: (message: string, data?: any) => void;
27
+ warn: (message: string, data?: any) => void;
28
+ error: (message: string, data?: any) => void;
29
+ debug: (message: string, data?: any) => void;
30
+ };
@@ -0,0 +1,59 @@
1
+ // src/utils/mcp-logger.ts
2
+ import '../types.js'; // Import for global types
3
+ /**
4
+ * MCP-compatible logger that sends log messages via JSON-RPC notifications
5
+ * Falls back to stderr if transport is not available
6
+ */
7
+ export class McpLogger {
8
+ constructor(component = 'desktop-commander') {
9
+ this.component = component;
10
+ }
11
+ logMessage(level, message, data) {
12
+ if (global.mcpTransport?.sendLog) {
13
+ // Send via MCP JSON-RPC notification
14
+ global.mcpTransport.sendLog(level, `[${this.component}] ${message}`, data);
15
+ }
16
+ else {
17
+ // Fallback to stderr
18
+ const timestamp = new Date().toISOString();
19
+ const dataStr = data ? ` - ${JSON.stringify(data)}` : '';
20
+ process.stderr.write(`${timestamp} [${level.toUpperCase()}] [${this.component}] ${message}${dataStr}\n`);
21
+ }
22
+ }
23
+ emergency(message, data) { this.logMessage('emergency', message, data); }
24
+ alert(message, data) { this.logMessage('alert', message, data); }
25
+ critical(message, data) { this.logMessage('critical', message, data); }
26
+ error(message, data) { this.logMessage('error', message, data); }
27
+ warning(message, data) { this.logMessage('warning', message, data); }
28
+ notice(message, data) { this.logMessage('notice', message, data); }
29
+ info(message, data) { this.logMessage('info', message, data); }
30
+ debug(message, data) { this.logMessage('debug', message, data); }
31
+ // Convenience methods
32
+ warn(message, data) { this.warning(message, data); }
33
+ logInfo(message, data) { this.info(message, data); }
34
+ // Tool execution logging
35
+ toolCall(toolName, args, result, error) {
36
+ const logData = {
37
+ tool: toolName,
38
+ args,
39
+ ...(result && { result }),
40
+ ...(error && { error: error.message, stack: error.stack })
41
+ };
42
+ if (error) {
43
+ this.error(`Tool ${toolName} failed`, logData);
44
+ }
45
+ else {
46
+ this.debug(`Tool ${toolName} executed`, logData);
47
+ }
48
+ }
49
+ }
50
+ // Create a global logger instance
51
+ export const logger = new McpLogger('desktop-commander');
52
+ // Create convenience functions that mimic console methods
53
+ export const mcpConsole = {
54
+ log: (message, data) => logger.info(message, data),
55
+ info: (message, data) => logger.info(message, data),
56
+ warn: (message, data) => logger.warning(message, data),
57
+ error: (message, data) => logger.error(message, data),
58
+ debug: (message, data) => logger.debug(message, data),
59
+ };
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Smithery CLI Detection Module
3
+ *
4
+ * Detects when a Node.js MCP server is being run through Smithery CLI
5
+ * which acts as a proxy/wrapper around MCP servers.
6
+ */
7
+ export interface SmitheryDetectionInfo {
8
+ isSmithery: boolean;
9
+ confidence: number;
10
+ evidence: string[];
11
+ details: {
12
+ sessionId?: string;
13
+ analyticsEnabled?: boolean;
14
+ clientType?: string;
15
+ connectionType?: 'stdio' | 'http' | 'streamable-http';
16
+ qualifiedName?: string;
17
+ profile?: string;
18
+ runtimeEnvironment?: string;
19
+ };
20
+ }
21
+ export declare class SmitheryDetector {
22
+ private static instance;
23
+ private _detectionInfo;
24
+ private constructor();
25
+ static getInstance(): SmitheryDetector;
26
+ /**
27
+ * Get Smithery detection information (cached after first call)
28
+ */
29
+ getSmitheryInfo(): SmitheryDetectionInfo;
30
+ /**
31
+ * Force re-detection (useful for testing)
32
+ */
33
+ forceRedetect(): SmitheryDetectionInfo;
34
+ private detectSmithery;
35
+ /**
36
+ * Check for Smithery-specific environment variables
37
+ */
38
+ private checkSmitheryEnvironmentVars;
39
+ /**
40
+ * Check process arguments for Smithery patterns
41
+ */
42
+ private checkProcessArguments;
43
+ /**
44
+ * Check parent process for Smithery CLI
45
+ */
46
+ private checkParentProcess;
47
+ /**
48
+ * Check for Smithery runtime environment patterns
49
+ */
50
+ private checkRuntimeEnvironment;
51
+ /**
52
+ * Check for analytics and session indicators
53
+ */
54
+ private checkAnalyticsIndicators;
55
+ /**
56
+ * Check for MCP transport patterns that indicate Smithery
57
+ */
58
+ private checkTransportPatterns;
59
+ /**
60
+ * Check stdio patterns that suggest Smithery proxying
61
+ */
62
+ private checkStdioPatterns;
63
+ /**
64
+ * Check if running through Smithery (convenience method)
65
+ */
66
+ isSmithery(): boolean;
67
+ /**
68
+ * Get the client type if running through Smithery
69
+ */
70
+ getClientType(): string | undefined;
71
+ /**
72
+ * Get the connection type if running through Smithery
73
+ */
74
+ getConnectionType(): string | undefined;
75
+ /**
76
+ * Get analytics status if running through Smithery
77
+ */
78
+ isAnalyticsEnabled(): boolean | undefined;
79
+ /**
80
+ * Get session ID if running through Smithery
81
+ */
82
+ getSessionId(): string | undefined;
83
+ /**
84
+ * Get server qualified name if running through Smithery
85
+ */
86
+ getQualifiedName(): string | undefined;
87
+ }
88
+ export declare const smitheryDetector: SmitheryDetector;
89
+ export declare const getSmitheryInfo: () => SmitheryDetectionInfo;
90
+ export declare const isSmithery: () => boolean;
91
+ export declare const getSmitheryClientType: () => string | undefined;
92
+ export declare const getSmitheryConnectionType: () => string | undefined;
93
+ export declare const getSmitherySessionId: () => string | undefined;
94
+ export declare const isSmitheryAnalyticsEnabled: () => boolean | undefined;