-
-
Notifications
You must be signed in to change notification settings - Fork 738
Add tool annotations and new manifest and MCPB/DXT build script #246
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
989d866
be05b25
9754420
7994c13
91a5f85
c05d554
6d04b5f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| { | ||
| "dxt_version": "0.1", | ||
| "manifest_version": "0.1", | ||
| "name": "desktop-commander", | ||
| "display_name": "Desktop Commander", | ||
| "version": "{{VERSION}}", | ||
| "description": "Execute long-running terminal commands and manage processes through Model Context Protocol (MCP)", | ||
| "author": { | ||
| "name": "Desktop Commander Team", | ||
| "url": "https://github.com/wonderwhy-er/DesktopCommanderMCP" | ||
| }, | ||
| "privacy_policies": [ | ||
| "https://legal.desktopcommander.app/privacy_desktop_commander_mcp" | ||
| ], | ||
| "server": { | ||
| "type": "node", | ||
| "entry_point": "dist/index.js", | ||
| "mcp_config": { | ||
| "command": "node", | ||
| "args": [ | ||
| "${__dirname}/dist/index.js" | ||
| ], | ||
| "env": { | ||
| "NODE_ENV": "production" | ||
| } | ||
| } | ||
| }, | ||
| "homepage": "https://github.com/wonderwhy-er/DesktopCommanderMCP", | ||
| "repository": { | ||
| "type": "git", | ||
| "url": "https://github.com/wonderwhy-er/DesktopCommanderMCP.git" | ||
| }, | ||
| "license": "MIT", | ||
| "icon": "logo.png" | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| { | ||
| "dxt_version": "0.1", | ||
| "name": "desktop-commander", | ||
| "display_name": "Desktop Commander", | ||
| "version": "{{VERSION}}", | ||
| "description": "Execute long-running terminal commands and manage processes through Model Context Protocol (MCP)", | ||
| "author": { | ||
| "name": "Desktop Commander Team", | ||
| "url": "https://github.com/wonderwhy-er/DesktopCommanderMCP" | ||
| }, | ||
| "server": { | ||
| "type": "node", | ||
| "entry_point": "dist/index.js", | ||
| "mcp_config": { | ||
| "command": "node", | ||
| "args": [ | ||
| "${__dirname}/dist/index.js" | ||
| ], | ||
| "env": { | ||
| "NODE_ENV": "production" | ||
| } | ||
| } | ||
| }, | ||
| "homepage": "https://github.com/wonderwhy-er/DesktopCommanderMCP", | ||
| "repository": { | ||
| "type": "git", | ||
| "url": "https://github.com/wonderwhy-er/DesktopCommanderMCP.git" | ||
| }, | ||
| "license": "MIT", | ||
| "icon": "logo.png" | ||
| } | ||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,170 @@ | ||||||||||||||||
| #!/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 with privacy policy | ||||||||||||||||
| * 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
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix bundle directory creation syntax error.
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
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||
| // 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...'); | ||||||||||||||||
|
|
||||||||||||||||
| // Check if we should use the future manifest (with privacy policies) | ||||||||||||||||
| const useFutureManifest = process.argv.includes('--future'); | ||||||||||||||||
| const manifestTemplatePath = path.join( | ||||||||||||||||
| PROJECT_ROOT, | ||||||||||||||||
| useFutureManifest ? 'manifest.future.json' : 'manifest.template.json' | ||||||||||||||||
| ); | ||||||||||||||||
|
|
||||||||||||||||
| console.log(`📄 Using manifest: ${useFutureManifest ? 'manifest.future.json' : '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', | ||||||||||||||||
| 'logo.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 for dependency info | ||||||||||||||||
| const bundlePackageJson = { | ||||||||||||||||
| name: manifest.name, | ||||||||||||||||
| version: manifest.version, | ||||||||||||||||
| description: manifest.description, | ||||||||||||||||
| main: "dist/index.js", | ||||||||||||||||
| author: manifest.author, | ||||||||||||||||
| license: manifest.license, | ||||||||||||||||
| repository: manifest.repository | ||||||||||||||||
| }; | ||||||||||||||||
|
|
||||||||||||||||
| fs.writeFileSync( | ||||||||||||||||
| path.join(BUNDLE_DIR, 'package.json'), | ||||||||||||||||
| JSON.stringify(bundlePackageJson, null, 2) | ||||||||||||||||
| ); | ||||||||||||||||
|
|
||||||||||||||||
| // 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
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion 🧩 Analysis chainVerify mcpb CLI tool availability. The script depends on the 🏁 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
fiLength of output: 118 Add availability check for mcpb CLI before execution. 🤖 Prompt for AI Agents |
||||||||||||||||
|
|
||||||||||||||||
| // 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('Build options:'); | ||||||||||||||||
| console.log('- Default: npm run build:mcpb (uses manifest.template.json)'); | ||||||||||||||||
| console.log('- Future: npm run build:mcpb -- --future (uses manifest.future.json with privacy policies)'); | ||||||||||||||||
| console.log(''); | ||||||||||||||||
| if (!useFutureManifest) { | ||||||||||||||||
| console.log('📝 Note: Using basic manifest for Claude Desktop compatibility.'); | ||||||||||||||||
| console.log(' Use --future flag when privacy policies are supported.'); | ||||||||||||||||
| } else { | ||||||||||||||||
| console.log('🔮 Using future manifest with privacy policies for Anthropic submission.'); | ||||||||||||||||
| } | ||||||||||||||||
| console.log(''); | ||||||||||||||||
| console.log('To submit to Anthropic directory:'); | ||||||||||||||||
| console.log('- Build with: npm run build:mcpb -- --future'); | ||||||||||||||||
| 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'); | ||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix the MCP command args path (literal
${__dirname}won’t resolve).mcp_config.argsis 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:
📝 Committable suggestion
🤖 Prompt for AI Agents