@wonderwhy-er/desktop-commander 0.1.22 → 0.1.25
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 -4
- package/dist/custom-stdio.d.ts +8 -0
- package/dist/custom-stdio.js +22 -0
- package/dist/index.js +49 -7
- package/dist/server.d.ts +19 -15
- package/dist/server.js +17 -1
- package/dist/setup-claude-server.js +142 -49
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +9 -6
- package/testemonials/analyticsindiamag.png +0 -0
package/README.md
CHANGED
|
@@ -23,6 +23,7 @@ Short version. Two key things. Terminal commands and diff based file editing.
|
|
|
23
23
|
- [Work in Progress and TODOs](#work-in-progress-and-todos)
|
|
24
24
|
- [Media links](#media)
|
|
25
25
|
- [Testimonials](#testimonials)
|
|
26
|
+
- [Frequently Asked Questions](#frequently-asked-questions)
|
|
26
27
|
- [Contributing](#contributing)
|
|
27
28
|
- [License](#license)
|
|
28
29
|
|
|
@@ -66,7 +67,12 @@ npx @wonderwhy-er/desktop-commander setup
|
|
|
66
67
|
Restart Claude if running
|
|
67
68
|
|
|
68
69
|
### Option 3: Add to claude_desktop_config by hand
|
|
69
|
-
Add this entry to your claude_desktop_config.json
|
|
70
|
+
Add this entry to your claude_desktop_config.json:
|
|
71
|
+
|
|
72
|
+
- On Mac: `~/Library/Application\ Support/Claude/claude_desktop_config.json`
|
|
73
|
+
- On Windows: `%APPDATA%\Claude\claude_desktop_config.json`
|
|
74
|
+
- On Linux: `~/.config/Claude/claude_desktop_config.json`
|
|
75
|
+
|
|
70
76
|
```json
|
|
71
77
|
{
|
|
72
78
|
"mcpServers": {
|
|
@@ -163,7 +169,8 @@ This project extends the MCP Filesystem Server to enable:
|
|
|
163
169
|
Created as part of exploring Claude MCPs: https://youtube.com/live/TlbjFDbl5Us
|
|
164
170
|
|
|
165
171
|
## DONE
|
|
166
|
-
- **
|
|
172
|
+
- **28-03-2025 Fixed "Watching /" JSON error** - Implemented custom stdio transport to handle non-JSON messages and prevent server crashes
|
|
173
|
+
- **25-03-2025 Better code search** ([merged](https://github.com/wonderwhy-er/ClaudeDesktopCommander/pull/17)) - Enhanced code exploration with context-aware results
|
|
167
174
|
|
|
168
175
|
## Work in Progress and TODOs
|
|
169
176
|
|
|
@@ -176,6 +183,10 @@ The following features are currently being developed or planned:
|
|
|
176
183
|
- **Support for SSH** - Remote server command execution
|
|
177
184
|
- **Installation troubleshooting guide** - Comprehensive help for setup issues
|
|
178
185
|
|
|
186
|
+
## Website
|
|
187
|
+
|
|
188
|
+
Visit our official website at [https://desktopcommander.app/](https://desktopcommander.app/) for the latest information, documentation, and updates.
|
|
189
|
+
|
|
179
190
|
## Media
|
|
180
191
|
Learn more about this project through these resources:
|
|
181
192
|
|
|
@@ -185,8 +196,12 @@ Learn more about this project through these resources:
|
|
|
185
196
|
### Video
|
|
186
197
|
[Claude Desktop Commander Video Tutorial](https://www.youtube.com/watch?v=ly3bed99Dy8) - Watch how to set up and use the Commander effectively.
|
|
187
198
|
|
|
199
|
+
### Publication at AnalyticsIndiaMag
|
|
200
|
+
[
|
|
201
|
+
This Developer Ditched Windsurf, Cursor Using Claude with MCPs](https://analyticsindiamag.com/ai-features/this-developer-ditched-windsurf-cursor-using-claude-with-mcps/)
|
|
202
|
+
|
|
188
203
|
### Community
|
|
189
|
-
Join our [Discord server](https://discord.gg/
|
|
204
|
+
Join our [Discord server](https://discord.gg/kQ27sNnZr7) to get help, share feedback, and connect with other users.
|
|
190
205
|
|
|
191
206
|
## Testimonials
|
|
192
207
|
|
|
@@ -226,6 +241,29 @@ All contributions, big or small, are greatly appreciated!
|
|
|
226
241
|
|
|
227
242
|
If you find this tool valuable for your workflow, please consider [supporting the project](https://www.buymeacoffee.com/wonderwhyer).
|
|
228
243
|
|
|
244
|
+
## Frequently Asked Questions
|
|
245
|
+
|
|
246
|
+
Here are answers to some common questions. For a more comprehensive FAQ, see our [detailed FAQ document](FAQ.md).
|
|
247
|
+
|
|
248
|
+
### What is Claude Desktop Commander?
|
|
249
|
+
It's an MCP tool that enables Claude Desktop to access your file system and terminal, turning Claude into a versatile assistant for coding, automation, codebase exploration, and more.
|
|
250
|
+
|
|
251
|
+
### How is this different from Cursor/Windsurf?
|
|
252
|
+
Unlike IDE-focused tools, Claude Desktop Commander provides a solution-centric approach that works with your entire OS, not just within a coding environment. Claude reads files in full rather than chunking them, can work across multiple projects simultaneously, and executes changes in one go rather than requiring constant review.
|
|
253
|
+
|
|
254
|
+
### Do I need to pay for API credits?
|
|
255
|
+
No. This tool works with Claude Desktop's standard Pro subscription ($20/month), not with API calls, so you won't incur additional costs beyond the subscription fee.
|
|
256
|
+
|
|
257
|
+
### What are the most common use cases?
|
|
258
|
+
- Exploring and understanding complex codebases
|
|
259
|
+
- Generating diagrams and documentation
|
|
260
|
+
- Automating tasks across your system
|
|
261
|
+
- Working with multiple projects simultaneously
|
|
262
|
+
- Making surgical code changes with precise control
|
|
263
|
+
|
|
264
|
+
### I'm having trouble installing or using the tool. Where can I get help?
|
|
265
|
+
Join our [Discord server](https://discord.gg/kQ27sNnZr7) for community support, check the [GitHub issues](https://github.com/wonderwhy-er/ClaudeComputerCommander/issues) for known problems, or review the [full FAQ](FAQ.md) for troubleshooting tips. You can also visit our [website FAQ section](https://desktopcommander.app#faq) for a more user-friendly experience. If you encounter a new issue, please consider [opening a GitHub issue](https://github.com/wonderwhy-er/ClaudeComputerCommander/issues/new) with details about your problem.
|
|
266
|
+
|
|
229
267
|
## License
|
|
230
268
|
|
|
231
|
-
MIT
|
|
269
|
+
MIT
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
2
|
+
/**
|
|
3
|
+
* Extended StdioServerTransport that filters out non-JSON messages.
|
|
4
|
+
* This prevents the "Watching /" error from crashing the server.
|
|
5
|
+
*/
|
|
6
|
+
export declare class FilteredStdioServerTransport extends StdioServerTransport {
|
|
7
|
+
constructor();
|
|
8
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
2
|
+
import process from "node:process";
|
|
3
|
+
/**
|
|
4
|
+
* Extended StdioServerTransport that filters out non-JSON messages.
|
|
5
|
+
* This prevents the "Watching /" error from crashing the server.
|
|
6
|
+
*/
|
|
7
|
+
export class FilteredStdioServerTransport extends StdioServerTransport {
|
|
8
|
+
constructor() {
|
|
9
|
+
// Create a proxy for stdout that only allows valid JSON to pass through
|
|
10
|
+
const originalStdoutWrite = process.stdout.write;
|
|
11
|
+
process.stdout.write = function (buffer) {
|
|
12
|
+
// Only intercept string output that doesn't look like JSON
|
|
13
|
+
if (typeof buffer === 'string' && !buffer.trim().startsWith('{')) {
|
|
14
|
+
return true; //process.stderr.write(buffer);
|
|
15
|
+
}
|
|
16
|
+
return originalStdoutWrite.apply(process.stdout, arguments);
|
|
17
|
+
};
|
|
18
|
+
super();
|
|
19
|
+
// Log initialization to stderr to avoid polluting the JSON stream
|
|
20
|
+
process.stderr.write(`[desktop-commander] Initialized FilteredStdioServerTransport\n`);
|
|
21
|
+
}
|
|
22
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -1,20 +1,51 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { FilteredStdioServerTransport } from './custom-stdio.js';
|
|
3
3
|
import { server } from './server.js';
|
|
4
4
|
import { commandManager } from './command-manager.js';
|
|
5
5
|
import { join, dirname } from 'path';
|
|
6
|
-
import { fileURLToPath } from 'url';
|
|
6
|
+
import { fileURLToPath, pathToFileURL } from 'url';
|
|
7
|
+
import { platform } from 'os';
|
|
7
8
|
const __filename = fileURLToPath(import.meta.url);
|
|
8
9
|
const __dirname = dirname(__filename);
|
|
10
|
+
const isWindows = platform() === 'win32';
|
|
11
|
+
// Helper function to properly convert file paths to URLs, especially for Windows
|
|
12
|
+
function createFileURL(filePath) {
|
|
13
|
+
if (isWindows) {
|
|
14
|
+
// Ensure path uses forward slashes for URL format
|
|
15
|
+
const normalizedPath = filePath.replace(/\\/g, '/');
|
|
16
|
+
// Ensure path has proper file:// prefix
|
|
17
|
+
if (normalizedPath.startsWith('/')) {
|
|
18
|
+
return new URL(`file://${normalizedPath}`);
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
return new URL(`file:///${normalizedPath}`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
// For non-Windows, we can use the built-in function
|
|
26
|
+
return pathToFileURL(filePath);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
9
29
|
async function runSetup() {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
30
|
+
try {
|
|
31
|
+
// Fix for Windows ESM path issue
|
|
32
|
+
const setupScriptPath = join(__dirname, 'setup-claude-server.js');
|
|
33
|
+
const setupScriptUrl = createFileURL(setupScriptPath);
|
|
34
|
+
// Now import using the URL format
|
|
35
|
+
const { default: setupModule } = await import(setupScriptUrl.href);
|
|
36
|
+
if (typeof setupModule === 'function') {
|
|
37
|
+
await setupModule();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
console.error('Error running setup:', error);
|
|
42
|
+
process.exit(1);
|
|
14
43
|
}
|
|
15
44
|
}
|
|
16
45
|
async function runServer() {
|
|
17
46
|
try {
|
|
47
|
+
const transport = new FilteredStdioServerTransport();
|
|
48
|
+
console.log("start");
|
|
18
49
|
// Check if first argument is "setup"
|
|
19
50
|
if (process.argv[2] === 'setup') {
|
|
20
51
|
await runSetup();
|
|
@@ -23,14 +54,25 @@ async function runServer() {
|
|
|
23
54
|
// Handle uncaught exceptions
|
|
24
55
|
process.on('uncaughtException', async (error) => {
|
|
25
56
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
57
|
+
// If this is a JSON parsing error, log it to stderr but don't crash
|
|
58
|
+
if (errorMessage.includes('JSON') && errorMessage.includes('Unexpected token')) {
|
|
59
|
+
process.stderr.write(`[desktop-commander] JSON parsing error: ${errorMessage}\n`);
|
|
60
|
+
return; // Don't exit on JSON parsing errors
|
|
61
|
+
}
|
|
62
|
+
process.stderr.write(`[desktop-commander] Uncaught exception: ${errorMessage}\n`);
|
|
26
63
|
process.exit(1);
|
|
27
64
|
});
|
|
28
65
|
// Handle unhandled rejections
|
|
29
66
|
process.on('unhandledRejection', async (reason) => {
|
|
30
67
|
const errorMessage = reason instanceof Error ? reason.message : String(reason);
|
|
68
|
+
// If this is a JSON parsing error, log it to stderr but don't crash
|
|
69
|
+
if (errorMessage.includes('JSON') && errorMessage.includes('Unexpected token')) {
|
|
70
|
+
process.stderr.write(`[desktop-commander] JSON parsing rejection: ${errorMessage}\n`);
|
|
71
|
+
return; // Don't exit on JSON parsing errors
|
|
72
|
+
}
|
|
73
|
+
process.stderr.write(`[desktop-commander] Unhandled rejection: ${errorMessage}\n`);
|
|
31
74
|
process.exit(1);
|
|
32
75
|
});
|
|
33
|
-
const transport = new StdioServerTransport();
|
|
34
76
|
// Load blocked commands from config file
|
|
35
77
|
await commandManager.loadBlockedCommands();
|
|
36
78
|
await server.connect(transport);
|
package/dist/server.d.ts
CHANGED
|
@@ -1,20 +1,24 @@
|
|
|
1
1
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2
2
|
export declare const server: Server<{
|
|
3
3
|
method: string;
|
|
4
|
-
params?:
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
progressToken
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
}, import("zod").ZodTypeAny, "passthrough">>>;
|
|
12
|
-
}, import("zod").ZodTypeAny, "passthrough"> | undefined;
|
|
4
|
+
params?: {
|
|
5
|
+
[x: string]: unknown;
|
|
6
|
+
_meta?: {
|
|
7
|
+
[x: string]: unknown;
|
|
8
|
+
progressToken?: string | number | undefined;
|
|
9
|
+
} | undefined;
|
|
10
|
+
} | undefined;
|
|
13
11
|
}, {
|
|
14
12
|
method: string;
|
|
15
|
-
params?:
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
13
|
+
params?: {
|
|
14
|
+
[x: string]: unknown;
|
|
15
|
+
_meta?: {
|
|
16
|
+
[x: string]: unknown;
|
|
17
|
+
} | undefined;
|
|
18
|
+
} | undefined;
|
|
19
|
+
}, {
|
|
20
|
+
[x: string]: unknown;
|
|
21
|
+
_meta?: {
|
|
22
|
+
[x: string]: unknown;
|
|
23
|
+
} | undefined;
|
|
24
|
+
}>;
|
package/dist/server.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2
|
-
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
2
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ListPromptsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
3
3
|
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
4
4
|
import { commandManager } from './command-manager.js';
|
|
5
5
|
import { ExecuteCommandArgsSchema, ReadOutputArgsSchema, ForceTerminateArgsSchema, ListSessionsArgsSchema, KillProcessArgsSchema, BlockCommandArgsSchema, UnblockCommandArgsSchema, ReadFileArgsSchema, ReadMultipleFilesArgsSchema, WriteFileArgsSchema, CreateDirectoryArgsSchema, ListDirectoryArgsSchema, MoveFileArgsSchema, SearchFilesArgsSchema, GetFileInfoArgsSchema, EditBlockArgsSchema, SearchCodeArgsSchema, } from './tools/schemas.js';
|
|
@@ -15,8 +15,24 @@ export const server = new Server({
|
|
|
15
15
|
}, {
|
|
16
16
|
capabilities: {
|
|
17
17
|
tools: {},
|
|
18
|
+
resources: {}, // Add empty resources capability
|
|
19
|
+
prompts: {}, // Add empty prompts capability
|
|
18
20
|
},
|
|
19
21
|
});
|
|
22
|
+
// Add handler for resources/list method
|
|
23
|
+
server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
24
|
+
// Return an empty list of resources
|
|
25
|
+
return {
|
|
26
|
+
resources: [],
|
|
27
|
+
};
|
|
28
|
+
});
|
|
29
|
+
// Add handler for prompts/list method
|
|
30
|
+
server.setRequestHandler(ListPromptsRequestSchema, async () => {
|
|
31
|
+
// Return an empty list of prompts
|
|
32
|
+
return {
|
|
33
|
+
prompts: [],
|
|
34
|
+
};
|
|
35
|
+
});
|
|
20
36
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
21
37
|
return {
|
|
22
38
|
tools: [
|
|
@@ -3,15 +3,31 @@ import { join } from 'path';
|
|
|
3
3
|
import { readFileSync, writeFileSync, existsSync, appendFileSync } from 'fs';
|
|
4
4
|
import { fileURLToPath } from 'url';
|
|
5
5
|
import { dirname } from 'path';
|
|
6
|
+
import { exec } from "node:child_process";
|
|
6
7
|
|
|
8
|
+
// Fix for Windows ESM path resolution
|
|
7
9
|
const __filename = fileURLToPath(import.meta.url);
|
|
8
10
|
const __dirname = dirname(__filename);
|
|
9
11
|
|
|
10
|
-
// Determine OS and set appropriate config path
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
// Determine OS and set appropriate config path
|
|
13
|
+
const os = platform();
|
|
14
|
+
const isWindows = os === 'win32'; // Define isWindows variable
|
|
15
|
+
let claudeConfigPath;
|
|
16
|
+
|
|
17
|
+
switch (os) {
|
|
18
|
+
case 'win32':
|
|
19
|
+
claudeConfigPath = join(process.env.APPDATA, 'Claude', 'claude_desktop_config.json');
|
|
20
|
+
break;
|
|
21
|
+
case 'darwin':
|
|
22
|
+
claudeConfigPath = join(homedir(), 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json');
|
|
23
|
+
break;
|
|
24
|
+
case 'linux':
|
|
25
|
+
claudeConfigPath = join(homedir(), '.config', 'Claude', 'claude_desktop_config.json');
|
|
26
|
+
break;
|
|
27
|
+
default:
|
|
28
|
+
// Fallback for other platforms
|
|
29
|
+
claudeConfigPath = join(homedir(), '.claude_desktop_config.json');
|
|
30
|
+
}
|
|
15
31
|
|
|
16
32
|
// Setup logging
|
|
17
33
|
const LOG_FILE = join(__dirname, 'setup.log');
|
|
@@ -38,6 +54,64 @@ function logToFile(message, isError = false) {
|
|
|
38
54
|
}
|
|
39
55
|
}
|
|
40
56
|
|
|
57
|
+
async function execAsync(command) {
|
|
58
|
+
return new Promise((resolve, reject) => {
|
|
59
|
+
// Use PowerShell on Windows for better Unicode support and consistency
|
|
60
|
+
const actualCommand = isWindows
|
|
61
|
+
? `cmd.exe /c ${command}`
|
|
62
|
+
: command;
|
|
63
|
+
|
|
64
|
+
exec(actualCommand, (error, stdout, stderr) => {
|
|
65
|
+
if (error) {
|
|
66
|
+
reject(error);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
resolve({ stdout, stderr });
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async function restartClaude() {
|
|
75
|
+
try {
|
|
76
|
+
const platform = process.platform
|
|
77
|
+
switch (platform) {
|
|
78
|
+
case "win32":
|
|
79
|
+
// ignore errors on windows when claude is not running.
|
|
80
|
+
// just silently kill the process
|
|
81
|
+
try {
|
|
82
|
+
await execAsync(
|
|
83
|
+
`taskkill /F /IM "Claude.exe"`,
|
|
84
|
+
)
|
|
85
|
+
} catch {}
|
|
86
|
+
break;
|
|
87
|
+
case "darwin":
|
|
88
|
+
await execAsync(
|
|
89
|
+
`killall "Claude"`,
|
|
90
|
+
)
|
|
91
|
+
break;
|
|
92
|
+
case "linux":
|
|
93
|
+
await execAsync(
|
|
94
|
+
`pkill -f "claude"`,
|
|
95
|
+
)
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
await new Promise((resolve) => setTimeout(resolve, 3000))
|
|
99
|
+
|
|
100
|
+
if (platform === "win32") {
|
|
101
|
+
// it will never start claude
|
|
102
|
+
// await execAsync(`start "" "Claude.exe"`)
|
|
103
|
+
} else if (platform === "darwin") {
|
|
104
|
+
await execAsync(`open -a "Claude"`)
|
|
105
|
+
} else if (platform === "linux") {
|
|
106
|
+
await execAsync(`claude`)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
logToFile(`Claude has been restarted.`)
|
|
110
|
+
} catch (error) {
|
|
111
|
+
logToFile(`Failed to restart Claude: ${error}`, true)
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
41
115
|
// Check if config file exists and create default if not
|
|
42
116
|
if (!existsSync(claudeConfigPath)) {
|
|
43
117
|
logToFile(`Claude config file not found at: ${claudeConfigPath}`);
|
|
@@ -49,7 +123,7 @@ if (!existsSync(claudeConfigPath)) {
|
|
|
49
123
|
import('fs').then(fs => fs.mkdirSync(configDir, { recursive: true }));
|
|
50
124
|
}
|
|
51
125
|
|
|
52
|
-
// Create default config
|
|
126
|
+
// Create default config with shell based on platform
|
|
53
127
|
const defaultConfig = {
|
|
54
128
|
"serverConfig": isWindows
|
|
55
129
|
? {
|
|
@@ -66,50 +140,69 @@ if (!existsSync(claudeConfigPath)) {
|
|
|
66
140
|
logToFile('Default config file created. Please update it with your Claude API credentials.');
|
|
67
141
|
}
|
|
68
142
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
// Determine if running through npx or locally
|
|
76
|
-
const isNpx = import.meta.url.endsWith('dist/setup-claude-server.js');
|
|
77
|
-
|
|
78
|
-
const serverConfig = isNpx ? {
|
|
79
|
-
"command": "npx",
|
|
80
|
-
"args": [
|
|
81
|
-
"@wonderwhy-er/desktop-commander"
|
|
82
|
-
]
|
|
83
|
-
} : {
|
|
84
|
-
"command": "node",
|
|
85
|
-
"args": [
|
|
86
|
-
join(__dirname, 'dist', 'index.js')
|
|
87
|
-
]
|
|
88
|
-
};
|
|
143
|
+
// Main function to export for ESM compatibility
|
|
144
|
+
export default async function setup() {
|
|
145
|
+
try {
|
|
146
|
+
// Read existing config
|
|
147
|
+
const configData = readFileSync(claudeConfigPath, 'utf8');
|
|
148
|
+
const config = JSON.parse(configData);
|
|
89
149
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
config.mcpServers.desktopCommander = serverConfig;
|
|
150
|
+
// Prepare the new server config based on OS
|
|
151
|
+
// Determine if running through npx or locally
|
|
152
|
+
const isNpx = import.meta.url.includes('node_modules');
|
|
96
153
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
154
|
+
// Fix Windows path handling for npx execution
|
|
155
|
+
let serverConfig;
|
|
156
|
+
if (isNpx) {
|
|
157
|
+
serverConfig = {
|
|
158
|
+
"command": isWindows ? "npx.cmd" : "npx",
|
|
159
|
+
"args": [
|
|
160
|
+
"@wonderwhy-er/desktop-commander"
|
|
161
|
+
]
|
|
162
|
+
};
|
|
163
|
+
} else {
|
|
164
|
+
// For local installation, use absolute path to handle Windows properly
|
|
165
|
+
const indexPath = join(__dirname, 'dist', 'index.js');
|
|
166
|
+
serverConfig = {
|
|
167
|
+
"command": "node",
|
|
168
|
+
"args": [
|
|
169
|
+
indexPath.replace(/\\/g, '\\\\') // Double escape backslashes for JSON
|
|
170
|
+
]
|
|
171
|
+
};
|
|
172
|
+
}
|
|
104
173
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
174
|
+
// Initialize mcpServers if it doesn't exist
|
|
175
|
+
if (!config.mcpServers) {
|
|
176
|
+
config.mcpServers = {};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Check if the old "desktopCommander" exists and remove it
|
|
180
|
+
if (config.mcpServers.desktopCommander) {
|
|
181
|
+
logToFile('Found old "desktopCommander" installation. Removing it...');
|
|
182
|
+
delete config.mcpServers.desktopCommander;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Add or update the terminal server config with the proper name "desktop-commander"
|
|
186
|
+
config.mcpServers["desktop-commander"] = serverConfig;
|
|
187
|
+
|
|
188
|
+
// Write the updated config back
|
|
189
|
+
writeFileSync(claudeConfigPath, JSON.stringify(config, null, 2), 'utf8');
|
|
190
|
+
|
|
191
|
+
logToFile('Successfully added MCP server to Claude configuration!');
|
|
192
|
+
logToFile(`Configuration location: ${claudeConfigPath}`);
|
|
193
|
+
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');
|
|
194
|
+
|
|
195
|
+
await restartClaude();
|
|
196
|
+
} catch (error) {
|
|
197
|
+
logToFile(`Error updating Claude configuration: ${error}`, true);
|
|
198
|
+
process.exit(1);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Allow direct execution
|
|
203
|
+
if (process.argv.length >= 2 && process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
204
|
+
setup().catch(error => {
|
|
205
|
+
logToFile(`Fatal error: ${error}`, true);
|
|
206
|
+
process.exit(1);
|
|
207
|
+
});
|
|
115
208
|
}
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const VERSION = "0.1.
|
|
1
|
+
export declare const VERSION = "0.1.25";
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = '0.1.
|
|
1
|
+
export const VERSION = '0.1.25';
|
package/package.json
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wonderwhy-er/desktop-commander",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.25",
|
|
4
4
|
"description": "MCP server for terminal operations and file editing",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Eduards Ruzga",
|
|
7
|
-
"homepage": "https://github.com/wonderwhy-er/
|
|
8
|
-
"bugs": "https://github.com/wonderwhy-er/
|
|
7
|
+
"homepage": "https://github.com/wonderwhy-er/DesktopCommanderMCP",
|
|
8
|
+
"bugs": "https://github.com/wonderwhy-er/DesktopCommanderMCP/issues",
|
|
9
9
|
"type": "module",
|
|
10
|
+
"engines": {
|
|
11
|
+
"node": ">=18.0.0"
|
|
12
|
+
},
|
|
10
13
|
"bin": {
|
|
11
14
|
"desktop-commander": "dist/index.js",
|
|
12
15
|
"setup": "dist/setup-claude-server.js"
|
|
@@ -31,7 +34,7 @@
|
|
|
31
34
|
"link:local": "npm run build && npm link",
|
|
32
35
|
"unlink:local": "npm unlink",
|
|
33
36
|
"inspector": "npx @modelcontextprotocol/inspector dist/index.js",
|
|
34
|
-
"
|
|
37
|
+
"publish": "npm publish"
|
|
35
38
|
},
|
|
36
39
|
"publishConfig": {
|
|
37
40
|
"access": "public"
|
|
@@ -56,14 +59,14 @@
|
|
|
56
59
|
"file-operations"
|
|
57
60
|
],
|
|
58
61
|
"dependencies": {
|
|
59
|
-
"@modelcontextprotocol/sdk": "1.0
|
|
62
|
+
"@modelcontextprotocol/sdk": "^1.8.0",
|
|
60
63
|
"@vscode/ripgrep": "^1.15.9",
|
|
61
64
|
"glob": "^10.3.10",
|
|
62
65
|
"zod": "^3.24.1",
|
|
63
66
|
"zod-to-json-schema": "^3.23.5"
|
|
64
67
|
},
|
|
65
68
|
"devDependencies": {
|
|
66
|
-
"@types/node": "^20.
|
|
69
|
+
"@types/node": "^20.17.24",
|
|
67
70
|
"nodemon": "^3.0.2",
|
|
68
71
|
"shx": "^0.3.4",
|
|
69
72
|
"typescript": "^5.3.3"
|
|
Binary file
|