Skip to content

Commit 0cfaf4a

Browse files
committed
Merge remote-tracking branch 'origin/main'
2 parents cfff0c7 + 2c6a842 commit 0cfaf4a

4 files changed

Lines changed: 143 additions & 50 deletions

File tree

README.md

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
# Desktop Commander MCP
22

3-
43
[![npm downloads](https://img.shields.io/npm/dw/@wonderwhy-er/desktop-commander)](https://www.npmjs.com/package/@wonderwhy-er/desktop-commander)
54
[![smithery badge](https://smithery.ai/badge/@wonderwhy-er/desktop-commander)](https://smithery.ai/server/@wonderwhy-er/desktop-commander)
65
[![Buy Me A Coffee](https://img.shields.io/badge/Buy%20Me%20A%20Coffee-support-yellow.svg)](https://www.buymeacoffee.com/wonderwhyer)
@@ -51,21 +50,21 @@ This is server that allows Claude desktop app to execute long-running terminal c
5150
## Installation
5251
First, ensure you've downloaded and installed the [Claude Desktop app](https://claude.ai/download) and you have [npm installed](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm).
5352

54-
### Option 1: Installing via Smithery
53+
### Option 1: Install through npx
54+
Just run this in terminal
55+
```
56+
npx @wonderwhy-er/desktop-commander setup
57+
```
58+
Restart Claude if running
59+
60+
### Option 2: Installing via Smithery
5561

5662
To install Desktop Commander for Claude Desktop automatically via [Smithery](https://smithery.ai/server/@wonderwhy-er/desktop-commander):
5763

5864
```bash
5965
npx -y @smithery/cli install @wonderwhy-er/desktop-commander --client claude
6066
```
6167

62-
### Option 2: Install trough npx
63-
Just run this in terminal
64-
```
65-
npx @wonderwhy-er/desktop-commander setup
66-
```
67-
Restart Claude if running
68-
6968
### Option 3: Add to claude_desktop_config by hand
7069
Add this entry to your claude_desktop_config.json:
7170

@@ -122,7 +121,7 @@ The server provides these tool categories:
122121
- `move_file`: Move/rename files
123122
- `search_files`: Pattern-based file search
124123
- `get_file_info`: File metadata
125-
- `code_search`: Recursive ripgrep based text and code search
124+
- `search_code`: Recursive ripgrep based text and code search
126125

127126
### Edit Tools
128127
- `edit_block`: Apply surgical text replacements (best for changes <20% of file size)
@@ -132,9 +131,9 @@ Search/Replace Block Format:
132131
```
133132
filepath.ext
134133
<<<<<<< SEARCH
135-
existing code to replace
134+
content to find
136135
=======
137-
new code to insert
136+
new content
138137
>>>>>>> REPLACE
139138
```
140139

@@ -264,6 +263,18 @@ No. This tool works with Claude Desktop's standard Pro subscription ($20/month),
264263
### I'm having trouble installing or using the tool. Where can I get help?
265264
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.
266265

266+
## Data Collection
267+
268+
During installation and setup, Desktop Commander collects anonymous usage data to help improve the tool. This includes:
269+
- Operating system information
270+
- Node.js and NPM versions
271+
- Installation method and shell environment
272+
- Error messages (if any occur during setup)
273+
274+
This data is collected using PostHog analytics and is associated with a machine-generated unique ID. No personal information is collected. This helps us understand how the tool is being used and identify common issues.
275+
276+
We are currently working on adding a built-in opt-out option for this data collection in an upcoming release. For now, if you wish to opt out, you can block network connections to `eu.i.posthog.com` in your firewall settings.
277+
267278
## License
268279

269280
MIT

docs/index.html

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -277,13 +277,13 @@ <h2>Installation</h2>
277277
</style>
278278

279279
<div class="tabs">
280-
<button class="tab-btn active" onclick="openTab(event, 'smithery')">Smithery Install</button>
281-
<button class="tab-btn" onclick="openTab(event, 'npx')">NPX Install</button>
280+
<button class="tab-btn active" onclick="openTab(event, 'npx')">NPX Install</button>
281+
<button class="tab-btn" onclick="openTab(event, 'smithery')">Smithery Install</button>
282282
<button class="tab-btn" onclick="openTab(event, 'manual')">Manual Configuration</button>
283283
<button class="tab-btn" onclick="openTab(event, 'local')">Local Installation</button>
284284
</div>
285285

286-
<div id="smithery" class="tab-content active">
286+
<div id="smithery" class="tab-content">
287287
<div class="step">
288288
<div class="step-content">
289289
<h3>Smithery Install</h3>
@@ -293,7 +293,7 @@ <h3>Smithery Install</h3>
293293
</div>
294294
</div>
295295

296-
<div id="npx" class="tab-content">
296+
<div id="npx" class="tab-content active">
297297
<div class="step">
298298
<div class="step-content">
299299
<h3>NPX Install</h3>
@@ -336,7 +336,6 @@ <h3>Local Installation</h3>
336336
</div>
337337
</div>
338338
<p style="margin-top: 10px; font-style: italic;">Remember to restart Claude after installation.</p>
339-
340339

341340
</div>
342341
</section>

setup-claude-server.js

Lines changed: 111 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { dirname } from 'path';
66
import { exec } from "node:child_process";
77
import { PostHog } from 'posthog-node';
88
import machineId from 'node-machine-id';
9+
import { version as nodeVersion } from 'process';
910

1011
const client = new PostHog(
1112
'phc_TFQqTkCwtFGxlwkXDY3gSs7uvJJcJu8GurfXd6mV063',
@@ -18,14 +19,113 @@ const client = new PostHog(
1819
// Get a unique user ID
1920
const uniqueUserId = machineId.machineIdSync();
2021

21-
client.capture({
22+
// Function to get npm version
23+
async function getNpmVersion() {
24+
try {
25+
return new Promise((resolve, reject) => {
26+
exec('npm --version', (error, stdout, stderr) => {
27+
if (error) {
28+
resolve('unknown');
29+
return;
30+
}
31+
resolve(stdout.trim());
32+
});
33+
});
34+
} catch (error) {
35+
return 'unknown';
36+
}
37+
}
38+
39+
// Function to detect shell environment
40+
function detectShell() {
41+
// Check for Windows shells
42+
if (process.platform === 'win32') {
43+
if (process.env.TERM_PROGRAM === 'vscode') return 'vscode-terminal';
44+
if (process.env.WT_SESSION) return 'windows-terminal';
45+
if (process.env.SHELL?.includes('bash')) return 'git-bash';
46+
if (process.env.TERM?.includes('xterm')) return 'xterm-on-windows';
47+
if (process.env.ComSpec?.toLowerCase().includes('powershell')) return 'powershell';
48+
if (process.env.PROMPT) return 'cmd';
49+
50+
// WSL detection
51+
if (process.env.WSL_DISTRO_NAME || process.env.WSLENV) {
52+
return `wsl-${process.env.WSL_DISTRO_NAME || 'unknown'}`;
53+
}
54+
55+
return 'windows-unknown';
56+
}
57+
58+
// Unix-based shells
59+
if (process.env.SHELL) {
60+
const shellPath = process.env.SHELL.toLowerCase();
61+
if (shellPath.includes('bash')) return 'bash';
62+
if (shellPath.includes('zsh')) return 'zsh';
63+
if (shellPath.includes('fish')) return 'fish';
64+
if (shellPath.includes('ksh')) return 'ksh';
65+
if (shellPath.includes('csh')) return 'csh';
66+
if (shellPath.includes('dash')) return 'dash';
67+
return `other-unix-${shellPath.split('/').pop()}`;
68+
}
69+
70+
// Terminal emulators and IDE terminals
71+
if (process.env.TERM_PROGRAM) {
72+
return process.env.TERM_PROGRAM.toLowerCase();
73+
}
74+
75+
return 'unknown-shell';
76+
}
77+
78+
// Function to determine execution context
79+
function getExecutionContext() {
80+
// Check if running from npx
81+
const isNpx = process.env.npm_lifecycle_event === 'npx' ||
82+
process.env.npm_execpath?.includes('npx') ||
83+
process.env._?.includes('npx') ||
84+
import.meta.url.includes('node_modules');
85+
86+
// Check if installed globally
87+
const isGlobal = process.env.npm_config_global === 'true' ||
88+
process.argv[1]?.includes('node_modules/.bin');
89+
90+
// Check if it's run from a script in package.json
91+
const isNpmScript = !!process.env.npm_lifecycle_script;
92+
93+
return {
94+
runMethod: isNpx ? 'npx' : (isGlobal ? 'global' : (isNpmScript ? 'npm_script' : 'direct')),
95+
isCI: !!process.env.CI || !!process.env.GITHUB_ACTIONS || !!process.env.TRAVIS || !!process.env.CIRCLECI,
96+
shell: detectShell()
97+
};
98+
}
99+
100+
// Helper function to get standard environment properties for tracking
101+
let npmVersionCache = null;
102+
async function getTrackingProperties(additionalProps = {}) {
103+
if (npmVersionCache === null) {
104+
npmVersionCache = await getNpmVersion();
105+
}
106+
107+
const context = getExecutionContext();
108+
109+
return {
110+
platform: platform(),
111+
nodeVersion: nodeVersion,
112+
npmVersion: npmVersionCache,
113+
executionContext: context.runMethod,
114+
isCI: context.isCI,
115+
shell: context.shell,
116+
timestamp: new Date().toISOString(),
117+
...additionalProps
118+
};
119+
}
120+
121+
// Initial tracking
122+
(async () => {
123+
client.capture({
22124
distinctId: uniqueUserId,
23125
event: 'npx_setup_start',
24-
properties: {
25-
platform: platform(),
26-
timestamp: new Date().toISOString()
27-
}
28-
});
126+
properties: await getTrackingProperties()
127+
});
128+
})();
29129

30130
// Fix for Windows ESM path resolution
31131
const __filename = fileURLToPath(import.meta.url);
@@ -134,11 +234,7 @@ async function restartClaude() {
134234
client.capture({
135235
distinctId: uniqueUserId,
136236
event: 'npx_setup_restart_claude_error',
137-
properties: {
138-
platform: platform(),
139-
timestamp: new Date().toISOString(),
140-
error: error.message
141-
}
237+
properties: await getTrackingProperties({ error: error.message })
142238
});
143239
logToFile(`Failed to restart Claude: ${error}`, true)
144240
}
@@ -153,10 +249,7 @@ if (!existsSync(claudeConfigPath)) {
153249
client.capture({
154250
distinctId: uniqueUserId,
155251
event: 'npx_setup_create_default_config',
156-
properties: {
157-
platform: platform(),
158-
timestamp: new Date().toISOString()
159-
}
252+
properties: await getTrackingProperties()
160253
});
161254

162255
// Create the directory if it doesn't exist
@@ -232,10 +325,7 @@ export default async function setup() {
232325
client.capture({
233326
distinctId: uniqueUserId,
234327
event: 'npx_setup_update_config',
235-
properties: {
236-
platform: platform(),
237-
timestamp: new Date().toISOString()
238-
}
328+
properties: await getTrackingProperties()
239329
});
240330
logToFile('Successfully added MCP server to Claude configuration!');
241331
logToFile(`Configuration location: ${claudeConfigPath}`);
@@ -246,21 +336,14 @@ export default async function setup() {
246336
client.capture({
247337
distinctId: uniqueUserId,
248338
event: 'npx_setup_complete',
249-
properties: {
250-
platform: platform(),
251-
timestamp: new Date().toISOString()
252-
}
339+
properties: await getTrackingProperties()
253340
});
254341
await client.shutdown()
255342
} catch (error) {
256343
client.capture({
257344
distinctId: uniqueUserId,
258345
event: 'npx_setup_final_error',
259-
properties: {
260-
platform: platform(),
261-
timestamp: new Date().toISOString(),
262-
error: error.message
263-
}
346+
properties: await getTrackingProperties({ error: error.message })
264347
});
265348
logToFile(`Error updating Claude configuration: ${error}`, true);
266349
await client.shutdown()

src/server.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
149149
name: "read_file",
150150
description:
151151
"Read the complete contents of a file from the file system. " +
152-
"Handles various text encodings and provides detailed error messages " +
152+
"Reads UTF-8 text and provides detailed error messages " +
153153
"if the file cannot be read. Only works within allowed directories.",
154154
inputSchema: zodToJsonSchema(ReadFileArgsSchema),
155155
},
@@ -195,7 +195,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
195195
{
196196
name: "search_files",
197197
description:
198-
"Recursively search for files and directories matching a pattern. " +
198+
"Finds files by name using a case-insensitive substring matching. " +
199199
"Searches through all subdirectories from the starting path. " +
200200
"Only searches within allowed directories.",
201201
inputSchema: zodToJsonSchema(SearchFilesArgsSchema),
@@ -231,8 +231,8 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
231231
name: "edit_block",
232232
description:
233233
"Apply surgical text replacements to files. Best for small changes (<20% of file size). " +
234-
"Multiple blocks can be used for separate changes. Will verify changes after application. " +
235-
"Format: filepath, then <<<<<<< SEARCH, content to find, =======, new content, >>>>>>> REPLACE.",
234+
"Call repeatedly to change multiple blocks. Will verify changes after application. " +
235+
"Format:\nfilepath\n<<<<<<< SEARCH\ncontent to find\n=======\nnew content\n>>>>>>> REPLACE",
236236
inputSchema: zodToJsonSchema(EditBlockArgsSchema),
237237
},
238238
],
@@ -428,4 +428,4 @@ server.setRequestHandler(CallToolRequestSchema, async (request: CallToolRequest)
428428
isError: true,
429429
};
430430
}
431-
});
431+
});

0 commit comments

Comments
 (0)