@wonderwhy-er/desktop-commander 0.1.34 → 0.1.36

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.
Files changed (63) hide show
  1. package/LICENSE +2 -2
  2. package/README.md +186 -56
  3. package/dist/command-manager.d.ts +1 -7
  4. package/dist/command-manager.js +31 -50
  5. package/dist/config-manager.d.ts +28 -16
  6. package/dist/config-manager.js +124 -189
  7. package/dist/config.d.ts +2 -2
  8. package/dist/config.js +7 -4
  9. package/dist/error-handlers.js +4 -0
  10. package/dist/handlers/edit-search-handlers.d.ts +3 -1
  11. package/dist/handlers/edit-search-handlers.js +9 -19
  12. package/dist/handlers/filesystem-handlers.d.ts +0 -4
  13. package/dist/handlers/filesystem-handlers.js +11 -19
  14. package/dist/handlers/index.d.ts +0 -1
  15. package/dist/handlers/index.js +0 -1
  16. package/dist/index.js +19 -4
  17. package/dist/polyform-license-src/edit/edit.d.ts +15 -0
  18. package/dist/polyform-license-src/edit/edit.js +163 -0
  19. package/dist/polyform-license-src/edit/fuzzySearch.d.ts +30 -0
  20. package/dist/polyform-license-src/edit/fuzzySearch.js +121 -0
  21. package/dist/polyform-license-src/edit/handlers.d.ts +16 -0
  22. package/dist/polyform-license-src/edit/handlers.js +24 -0
  23. package/dist/polyform-license-src/edit/index.d.ts +12 -0
  24. package/dist/polyform-license-src/edit/index.js +13 -0
  25. package/dist/polyform-license-src/edit/schemas.d.ts +25 -0
  26. package/dist/polyform-license-src/edit/schemas.js +16 -0
  27. package/dist/polyform-license-src/index.d.ts +9 -0
  28. package/dist/polyform-license-src/index.js +10 -0
  29. package/dist/sandbox/index.d.ts +9 -0
  30. package/dist/sandbox/index.js +50 -0
  31. package/dist/sandbox/mac-sandbox.d.ts +19 -0
  32. package/dist/sandbox/mac-sandbox.js +174 -0
  33. package/dist/server.js +181 -176
  34. package/dist/setup-claude-server.js +554 -244
  35. package/dist/terminal-manager.d.ts +1 -1
  36. package/dist/terminal-manager.js +22 -3
  37. package/dist/tools/config.d.ts +0 -58
  38. package/dist/tools/config.js +44 -107
  39. package/dist/tools/debug-path.d.ts +1 -0
  40. package/dist/tools/debug-path.js +44 -0
  41. package/dist/tools/edit.d.ts +8 -6
  42. package/dist/tools/edit.js +165 -35
  43. package/dist/tools/execute.js +6 -6
  44. package/dist/tools/filesystem-fixed.d.ts +22 -0
  45. package/dist/tools/filesystem-fixed.js +176 -0
  46. package/dist/tools/filesystem.d.ts +4 -6
  47. package/dist/tools/filesystem.js +157 -87
  48. package/dist/tools/fuzzySearch.d.ts +22 -0
  49. package/dist/tools/fuzzySearch.js +113 -0
  50. package/dist/tools/pdf-reader.d.ts +13 -0
  51. package/dist/tools/pdf-reader.js +214 -0
  52. package/dist/tools/schemas.d.ts +29 -19
  53. package/dist/tools/schemas.js +15 -8
  54. package/dist/tools/search.js +5 -4
  55. package/dist/utils/capture.d.ts +15 -0
  56. package/dist/utils/capture.js +175 -0
  57. package/dist/utils/withTimeout.d.ts +11 -0
  58. package/dist/utils/withTimeout.js +52 -0
  59. package/dist/utils.d.ts +15 -1
  60. package/dist/utils.js +174 -41
  61. package/dist/version.d.ts +1 -1
  62. package/dist/version.js +1 -1
  63. package/package.json +2 -3
