Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,15 @@ plans/
planning/

# Test output files
test/test_output/
test/test_output/

# MCPB related files
*.mcpb
mcpb-bundle/
.mcpregistry_github_token
.mcpregistry_registry_token

# Test files
test_files/
test_fix.js
test_search.js
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -954,7 +954,7 @@ External telemetry (sent to analytics services) is enabled by default but can be

**Note:** This only disables external telemetry. Local usage analytics remain active for tool functionality but is not share externally

For complete details about data collection, please see our [Privacy Policy](PRIVACY.md).
For complete details about data collection, please see our [Privacy Policy](https://legal.desktopcommander.app/privacy_desktop_commander_mcp).

## Verifications
[![Verified on MseeP](https://mseep.ai/badge.svg)](https://mseep.ai/app/25ff7a06-58bc-40b8-bd79-ebb715140f1a)
Expand Down
Binary file added icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
161 changes: 161 additions & 0 deletions manifest.template.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
{
"$schema": "../../dist/mcpb-manifest.schema.json",
"manifest_version": "0.1",
"name": "desktop-commander",
"display_name": "Desktop Commander",
"version": "{{VERSION}}",
"description": "Build, explore, and automate on your local machine with access to files and terminal.",
"long_description": "Combine local filesystem access with full terminal control to handle technical tasks through natural language. Desktop Commander empowers you to build, explore, and automate - from organizing repositories to creating complete applications:\n* **Build from scratch** - Create features and applications with simple commands\n* **Manage development environments** - Set up servers, configure systems, and handle processes\n* **Manage context and documentation** - Keep track of project details and technical specifications\n* **Explore existing codebases and projects** - Navigate and understand complex repositories\n\nThis extension bridges technical skill gaps by providing full command-line superpowers through an interface that understands your intent and handles complexity automatically.",
"author": {
"name": "Desktop Commander Team",
"email": "er@desktopcommander.app",
"url": "https://desktopcommander.app/"
},
"repository": {
"type": "git",
"url": "https://github.com/wonderwhy-er/DesktopCommanderMCP.git"
},
"homepage": "https://github.com/wonderwhy-er/DesktopCommanderMCP",
"documentation": "https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/FAQ.md",
"support": "https://github.com/wonderwhy-er/DesktopCommanderMCP/issues",
"icon": "icon.png",
"server": {
"type": "node",
"entry_point": "dist/index.js",
"mcp_config": {
"command": "node",
"args": [
"${__dirname}/dist/index.js"
],
Comment on lines +25 to +29

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix the MCP command args path (literal ${__dirname} won’t resolve).

mcp_config.args is passed verbatim to the client process runner, so "${__dirname}/dist/index.js" remains a literal string and the launch fails with “ENOENT: no such file or directory, open '${__dirname}/dist/index.js'”. Use a relative path instead so the bundle starts correctly.

Apply this diff:

-        "${__dirname}/dist/index.js"
+        "./dist/index.js"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"mcp_config": {
"command": "node",
"args": [
"${__dirname}/dist/index.js"
],
"mcp_config": {
"command": "node",
"args": [
"./dist/index.js"
],
🤖 Prompt for AI Agents
In manifest.template.json around lines 24 to 28, the mcp_config.args entry uses
the literal string "${__dirname}/dist/index.js" which won’t be resolved at
runtime; replace that element with a relative path to the bundled entry (for
example "./dist/index.js" or "dist/index.js") so the node process can locate and
start the bundle correctly when invoked by the client process runner.

"env": {
"MCP_DXT": "true",
"NODE_ENV": "production"
}
}
},
"tools": [
{
"name": "get_config",
"description": "Get the complete server configuration including blocked commands, allowed directories, file limits, telemetry settings, client info, and system information."
},
{
"name": "set_config_value",
"description": "Set a specific configuration value by key. WARNING: Should be used in a separate chat from file operations to prevent security issues."
},
{
"name": "read_file",
"description": "Read the contents of a file from the file system or URL with optional offset and length parameters. Supports partial reading and handles images."
},
{
"name": "read_multiple_files",
"description": "Read the contents of multiple files simultaneously. Handles text files and renders images."
},
{
"name": "write_file",
"description": "Write or append to file contents. Always write files in chunks of 25-30 lines maximum for best performance."
},
{
"name": "create_directory",
"description": "Create a new directory or ensure a directory exists. Can create multiple nested directories in one operation."
},
{
"name": "list_directory",
"description": "Get a detailed listing of all files and directories in a specified path."
},
{
"name": "move_file",
"description": "Move or rename files and directories between locations."
},
{
"name": "start_search",
"description": "Start a streaming search for files by name or content within files. Returns results progressively with session ID."
},
{
"name": "get_more_search_results",
"description": "Get more results from an active search with offset-based pagination."
},
{
"name": "stop_search",
"description": "Stop an active search gracefully when you've found what you need."
},
{
"name": "list_searches",
"description": "List all active searches with their status and runtime information."
},
{
"name": "get_file_info",
"description": "Retrieve detailed metadata about a file or directory including size, timestamps, permissions, and line counts."
},
{
"name": "edit_block",
"description": "Apply surgical text replacements to files. Make small, focused edits with minimal context for precision."
},
{
"name": "start_process",
"description": "Start a new terminal process with intelligent state detection. Primary tool for local file analysis (CSV, JSON, logs, etc.)."
},
{
"name": "read_process_output",
"description": "Read output from a running process with intelligent completion detection and REPL prompt recognition."
},
{
"name": "interact_with_process",
"description": "Send input to a running process and automatically receive the response. Critical tool for all local file analysis."
},
{
"name": "force_terminate",
"description": "Force terminate a running terminal session."
},
{
"name": "list_sessions",
"description": "List all active terminal sessions with status information including blocked state and runtime."
},
{
"name": "list_processes",
"description": "List all running processes with PID, command name, CPU usage, and memory usage."
},
{
"name": "kill_process",
"description": "Terminate a running process by PID. Use with caution."
},
{
"name": "get_usage_stats",
"description": "Get usage statistics for debugging and analysis including tool usage and performance metrics."
},
{
"name": "give_feedback_to_desktop_commander",
"description": "Open feedback form in browser to provide feedback about Desktop Commander."
},
{
"name": "get_prompts",
"description": "Browse and retrieve curated Desktop Commander prompts for various tasks and workflows."
}
],
"keywords": [
"orchestration",
"workflow",
"development",
"prototyping",
"terminal",
"processes",
"file-management",
"automation",
"productivity",
"end-to-end",
"data-analysis"
],
"license": "MIT",
"privacy_policies": [
"https://legal.desktopcommander.app/privacy_desktop_commander_mcp"
],
"compatibility": {
"platforms": [
"darwin",
"win32",
"linux"
],
"runtimes": {
"node": ">=18.0.0"
}
}
}
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"link:local": "npm run build && npm link",
"unlink:local": "npm unlink",
"inspector": "npx @modelcontextprotocol/inspector dist/index.js",
"build:mcpb": "node scripts/build-mcpb.cjs",
"logs:view": "npm run build && node scripts/view-fuzzy-logs.js",
"logs:analyze": "npm run build && node scripts/analyze-fuzzy-logs.js",
"logs:clear": "npm run build && node scripts/clear-fuzzy-logs.js",
Expand Down
168 changes: 168 additions & 0 deletions scripts/build-mcpb.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
#!/usr/bin/env node

/**
* Build script for creating Desktop Commander MCPB bundle
*
* This script:
* 1. Builds the TypeScript project
* 2. Creates a bundle directory structure
* 3. Generates a proper MCPB manifest.json
* 4. Copies the built server and dependencies
* 5. Uses mcpb CLI to create the final .mcpb bundle
*/

const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');

const PROJECT_ROOT = path.resolve(__dirname, '..');
const BUNDLE_DIR = path.join(PROJECT_ROOT, 'mcpb-bundle');
const MANIFEST_PATH = path.join(BUNDLE_DIR, 'manifest.json');

console.log('🏗️ Building Desktop Commander MCPB Bundle...');

// Step 1: Build the TypeScript project
console.log('📦 Building TypeScript project...');
try {
execSync('npm run build', { cwd: PROJECT_ROOT, stdio: 'inherit' });
console.log('✅ TypeScript build completed');
} catch (error) {
console.error('❌ TypeScript build failed:', error.message);
process.exit(1);
}

// Step 2: Clean and create bundle directory
if (fs.existsSync(BUNDLE_DIR)) {
fs.rmSync(BUNDLE_DIR, { recursive: true });
}fs.mkdirSync(BUNDLE_DIR, { recursive: true });

Comment on lines +35 to +38

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix bundle directory creation syntax error.

} and fs.mkdirSync are glued together (}fs), so the script dies with Unexpected identifier before doing any work. Split them so the bundle directory actually gets created.

 if (fs.existsSync(BUNDLE_DIR)) {
     fs.rmSync(BUNDLE_DIR, { recursive: true });
-}fs.mkdirSync(BUNDLE_DIR, { recursive: true });
+}
+fs.mkdirSync(BUNDLE_DIR, { recursive: true });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (fs.existsSync(BUNDLE_DIR)) {
fs.rmSync(BUNDLE_DIR, { recursive: true });
}fs.mkdirSync(BUNDLE_DIR, { recursive: true });
if (fs.existsSync(BUNDLE_DIR)) {
fs.rmSync(BUNDLE_DIR, { recursive: true });
}
fs.mkdirSync(BUNDLE_DIR, { recursive: true });
🤖 Prompt for AI Agents
In scripts/build-mcpb.cjs around lines 35 to 38, there's a syntax error caused
by `}` and `fs.mkdirSync` being concatenated (`}fs`) which throws an Unexpected
identifier; separate the statements by placing a semicolon or newline after the
closing brace so the fs.mkdirSync(BUNDLE_DIR, { recursive: true }) call is a
standalone statement, ensuring the bundle directory is created when it does not
exist.

// Step 3: Read package.json for version and metadata
const packageJson = JSON.parse(fs.readFileSync(path.join(PROJECT_ROOT, 'package.json'), 'utf8'));

// Step 4: Load and process manifest template
console.log('📝 Processing manifest template...');

const manifestTemplatePath = path.join(PROJECT_ROOT, 'manifest.template.json');
console.log(`📄 Using manifest: manifest.template.json`);

let manifestTemplate;
try {
manifestTemplate = fs.readFileSync(manifestTemplatePath, 'utf8');
} catch (error) {
console.error('❌ Failed to read manifest template:', manifestTemplatePath);
process.exit(1);
}

// Replace template variables
const manifestContent = manifestTemplate.replace('{{VERSION}}', packageJson.version);

// Parse and validate the resulting manifest
let manifest;
try {
manifest = JSON.parse(manifestContent);
} catch (error) {
console.error('❌ Invalid JSON in manifest template:', error.message);
process.exit(1);
}

// Write manifest
fs.writeFileSync(MANIFEST_PATH, JSON.stringify(manifest, null, 2));
console.log('✅ Created manifest.json');
// Step 5: Copy necessary files
const filesToCopy = [
'dist',
'package.json',
'README.md',
'LICENSE',
'PRIVACY.md',
'icon.png'
];

filesToCopy.forEach(file => {
const srcPath = path.join(PROJECT_ROOT, file);
const destPath = path.join(BUNDLE_DIR, file);

if (fs.existsSync(srcPath)) {
if (fs.statSync(srcPath).isDirectory()) {
// Copy directory recursively
fs.cpSync(srcPath, destPath, { recursive: true });
} else {
// Copy file
fs.copyFileSync(srcPath, destPath);
}
console.log(`✅ Copied ${file}`);
} else {
console.log(`⚠️ Skipped ${file} (not found)`);
}
});

// Step 6: Create package.json in bundle with production dependencies
const bundlePackageJson = {
name: manifest.name,
version: manifest.version,
description: manifest.description,
main: "dist/index.js",
author: manifest.author,
license: manifest.license,
repository: manifest.repository, dependencies: {
"@modelcontextprotocol/sdk": "^1.9.0",
"@vscode/ripgrep": "^1.15.9",
"cross-fetch": "^4.1.0",
"fastest-levenshtein": "^1.0.16",
"glob": "^10.3.10",
"isbinaryfile": "^5.0.4",
"zod": "^3.24.1",
"zod-to-json-schema": "^3.23.5"
}
};

fs.writeFileSync(
path.join(BUNDLE_DIR, 'package.json'),
JSON.stringify(bundlePackageJson, null, 2)
);

// Step 6b: Install dependencies in bundle directory
console.log('📦 Installing production dependencies in bundle...');
try {
execSync('npm install --omit=dev --production', { cwd: BUNDLE_DIR, stdio: 'inherit' });
console.log('✅ Dependencies installed');
} catch (error) {
console.error('❌ Failed to install dependencies:', error.message);
process.exit(1);
}

// Step 7: Validate manifest
console.log('🔍 Validating manifest...');
try {
execSync(`mcpb validate "${MANIFEST_PATH}"`, { stdio: 'inherit' });
console.log('✅ Manifest validation passed');
} catch (error) {
console.error('❌ Manifest validation failed:', error.message);
process.exit(1);
}
Comment on lines +137 to +142

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

🧩 Analysis chain

Verify mcpb CLI tool availability.

The script depends on the mcpb CLI tool for validation and packing but doesn't check if it's installed. This could cause runtime failures during the build process.


🏁 Script executed:

#!/bin/bash
# Description: Check if mcpb CLI tool is available and working

# Check if mcpb command exists
if command -v mcpb >/dev/null 2>&1; then
    echo "✅ mcpb CLI tool found"
    mcpb --version 2>/dev/null || echo "mcpb version command failed"
else
    echo "❌ mcpb CLI tool not found"
    echo "This tool is required for bundle validation and packing"
    echo "Install it with: npm install -g mcpb"
    exit 1
fi

Length of output: 118


Add availability check for mcpb CLI before execution.
scripts/build-mcpb.cjs:126-131
The script currently invokes mcpb without verifying it’s installed, leading to opaque failures. Insert a preflight check (e.g. command -v mcpb or spawnSync('mcpb',['--version'])) and emit a clear error with install instructions before calling mcpb validate.

🤖 Prompt for AI Agents
In scripts/build-mcpb.cjs around lines 126 to 131, the script calls `mcpb
validate` without checking that the `mcpb` CLI is installed; add a preflight
availability check (for example use child_process.spawnSync('mcpb',
['--version']) or run a shell `command -v mcpb`) prior to execSync, and if the
check fails log a clear error message that explains mcpb is not found and gives
install instructions (or a link) and exit with code 1; only proceed to
execSync(`mcpb validate ...`) when the check succeeds.

// Step 8: Pack the bundle
console.log('📦 Creating .mcpb bundle...');
const outputFile = path.join(PROJECT_ROOT, `${manifest.name}-${manifest.version}.mcpb`);

try {
execSync(`mcpb pack "${BUNDLE_DIR}" "${outputFile}"`, { stdio: 'inherit' });
console.log('✅ MCPB bundle created successfully!');
console.log(`📁 Bundle location: ${outputFile}`);
} catch (error) {
console.error('❌ Bundle creation failed:', error.message);
process.exit(1);
}

console.log('');
console.log('🎉 Desktop Commander MCPB bundle is ready!');
console.log('');
console.log('Next steps:');
console.log('1. Test the bundle by installing it in Claude Desktop:');
console.log(' Settings → Extensions → Advanced Settings → Install Extension');
console.log(`2. Select the file: ${outputFile}`);
console.log('3. Configure any settings and test the functionality');
console.log('');
console.log('To submit to Anthropic directory:');
console.log('- Ensure privacy policy is accessible at the GitHub URL');
console.log('- Complete destructive operation annotations (✅ Done)');
console.log('- Submit via Anthropic desktop extensions interest form');
Loading