@wonderwhy-er/desktop-commander 0.2.12 → 0.2.14

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 CHANGED
@@ -22,6 +22,7 @@ Work with code and text, run processes, and automate tasks, going far beyond oth
22
22
  ## Table of Contents
23
23
  - [Features](#features)
24
24
  - [How to install](#how-to-install)
25
+ - [Getting Started](#getting-started)
25
26
  - [Usage](#usage)
26
27
  - [Handling Long-Running Commands](#handling-long-running-commands)
27
28
  - [Work in Progress and TODOs](#roadmap)
@@ -378,6 +379,28 @@ Close and restart Claude Desktop to complete the removal.
378
379
  **Need help?**
379
380
  - Join our Discord community: https://discord.com/invite/kQ27sNnZr7
380
381
 
382
+ ## Getting Started
383
+
384
+ Once Desktop Commander is installed and Claude Desktop is restarted, you're ready to supercharge your Claude experience!
385
+
386
+ ### 🚀 New User Onboarding
387
+
388
+ Desktop Commander includes intelligent onboarding to help you discover what's possible:
389
+
390
+ **For New Users:** When you're just getting started (fewer than 10 successful commands), Claude will automatically offer helpful getting-started guidance and practical tutorials after you use Desktop Commander successfully.
391
+
392
+ **Request Help Anytime:** You can ask for onboarding assistance at any time by simply saying:
393
+ - *"Help me get started with Desktop Commander"*
394
+ - *"Show me Desktop Commander examples"*
395
+ - *"What can I do with Desktop Commander?"*
396
+
397
+ Claude will then show you beginner-friendly tutorials and examples, including:
398
+ - 📁 Organizing your Downloads folder automatically
399
+ - 📊 Analyzing CSV/Excel files with Python
400
+ - ⚙️ Setting up GitHub Actions CI/CD
401
+ - 🔍 Exploring and understanding codebases
402
+ - 🤖 Running interactive development environments
403
+
381
404
  ## Usage
382
405
 
383
406
  The server provides a comprehensive set of tools organized into several categories:
@@ -6,8 +6,9 @@
6
6
  "id": "onb_001",
7
7
  "title": "Organize my Downloads folder",
8
8
  "description": "Clean up and organize your messy Downloads folder into relevant subfolders automatically.",
9
- "prompt": "I want you to help me organize my Downloads folder. Let's do this step by step:\n\n**Phase 1: Analysis**\n1. First, analyze what's in my Downloads folder to understand the types of files\n2. Show me what file types and categories I have\n\n**Phase 2: Planning** \n3. Based on what you find, propose a logical folder structure\n4. Ask for my approval of the organization plan before proceeding\n\n**Phase 3: Organization**\n5. After I approve, create the necessary subfolders\n6. Move files into appropriate categories (documents, images, videos, software, etc.)\n7. Provide a summary of what was organized\n\n**Important**: Don't move any files until I approve your proposed organization structure.\n\nStart by exploring my Downloads folder and showing me what you find, then wait for my approval before organizing.",
9
+ "prompt": "Let's organize your Downloads folder! \n\nFirst, let me check what we're working with. I'll look at your Downloads folder to see how many files are there and what types.\n\nShould I start by analyzing your Downloads folder?",
10
10
  "categories": ["onboarding"],
11
+ "secondaryTag": "Quick Start",
11
12
  "votes": 0,
12
13
  "gaClicks": 0,
13
14
  "icon": "FolderOpen",
@@ -16,13 +17,14 @@
16
17
  },
17
18
  {
18
19
  "id": "onb_002",
19
- "title": "Get my IP address and system info",
20
- "description": "Quickly get your IP address and basic system information.",
21
- "prompt": "Please help me get my system information:\n\n1. Get my current IP address (both local and public if possible)\n2. Show basic system information like:\n - Operating system and version\n - Available disk space\n - Memory usage\n - Current date and time\n - Network connectivity status\n\nProvide this information in a clear, easy-to-read format.",
20
+ "title": "Set up GitHub Actions CI/CD",
21
+ "description": "Set up GitHub Actions for your project to automatically run tests on every push with proper CI/CD workflow.",
22
+ "prompt": "Let's set up GitHub Actions CI/CD for your project! 🚀\n\n**What's the path to your project folder?**\n\n*Try: `~/work/my-project` (replace with your path) or give me a different path.*\n\nI'll analyze your project type and set up automated testing and deployment in about 15 minutes!",
22
23
  "categories": ["onboarding"],
24
+ "secondaryTag": "Build & Deploy",
23
25
  "votes": 0,
24
26
  "gaClicks": 0,
25
- "icon": "Monitor",
27
+ "icon": "GitBranch",
26
28
  "author": "DC team",
27
29
  "verified": true
28
30
  },
@@ -30,8 +32,9 @@
30
32
  "id": "onb_003",
31
33
  "title": "Create organized knowledge/documents folder",
32
34
  "description": "Set up a well-structured knowledge base or document organization system with templates and suggested categories.",
33
- "prompt": "I want to help you create a well-organized knowledge base and document system. Let me understand your needs first:\n\n**Option 1: Create New Knowledge Base**\n- What type of work or projects do you typically do?\n- What subjects or topics would you like to organize knowledge about?\n- Do you prefer a general-purpose system or something specialized?\n\n**Option 2: Organize Existing Documents**\n- Do you have an existing folder with documents that needs organization?\n- What's the path to the folder you'd like me to analyze?\n\n**My Process:**\n1. **Assessment**: Understand your needs and existing content\n2. **Structure Design**: Propose a folder hierarchy tailored to your work\n3. **Template Creation**: Create useful template files (notes, project plans, etc.)\n4. **Organization**: Set up the complete system with guidelines\n\nWhich option sounds better - creating a new knowledge base from scratch, or organizing existing documents? And what type of work/subjects do you focus on?",
35
+ "prompt": "Let's create an organized knowledge base! 📚\n\n**Where should I set it up?**\n\n*I suggest: `~/Documents/Knowledge-Base` (replace with your path) or give me a different location.*\n\nI'll create a clean folder structure with templates and organize any existing documents you have!",
34
36
  "categories": ["onboarding"],
37
+ "secondaryTag": "Quick Start",
35
38
  "votes": 0,
36
39
  "gaClicks": 0,
37
40
  "icon": "BookOpen",
@@ -42,8 +45,9 @@
42
45
  "id": "onb_004",
43
46
  "title": "Explain codebase or repository to me",
44
47
  "description": "Analyze and explain any codebase - local project or GitHub repository - including architecture, dependencies, and how it works.",
45
- "prompt": "I'll help you understand a codebase thoroughly. Let me know what you'd like to analyze:\n\n**Option 1: Local Project**\n- What's the folder path to your local project?\n\n**Option 2: GitHub Repository**\n- What's the GitHub repository URL you'd like me to analyze?\n- If you don't have git/gh installed, I'll help you set them up\n- If needed, I'll help you log in to GitHub\n\n**What I'll do:**\n1. **Setup**: Install git/gh CLI tools if needed and help with GitHub login\n2. **Code Retrieval**: Clone the repo or analyze your local folder\n3. **Initial Analysis**: Examine project structure, files, and technologies\n4. **Architecture Overview**: Explain the overall design and patterns\n5. **Key Components**: Break down main modules and features\n6. **Dependencies**: Document libraries and frameworks used\n7. **Setup Guide**: Provide step-by-step setup and running instructions\n8. **Code Flow**: Explain how the main functionality works\n9. **Summary Document**: Create comprehensive documentation\n\nPlease provide either:\n- Local folder path: `/path/to/your/project`\n- GitHub URL: `https://github.com/user/repo` or `git@github.com:user/repo.git`",
48
+ "prompt": "I'll analyze and explain any codebase for you! 🔍\n\n**What should I analyze?**\n\n*Local project:* `~/work/my-project` (replace with your path)\n*GitHub repo:* `https://github.com/user/repo`\n\nI'll break down the architecture, dependencies, and how everything works together!",
46
49
  "categories": ["onboarding"],
50
+ "secondaryTag": "Code Analysis",
47
51
  "votes": 0,
48
52
  "gaClicks": 0,
49
53
  "icon": "Code",
@@ -54,8 +58,9 @@
54
58
  "id": "onb_005",
55
59
  "title": "Clean up unused code in my project",
56
60
  "description": "Scan your codebase to find unused imports, dead functions, and redundant code that can be safely removed.",
57
- "prompt": "I'll help you clean up unused and dead code in your project. Let's start safely:\n\n**First, I need to know:**\n- What's the folder path to your project?\n- What programming language/framework is it? (JavaScript, Python, Java, etc.)\n\n**My systematic approach:**\n\n**Phase 1: Analysis**\n1. **Project Understanding**: Analyze structure and main technologies\n2. **Dependency Mapping**: Understand how files reference each other\n3. **Code Scanning**: Identify potential unused code:\n - Unused imports and dependencies\n - Dead/unreferenced functions and variables\n - Unreachable code blocks\n - Unused configuration files\n\n**Phase 2: Safety & Backup**\n4. **Impact Assessment**: Explain what each finding does and removal safety\n5. **Backup Strategy**: Recommend backup before making changes\n6. **Show Before Remove**: Present everything I plan to remove\n\n**Phase 3: Cleanup (Only with your approval)**\n7. **Selective Removal**: Remove only what you approve\n8. **Testing**: Suggest running tests after cleanup\n9. **Summary Report**: Document what was cleaned and space saved\n\n**Safety Promise**: I will NEVER remove code without your explicit approval for each change.\n\nWhat's the path to your project folder?",
61
+ "prompt": "Let's clean up unused code in your project! 🧹\n\n**What's your project folder path?**\n\n*Try: `~/work/my-project` (replace with your path)*\n\nI'll safely scan for dead code and unused imports, then show you exactly what can be removed before making any changes!",
58
62
  "categories": ["onboarding"],
63
+ "secondaryTag": "Code Analysis",
59
64
  "votes": 0,
60
65
  "gaClicks": 0,
61
66
  "icon": "Trash2",
@@ -65,9 +70,10 @@
65
70
  {
66
71
  "id": "onb_006",
67
72
  "title": "Build shopping list app and deploy online",
68
- "description": "Create a complete shopping list web application from scratch, run it locally, and deploy it to GitHub Pages.",
69
- "prompt": "Let's build and deploy a complete shopping list web application! I'll guide you step-by-step:\n\n**Step 1: Project Setup**\nFirst, where would you like to work on this project? Please provide a folder path where I should create the shopping list app.\n\n**My Complete Process:**\n\n**Phase 1: Planning & Setup**\n1. Create project folder and basic structure\n2. Plan app features (add/remove items, mark as bought, categories)\n3. Set up HTML, CSS, and JavaScript foundation\n\n**Phase 2: Development**\n4. Build clean, modern user interface\n5. Implement core functionality (CRUD operations)\n6. Add local storage for persistence\n7. Create a simple local development server\n8. Open the app in your browser for testing\n\n**Phase 3: GitHub & Deployment** \n9. Check if you have git/gh CLI installed (install if needed)\n10. Help you log in to GitHub\n11. Create a new GitHub repository\n12. Commit and push your code\n13. Set up GitHub Pages hosting\n14. Deploy your app online with a live URL\n\n**App Features:**\n- Add/remove shopping items\n- Mark items as purchased with checkboxes\n- Organize items by categories\n- Persistent storage (survives browser refresh)\n- Responsive design (works on mobile)\n- Modern, clean interface\n\nReady to start? What folder should I use for your shopping list app project?",
73
+ "description": "Create a simple shopping list web app from scratch and deploy it online - perfect for learning web development basics.",
74
+ "prompt": "Let's build a simple shopping list web app and deploy it online! 🛒\n\n**Quick question:** Where should I create the project folder?\n\n*I suggest using `~/Downloads/shopping-app` for quick testing, or give me a different path if you prefer.*\n\nOnce I have the folder, I'll build a working app step-by-step and get it online in about 20 minutes!",
70
75
  "categories": ["onboarding"],
76
+ "secondaryTag": "Build & Deploy",
71
77
  "votes": 0,
72
78
  "gaClicks": 0,
73
79
  "icon": "ShoppingCart",
@@ -78,8 +84,9 @@
78
84
  "id": "onb_007",
79
85
  "title": "Analyze my data file",
80
86
  "description": "Upload or point to any data file (CSV, JSON, Excel, etc.) and get comprehensive analysis including patterns, insights, and summary reports.",
81
- "prompt": "I have a data file that I'd like to understand better. Please help me analyze it:\n\n1. **File Examination**: First, let me know the path to your data file or describe what kind of data it contains\n2. **Data Overview**: Analyze the structure, size, and format of the data\n3. **Content Analysis**: Examine what columns/fields exist and their data types\n4. **Pattern Detection**: Look for interesting patterns, trends, or anomalies\n5. **Statistical Summary**: Provide key statistics (counts, averages, distributions, etc.)\n6. **Data Quality**: Identify any missing data, duplicates, or quality issues\n7. **Key Insights**: Highlight the most interesting findings\n8. **Summary Report**: Create a clear, non-technical summary of what the data shows\n\nWhat's the path to your data file, or would you like to tell me what kind of data you're working with first?",
87
+ "prompt": "I'll help you analyze your data file! \n\nWhat's the path to your data file? (e.g., `/Users/yourname/data.csv`)\n\nOnce you give me the path, I'll start by checking what type of file it is and show you a quick preview, then we can dive deeper step by step.",
82
88
  "categories": ["onboarding"],
89
+ "secondaryTag": "Quick Start",
83
90
  "votes": 0,
84
91
  "gaClicks": 0,
85
92
  "icon": "BarChart3",
@@ -90,8 +97,9 @@
90
97
  "id": "onb_008",
91
98
  "title": "Check system health and resources",
92
99
  "description": "Analyze your system's health, resource usage, running processes, and generate a comprehensive system status report.",
93
- "prompt": "Let me perform a comprehensive system health check and resource analysis:\n\n**System Resource Analysis:**\n1. **CPU Usage**: Current and recent CPU utilization patterns\n2. **Memory Usage**: RAM usage, available memory, swap usage\n3. **Disk Space**: Storage usage across all drives/partitions\n4. **Network Status**: Network interfaces, connectivity, and usage\n\n**Process Analysis:**\n5. **Running Processes**: Top resource-consuming processes\n6. **System Services**: Critical service status\n7. **Port Usage**: Open ports and listening services\n\n**Health Indicators:**\n8. **System Load**: Overall system performance indicators\n9. **Uptime**: System uptime and stability\n10. **Temperature**: System temperature if available\n11. **Logs**: Recent system errors or warnings\n\n**Deliverable:**\n- Comprehensive system health report\n- Resource usage summary with recommendations\n- Identification of potential issues\n- Performance optimization suggestions\n\nI'll gather all this information using system commands and present it in a clear, actionable format.",
100
+ "prompt": "Let me check your system health and resources!\n\nI'll start by looking at your CPU, memory, and disk usage, then check for any performance issues.\n\nShould I begin the system analysis?",
94
101
  "categories": ["onboarding"],
102
+ "secondaryTag": "Quick Start",
95
103
  "votes": 0,
96
104
  "gaClicks": 0,
97
105
  "icon": "Activity",
@@ -102,8 +110,9 @@
102
110
  "id": "onb_009",
103
111
  "title": "Find Patterns and Errors in Log Files",
104
112
  "description": "Analyze log files to identify errors, patterns, performance issues, and security concerns with detailed insights and recommendations.",
105
- "prompt": "I'll help you analyze log files to find patterns, errors, and insights. Let me know which approach you prefer:\n\n**Option 1: Analyze Specific Log File**\n- Do you have a specific log file you want me to analyze?\n- What's the full path to your log file?\n\n**Option 2: Find Log Files on Your System**\n- I can search your system for common log file locations\n- What type of application/service logs are you interested in? (web server, database, application, system logs, etc.)\n\n**What I'll analyze:**\n\n**Error Detection:**\n1. **Critical Errors**: Fatal errors, exceptions, crashes\n2. **Warning Patterns**: Recurring warnings that might indicate issues\n3. **Error Frequency**: How often specific errors occur\n4. **Error Trends**: Are errors increasing over time?\n\n**Performance Analysis:**\n5. **Response Times**: Slow queries, long-running operations\n6. **Resource Usage**: Memory, CPU, disk I/O patterns\n7. **Traffic Patterns**: Peak usage times, load distribution\n8. **Bottlenecks**: Identify performance constraints\n\n**Security & Anomalies:**\n9. **Failed Login Attempts**: Potential security threats\n10. **Unusual Access Patterns**: Suspicious activity\n11. **IP Analysis**: Frequent visitors, geographic patterns\n12. **Rate Limiting**: Abuse detection\n\n**Deliverables:**\n- **Error Summary Report**: Top errors with occurrence counts\n- **Timeline Analysis**: When issues happen most frequently\n- **Pattern Recognition**: Recurring themes and root causes\n- **Actionable Recommendations**: Specific steps to fix identified issues\n- **Monitoring Suggestions**: What to watch for in the future\n\nWhich option would you prefer? Please provide either:\n- **Specific file**: `/path/to/your/logfile.log`\n- **Search request**: \"Find web server logs\" or \"Look for application logs\"",
113
+ "prompt": "I'll analyze your log files to find errors and patterns! 🔍\n\n**What log file should I analyze?**\n\n*Try: `/var/log/system.log` (macOS/Linux) or `~/app.log`, or I can search for logs on your system.*\n\nI'll find errors, performance issues, and suspicious patterns with actionable recommendations!",
106
114
  "categories": ["onboarding"],
115
+ "secondaryTag": "Code Analysis",
107
116
  "votes": 0,
108
117
  "gaClicks": 0,
109
118
  "icon": "Search",
@@ -24,6 +24,7 @@ export async function handleStartSearch(args) {
24
24
  contextLines: parsed.data.contextLines,
25
25
  timeout: parsed.data.timeout_ms,
26
26
  earlyTermination: parsed.data.earlyTermination,
27
+ literalSearch: parsed.data.literalSearch,
27
28
  });
28
29
  const searchTypeText = parsed.data.searchType === 'content' ? 'content search' : 'file search';
29
30
  let output = `Started ${searchTypeText} session: ${result.sessionId}\n`;
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,201 @@
1
+ #!/usr/bin/env node
2
+ import { FilteredStdioServerTransport } from './custom-stdio.js';
3
+ import { server } from './server.js';
4
+ import { configManager } from './config-manager.js';
5
+ import { runSetup } from './npm-scripts/setup.js';
6
+ import { runUninstall } from './npm-scripts/uninstall.js';
7
+ import { capture } from './utils/capture.js';
8
+ import { logToStderr, logger } from './utils/logger.js';
9
+ import { OAuthHttpServer } from './oauth/server.js';
10
+ async function runServer() {
11
+ try {
12
+ // Check if first argument is "setup"
13
+ if (process.argv[2] === 'setup') {
14
+ await runSetup();
15
+ return;
16
+ }
17
+ // Check if first argument is "remove"
18
+ if (process.argv[2] === 'remove') {
19
+ await runUninstall();
20
+ return;
21
+ }
22
+ // Check for HTTP mode with OAuth
23
+ const httpMode = process.argv.includes('--http') || process.argv.includes('--oauth');
24
+ const port = getPortFromArgs() || 8000;
25
+ if (httpMode) {
26
+ await runHttpServer(port);
27
+ }
28
+ else {
29
+ await runStdioServer();
30
+ }
31
+ }
32
+ catch (error) {
33
+ logger.error('Failed to start server:', error);
34
+ process.exit(1);
35
+ }
36
+ }
37
+ async function runStdioServer() {
38
+ logger.info('Loading server.ts');
39
+ logger.info('Setting up request handlers...');
40
+ try {
41
+ logger.info('Loading configuration...');
42
+ await configManager.loadConfig();
43
+ logger.info('Configuration loaded successfully');
44
+ const transport = new FilteredStdioServerTransport();
45
+ logger.info('Enhanced FilteredStdioServerTransport initialized');
46
+ logger.info('Connecting server...');
47
+ await server.connect(transport);
48
+ logger.info('Server connected successfully');
49
+ }
50
+ catch (error) {
51
+ await capture('error_start_stdio_server', { error });
52
+ logToStderr('error', `Failed to start stdio server: ${error}`);
53
+ throw error;
54
+ }
55
+ }
56
+ async function runHttpServer(port) {
57
+ logger.info(`Starting HTTP server with OAuth on port ${port}`);
58
+ try {
59
+ logger.info('Loading configuration...');
60
+ await configManager.loadConfig();
61
+ logger.info('Configuration loaded successfully');
62
+ // OAuth configuration
63
+ const baseUrl = `http://localhost:${port}`;
64
+ const oauthConfig = {
65
+ enabled: true,
66
+ clientId: 'desktop-commander-mcp',
67
+ clientSecret: 'dc-secret-' + Math.random().toString(36).substring(7),
68
+ redirectUri: `${baseUrl}/callback`,
69
+ authorizationUrl: `${baseUrl}/authorize`,
70
+ tokenUrl: `${baseUrl}/token`,
71
+ scope: 'mcp:access mcp:tools mcp:resources',
72
+ issuer: baseUrl
73
+ };
74
+ // Create MCP handler for HTTP requests
75
+ const mcpHandler = createMcpHttpHandler();
76
+ // Create OAuth HTTP server
77
+ const oauthServer = new OAuthHttpServer(oauthConfig, mcpHandler);
78
+ oauthServer.listen(port, () => {
79
+ logger.info(`HTTP server running on port ${port}`);
80
+ logger.info(`Authorization Server Metadata: ${baseUrl}/.well-known/oauth-authorization-server`);
81
+ logger.info(`MCP Endpoint: ${baseUrl}/mcp`);
82
+ logger.info(`SSE Endpoint: ${baseUrl}/sse`);
83
+ console.log(`\n🚀 DesktopCommanderMCP HTTP Server Started!`);
84
+ console.log(`📍 Server URL: ${baseUrl}`);
85
+ console.log(`🔐 OAuth Metadata: ${baseUrl}/.well-known/oauth-authorization-server`);
86
+ console.log(`🔧 MCP Endpoint: ${baseUrl}/mcp`);
87
+ console.log(`⚡ SSE Endpoint: ${baseUrl}/sse`);
88
+ console.log(`\n📝 For Claude Custom Connectors:`);
89
+ console.log(` URL: ${baseUrl}/sse`);
90
+ console.log(` Authentication: OAuth`);
91
+ });
92
+ // Graceful shutdown
93
+ process.on('SIGINT', () => {
94
+ logger.info('Received SIGINT, shutting down gracefully...');
95
+ oauthServer.close(() => {
96
+ process.exit(0);
97
+ });
98
+ });
99
+ }
100
+ catch (error) {
101
+ await capture('error_start_http_server', { error });
102
+ logToStderr('error', `Failed to start HTTP server: ${error}`);
103
+ throw error;
104
+ }
105
+ }
106
+ function createMcpHttpHandler() {
107
+ return (req, res) => {
108
+ const url = new URL(req.url, `http://${req.headers.host}`);
109
+ // Handle SSE endpoint for MCP clients
110
+ if (url.pathname === '/sse') {
111
+ handleSSE(req, res);
112
+ return;
113
+ }
114
+ // Handle Streamable HTTP endpoint
115
+ if (url.pathname === '/mcp') {
116
+ handleStreamableHttp(req, res);
117
+ return;
118
+ }
119
+ // Health check
120
+ if (url.pathname === '/health') {
121
+ res.writeHead(200, { 'Content-Type': 'application/json' });
122
+ res.end(JSON.stringify({ status: 'ok', service: 'DesktopCommanderMCP' }));
123
+ return;
124
+ }
125
+ // 404 for other paths
126
+ res.writeHead(404, { 'Content-Type': 'application/json' });
127
+ res.end(JSON.stringify({ error: 'Not found' }));
128
+ };
129
+ }
130
+ function handleSSE(req, res) {
131
+ // Set SSE headers
132
+ res.writeHead(200, {
133
+ 'Content-Type': 'text/event-stream',
134
+ 'Cache-Control': 'no-cache',
135
+ 'Connection': 'keep-alive',
136
+ 'Access-Control-Allow-Origin': '*',
137
+ 'Access-Control-Allow-Headers': 'Cache-Control'
138
+ });
139
+ // SSE implementation would go here
140
+ // For now, just indicate that SSE is available
141
+ res.write('event: connected\n');
142
+ res.write('data: {"type":"connected","message":"DesktopCommanderMCP SSE endpoint"}\n\n');
143
+ // Keep connection alive
144
+ const heartbeat = setInterval(() => {
145
+ res.write('event: heartbeat\n');
146
+ res.write('data: {"type":"heartbeat","timestamp":' + Date.now() + '}\n\n');
147
+ }, 30000);
148
+ req.on('close', () => {
149
+ clearInterval(heartbeat);
150
+ });
151
+ }
152
+ function handleStreamableHttp(req, res) {
153
+ // Handle Streamable HTTP for MCP
154
+ res.writeHead(200, { 'Content-Type': 'application/json' });
155
+ if (req.method === 'GET') {
156
+ // Return server info
157
+ res.end(JSON.stringify({
158
+ name: 'DesktopCommanderMCP',
159
+ version: '0.2.13',
160
+ transport: 'streamable-http',
161
+ authenticated: !!req.user
162
+ }));
163
+ return;
164
+ }
165
+ // Handle MCP protocol messages
166
+ if (req.method === 'POST') {
167
+ let body = '';
168
+ req.on('data', chunk => body += chunk.toString());
169
+ req.on('end', () => {
170
+ try {
171
+ const message = JSON.parse(body);
172
+ // Process MCP message through the server
173
+ // This would need to be integrated with the existing MCP server
174
+ res.end(JSON.stringify({
175
+ jsonrpc: '2.0',
176
+ id: message.id,
177
+ result: { message: 'MCP message received' }
178
+ }));
179
+ }
180
+ catch (error) {
181
+ res.writeHead(400, { 'Content-Type': 'application/json' });
182
+ res.end(JSON.stringify({ error: 'Invalid JSON' }));
183
+ }
184
+ });
185
+ return;
186
+ }
187
+ res.writeHead(405, { 'Content-Type': 'application/json' });
188
+ res.end(JSON.stringify({ error: 'Method not allowed' }));
189
+ }
190
+ function getPortFromArgs() {
191
+ const portIndex = process.argv.findIndex(arg => arg === '--port');
192
+ if (portIndex !== -1 && process.argv[portIndex + 1]) {
193
+ return parseInt(process.argv[portIndex + 1], 10);
194
+ }
195
+ return null;
196
+ }
197
+ // Run the server
198
+ runServer().catch(error => {
199
+ console.error('Fatal error:', error);
200
+ process.exit(1);
201
+ });
@@ -0,0 +1,22 @@
1
+ import type { OAuthConfig, AuthorizationServerMetadata, TokenResponse, AccessToken, AuthorizationRequest, TokenRequest } from './types.js';
2
+ export declare class OAuthProvider {
3
+ private config;
4
+ private users;
5
+ private authCodes;
6
+ private accessTokens;
7
+ constructor(config: OAuthConfig);
8
+ static generatePKCE(): {
9
+ codeVerifier: string;
10
+ codeChallenge: string;
11
+ };
12
+ getAuthorizationServerMetadata(): AuthorizationServerMetadata;
13
+ handleAuthorizationRequest(params: AuthorizationRequest): {
14
+ authUrl: string;
15
+ authCode?: string;
16
+ error?: string;
17
+ };
18
+ handleTokenRequest(params: TokenRequest): Promise<TokenResponse | {
19
+ error: string;
20
+ }>;
21
+ validateAccessToken(token: string): AccessToken | null;
22
+ }
@@ -0,0 +1,124 @@
1
+ import crypto from 'crypto';
2
+ export class OAuthProvider {
3
+ constructor(config) {
4
+ this.users = new Map();
5
+ this.authCodes = new Map();
6
+ this.accessTokens = new Map();
7
+ this.config = config;
8
+ // Add a default user for testing
9
+ this.users.set('admin', {
10
+ email: 'admin@localhost',
11
+ password: 'admin123' // In production, hash this!
12
+ });
13
+ }
14
+ // Generate PKCE code verifier and challenge
15
+ static generatePKCE() {
16
+ const codeVerifier = crypto.randomBytes(32).toString('base64url');
17
+ const codeChallenge = crypto
18
+ .createHash('sha256')
19
+ .update(codeVerifier)
20
+ .digest('base64url');
21
+ return { codeVerifier, codeChallenge };
22
+ }
23
+ // Get Authorization Server Metadata (required by MCP spec)
24
+ getAuthorizationServerMetadata() {
25
+ const baseUrl = this.config.issuer;
26
+ return {
27
+ issuer: baseUrl,
28
+ authorization_endpoint: `${baseUrl}/authorize`,
29
+ token_endpoint: `${baseUrl}/token`,
30
+ registration_endpoint: `${baseUrl}/register`,
31
+ revocation_endpoint: `${baseUrl}/revoke`,
32
+ jwks_uri: `${baseUrl}/.well-known/jwks.json`,
33
+ response_types_supported: ['code'],
34
+ grant_types_supported: ['authorization_code', 'refresh_token'],
35
+ token_endpoint_auth_methods_supported: ['none', 'client_secret_basic'],
36
+ code_challenge_methods_supported: ['S256'],
37
+ scopes_supported: ['mcp:access', 'mcp:tools', 'mcp:resources']
38
+ };
39
+ }
40
+ // Handle authorization request
41
+ handleAuthorizationRequest(params) {
42
+ if (!params.client_id || !params.redirect_uri || !params.code_challenge) {
43
+ return { authUrl: '', error: 'Missing required parameters' };
44
+ }
45
+ if (params.code_challenge_method !== 'S256') {
46
+ return { authUrl: '', error: 'Unsupported code_challenge_method' };
47
+ }
48
+ // Generate authorization code
49
+ const authCode = crypto.randomBytes(32).toString('hex');
50
+ const expiresAt = Date.now() + 10 * 60 * 1000; // 10 minutes
51
+ // Store authorization code
52
+ this.authCodes.set(authCode, {
53
+ userId: 'admin', // For simplicity, auto-approve for admin user
54
+ clientId: params.client_id,
55
+ redirectUri: params.redirect_uri,
56
+ scope: params.scope || 'mcp:access',
57
+ codeChallenge: params.code_challenge,
58
+ codeChallengeMethod: params.code_challenge_method,
59
+ expiresAt
60
+ });
61
+ // Build redirect URL with auth code
62
+ const redirectUrl = new URL(params.redirect_uri);
63
+ redirectUrl.searchParams.set('code', authCode);
64
+ if (params.state) {
65
+ redirectUrl.searchParams.set('state', params.state);
66
+ }
67
+ return { authUrl: redirectUrl.toString(), authCode };
68
+ }
69
+ // Exchange authorization code for access token
70
+ async handleTokenRequest(params) {
71
+ const authCodeData = this.authCodes.get(params.code);
72
+ if (!authCodeData) {
73
+ return { error: 'Invalid authorization code' };
74
+ }
75
+ if (Date.now() > authCodeData.expiresAt) {
76
+ this.authCodes.delete(params.code);
77
+ return { error: 'Authorization code expired' };
78
+ }
79
+ // Verify PKCE
80
+ const computedChallenge = crypto
81
+ .createHash('sha256')
82
+ .update(params.code_verifier)
83
+ .digest('base64url');
84
+ if (computedChallenge !== authCodeData.codeChallenge) {
85
+ return { error: 'Invalid code_verifier' };
86
+ }
87
+ // Verify client and redirect URI
88
+ if (params.client_id !== authCodeData.clientId ||
89
+ params.redirect_uri !== authCodeData.redirectUri) {
90
+ return { error: 'Invalid client_id or redirect_uri' };
91
+ }
92
+ // Generate access token
93
+ const accessToken = crypto.randomBytes(32).toString('hex');
94
+ const expiresIn = 3600; // 1 hour
95
+ const tokenData = {
96
+ sub: authCodeData.userId,
97
+ aud: params.client_id,
98
+ iss: this.config.issuer,
99
+ exp: Math.floor(Date.now() / 1000) + expiresIn,
100
+ iat: Math.floor(Date.now() / 1000),
101
+ scope: authCodeData.scope
102
+ };
103
+ this.accessTokens.set(accessToken, tokenData);
104
+ this.authCodes.delete(params.code);
105
+ return {
106
+ access_token: accessToken,
107
+ token_type: 'Bearer',
108
+ expires_in: expiresIn,
109
+ scope: authCodeData.scope
110
+ };
111
+ }
112
+ // Validate access token
113
+ validateAccessToken(token) {
114
+ const tokenData = this.accessTokens.get(token);
115
+ if (!tokenData) {
116
+ return null;
117
+ }
118
+ if (Date.now() / 1000 > tokenData.exp) {
119
+ this.accessTokens.delete(token);
120
+ return null;
121
+ }
122
+ return tokenData;
123
+ }
124
+ }
@@ -0,0 +1,18 @@
1
+ import http from 'http';
2
+ import type { OAuthConfig } from './types.js';
3
+ export declare class OAuthHttpServer {
4
+ private server;
5
+ private oauthProvider;
6
+ private mcpHandler;
7
+ constructor(config: OAuthConfig, mcpHandler: (req: http.IncomingMessage, res: http.ServerResponse) => void);
8
+ private handleRequest;
9
+ private setCorsHeaders;
10
+ private handleAuthServerMetadata;
11
+ private handleAuthorize;
12
+ private handleToken;
13
+ private handleCallback;
14
+ private sendUnauthorized;
15
+ private getRequestBody;
16
+ listen(port: number, callback?: () => void): void;
17
+ close(callback?: () => void): void;
18
+ }