@@ -0,0 +1,174 @@
1
+ import { spawn } from 'child_process';
2
+ import fs from 'fs/promises';
3
+ import path from 'path';
4
+ import os from 'os';
5
+ /**
6
+ * Generate a temporary sandbox profile for macOS that restricts access to allowed directories
7
+ * @param allowedDirectories Array of directories that should be accessible
8
+ * @returns Path to the generated sandbox profile file
9
+ */
10
+ export async function generateSandboxProfile(allowedDirectories) {
11
+ // Create a temporary directory for the sandbox profile
12
+ const tempDir = path.join(os.tmpdir(), 'claude-server-sandbox');
13
+ // Ensure temp directory exists
14
+ try {
15
+ // Check if directory exists first
16
+ try {
17
+ await fs.access(tempDir);
18
+ console.log(`Temp directory exists: ${tempDir}`);
19
+ }
20
+ catch {
21
+ // Directory doesn't exist, create it
22
+ console.log(`Creating temp directory: ${tempDir}`);
23
+ await fs.mkdir(tempDir, { recursive: true });
24
+ console.log(`Temp directory created: ${tempDir}`);
25
+ }
26
+ }
27
+ catch (error) {
28
+ console.error('Error creating temp directory for sandbox:', error);
29
+ throw new Error(`Failed to create sandbox temp directory: ${error}`);
30
+ }
31
+ // Create the sandbox profile content - based on our tests, we need a simpler approach
32
+ // that just allows by default and then adds specific permissions for allowed directories
33
+ // Use a more restrictive approach - deny all file access, then explicitly allow only what we need
34
+ let profileContent = `(version 1)
35
+ (debug deny)
36
+ (allow default)
37
+ (deny file-read* file-write*)
38
+ `;
39
+ // Add explicit permissions for allowed directories
40
+ for (const dir of allowedDirectories) {
41
+ // Ensure path is absolute
42
+ const absPath = path.resolve(dir);
43
+ profileContent += `(allow file-read* file-write* (subpath "${absPath}"))\n`;
44
+ }
45
+ // Add system paths needed for basic command execution
46
+ profileContent += `
47
+ (allow file-read* (subpath "/usr"))
48
+ (allow file-read* (subpath "/bin"))
49
+ (allow file-read* (subpath "/sbin"))
50
+ (allow file-read* (subpath "/Library"))
51
+ (allow file-read* (subpath "/System"))
52
+ (allow file-read* (subpath "/tmp"))
53
+ (allow file-read* (subpath "/dev"))
54
+ (allow file-read* (subpath "/etc"))
55
+ (allow file-read* (subpath "${os.homedir()}/.zshrc"))
56
+ (allow file-read* (subpath "${os.homedir()}/.bash_profile"))
57
+ (allow file-read* (subpath "${os.homedir()}/.bashrc"))
58
+ (allow process-exec)
59
+ (allow process-fork)
60
+ (allow mach-lookup)
61
+ (allow network-outbound)
62
+ (allow system-socket)
63
+ (allow sysctl-read)
64
+ `;
65
+ // Add comment for debugging
66
+ profileContent += `\n; Allowed directories: ${allowedDirectories.join(', ')}\n`;
67
+ // Write the profile to a temporary file
68
+ const profilePath = path.join(tempDir, 'sandbox-profile.sb');
69
+ try {
70
+ // Write the profile with verbose logging
71
+ console.log(`Writing sandbox profile to: ${profilePath}`);
72
+ await fs.writeFile(profilePath, profileContent, 'utf-8');
73
+ // Verify the file was created
74
+ await fs.access(profilePath);
75
+ console.log(`Sandbox profile created successfully`);
76
+ // Log the profile content for debugging
77
+ console.log(`Sandbox profile content:\n${profileContent}`);
78
+ return profilePath;
79
+ }
80
+ catch (error) {
81
+ console.error(`Error creating sandbox profile at ${profilePath}:`, error);
82
+ throw new Error(`Failed to create sandbox profile: ${error}`);
83
+ }
84
+ }
85
+ /**
86
+ * Execute a command in a macOS sandbox with access restricted to allowed directories
87
+ * @param command Command to execute
88
+ * @param allowedDirectories Array of allowed directory paths
89
+ * @param options Additional execution options
90
+ * @returns Promise resolving to the execution result
91
+ */
92
+ export async function executeSandboxedCommand(command, allowedDirectories, timeoutMs = 30000) {
93
+ try {
94
+ // Generate the sandbox profile
95
+ const profilePath = await generateSandboxProfile(allowedDirectories);
96
+ // Create a wrapper script that will perform additional path checks
97
+ // This is a belt-and-suspenders approach since our tests show the sandbox
98
+ // doesn't perfectly restrict access to only allowed directories
99
+ const wrapperScriptPath = path.join(os.tmpdir(), `claude-sandbox-wrapper-${Date.now()}.sh`);
100
+ // Generate a script that does additional path validation
101
+ // This will extract file paths from the command and verify they're in allowed directories
102
+ const wrapperScript = `#!/bin/sh
103
+ # Wrapper script for sandboxed execution
104
+ # Only allows access to specific directories: ${allowedDirectories.join(', ')}
105
+
106
+ # The actual command to run
107
+ COMMAND="${command.replace(/"/g, '\\"')}"
108
+
109
+ # Run the command in sandbox
110
+ sandbox-exec -f "${profilePath}" /bin/sh -c "$COMMAND"
111
+ EXIT_CODE=$?
112
+
113
+ # Return the exit code from the sandbox
114
+ exit $EXIT_CODE
115
+ `;
116
+ // Write the wrapper script
117
+ await fs.writeFile(wrapperScriptPath, wrapperScript, { mode: 0o755 });
118
+ // Log what we're doing
119
+ console.log(`Executing sandboxed command via wrapper script`);
120
+ console.log(`Command: ${command}`);
121
+ console.log(`Allowed directories: ${allowedDirectories.join(', ')}`);
122
+ // Execute the wrapper script
123
+ const process = spawn(wrapperScriptPath, []);
124
+ let output = '';
125
+ let isBlocked = false;
126
+ // Set up timeout
127
+ const timeoutId = setTimeout(() => {
128
+ isBlocked = true;
129
+ }, timeoutMs);
130
+ // Handle output
131
+ process.stdout.on('data', (data) => {
132
+ const text = data.toString();
133
+ console.log(`Sandbox stdout: ${text}`);
134
+ output += text;
135
+ });
136
+ process.stderr.on('data', (data) => {
137
+ const text = data.toString();
138
+ console.log(`Sandbox stderr: ${text}`);
139
+ output += text;
140
+ });
141
+ // Return a promise that resolves when the process exits
142
+ return new Promise((resolve) => {
143
+ process.on('exit', (code) => {
144
+ clearTimeout(timeoutId);
145
+ console.log(`Sandbox process exited with code: ${code}`);
146
+ // Clean up the temporary files
147
+ Promise.all([
148
+ fs.unlink(profilePath).catch(err => {
149
+ console.error('Error removing temporary sandbox profile:', err);
150
+ }),
151
+ fs.unlink(wrapperScriptPath).catch(err => {
152
+ console.error('Error removing temporary wrapper script:', err);
153
+ })
154
+ ]).finally(() => {
155
+ resolve({
156
+ output,
157
+ exitCode: code,
158
+ isBlocked,
159
+ pid: process.pid || -1
160
+ });
161
+ });
162
+ });
163
+ });
164
+ }
165
+ catch (error) {
166
+ console.error('Error in sandbox execution:', error);
167
+ return {
168
+ output: `Sandbox execution error: ${error instanceof Error ? error.message : String(error)}`,
169
+ exitCode: 1,
170
+ isBlocked: false,
171
+ pid: -1
172
+ };
173
+ }
174
+ }
package/dist/server.js CHANGED
@@ -1,9 +1,13 @@
1
1
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
2
2
  import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ListPromptsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
