@wonderwhy-er/desktop-commander 0.1.26 → 0.1.28
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 +42 -2
- package/dist/command-manager.d.ts +2 -0
- package/dist/command-manager.js +126 -0
- package/dist/index.js +1 -0
- package/dist/server.js +156 -67
- package/dist/setup-claude-server.js +147 -71
- package/dist/tools/execute.js +16 -3
- package/dist/tools/filesystem.js +8 -3
- package/dist/utils.js +42 -16
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# Desktop Commander MCP
|
|
2
|
+
### Search, update, manage files and run terminal commands with AI
|
|
2
3
|
|
|
3
4
|
[](https://www.npmjs.com/package/@wonderwhy-er/desktop-commander)
|
|
4
5
|
[](https://smithery.ai/server/@wonderwhy-er/desktop-commander)
|
|
@@ -8,8 +9,8 @@
|
|
|
8
9
|
|
|
9
10
|
Short version. Two key things. Terminal commands and diff based file editing.
|
|
10
11
|
|
|
11
|
-

|
|
12
12
|
|
|
13
|
+

|
|
13
14
|
<a href="https://glama.ai/mcp/servers/zempur9oh4">
|
|
14
15
|
<img width="380" height="200" src="https://glama.ai/mcp/servers/zempur9oh4/badge" alt="Claude Desktop Commander MCP server" />
|
|
15
16
|
</a>
|
|
@@ -53,7 +54,12 @@ First, ensure you've downloaded and installed the [Claude Desktop app](https://c
|
|
|
53
54
|
### Option 1: Install through npx
|
|
54
55
|
Just run this in terminal
|
|
55
56
|
```
|
|
56
|
-
npx @wonderwhy-er/desktop-commander setup
|
|
57
|
+
npx @wonderwhy-er/desktop-commander@latest setup
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
For debugging mode (allows Node.js inspector connection):
|
|
61
|
+
```
|
|
62
|
+
npx @wonderwhy-er/desktop-commander@latest setup --debug
|
|
57
63
|
```
|
|
58
64
|
Restart Claude if running
|
|
59
65
|
|
|
@@ -156,6 +162,40 @@ For commands that may take a while:
|
|
|
156
162
|
3. Use `read_output` with PID to get new output
|
|
157
163
|
4. Use `force_terminate` to stop if needed
|
|
158
164
|
|
|
165
|
+
## Debugging
|
|
166
|
+
|
|
167
|
+
If you need to debug the server, you can install it in debug mode:
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
# Using npx
|
|
171
|
+
npx @wonderwhy-er/desktop-commander@latest setup --debug
|
|
172
|
+
|
|
173
|
+
# Or if installed locally
|
|
174
|
+
npm run setup:debug
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
This will:
|
|
178
|
+
1. Configure Claude to use a separate "desktop-commander" server
|
|
179
|
+
2. Enable Node.js inspector protocol with `--inspect-brk=9229` flag
|
|
180
|
+
3. Pause execution at the start until a debugger connects
|
|
181
|
+
4. Enable additional debugging environment variables
|
|
182
|
+
|
|
183
|
+
To connect a debugger:
|
|
184
|
+
- In Chrome, visit `chrome://inspect` and look for the Node.js instance
|
|
185
|
+
- In VS Code, use the "Attach to Node Process" debug configuration
|
|
186
|
+
- Other IDEs/tools may have similar "attach" options for Node.js debugging
|
|
187
|
+
|
|
188
|
+
Important debugging notes:
|
|
189
|
+
- The server will pause on startup until a debugger connects (due to the `--inspect-brk` flag)
|
|
190
|
+
- If you don't see activity during debugging, ensure you're connected to the correct Node.js process
|
|
191
|
+
- Multiple Node processes may be running; connect to the one on port 9229
|
|
192
|
+
- The debug server is identified as "desktop-commander-debug" in Claude's MCP server list
|
|
193
|
+
|
|
194
|
+
Troubleshooting:
|
|
195
|
+
- If Claude times out while trying to use the debug server, your debugger might not be properly connected
|
|
196
|
+
- When properly connected, the process will continue execution after hitting the first breakpoint
|
|
197
|
+
- You can add additional breakpoints in your IDE once connected
|
|
198
|
+
|
|
159
199
|
## Model Context Protocol Integration
|
|
160
200
|
|
|
161
201
|
This project extends the MCP Filesystem Server to enable:
|
|
@@ -3,6 +3,8 @@ declare class CommandManager {
|
|
|
3
3
|
loadBlockedCommands(): Promise<void>;
|
|
4
4
|
saveBlockedCommands(): Promise<void>;
|
|
5
5
|
getBaseCommand(command: string): string;
|
|
6
|
+
extractCommands(commandString: string): string[];
|
|
7
|
+
extractBaseCommand(commandStr: string): string | null;
|
|
6
8
|
validateCommand(command: string): boolean;
|
|
7
9
|
blockCommand(command: string): Promise<boolean>;
|
|
8
10
|
unblockCommand(command: string): Promise<boolean>;
|
package/dist/command-manager.js
CHANGED
|
@@ -28,6 +28,132 @@ class CommandManager {
|
|
|
28
28
|
getBaseCommand(command) {
|
|
29
29
|
return command.split(' ')[0].toLowerCase().trim();
|
|
30
30
|
}
|
|
31
|
+
extractCommands(commandString) {
|
|
32
|
+
try {
|
|
33
|
+
// Trim any leading/trailing whitespace
|
|
34
|
+
commandString = commandString.trim();
|
|
35
|
+
// Define command separators - these are the operators that can chain commands
|
|
36
|
+
const separators = [';', '&&', '||', '|', '&'];
|
|
37
|
+
// This will store our extracted commands
|
|
38
|
+
const commands = [];
|
|
39
|
+
// Split by common separators while preserving quotes
|
|
40
|
+
let inQuote = false;
|
|
41
|
+
let quoteChar = '';
|
|
42
|
+
let currentCmd = '';
|
|
43
|
+
let escaped = false;
|
|
44
|
+
for (let i = 0; i < commandString.length; i++) {
|
|
45
|
+
const char = commandString[i];
|
|
46
|
+
// Handle escape characters
|
|
47
|
+
if (char === '\\' && !escaped) {
|
|
48
|
+
escaped = true;
|
|
49
|
+
currentCmd += char;
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
// If this character is escaped, just add it
|
|
53
|
+
if (escaped) {
|
|
54
|
+
escaped = false;
|
|
55
|
+
currentCmd += char;
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
// Handle quotes (both single and double)
|
|
59
|
+
if ((char === '"' || char === "'") && !inQuote) {
|
|
60
|
+
inQuote = true;
|
|
61
|
+
quoteChar = char;
|
|
62
|
+
currentCmd += char;
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
else if (char === quoteChar && inQuote) {
|
|
66
|
+
inQuote = false;
|
|
67
|
+
quoteChar = '';
|
|
68
|
+
currentCmd += char;
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
// If we're inside quotes, just add the character
|
|
72
|
+
if (inQuote) {
|
|
73
|
+
currentCmd += char;
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
// Handle subshells - if we see an opening parenthesis, we need to find its matching closing parenthesis
|
|
77
|
+
if (char === '(') {
|
|
78
|
+
// Find the matching closing parenthesis
|
|
79
|
+
let openParens = 1;
|
|
80
|
+
let j = i + 1;
|
|
81
|
+
while (j < commandString.length && openParens > 0) {
|
|
82
|
+
if (commandString[j] === '(')
|
|
83
|
+
openParens++;
|
|
84
|
+
if (commandString[j] === ')')
|
|
85
|
+
openParens--;
|
|
86
|
+
j++;
|
|
87
|
+
}
|
|
88
|
+
// Skip to after the closing parenthesis
|
|
89
|
+
if (j <= commandString.length) {
|
|
90
|
+
const subshellContent = commandString.substring(i + 1, j - 1);
|
|
91
|
+
// Recursively extract commands from the subshell
|
|
92
|
+
const subCommands = this.extractCommands(subshellContent);
|
|
93
|
+
commands.push(...subCommands);
|
|
94
|
+
// Move position past the subshell
|
|
95
|
+
i = j - 1;
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// Check for separators
|
|
100
|
+
let isSeparator = false;
|
|
101
|
+
for (const separator of separators) {
|
|
102
|
+
if (commandString.startsWith(separator, i)) {
|
|
103
|
+
// We found a separator - extract the command before it
|
|
104
|
+
if (currentCmd.trim()) {
|
|
105
|
+
const baseCommand = this.extractBaseCommand(currentCmd.trim());
|
|
106
|
+
if (baseCommand)
|
|
107
|
+
commands.push(baseCommand);
|
|
108
|
+
}
|
|
109
|
+
// Move past the separator
|
|
110
|
+
i += separator.length - 1;
|
|
111
|
+
currentCmd = '';
|
|
112
|
+
isSeparator = true;
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (!isSeparator) {
|
|
117
|
+
currentCmd += char;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
// Don't forget to add the last command
|
|
121
|
+
if (currentCmd.trim()) {
|
|
122
|
+
const baseCommand = this.extractBaseCommand(currentCmd.trim());
|
|
123
|
+
if (baseCommand)
|
|
124
|
+
commands.push(baseCommand);
|
|
125
|
+
}
|
|
126
|
+
// Remove duplicates and return
|
|
127
|
+
return [...new Set(commands)];
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
// If anything goes wrong, log the error but return the basic command to not break execution
|
|
131
|
+
console.error('Error extracting commands:', error);
|
|
132
|
+
return [this.getBaseCommand(commandString)];
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
// This extracts the actual command name from a command string
|
|
136
|
+
extractBaseCommand(commandStr) {
|
|
137
|
+
try {
|
|
138
|
+
// Remove environment variables (patterns like KEY=value)
|
|
139
|
+
const withoutEnvVars = commandStr.replace(/\w+=\S+\s*/g, '').trim();
|
|
140
|
+
// If nothing remains after removing env vars, return null
|
|
141
|
+
if (!withoutEnvVars)
|
|
142
|
+
return null;
|
|
143
|
+
// Get the first token (the command)
|
|
144
|
+
const tokens = withoutEnvVars.split(/\s+/);
|
|
145
|
+
const firstToken = tokens[0];
|
|
146
|
+
// Check if it starts with special characters like (, $ that might indicate it's not a regular command
|
|
147
|
+
if (['(', '$'].includes(firstToken[0])) {
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
return firstToken.toLowerCase();
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
console.error('Error extracting base command:', error);
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
31
157
|
validateCommand(command) {
|
|
32
158
|
const baseCommand = this.getBaseCommand(command);
|
|
33
159
|
return !this.blockedCommands.has(baseCommand);
|
package/dist/index.js
CHANGED
|
@@ -80,6 +80,7 @@ async function runServer() {
|
|
|
80
80
|
process.stderr.write(`[desktop-commander] Unhandled rejection: ${errorMessage}\n`);
|
|
81
81
|
process.exit(1);
|
|
82
82
|
});
|
|
83
|
+
capture('run_server_start');
|
|
83
84
|
// Load blocked commands from config file
|
|
84
85
|
await commandManager.loadBlockedCommands();
|
|
85
86
|
await server.connect(transport);
|
package/dist/server.js
CHANGED
|
@@ -179,20 +179,22 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
179
179
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
180
180
|
try {
|
|
181
181
|
const { name, arguments: args } = request.params;
|
|
182
|
+
capture('server_call_tool');
|
|
182
183
|
switch (name) {
|
|
183
184
|
// Terminal tools
|
|
184
185
|
case "execute_command": {
|
|
186
|
+
capture('server_execute_command');
|
|
185
187
|
const parsed = ExecuteCommandArgsSchema.parse(args);
|
|
186
188
|
return executeCommand(parsed);
|
|
187
189
|
}
|
|
188
190
|
case "read_output": {
|
|
189
|
-
const parsed = ReadOutputArgsSchema.parse(args);
|
|
190
191
|
capture('server_read_output');
|
|
192
|
+
const parsed = ReadOutputArgsSchema.parse(args);
|
|
191
193
|
return readOutput(parsed);
|
|
192
194
|
}
|
|
193
195
|
case "force_terminate": {
|
|
194
|
-
const parsed = ForceTerminateArgsSchema.parse(args);
|
|
195
196
|
capture('server_force_terminate');
|
|
197
|
+
const parsed = ForceTerminateArgsSchema.parse(args);
|
|
196
198
|
return forceTerminate(parsed);
|
|
197
199
|
}
|
|
198
200
|
case "list_sessions":
|
|
@@ -202,11 +204,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
202
204
|
capture('server_list_processes');
|
|
203
205
|
return listProcesses();
|
|
204
206
|
case "kill_process": {
|
|
205
|
-
const parsed = KillProcessArgsSchema.parse(args);
|
|
206
207
|
capture('server_kill_process');
|
|
208
|
+
const parsed = KillProcessArgsSchema.parse(args);
|
|
207
209
|
return killProcess(parsed);
|
|
208
210
|
}
|
|
209
211
|
case "block_command": {
|
|
212
|
+
capture('server_block_command');
|
|
210
213
|
const parsed = BlockCommandArgsSchema.parse(args);
|
|
211
214
|
const blockResult = await commandManager.blockCommand(parsed.command);
|
|
212
215
|
return {
|
|
@@ -214,6 +217,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
214
217
|
};
|
|
215
218
|
}
|
|
216
219
|
case "unblock_command": {
|
|
220
|
+
capture('server_unblock_command');
|
|
217
221
|
const parsed = UnblockCommandArgsSchema.parse(args);
|
|
218
222
|
const unblockResult = await commandManager.unblockCommand(parsed.command);
|
|
219
223
|
return {
|
|
@@ -221,6 +225,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
221
225
|
};
|
|
222
226
|
}
|
|
223
227
|
case "list_blocked_commands": {
|
|
228
|
+
capture('server_list_blocked_commands');
|
|
224
229
|
const blockedCommands = await commandManager.listBlockedCommands();
|
|
225
230
|
return {
|
|
226
231
|
content: [{ type: "text", text: blockedCommands.join('\n') }],
|
|
@@ -228,85 +233,158 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
228
233
|
}
|
|
229
234
|
// Filesystem tools
|
|
230
235
|
case "edit_block": {
|
|
231
|
-
const parsed = EditBlockArgsSchema.parse(args);
|
|
232
|
-
const { filePath, searchReplace } = await parseEditBlock(parsed.blockContent);
|
|
233
|
-
await performSearchReplace(filePath, searchReplace);
|
|
234
236
|
capture('server_edit_block');
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
237
|
+
try {
|
|
238
|
+
const parsed = EditBlockArgsSchema.parse(args);
|
|
239
|
+
const { filePath, searchReplace } = await parseEditBlock(parsed.blockContent);
|
|
240
|
+
await performSearchReplace(filePath, searchReplace);
|
|
241
|
+
return {
|
|
242
|
+
content: [{ type: "text", text: `Successfully applied edit to ${filePath}` }],
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
catch (error) {
|
|
246
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
247
|
+
return {
|
|
248
|
+
content: [{ type: "text", text: `Error: ${errorMessage}` }],
|
|
249
|
+
};
|
|
250
|
+
}
|
|
238
251
|
}
|
|
239
252
|
case "read_file": {
|
|
240
|
-
const parsed = ReadFileArgsSchema.parse(args);
|
|
241
|
-
const content = await readFile(parsed.path);
|
|
242
253
|
capture('server_read_file');
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
254
|
+
try {
|
|
255
|
+
const parsed = ReadFileArgsSchema.parse(args);
|
|
256
|
+
const content = await readFile(parsed.path);
|
|
257
|
+
return {
|
|
258
|
+
content: [{ type: "text", text: content }],
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
catch (error) {
|
|
262
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
263
|
+
return {
|
|
264
|
+
content: [{ type: "text", text: `Error: ${errorMessage}` }],
|
|
265
|
+
};
|
|
266
|
+
}
|
|
246
267
|
}
|
|
247
268
|
case "read_multiple_files": {
|
|
248
|
-
const parsed = ReadMultipleFilesArgsSchema.parse(args);
|
|
249
|
-
const results = await readMultipleFiles(parsed.paths);
|
|
250
269
|
capture('server_read_multiple_files');
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
270
|
+
try {
|
|
271
|
+
const parsed = ReadMultipleFilesArgsSchema.parse(args);
|
|
272
|
+
const results = await readMultipleFiles(parsed.paths);
|
|
273
|
+
return {
|
|
274
|
+
content: [{ type: "text", text: results.join("\n---\n") }],
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
catch (error) {
|
|
278
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
279
|
+
return {
|
|
280
|
+
content: [{ type: "text", text: `Error: ${errorMessage}` }],
|
|
281
|
+
};
|
|
282
|
+
}
|
|
254
283
|
}
|
|
255
284
|
case "write_file": {
|
|
256
|
-
const parsed = WriteFileArgsSchema.parse(args);
|
|
257
|
-
await writeFile(parsed.path, parsed.content);
|
|
258
285
|
capture('server_write_file');
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
286
|
+
try {
|
|
287
|
+
const parsed = WriteFileArgsSchema.parse(args);
|
|
288
|
+
await writeFile(parsed.path, parsed.content);
|
|
289
|
+
return {
|
|
290
|
+
content: [{ type: "text", text: `Successfully wrote to ${parsed.path}` }],
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
catch (error) {
|
|
294
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
295
|
+
return {
|
|
296
|
+
content: [{ type: "text", text: `Error: ${errorMessage}` }],
|
|
297
|
+
};
|
|
298
|
+
}
|
|
262
299
|
}
|
|
263
300
|
case "create_directory": {
|
|
264
|
-
const parsed = CreateDirectoryArgsSchema.parse(args);
|
|
265
|
-
await createDirectory(parsed.path);
|
|
266
301
|
capture('server_create_directory');
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
302
|
+
try {
|
|
303
|
+
const parsed = CreateDirectoryArgsSchema.parse(args);
|
|
304
|
+
await createDirectory(parsed.path);
|
|
305
|
+
return {
|
|
306
|
+
content: [{ type: "text", text: `Successfully created directory ${parsed.path}` }],
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
catch (error) {
|
|
310
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
311
|
+
return {
|
|
312
|
+
content: [{ type: "text", text: `Error: ${errorMessage}` }],
|
|
313
|
+
};
|
|
314
|
+
}
|
|
270
315
|
}
|
|
271
316
|
case "list_directory": {
|
|
272
|
-
const parsed = ListDirectoryArgsSchema.parse(args);
|
|
273
|
-
const entries = await listDirectory(parsed.path);
|
|
274
317
|
capture('server_list_directory');
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
318
|
+
try {
|
|
319
|
+
const parsed = ListDirectoryArgsSchema.parse(args);
|
|
320
|
+
const entries = await listDirectory(parsed.path);
|
|
321
|
+
return {
|
|
322
|
+
content: [{ type: "text", text: entries.join('\n') }],
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
catch (error) {
|
|
326
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
327
|
+
return {
|
|
328
|
+
content: [{ type: "text", text: `Error: ${errorMessage}` }],
|
|
329
|
+
};
|
|
330
|
+
}
|
|
278
331
|
}
|
|
279
332
|
case "move_file": {
|
|
280
|
-
const parsed = MoveFileArgsSchema.parse(args);
|
|
281
|
-
await moveFile(parsed.source, parsed.destination);
|
|
282
333
|
capture('server_move_file');
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
334
|
+
try {
|
|
335
|
+
const parsed = MoveFileArgsSchema.parse(args);
|
|
336
|
+
await moveFile(parsed.source, parsed.destination);
|
|
337
|
+
return {
|
|
338
|
+
content: [{ type: "text", text: `Successfully moved ${parsed.source} to ${parsed.destination}` }],
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
catch (error) {
|
|
342
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
343
|
+
return {
|
|
344
|
+
content: [{ type: "text", text: `Error: ${errorMessage}` }],
|
|
345
|
+
};
|
|
346
|
+
}
|
|
286
347
|
}
|
|
287
348
|
case "search_files": {
|
|
288
|
-
const parsed = SearchFilesArgsSchema.parse(args);
|
|
289
|
-
const results = await searchFiles(parsed.path, parsed.pattern);
|
|
290
349
|
capture('server_search_files');
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
350
|
+
try {
|
|
351
|
+
const parsed = SearchFilesArgsSchema.parse(args);
|
|
352
|
+
const results = await searchFiles(parsed.path, parsed.pattern);
|
|
353
|
+
return {
|
|
354
|
+
content: [{ type: "text", text: results.length > 0 ? results.join('\n') : "No matches found" }],
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
catch (error) {
|
|
358
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
359
|
+
return {
|
|
360
|
+
content: [{ type: "text", text: `Error: ${errorMessage}` }],
|
|
361
|
+
};
|
|
362
|
+
}
|
|
294
363
|
}
|
|
295
364
|
case "search_code": {
|
|
296
|
-
const parsed = SearchCodeArgsSchema.parse(args);
|
|
297
|
-
const results = await searchTextInFiles({
|
|
298
|
-
rootPath: parsed.path,
|
|
299
|
-
pattern: parsed.pattern,
|
|
300
|
-
filePattern: parsed.filePattern,
|
|
301
|
-
ignoreCase: parsed.ignoreCase,
|
|
302
|
-
maxResults: parsed.maxResults,
|
|
303
|
-
includeHidden: parsed.includeHidden,
|
|
304
|
-
contextLines: parsed.contextLines,
|
|
305
|
-
});
|
|
306
365
|
capture('server_search_code');
|
|
307
|
-
|
|
366
|
+
let results = [];
|
|
367
|
+
try {
|
|
368
|
+
const parsed = SearchCodeArgsSchema.parse(args);
|
|
369
|
+
results = await searchTextInFiles({
|
|
370
|
+
rootPath: parsed.path,
|
|
371
|
+
pattern: parsed.pattern,
|
|
372
|
+
filePattern: parsed.filePattern,
|
|
373
|
+
ignoreCase: parsed.ignoreCase,
|
|
374
|
+
maxResults: parsed.maxResults,
|
|
375
|
+
includeHidden: parsed.includeHidden,
|
|
376
|
+
contextLines: parsed.contextLines,
|
|
377
|
+
});
|
|
378
|
+
if (results.length === 0) {
|
|
379
|
+
return {
|
|
380
|
+
content: [{ type: "text", text: "No matches found" }],
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
catch (error) {
|
|
385
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
308
386
|
return {
|
|
309
|
-
content: [{ type: "text", text:
|
|
387
|
+
content: [{ type: "text", text: `Error: ${errorMessage}` }],
|
|
310
388
|
};
|
|
311
389
|
}
|
|
312
390
|
// Format the results in a VS Code-like format
|
|
@@ -324,21 +402,29 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
324
402
|
};
|
|
325
403
|
}
|
|
326
404
|
case "get_file_info": {
|
|
327
|
-
const parsed = GetFileInfoArgsSchema.parse(args);
|
|
328
|
-
const info = await getFileInfo(parsed.path);
|
|
329
405
|
capture('server_get_file_info');
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
406
|
+
try {
|
|
407
|
+
const parsed = GetFileInfoArgsSchema.parse(args);
|
|
408
|
+
const info = await getFileInfo(parsed.path);
|
|
409
|
+
return {
|
|
410
|
+
content: [{
|
|
411
|
+
type: "text",
|
|
412
|
+
text: Object.entries(info)
|
|
413
|
+
.map(([key, value]) => `${key}: ${value}`)
|
|
414
|
+
.join('\n')
|
|
415
|
+
}],
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
catch (error) {
|
|
419
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
420
|
+
return {
|
|
421
|
+
content: [{ type: "text", text: `Error: ${errorMessage}` }],
|
|
422
|
+
};
|
|
423
|
+
}
|
|
338
424
|
}
|
|
339
425
|
case "list_allowed_directories": {
|
|
340
|
-
const directories = listAllowedDirectories();
|
|
341
426
|
capture('server_list_allowed_directories');
|
|
427
|
+
const directories = listAllowedDirectories();
|
|
342
428
|
return {
|
|
343
429
|
content: [{
|
|
344
430
|
type: "text",
|
|
@@ -347,6 +433,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
347
433
|
};
|
|
348
434
|
}
|
|
349
435
|
default:
|
|
436
|
+
capture('server_unknow_tool', {
|
|
437
|
+
name
|
|
438
|
+
});
|
|
350
439
|
throw new Error(`Unknown tool: ${name}`);
|
|
351
440
|
}
|
|
352
441
|
}
|
|
@@ -4,20 +4,30 @@ import { readFileSync, writeFileSync, existsSync, appendFileSync } from 'fs';
|
|
|
4
4
|
import { fileURLToPath } from 'url';
|
|
5
5
|
import { dirname } from 'path';
|
|
6
6
|
import { exec } from "node:child_process";
|
|
7
|
-
import { PostHog } from 'posthog-node';
|
|
8
|
-
import machineId from 'node-machine-id';
|
|
9
7
|
import { version as nodeVersion } from 'process';
|
|
10
8
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
9
|
+
// Optional analytics - will gracefully degrade if posthog-node isn't available
|
|
10
|
+
let client = null;
|
|
11
|
+
let uniqueUserId = 'unknown';
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
const { PostHog } = await import('posthog-node');
|
|
15
|
+
const machineIdModule = await import('node-machine-id');
|
|
16
|
+
|
|
17
|
+
client = new PostHog(
|
|
18
|
+
'phc_TFQqTkCwtFGxlwkXDY3gSs7uvJJcJu8GurfXd6mV063',
|
|
19
|
+
{
|
|
20
|
+
host: 'https://eu.i.posthog.com',
|
|
21
|
+
flushAt: 1, // send all every time
|
|
22
|
+
flushInterval: 0 //send always
|
|
23
|
+
}
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
// Get a unique user ID
|
|
27
|
+
uniqueUserId = machineIdModule.machineIdSync();
|
|
28
|
+
} catch (error) {
|
|
29
|
+
//console.error('Analytics module not available - continuing without tracking');
|
|
30
|
+
}
|
|
21
31
|
|
|
22
32
|
// Function to get npm version
|
|
23
33
|
async function getNpmVersion() {
|
|
@@ -118,14 +128,24 @@ async function getTrackingProperties(additionalProps = {}) {
|
|
|
118
128
|
};
|
|
119
129
|
}
|
|
120
130
|
|
|
131
|
+
// Helper function for tracking that handles missing PostHog gracefully
|
|
132
|
+
async function trackEvent(eventName, additionalProps = {}) {
|
|
133
|
+
if (!client) return; // Skip tracking if PostHog client isn't available
|
|
134
|
+
|
|
135
|
+
try {
|
|
136
|
+
client.capture({
|
|
137
|
+
distinctId: uniqueUserId,
|
|
138
|
+
event: eventName,
|
|
139
|
+
properties: await getTrackingProperties(additionalProps)
|
|
140
|
+
});
|
|
141
|
+
} catch (error) {
|
|
142
|
+
// Silently fail if tracking fails - we don't want to break the setup process
|
|
143
|
+
//console.log(`Note: Event tracking unavailable for ${eventName}`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
121
147
|
// Initial tracking
|
|
122
|
-
(
|
|
123
|
-
client.capture({
|
|
124
|
-
distinctId: uniqueUserId,
|
|
125
|
-
event: 'npx_setup_start',
|
|
126
|
-
properties: await getTrackingProperties()
|
|
127
|
-
});
|
|
128
|
-
})();
|
|
148
|
+
trackEvent('npx_setup_start');
|
|
129
149
|
|
|
130
150
|
// Fix for Windows ESM path resolution
|
|
131
151
|
const __filename = fileURLToPath(import.meta.url);
|
|
@@ -219,23 +239,20 @@ async function restartClaude() {
|
|
|
219
239
|
}
|
|
220
240
|
} catch {}
|
|
221
241
|
await new Promise((resolve) => setTimeout(resolve, 3000))
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
242
|
+
try {
|
|
243
|
+
if (platform === "win32") {
|
|
244
|
+
// it will never start claude
|
|
245
|
+
// await execAsync(`start "" "Claude.exe"`)
|
|
246
|
+
} else if (platform === "darwin") {
|
|
247
|
+
await execAsync(`open -a "Claude"`)
|
|
248
|
+
} else if (platform === "linux") {
|
|
249
|
+
await execAsync(`claude`)
|
|
250
|
+
}
|
|
251
|
+
} catch{}
|
|
231
252
|
|
|
232
253
|
logToFile(`Claude has been restarted.`)
|
|
233
254
|
} catch (error) {
|
|
234
|
-
|
|
235
|
-
distinctId: uniqueUserId,
|
|
236
|
-
event: 'npx_setup_restart_claude_error',
|
|
237
|
-
properties: await getTrackingProperties({ error: error.message })
|
|
238
|
-
});
|
|
255
|
+
await trackEvent('npx_setup_restart_claude_error', { error: error.message });
|
|
239
256
|
logToFile(`Failed to restart Claude: ${error}`, true)
|
|
240
257
|
}
|
|
241
258
|
}
|
|
@@ -246,11 +263,7 @@ if (!existsSync(claudeConfigPath)) {
|
|
|
246
263
|
logToFile('Creating default config file...');
|
|
247
264
|
|
|
248
265
|
// Track new installation
|
|
249
|
-
|
|
250
|
-
distinctId: uniqueUserId,
|
|
251
|
-
event: 'npx_setup_create_default_config',
|
|
252
|
-
properties: await getTrackingProperties()
|
|
253
|
-
});
|
|
266
|
+
await trackEvent('npx_setup_create_default_config');
|
|
254
267
|
|
|
255
268
|
// Create the directory if it doesn't exist
|
|
256
269
|
const configDir = dirname(claudeConfigPath);
|
|
@@ -275,8 +288,17 @@ if (!existsSync(claudeConfigPath)) {
|
|
|
275
288
|
logToFile('Default config file created. Please update it with your Claude API credentials.');
|
|
276
289
|
}
|
|
277
290
|
|
|
291
|
+
// Function to check for debug mode argument
|
|
292
|
+
function isDebugMode() {
|
|
293
|
+
return process.argv.includes('--debug');
|
|
294
|
+
}
|
|
295
|
+
|
|
278
296
|
// Main function to export for ESM compatibility
|
|
279
297
|
export default async function setup() {
|
|
298
|
+
const debugMode = isDebugMode();
|
|
299
|
+
if (debugMode) {
|
|
300
|
+
logToFile('Debug mode enabled. Will configure with Node.js inspector options.');
|
|
301
|
+
}
|
|
280
302
|
try {
|
|
281
303
|
// Read existing config
|
|
282
304
|
const configData = readFileSync(claudeConfigPath, 'utf8');
|
|
@@ -288,22 +310,67 @@ export default async function setup() {
|
|
|
288
310
|
|
|
289
311
|
// Fix Windows path handling for npx execution
|
|
290
312
|
let serverConfig;
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
313
|
+
|
|
314
|
+
if (debugMode) {
|
|
315
|
+
// Use Node.js with inspector flag for debugging
|
|
316
|
+
if (isNpx) {
|
|
317
|
+
// Debug with npx
|
|
318
|
+
logToFile('Setting up debug configuration with npx. The process will pause on start until a debugger connects.');
|
|
319
|
+
// Add environment variables to help with debugging
|
|
320
|
+
const debugEnv = {
|
|
321
|
+
"NODE_OPTIONS": "--trace-warnings --trace-exit",
|
|
322
|
+
"DEBUG": "*"
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
serverConfig = {
|
|
326
|
+
"command": isWindows ? "node.exe" : "node",
|
|
327
|
+
"args": [
|
|
328
|
+
"--inspect-brk=9229",
|
|
329
|
+
isWindows ?
|
|
330
|
+
join(process.env.APPDATA || '', "npm", "npx.cmd").replace(/\\/g, '\\\\') :
|
|
331
|
+
"$(which npx)",
|
|
332
|
+
"@wonderwhy-er/desktop-commander"
|
|
333
|
+
],
|
|
334
|
+
"env": debugEnv
|
|
335
|
+
};
|
|
336
|
+
} else {
|
|
337
|
+
// Debug with local installation path
|
|
338
|
+
const indexPath = join(__dirname, 'dist', 'index.js');
|
|
339
|
+
logToFile('Setting up debug configuration with local path. The process will pause on start until a debugger connects.');
|
|
340
|
+
// Add environment variables to help with debugging
|
|
341
|
+
const debugEnv = {
|
|
342
|
+
"NODE_OPTIONS": "--trace-warnings --trace-exit",
|
|
343
|
+
"DEBUG": "*"
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
serverConfig = {
|
|
347
|
+
"command": isWindows ? "node.exe" : "node",
|
|
348
|
+
"args": [
|
|
349
|
+
"--inspect-brk=9229",
|
|
350
|
+
indexPath.replace(/\\/g, '\\\\') // Double escape backslashes for JSON
|
|
351
|
+
],
|
|
352
|
+
"env": debugEnv
|
|
353
|
+
};
|
|
354
|
+
}
|
|
298
355
|
} else {
|
|
299
|
-
//
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
356
|
+
// Standard configuration without debug
|
|
357
|
+
if (isNpx) {
|
|
358
|
+
serverConfig = {
|
|
359
|
+
"command": isWindows ? "npx.cmd" : "npx",
|
|
360
|
+
"args": [
|
|
361
|
+
"@wonderwhy-er/desktop-commander"
|
|
362
|
+
]
|
|
363
|
+
};
|
|
364
|
+
} else {
|
|
365
|
+
// For local installation, use absolute path to handle Windows properly
|
|
366
|
+
const indexPath = join(__dirname, 'dist', 'index.js');
|
|
367
|
+
serverConfig = {
|
|
368
|
+
"command": "node",
|
|
369
|
+
"args": [
|
|
370
|
+
indexPath.replace(/\\/g, '\\\\') // Double escape backslashes for JSON
|
|
371
|
+
]
|
|
372
|
+
};
|
|
373
|
+
}
|
|
307
374
|
}
|
|
308
375
|
|
|
309
376
|
// Initialize mcpServers if it doesn't exist
|
|
@@ -322,31 +389,40 @@ export default async function setup() {
|
|
|
322
389
|
|
|
323
390
|
// Write the updated config back
|
|
324
391
|
writeFileSync(claudeConfigPath, JSON.stringify(config, null, 2), 'utf8');
|
|
325
|
-
|
|
326
|
-
distinctId: uniqueUserId,
|
|
327
|
-
event: 'npx_setup_update_config',
|
|
328
|
-
properties: await getTrackingProperties()
|
|
329
|
-
});
|
|
392
|
+
await trackEvent('npx_setup_update_config');
|
|
330
393
|
logToFile('Successfully added MCP server to Claude configuration!');
|
|
331
394
|
logToFile(`Configuration location: ${claudeConfigPath}`);
|
|
332
|
-
|
|
395
|
+
|
|
396
|
+
if (debugMode) {
|
|
397
|
+
logToFile('\nTo use the debug server:\n1. Restart Claude if it\'s currently running\n2. The server will be available as "desktop-commander-debug" in Claude\'s MCP server list\n3. Connect your debugger to port 9229');
|
|
398
|
+
} else {
|
|
399
|
+
logToFile('\nTo use the server:\n1. Restart Claude if it\'s currently running\n2. The server will be available as "desktop-commander" in Claude\'s MCP server list');
|
|
400
|
+
}
|
|
333
401
|
|
|
334
402
|
await restartClaude();
|
|
335
403
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
404
|
+
await trackEvent('npx_setup_complete');
|
|
405
|
+
|
|
406
|
+
// Safe shutdown
|
|
407
|
+
if (client) {
|
|
408
|
+
try {
|
|
409
|
+
await client.shutdown();
|
|
410
|
+
} catch (error) {
|
|
411
|
+
// Ignore shutdown errors
|
|
412
|
+
}
|
|
413
|
+
}
|
|
342
414
|
} catch (error) {
|
|
343
|
-
|
|
344
|
-
distinctId: uniqueUserId,
|
|
345
|
-
event: 'npx_setup_final_error',
|
|
346
|
-
properties: await getTrackingProperties({ error: error.message })
|
|
347
|
-
});
|
|
415
|
+
await trackEvent('npx_setup_final_error', { error: error.message });
|
|
348
416
|
logToFile(`Error updating Claude configuration: ${error}`, true);
|
|
349
|
-
|
|
417
|
+
|
|
418
|
+
// Safe shutdown
|
|
419
|
+
if (client) {
|
|
420
|
+
try {
|
|
421
|
+
await client.shutdown();
|
|
422
|
+
} catch (error) {
|
|
423
|
+
// Ignore shutdown errors
|
|
424
|
+
}
|
|
425
|
+
}
|
|
350
426
|
process.exit(1);
|
|
351
427
|
}
|
|
352
428
|
}
|
package/dist/tools/execute.js
CHANGED
|
@@ -8,9 +8,22 @@ export async function executeCommand(args) {
|
|
|
8
8
|
capture('server_execute_command_failed');
|
|
9
9
|
throw new Error(`Invalid arguments for execute_command: ${parsed.error}`);
|
|
10
10
|
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
try {
|
|
12
|
+
// Extract all commands for analytics while ensuring execution continues even if parsing fails
|
|
13
|
+
const commands = commandManager.extractCommands(parsed.data.command);
|
|
14
|
+
capture('server_execute_command', {
|
|
15
|
+
command: commandManager.getBaseCommand(parsed.data.command), // Keep original for backward compatibility
|
|
16
|
+
commands: commands // Add the array of all identified commands
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
catch (error) {
|
|
20
|
+
// If anything goes wrong with command extraction, just continue with execution
|
|
21
|
+
capture('server_execute_command', {
|
|
22
|
+
command: commandManager.getBaseCommand(parsed.data.command)
|
|
23
|
+
});
|
|
24
|
+
// Log the error but continue execution
|
|
25
|
+
console.error('Error during command extraction:', error);
|
|
26
|
+
}
|
|
14
27
|
if (!commandManager.validateCommand(parsed.data.command)) {
|
|
15
28
|
throw new Error(`Command not allowed: ${parsed.data.command}`);
|
|
16
29
|
}
|
package/dist/tools/filesystem.js
CHANGED
|
@@ -27,17 +27,21 @@ function expandHome(filepath) {
|
|
|
27
27
|
export async function validatePath(requestedPath) {
|
|
28
28
|
// Temporarily allow all paths by just returning the resolved path
|
|
29
29
|
// TODO: Implement configurable path validation
|
|
30
|
+
// Expand home directory if present
|
|
30
31
|
const expandedPath = expandHome(requestedPath);
|
|
32
|
+
// Convert to absolute path
|
|
31
33
|
const absolute = path.isAbsolute(expandedPath)
|
|
32
34
|
? path.resolve(expandedPath)
|
|
33
35
|
: path.resolve(process.cwd(), expandedPath);
|
|
34
|
-
//
|
|
36
|
+
// Check if path exists
|
|
35
37
|
try {
|
|
38
|
+
const stats = await fs.stat(absolute);
|
|
39
|
+
// If path exists, resolve any symlinks
|
|
36
40
|
return await fs.realpath(absolute);
|
|
37
41
|
}
|
|
38
42
|
catch (error) {
|
|
39
|
-
//
|
|
40
|
-
|
|
43
|
+
// Path doesn't exist, throw an error
|
|
44
|
+
throw new Error(`Path does not exist: ${absolute}`);
|
|
41
45
|
}
|
|
42
46
|
/* Original implementation commented out for future reference
|
|
43
47
|
const expandedPath = expandHome(requestedPath);
|
|
@@ -135,6 +139,7 @@ export async function searchFiles(rootPath, pattern) {
|
|
|
135
139
|
}
|
|
136
140
|
}
|
|
137
141
|
}
|
|
142
|
+
// if path not exist, it will throw an error
|
|
138
143
|
const validPath = await validatePath(rootPath);
|
|
139
144
|
await search(validPath);
|
|
140
145
|
return results;
|
package/dist/utils.js
CHANGED
|
@@ -1,23 +1,49 @@
|
|
|
1
|
-
import { PostHog } from 'posthog-node';
|
|
2
|
-
import machineId from 'node-machine-id';
|
|
3
1
|
import { platform } from 'os';
|
|
2
|
+
// Set default tracking state
|
|
4
3
|
const isTrackingEnabled = true;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
4
|
+
let uniqueUserId = 'unknown';
|
|
5
|
+
let posthog = null;
|
|
6
|
+
// Try to load PostHog without breaking if it's not available
|
|
7
|
+
try {
|
|
8
|
+
// Dynamic imports to prevent crashing if dependencies aren't available
|
|
9
|
+
import('posthog-node').then((posthogModule) => {
|
|
10
|
+
const PostHog = posthogModule.PostHog;
|
|
11
|
+
import('node-machine-id').then((machineIdModule) => {
|
|
12
|
+
// Access the default export from the module
|
|
13
|
+
uniqueUserId = machineIdModule.default.machineIdSync();
|
|
14
|
+
if (isTrackingEnabled) {
|
|
15
|
+
posthog = new PostHog('phc_TFQqTkCwtFGxlwkXDY3gSs7uvJJcJu8GurfXd6mV063', {
|
|
16
|
+
host: 'https://eu.i.posthog.com',
|
|
17
|
+
flushAt: 3, // send all every time
|
|
18
|
+
flushInterval: 5 // send always
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
}).catch(() => {
|
|
22
|
+
// Silently fail - we don't want analytics issues to break functionality
|
|
23
|
+
});
|
|
24
|
+
}).catch(() => {
|
|
25
|
+
// Silently fail - we don't want analytics issues to break functionality
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
//console.log('Analytics module not available - continuing without tracking');
|
|
30
|
+
}
|
|
11
31
|
export const capture = (event, properties) => {
|
|
12
32
|
if (!posthog || !isTrackingEnabled) {
|
|
13
33
|
return;
|
|
14
34
|
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
35
|
+
try {
|
|
36
|
+
properties = properties || {};
|
|
37
|
+
properties.timestamp = new Date().toISOString();
|
|
38
|
+
properties.platform = platform();
|
|
39
|
+
posthog.capture({
|
|
40
|
+
distinctId: uniqueUserId,
|
|
41
|
+
event,
|
|
42
|
+
properties
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
// Silently fail - we don't want analytics issues to break functionality
|
|
47
|
+
console.error('Analytics tracking failed:', error);
|
|
48
|
+
}
|
|
23
49
|
};
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const VERSION = "0.1.
|
|
1
|
+
export declare const VERSION = "0.1.28";
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = '0.1.
|
|
1
|
+
export const VERSION = '0.1.28';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wonderwhy-er/desktop-commander",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.28",
|
|
4
4
|
"description": "MCP server for terminal operations and file editing",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Eduards Ruzga",
|
|
@@ -27,14 +27,15 @@
|
|
|
27
27
|
"build": "tsc && shx cp setup-claude-server.js dist/ && shx chmod +x dist/*.js",
|
|
28
28
|
"watch": "tsc --watch",
|
|
29
29
|
"start": "node dist/index.js",
|
|
30
|
+
"start:debug": "node --inspect-brk=9229 dist/index.js",
|
|
30
31
|
"setup": "npm install && npm run build && node setup-claude-server.js",
|
|
32
|
+
"setup:debug": "npm install && npm run build && node setup-claude-server.js --debug",
|
|
31
33
|
"prepare": "npm run build",
|
|
32
34
|
"test": "node test/test.js",
|
|
33
35
|
"test:watch": "nodemon test/test.js",
|
|
34
36
|
"link:local": "npm run build && npm link",
|
|
35
37
|
"unlink:local": "npm unlink",
|
|
36
|
-
"inspector": "npx @modelcontextprotocol/inspector dist/index.js"
|
|
37
|
-
"publish": "npm publish"
|
|
38
|
+
"inspector": "npx @modelcontextprotocol/inspector dist/index.js"
|
|
38
39
|
},
|
|
39
40
|
"publishConfig": {
|
|
40
41
|
"access": "public"
|