3
3
  import { zodToJsonSchema } from "zod-to-json-schema";
4
- import { ExecuteCommandArgsSchema, ReadOutputArgsSchema, ForceTerminateArgsSchema, ListSessionsArgsSchema, KillProcessArgsSchema, BlockCommandArgsSchema, UnblockCommandArgsSchema, ReadFileArgsSchema, ReadMultipleFilesArgsSchema, WriteFileArgsSchema, CreateDirectoryArgsSchema, ListDirectoryArgsSchema, MoveFileArgsSchema, SearchFilesArgsSchema, GetFileInfoArgsSchema, EditBlockArgsSchema, SearchCodeArgsSchema, } from './tools/schemas.js';
4
+ // Shared constants for tool descriptions
5
+ const PATH_GUIDANCE = `IMPORTANT: Always use absolute paths (starting with '/' or drive letter like 'C:\\') for reliability. Relative paths may fail as they depend on the current working directory. Tilde paths (~/...) might not work in all contexts. Unless the user explicitly asks for relative paths, use absolute paths.`;
6
+ import { ExecuteCommandArgsSchema, ReadOutputArgsSchema, ForceTerminateArgsSchema, ListSessionsArgsSchema, KillProcessArgsSchema, ReadFileArgsSchema, ReadMultipleFilesArgsSchema, WriteFileArgsSchema, CreateDirectoryArgsSchema, ListDirectoryArgsSchema, MoveFileArgsSchema, SearchFilesArgsSchema, GetFileInfoArgsSchema, SearchCodeArgsSchema, GetConfigArgsSchema, SetConfigValueArgsSchema, ListProcessesArgsSchema, EditBlockArgsSchema, } from './tools/schemas.js';
7
+ import { getConfig, setConfigValue } from './tools/config.js';
5
8
  import { VERSION } from './version.js';
6
- import { capture } from "./utils.js";
9
+ import { capture } from "./utils/capture.js";
10
+ console.error("Loading server.ts");
7
11
  export const server = new Server({
8
12
  name: "desktop-commander",
9
13
  version: VERSION,
@@ -28,207 +32,208 @@ server.setRequestHandler(ListPromptsRequestSchema, async () => {
28
32
  prompts: [],
29
33
  };
30
34
  });
35
+ console.error("Setting up request handlers...");
31
36
  server.setRequestHandler(ListToolsRequestSchema, async () => {
32
- return {
33
- tools: [
34
- // Terminal tools
35
- {
36
- name: "execute_command",
37
- description: "Execute a terminal command with timeout. Command will continue running in background if it doesn't complete within timeout.",
38
- inputSchema: zodToJsonSchema(ExecuteCommandArgsSchema),
39
- },
40
- {
41
- name: "read_output",
42
- description: "Read new output from a running terminal session.",
43
- inputSchema: zodToJsonSchema(ReadOutputArgsSchema),
44
- },
45
- {
46
- name: "force_terminate",
47
- description: "Force terminate a running terminal session.",
48
- inputSchema: zodToJsonSchema(ForceTerminateArgsSchema),
49
- },
50
- {
51
- name: "list_sessions",
52
- description: "List all active terminal sessions.",
53
- inputSchema: zodToJsonSchema(ListSessionsArgsSchema),
54
- },
55
- {
56
- name: "list_processes",
57
- description: "List all running processes. Returns process information including PID, " +
58
- "command name, CPU usage, and memory usage.",
59
- inputSchema: {
60
- type: "object",
61
- properties: {},
62
- required: [],
63
- },
64
- },
65
- {
66
- name: "kill_process",
67
- description: "Terminate a running process by PID. Use with caution as this will " +
68
- "forcefully terminate the specified process.",
69
- inputSchema: zodToJsonSchema(KillProcessArgsSchema),
70
- },
71
- {
72
- name: "block_command",
73
- description: "Add a command to the blacklist. Once blocked, the command cannot be executed until unblocked.",
74
- inputSchema: zodToJsonSchema(BlockCommandArgsSchema),
75
- },
76
- {
77
- name: "unblock_command",
78
- description: "Remove a command from the blacklist. Once unblocked, the command can be executed normally.",
79
- inputSchema: zodToJsonSchema(UnblockCommandArgsSchema),
80
- },
81
- {
82
- name: "list_blocked_commands",
83
- description: "List all currently blocked commands.",
84
- inputSchema: {
85
- type: "object",
86
- properties: {},
87
- required: [],
88
- },
89
- },
90
- // Filesystem tools
91
- {
92
- name: "read_file",
93
- description: "Read the complete contents of a file from the file system or a URL. " +
94
- "When reading from the file system, only works within allowed directories. " +
95
- "Can fetch content from URLs when isUrl parameter is set to true. " +
96
- "Handles text files normally and image files are returned as viewable images. " +
97
- "Recognized image types: PNG, JPEG, GIF, WebP.",
98
- inputSchema: zodToJsonSchema(ReadFileArgsSchema),
99
- },
100
- {
101
- name: "read_multiple_files",
102
- description: "Read the contents of multiple files simultaneously. " +
103
- "Each file's content is returned with its path as a reference. " +
104
- "Handles text files normally and renders images as viewable content. " +
105
- "Recognized image types: PNG, JPEG, GIF, WebP. " +
106
- "Failed reads for individual files won't stop the entire operation. " +
107
- "Only works within allowed directories.",
108
- inputSchema: zodToJsonSchema(ReadMultipleFilesArgsSchema),
109
- },
110
- {
111
- name: "write_file",
112
- description: "Completely replace file contents. Best for large changes (>20% of file) or when edit_block fails. " +
113
- "Use with caution as it will overwrite existing files. Only works within allowed directories.",
114
- inputSchema: zodToJsonSchema(WriteFileArgsSchema),
115
- },
116
- {
117
- name: "create_directory",
118
- description: "Create a new directory or ensure a directory exists. Can create multiple " +
119
- "nested directories in one operation. Only works within allowed directories.",
120
- inputSchema: zodToJsonSchema(CreateDirectoryArgsSchema),
121
- },
122
- {
123
- name: "list_directory",
124
- description: "Get a detailed listing of all files and directories in a specified path. " +
125
- "Results distinguish between files and directories with [FILE] and [DIR] prefixes. " +
126
- "Only works within allowed directories.",
127
- inputSchema: zodToJsonSchema(ListDirectoryArgsSchema),
128
- },
129
- {
130
- name: "move_file",
131
- description: "Move or rename files and directories. Can move files between directories " +
132
- "and rename them in a single operation. Both source and destination must be " +
133
- "within allowed directories.",
134
- inputSchema: zodToJsonSchema(MoveFileArgsSchema),
135
- },
136
- {
137
- name: "search_files",
138
- description: "Finds files by name using a case-insensitive substring matching. " +
139
- "Searches through all subdirectories from the starting path. " +
140
- "Has a default timeout of 30 seconds which can be customized using the timeoutMs parameter. " +
141
- "Only searches within allowed directories.",
142
- inputSchema: zodToJsonSchema(SearchFilesArgsSchema),
143
- },
144
- {
145
- name: "search_code",
146
- description: "Search for text/code patterns within file contents using ripgrep. " +
147
- "Fast and powerful search similar to VS Code search functionality. " +
148
- "Supports regular expressions, file pattern filtering, and context lines. " +
149
- "Has a default timeout of 30 seconds which can be customized. " +
150
- "Only searches within allowed directories.",
151
- inputSchema: zodToJsonSchema(SearchCodeArgsSchema),
152
- },
153
- {
154
- name: "get_file_info",
155
- description: "Retrieve detailed metadata about a file or directory including size, " +
156
- "creation time, last modified time, permissions, and type. " +
157
- "Only works within allowed directories.",
158
- inputSchema: zodToJsonSchema(GetFileInfoArgsSchema),
159
- },
160
- {
161
- name: "list_allowed_directories",
162
- description: "Returns the list of directories that this server is allowed to access.",
163
- inputSchema: {
164
- type: "object",
165
- properties: {},
166
- required: [],
167
- },
168
- },
169
- {
170
- name: "edit_block",
171
- description: "Apply surgical text replacements to files. Best for small changes (<20% of file size). " +
172
- "Call repeatedly to change multiple blocks. Will verify changes after application. " +
173
- "Format:\nfilepath\n<<<<<<< SEARCH\ncontent to find\n=======\nnew content\n>>>>>>> REPLACE",
174
- inputSchema: zodToJsonSchema(EditBlockArgsSchema),
175
- },
176
- ],
177
- };
37
+ try {
38
+ console.error("Generating tools list...");
39
+ return {
40
+ tools: [
41
+ // Configuration tools
42
+ {
43
+ name: "get_config",
44
+ description: "Get the complete server configuration as JSON. Config includes fields for: blockedCommands (array of blocked shell commands), defaultShell (shell to use for commands), allowedDirectories (paths the server can access).",
45
+ inputSchema: zodToJsonSchema(GetConfigArgsSchema),
46
+ },
47
+ {
48
+ name: "set_config_value",
49
+ description: "Set a specific configuration value by key. WARNING: Should be used in a separate chat from file operations and command execution to prevent security issues. Config keys include: blockedCommands (array), defaultShell (string), allowedDirectories (array of paths). IMPORTANT: Setting allowedDirectories to an empty array ([]) allows full access to the entire file system, regardless of the operating system.",
50
+ inputSchema: zodToJsonSchema(SetConfigValueArgsSchema),
51
+ },
52
+ // Filesystem tools
53
+ {
54
+ name: "read_file",
55
+ description: `Read the complete contents of a file from the file system or a URL. Prefer this over 'execute_command' with cat/type for viewing files. When reading from the file system, only works within allowed directories. Can fetch content from URLs when isUrl parameter is set to true. Handles text files normally and image files are returned as viewable images. Recognized image types: PNG, JPEG, GIF, WebP. ${PATH_GUIDANCE}`,
56
+ inputSchema: zodToJsonSchema(ReadFileArgsSchema),
57
+ },
58
+ {
59
+ name: "read_multiple_files",
60
+ description: `Read the contents of multiple files simultaneously. Each file's content is returned with its path as a reference. Handles text files normally and renders images as viewable content. Recognized image types: PNG, JPEG, GIF, WebP. Failed reads for individual files won't stop the entire operation. Only works within allowed directories. ${PATH_GUIDANCE}`,
61
+ inputSchema: zodToJsonSchema(ReadMultipleFilesArgsSchema),
62
+ },
63
+ {
64
+ name: "write_file",
65
+ description: `Completely replace file contents. Best for large changes (>20% of file) or when edit_block fails. Use with caution as it will overwrite existing files. Only works within allowed directories. ${PATH_GUIDANCE}`,
66
+ inputSchema: zodToJsonSchema(WriteFileArgsSchema),
67
+ },
68
+ {
69
+ name: "create_directory",
70
+ description: `Create a new directory or ensure a directory exists. Can create multiple nested directories in one operation. Only works within allowed directories. ${PATH_GUIDANCE}`,
71
+ inputSchema: zodToJsonSchema(CreateDirectoryArgsSchema),
72
+ },
73
+ {
74
+ name: "list_directory",
75
+ description: `Get a detailed listing of all files and directories in a specified path. Use this instead of 'execute_command' with ls/dir commands. Results distinguish between files and directories with [FILE] and [DIR] prefixes. Only works within allowed directories. ${PATH_GUIDANCE}`,
76
+ inputSchema: zodToJsonSchema(ListDirectoryArgsSchema),
77
+ },
78
+ {
79
+ name: "move_file",
80
+ description: `Move or rename files and directories.
81
+ Can move files between directories and rename them in a single operation.
82
+ Both source and destination must be within allowed directories. ${PATH_GUIDANCE}`,
83
+ inputSchema: zodToJsonSchema(MoveFileArgsSchema),
84
+ },
85
+ {
86
+ name: "search_files",
87
+ description: `Finds files by name using a case-insensitive substring matching.
88
+ Use this instead of 'execute_command' with find/dir/ls for locating files.
89
+ Searches through all subdirectories from the starting path.
90
+ Has a default timeout of 30 seconds which can be customized using the timeoutMs parameter.
91
+ Only searches within allowed directories. ${PATH_GUIDANCE}`,
92
+ inputSchema: zodToJsonSchema(SearchFilesArgsSchema),
93
+ },
94
+ {
95
+ name: "search_code",
96
+ description: `Search for text/code patterns within file contents using ripgrep.
97
+ Use this instead of 'execute_command' with grep/find for searching code content.
98
+ Fast and powerful search similar to VS Code search functionality.
99
+ Supports regular expressions, file pattern filtering, and context lines.
100
+ Has a default timeout of 30 seconds which can be customized.
101
+ Only searches within allowed directories.
102
+ ${PATH_GUIDANCE}`,
103
+ inputSchema: zodToJsonSchema(SearchCodeArgsSchema),
104
+ },
105
+ {
106
+ name: "get_file_info",
107
+ description: `Retrieve detailed metadata about a file or directory including size, creation time, last modified time,
108
+ permissions, and type.
109
+ Only works within allowed directories. ${PATH_GUIDANCE}`,
110
+ inputSchema: zodToJsonSchema(GetFileInfoArgsSchema),
111
+ },
112
+ // Note: list_allowed_directories removed - use get_config to check allowedDirectories
113
+ // Text editing tools
114
+ {
115
+ name: "edit_block",
116
+ description: `Apply surgical text replacements to files.
117
+ BEST PRACTICE: Make multiple small, focused edits rather than one large edit.
118
+ Each edit_block call should change only what needs to be changed - include just enough context to uniquely identify the text being modified.
119
+ Takes file_path, old_string (text to replace), new_string (replacement text), and optional expected_replacements parameter.
120
+ By default, replaces only ONE occurrence of the search text.
121
+ To replace multiple occurrences, provide the expected_replacements parameter with the exact number of matches expected.
122
+ UNIQUENESS REQUIREMENT: When expected_replacements=1 (default), include the minimal amount of context necessary (typically 1-3 lines) before and after the change point, with exact whitespace and indentation.
123
+ When editing multiple sections, make separate edit_block calls for each distinct change rather than one large replacement.
124
+ When a close but non-exact match is found, a character-level diff is shown in the format: common_prefix{-removed-}{+added+}common_suffix to help you identify what's different.
125
+ ${PATH_GUIDANCE}`,
126
+ inputSchema: zodToJsonSchema(EditBlockArgsSchema),
127
+ },
128
+ // Terminal tools
129
+ {
130
+ name: "execute_command",
131
+ description: `Execute a terminal command with timeout.
132
+ Command will continue running in background if it doesn't complete within timeout.
133
+ NOTE: For file operations, prefer specialized tools like read_file, search_code, list_directory instead of cat, grep, or ls commands.
134
+ ${PATH_GUIDANCE}`,
135
+ inputSchema: zodToJsonSchema(ExecuteCommandArgsSchema),
136
+ },
137
+ {
138
+ name: "read_output",
139
+ description: "Read new output from a running terminal session.",
140
+ inputSchema: zodToJsonSchema(ReadOutputArgsSchema),
141
+ },
142
+ {
143
+ name: "force_terminate",
144
+ description: "Force terminate a running terminal session.",
145
+ inputSchema: zodToJsonSchema(ForceTerminateArgsSchema),
146
+ },
147
+ {
148
+ name: "list_sessions",
149
+ description: "List all active terminal sessions.",
150
+ inputSchema: zodToJsonSchema(ListSessionsArgsSchema),
151
+ },
152
+ {
153
+ name: "list_processes",
154
+ description: "List all running processes. Returns process information including PID, command name, CPU usage, and memory usage.",
155
+ inputSchema: zodToJsonSchema(ListProcessesArgsSchema),
156
+ },
157
+ {
158
+ name: "kill_process",
159
+ description: "Terminate a running process by PID. Use with caution as this will forcefully terminate the specified process.",
160
+ inputSchema: zodToJsonSchema(KillProcessArgsSchema),
161
+ },
162
+ ],
163
+ };
164
+ }
165
+ catch (error) {
166
+ console.error("Error in list_tools request handler:", error);
167
+ throw error;
168
+ }
178
169
  });
179
170
  import * as handlers from './handlers/index.js';
180
171
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
181
172
  try {
182
173
  const { name, arguments: args } = request.params;
183
- capture('server_call_tool');
184
- // Add a single dynamic capture for the specific tool
185
- capture('server_' + name);
174
+ capture('server_call_tool', {
175
+ name
176
+ });
186
177
  // Using a more structured approach with dedicated handlers
187
178
  switch (name) {
179
+ // Config tools
180
+ case "get_config":
181
+ try {
182
+ return await getConfig();
183
+ }
184
+ catch (error) {
185
+ capture('server_request_error', { message: `Error in get_config handler: ${error}` });
186
+ return {
187
+ content: [{ type: "text", text: `Error: Failed to get configuration` }],
188
+ isError: true,
189
+ };
190
+ }
191
+ case "set_config_value":
192
+ try {
193
+ return await setConfigValue(args);
194
+ }
195
+ catch (error) {
196
+ capture('server_request_error', { message: `Error in set_config_value handler: ${error}` });
197
+ return {
198
+ content: [{ type: "text", text: `Error: Failed to set configuration value` }],
199
+ isError: true,
200
+ };
201
+ }
188
202
  // Terminal tools
189
203
  case "execute_command":
190
- return handlers.handleExecuteCommand(args);
204
+ return await handlers.handleExecuteCommand(args);
191
205
  case "read_output":
192
- return handlers.handleReadOutput(args);
206
+ return await handlers.handleReadOutput(args);
193
207
  case "force_terminate":
194
- return handlers.handleForceTerminate(args);
208
+ return await handlers.handleForceTerminate(args);
195
209
  case "list_sessions":
196
- return handlers.handleListSessions();
210
+ return await handlers.handleListSessions();
197
211
  // Process tools
198
212
  case "list_processes":
199
- return handlers.handleListProcesses();
213
+ return await handlers.handleListProcesses();
200
214
  case "kill_process":
201
- return handlers.handleKillProcess(args);
202
- // Command management tools
203
- case "block_command":
204
- return handlers.handleBlockCommand(args);
205
- case "unblock_command":
206
- return handlers.handleUnblockCommand(args);
207
- case "list_blocked_commands":
208
- return handlers.handleListBlockedCommands();
215
+ return await handlers.handleKillProcess(args);
209
216
  // Filesystem tools
210
217
  case "read_file":
211
- return handlers.handleReadFile(args);
218
+ return await handlers.handleReadFile(args);
212
219
  case "read_multiple_files":
213
- return handlers.handleReadMultipleFiles(args);
220
+ return await handlers.handleReadMultipleFiles(args);
214
221
  case "write_file":
215
- return handlers.handleWriteFile(args);
222
+ return await handlers.handleWriteFile(args);
216
223
  case "create_directory":
217
- return handlers.handleCreateDirectory(args);
224
+ return await handlers.handleCreateDirectory(args);
218
225
  case "list_directory":
219
- return handlers.handleListDirectory(args);
226
+ return await handlers.handleListDirectory(args);
220
227
  case "move_file":
221
- return handlers.handleMoveFile(args);
228
+ return await handlers.handleMoveFile(args);
222
229
  case "search_files":
223
- return handlers.handleSearchFiles(args);
230
+ return await handlers.handleSearchFiles(args);
224
231
  case "search_code":
225
- return handlers.handleSearchCode(args);
232
+ return await handlers.handleSearchCode(args);
226
233
  case "get_file_info":
227
- return handlers.handleGetFileInfo(args);
228
- case "list_allowed_directories":
229
- return handlers.handleListAllowedDirectories();
234
+ return await handlers.handleGetFileInfo(args);
230
235
  case "edit_block":
231
- return handlers.handleEditBlock(args);
236
+ return await handlers.handleEditBlock(args);
232
237
  default:
233
238
  capture('server_unknown_tool', { name });
234
239
  return {