@wonderwhy-er/desktop-commander 0.2.23 → 0.2.24
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 +14 -55
- package/dist/custom-stdio.d.ts +1 -0
- package/dist/custom-stdio.js +19 -0
- package/dist/handlers/filesystem-handlers.d.ts +4 -0
- package/dist/handlers/filesystem-handlers.js +120 -14
- package/dist/handlers/node-handlers.d.ts +6 -0
- package/dist/handlers/node-handlers.js +73 -0
- package/dist/index.js +5 -3
- package/dist/search-manager.d.ts +25 -0
- package/dist/search-manager.js +212 -0
- package/dist/server.js +160 -73
- package/dist/terminal-manager.d.ts +56 -2
- package/dist/terminal-manager.js +169 -13
- package/dist/tools/edit.d.ts +28 -4
- package/dist/tools/edit.js +87 -4
- package/dist/tools/filesystem.d.ts +23 -12
- package/dist/tools/filesystem.js +201 -416
- package/dist/tools/improved-process-tools.d.ts +2 -2
- package/dist/tools/improved-process-tools.js +244 -214
- package/dist/tools/mime-types.d.ts +1 -0
- package/dist/tools/mime-types.js +7 -0
- package/dist/tools/pdf/extract-images.d.ts +34 -0
- package/dist/tools/pdf/extract-images.js +132 -0
- package/dist/tools/pdf/index.d.ts +6 -0
- package/dist/tools/pdf/index.js +3 -0
- package/dist/tools/pdf/lib/pdf2md.d.ts +36 -0
- package/dist/tools/pdf/lib/pdf2md.js +76 -0
- package/dist/tools/pdf/manipulations.d.ts +13 -0
- package/dist/tools/pdf/manipulations.js +96 -0
- package/dist/tools/pdf/markdown.d.ts +7 -0
- package/dist/tools/pdf/markdown.js +37 -0
- package/dist/tools/pdf/utils.d.ts +12 -0
- package/dist/tools/pdf/utils.js +34 -0
- package/dist/tools/schemas.d.ts +167 -12
- package/dist/tools/schemas.js +54 -5
- package/dist/types.d.ts +2 -1
- package/dist/utils/feature-flags.js +7 -4
- package/dist/utils/files/base.d.ts +167 -0
- package/dist/utils/files/base.js +5 -0
- package/dist/utils/files/binary.d.ts +21 -0
- package/dist/utils/files/binary.js +65 -0
- package/dist/utils/files/excel.d.ts +24 -0
- package/dist/utils/files/excel.js +416 -0
- package/dist/utils/files/factory.d.ts +40 -0
- package/dist/utils/files/factory.js +101 -0
- package/dist/utils/files/image.d.ts +21 -0
- package/dist/utils/files/image.js +78 -0
- package/dist/utils/files/index.d.ts +10 -0
- package/dist/utils/files/index.js +13 -0
- package/dist/utils/files/pdf.d.ts +32 -0
- package/dist/utils/files/pdf.js +142 -0
- package/dist/utils/files/text.d.ts +63 -0
- package/dist/utils/files/text.js +357 -0
- package/dist/utils/ripgrep-resolver.js +3 -2
- package/dist/utils/system-info.d.ts +5 -0
- package/dist/utils/system-info.js +71 -3
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +14 -3
package/README.md
CHANGED
|
@@ -39,7 +39,9 @@ Execute long-running terminal commands on your computer and manage processes thr
|
|
|
39
39
|
|
|
40
40
|
- **Enhanced terminal commands with interactive process control**
|
|
41
41
|
- **Execute code in memory (Python, Node.js, R) without saving files**
|
|
42
|
-
- **Instant data analysis - just ask to analyze CSV/JSON files**
|
|
42
|
+
- **Instant data analysis - just ask to analyze CSV/JSON/Excel files**
|
|
43
|
+
- **Native Excel file support** - Read, write, edit, and search Excel files (.xlsx, .xls, .xlsm) without external tools
|
|
44
|
+
- **PDF support** - Read PDFs with text extraction, create new PDFs from markdown, modify existing PDFs
|
|
43
45
|
- **Interact with running processes (SSH, databases, development servers)**
|
|
44
46
|
- Execute terminal commands with output streaming
|
|
45
47
|
- Command timeout and background execution support
|
|
@@ -50,10 +52,10 @@ Execute long-running terminal commands on your computer and manage processes thr
|
|
|
50
52
|
- Update multiple settings at once
|
|
51
53
|
- Dynamic configuration changes without server restart
|
|
52
54
|
- Full filesystem operations:
|
|
53
|
-
- Read/write files
|
|
55
|
+
- Read/write files (text, Excel, PDF)
|
|
54
56
|
- Create/list directories
|
|
55
57
|
- Move files/directories
|
|
56
|
-
- Search files
|
|
58
|
+
- Search files and content (including Excel content)
|
|
57
59
|
- Get file metadata
|
|
58
60
|
- **Negative offset file reading**: Read from end of files using negative offset values (like Unix tail)
|
|
59
61
|
- Code editing capabilities:
|
|
@@ -420,18 +422,19 @@ The server provides a comprehensive set of tools organized into several categori
|
|
|
420
422
|
| | `list_sessions` | List all active terminal sessions |
|
|
421
423
|
| | `list_processes` | List all running processes with detailed information |
|
|
422
424
|
| | `kill_process` | Terminate a running process by PID |
|
|
423
|
-
| **Filesystem** | `read_file` | Read contents from local filesystem
|
|
425
|
+
| **Filesystem** | `read_file` | Read contents from local filesystem, URLs, Excel files (.xlsx, .xls, .xlsm), and PDFs with line/page-based pagination |
|
|
424
426
|
| | `read_multiple_files` | Read multiple files simultaneously |
|
|
425
|
-
| | `write_file` | Write file contents with options for rewrite or append mode (
|
|
427
|
+
| | `write_file` | Write file contents with options for rewrite or append mode. Supports Excel files (JSON 2D array format). For PDFs, use `write_pdf` |
|
|
428
|
+
| | `write_pdf` | Create new PDF files from markdown or modify existing PDFs (insert/delete pages). Supports HTML/CSS styling and SVG graphics |
|
|
426
429
|
| | `create_directory` | Create a new directory or ensure it exists |
|
|
427
430
|
| | `list_directory` | Get detailed recursive listing of files and directories (supports depth parameter, default depth=2) |
|
|
428
431
|
| | `move_file` | Move or rename files and directories |
|
|
429
|
-
| | `start_search` | Start streaming search for files by name or content patterns (
|
|
432
|
+
| | `start_search` | Start streaming search for files by name or content patterns (searches text files and Excel content) |
|
|
430
433
|
| | `get_more_search_results` | Get paginated results from active search with offset support |
|
|
431
434
|
| | `stop_search` | Stop an active search gracefully |
|
|
432
435
|
| | `list_searches` | List all active search sessions |
|
|
433
|
-
| | `get_file_info` | Retrieve detailed metadata about a file or directory |
|
|
434
|
-
| **Text Editing** | `edit_block` | Apply targeted text replacements
|
|
436
|
+
| | `get_file_info` | Retrieve detailed metadata about a file or directory (includes sheet info for Excel files) |
|
|
437
|
+
| **Text Editing** | `edit_block` | Apply targeted text replacements for text files, or range-based cell updates for Excel files |
|
|
435
438
|
| **Analytics** | `get_usage_stats` | Get usage statistics for your own insight |
|
|
436
439
|
| | `get_recent_tool_calls` | Get recent tool call history with arguments and outputs for debugging and context recovery |
|
|
437
440
|
| | `give_feedback_to_desktop_commander` | Open feedback form in browser to provide feedback to Desktop Commander Team |
|
|
@@ -760,33 +763,6 @@ This project extends the MCP Filesystem Server to enable:
|
|
|
760
763
|
|
|
761
764
|
Created as part of exploring Claude MCPs: https://youtube.com/live/TlbjFDbl5Us
|
|
762
765
|
|
|
763
|
-
## DONE
|
|
764
|
-
- **20-05-2025 v0.1.40 Release** - Added audit logging for all tool calls, improved line-based file operations, enhanced edit_block with better prompting for smaller edits, added explicit telemetry opt-out prompting
|
|
765
|
-
- **05-05-2025 Fuzzy Search Logging** - Added comprehensive logging system for fuzzy search operations with detailed analysis tools, character-level diffs, and performance metrics to help debug edit_block failures
|
|
766
|
-
- **29-04-2025 Telemetry Opt Out through configuration** - There is now setting to disable telemetry in config, ask in chat
|
|
767
|
-
- **23-04-2025 Enhanced edit functionality** - Improved format, added fuzzy search and multi-occurrence replacements, should fail less and use edit block more often
|
|
768
|
-
- **16-04-2025 Better configurations** - Improved settings for allowed paths, commands and shell environments
|
|
769
|
-
- **14-04-2025 Windows environment fixes** - Resolved issues specific to Windows platforms
|
|
770
|
-
- **14-04-2025 Linux improvements** - Enhanced compatibility with various Linux distributions
|
|
771
|
-
- **12-04-2025 Better allowed directories and blocked commands** - Improved security and path validation for file read/write and terminal command restrictions.
|
|
772
|
-
Terminal still can access files ignoring allowed directories.
|
|
773
|
-
- **11-04-2025 Shell configuration** - Added ability to configure preferred shell for command execution
|
|
774
|
-
- **07-04-2025 Added URL support** - `read_file` command can now fetch content from URLs
|
|
775
|
-
- **28-03-2025 Fixed "Watching /" JSON error** - Implemented custom stdio transport to handle non-JSON messages and prevent server crashes
|
|
776
|
-
- **25-03-2025 Better code search** ([merged](https://github.com/wonderwhy-er/ClaudeServerCommander/pull/17)) - Enhanced code exploration with context-aware results
|
|
777
|
-
|
|
778
|
-
## Roadmap
|
|
779
|
-
|
|
780
|
-
The following features are currently being explored:
|
|
781
|
-
|
|
782
|
-
- **Support for WSL** - Windows Subsystem for Linux integration
|
|
783
|
-
- **Support for SSH** - Remote server command execution
|
|
784
|
-
- **Better file support for formats like CSV/PDF**
|
|
785
|
-
- **Terminal sandboxing for Mac/Linux/Windows for better security**
|
|
786
|
-
- **File reading modes** - For example, allow reading HTML as plain text or markdown
|
|
787
|
-
- **Interactive shell support** - ssh, node/python repl
|
|
788
|
-
- **Improve large file reading and writing**
|
|
789
|
-
|
|
790
766
|
## Support Desktop Commander
|
|
791
767
|
|
|
792
768
|
<div align="center">
|
|
@@ -931,28 +907,11 @@ Please create a [GitHub Issue](https://github.com/wonderwhy-er/DesktopCommanderM
|
|
|
931
907
|
|
|
932
908
|
## Data Collection & Privacy
|
|
933
909
|
|
|
934
|
-
Desktop Commander collects limited
|
|
935
|
-
|
|
936
|
-
### Usage Analytics (Local Only)
|
|
937
|
-
- **Local usage statistics** are always collected and stored locally on your machine for functionality and the `get_usage_stats` tool
|
|
938
|
-
- Use the `get_usage_stats` tool to view your personal usage patterns, success rates, and performance metrics
|
|
939
|
-
- **This data is NOT sent anywhere** - it remains on your computer for your personal insights
|
|
940
|
-
|
|
941
|
-
### Feedback System
|
|
942
|
-
- Use the `give_feedback_to_desktop_commander` tool to provide feedback about Desktop Commander
|
|
943
|
-
- Opens a browser-based feedback form to send suggestions and feedback to the development team
|
|
944
|
-
- Only basic usage statistics (tool call count, days using, platform) are pre-filled to provide context but you can remove them
|
|
945
|
-
|
|
946
|
-
### External Telemetry Opt-Out
|
|
947
|
-
External telemetry (sent to analytics services) is enabled by default but can be disabled:
|
|
948
|
-
|
|
949
|
-
1. Open the chat and simply ask:
|
|
950
|
-
**"Disable telemetry"**
|
|
951
|
-
2. The chatbot will update your settings automatically.
|
|
910
|
+
Desktop Commander collects limited, pseudonymous telemetry to improve the tool. We do not collect file contents, file paths, or command arguments.
|
|
952
911
|
|
|
953
|
-
**
|
|
912
|
+
**Opt-out:** Ask Claude to "disable Desktop Commander telemetry" or set `"telemetryEnabled": false` in your config.
|
|
954
913
|
|
|
955
|
-
For complete details
|
|
914
|
+
For complete details, see our [Privacy Policy](PRIVACY.md).
|
|
956
915
|
|
|
957
916
|
## Verifications
|
|
958
917
|
[](https://mseep.ai/app/25ff7a06-58bc-40b8-bd79-ebb715140f1a)
|
package/dist/custom-stdio.d.ts
CHANGED
|
@@ -33,6 +33,7 @@ export declare class FilteredStdioServerTransport extends StdioServerTransport {
|
|
|
33
33
|
private sendLogNotification;
|
|
34
34
|
/**
|
|
35
35
|
* Public method to send log notifications from anywhere in the application
|
|
36
|
+
* Now properly buffers messages before MCP initialization to avoid breaking stdio protocol
|
|
36
37
|
*/
|
|
37
38
|
sendLog(level: "emergency" | "alert" | "critical" | "error" | "warning" | "notice" | "info" | "debug", message: string, data?: any): void;
|
|
38
39
|
/**
|
package/dist/custom-stdio.js
CHANGED
|
@@ -233,12 +233,23 @@ export class FilteredStdioServerTransport extends StdioServerTransport {
|
|
|
233
233
|
}
|
|
234
234
|
/**
|
|
235
235
|
* Public method to send log notifications from anywhere in the application
|
|
236
|
+
* Now properly buffers messages before MCP initialization to avoid breaking stdio protocol
|
|
236
237
|
*/
|
|
237
238
|
sendLog(level, message, data) {
|
|
238
239
|
// Skip if notifications are disabled (e.g., for Cline)
|
|
239
240
|
if (this.disableNotifications) {
|
|
240
241
|
return;
|
|
241
242
|
}
|
|
243
|
+
// Buffer messages before initialization to avoid breaking MCP protocol
|
|
244
|
+
// MCP requires client to send first message - server cannot write to stdout before that
|
|
245
|
+
if (!this.isInitialized) {
|
|
246
|
+
this.messageBuffer.push({
|
|
247
|
+
level,
|
|
248
|
+
args: [data ? { message, ...data } : message],
|
|
249
|
+
timestamp: Date.now()
|
|
250
|
+
});
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
242
253
|
try {
|
|
243
254
|
const notification = {
|
|
244
255
|
jsonrpc: "2.0",
|
|
@@ -269,6 +280,10 @@ export class FilteredStdioServerTransport extends StdioServerTransport {
|
|
|
269
280
|
* Send a progress notification (useful for long-running operations)
|
|
270
281
|
*/
|
|
271
282
|
sendProgress(token, value, total) {
|
|
283
|
+
// Don't send progress before initialization - would break MCP protocol
|
|
284
|
+
if (!this.isInitialized) {
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
272
287
|
try {
|
|
273
288
|
const notification = {
|
|
274
289
|
jsonrpc: "2.0",
|
|
@@ -299,6 +314,10 @@ export class FilteredStdioServerTransport extends StdioServerTransport {
|
|
|
299
314
|
* Send a custom notification with any method name
|
|
300
315
|
*/
|
|
301
316
|
sendCustomNotification(method, params) {
|
|
317
|
+
// Don't send custom notifications before initialization - would break MCP protocol
|
|
318
|
+
if (!this.isInitialized) {
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
302
321
|
try {
|
|
303
322
|
const notification = {
|
|
304
323
|
jsonrpc: "2.0",
|
|
@@ -27,3 +27,7 @@ export declare function handleMoveFile(args: unknown): Promise<ServerResult>;
|
|
|
27
27
|
* Handle get_file_info command
|
|
28
28
|
*/
|
|
29
29
|
export declare function handleGetFileInfo(args: unknown): Promise<ServerResult>;
|
|
30
|
+
/**
|
|
31
|
+
* Handle write_pdf command
|
|
32
|
+
*/
|
|
33
|
+
export declare function handleWritePdf(args: unknown): Promise<ServerResult>;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { readFile, readMultipleFiles, writeFile, createDirectory, listDirectory, moveFile, getFileInfo } from '../tools/filesystem.js';
|
|
1
|
+
import { readFile, readMultipleFiles, writeFile, createDirectory, listDirectory, moveFile, getFileInfo, writePdf } from '../tools/filesystem.js';
|
|
2
2
|
import { withTimeout } from '../utils/withTimeout.js';
|
|
3
3
|
import { createErrorResponse } from '../error-handlers.js';
|
|
4
4
|
import { configManager } from '../config-manager.js';
|
|
5
|
-
import { ReadFileArgsSchema, ReadMultipleFilesArgsSchema, WriteFileArgsSchema, CreateDirectoryArgsSchema, ListDirectoryArgsSchema, MoveFileArgsSchema, GetFileInfoArgsSchema } from '../tools/schemas.js';
|
|
5
|
+
import { ReadFileArgsSchema, ReadMultipleFilesArgsSchema, WriteFileArgsSchema, CreateDirectoryArgsSchema, ListDirectoryArgsSchema, MoveFileArgsSchema, GetFileInfoArgsSchema, WritePdfArgsSchema } from '../tools/schemas.js';
|
|
6
6
|
/**
|
|
7
7
|
* Helper function to check if path contains an error
|
|
8
8
|
*/
|
|
@@ -32,12 +32,52 @@ export async function handleReadFile(args) {
|
|
|
32
32
|
return createErrorResponse('Configuration not available');
|
|
33
33
|
}
|
|
34
34
|
const defaultLimit = config.fileReadLineLimit ?? 1000;
|
|
35
|
-
//
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
35
|
+
// Convert sheet parameter: numeric strings become numbers for Excel index access
|
|
36
|
+
let sheetParam = parsed.sheet;
|
|
37
|
+
if (parsed.sheet !== undefined && /^\d+$/.test(parsed.sheet)) {
|
|
38
|
+
sheetParam = parseInt(parsed.sheet, 10);
|
|
39
|
+
}
|
|
40
|
+
const options = {
|
|
41
|
+
isUrl: parsed.isUrl,
|
|
42
|
+
offset: parsed.offset ?? 0,
|
|
43
|
+
length: parsed.length ?? defaultLimit,
|
|
44
|
+
sheet: sheetParam,
|
|
45
|
+
range: parsed.range
|
|
46
|
+
};
|
|
47
|
+
const fileResult = await readFile(parsed.path, options);
|
|
48
|
+
// Handle PDF files
|
|
49
|
+
if (fileResult.metadata?.isPdf) {
|
|
50
|
+
const meta = fileResult.metadata;
|
|
51
|
+
const author = meta?.author ? `, Author: ${meta?.author}` : "";
|
|
52
|
+
const title = meta?.title ? `, Title: ${meta?.title}` : "";
|
|
53
|
+
const pdfContent = fileResult.metadata?.pages?.flatMap((p) => [
|
|
54
|
+
...(p.images?.map((image) => ({
|
|
55
|
+
type: "image",
|
|
56
|
+
data: image.data,
|
|
57
|
+
mimeType: image.mimeType
|
|
58
|
+
})) ?? []),
|
|
59
|
+
{
|
|
60
|
+
type: "text",
|
|
61
|
+
text: `<!-- Page: ${p.pageNumber} -->\n${p.text}`,
|
|
62
|
+
},
|
|
63
|
+
]) ?? [];
|
|
64
|
+
return {
|
|
65
|
+
content: [
|
|
66
|
+
{
|
|
67
|
+
type: "text",
|
|
68
|
+
text: `PDF file: ${parsed.path}${author}${title} (${meta?.totalPages} pages) \n`
|
|
69
|
+
},
|
|
70
|
+
...pdfContent
|
|
71
|
+
]
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
// Handle image files
|
|
75
|
+
if (fileResult.metadata?.isImage) {
|
|
40
76
|
// For image files, return as an image content type
|
|
77
|
+
// Content should already be base64-encoded string from handler
|
|
78
|
+
const imageData = typeof fileResult.content === 'string'
|
|
79
|
+
? fileResult.content
|
|
80
|
+
: fileResult.content.toString('base64');
|
|
41
81
|
return {
|
|
42
82
|
content: [
|
|
43
83
|
{
|
|
@@ -46,7 +86,7 @@ export async function handleReadFile(args) {
|
|
|
46
86
|
},
|
|
47
87
|
{
|
|
48
88
|
type: "image",
|
|
49
|
-
data:
|
|
89
|
+
data: imageData,
|
|
50
90
|
mimeType: fileResult.mimeType
|
|
51
91
|
}
|
|
52
92
|
],
|
|
@@ -54,8 +94,11 @@ export async function handleReadFile(args) {
|
|
|
54
94
|
}
|
|
55
95
|
else {
|
|
56
96
|
// For all other files, return as text
|
|
97
|
+
const textContent = typeof fileResult.content === 'string'
|
|
98
|
+
? fileResult.content
|
|
99
|
+
: fileResult.content.toString('utf8');
|
|
57
100
|
return {
|
|
58
|
-
content: [{ type: "text", text:
|
|
101
|
+
content: [{ type: "text", text: textContent }],
|
|
59
102
|
};
|
|
60
103
|
}
|
|
61
104
|
};
|
|
@@ -78,6 +121,9 @@ export async function handleReadMultipleFiles(args) {
|
|
|
78
121
|
if (result.error) {
|
|
79
122
|
return `${result.path}: Error - ${result.error}`;
|
|
80
123
|
}
|
|
124
|
+
else if (result.isPdf) {
|
|
125
|
+
return `${result.path}: PDF file with ${result.payload?.pages?.length} pages`;
|
|
126
|
+
}
|
|
81
127
|
else if (result.mimeType) {
|
|
82
128
|
return `${result.path}: ${result.mimeType} ${result.isImage ? '(image)' : '(text)'}`;
|
|
83
129
|
}
|
|
@@ -92,7 +138,22 @@ export async function handleReadMultipleFiles(args) {
|
|
|
92
138
|
// Add each file content
|
|
93
139
|
for (const result of fileResults) {
|
|
94
140
|
if (!result.error && result.content !== undefined) {
|
|
95
|
-
if (result.
|
|
141
|
+
if (result.isPdf) {
|
|
142
|
+
result.payload?.pages.forEach((page, i) => {
|
|
143
|
+
page.images.forEach((image, i) => {
|
|
144
|
+
contentItems.push({
|
|
145
|
+
type: "image",
|
|
146
|
+
data: image.data,
|
|
147
|
+
mimeType: image.mimeType
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
contentItems.push({
|
|
151
|
+
type: "text",
|
|
152
|
+
text: page.text,
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
else if (result.isImage && result.mimeType) {
|
|
96
157
|
// For image files, add an image content item
|
|
97
158
|
contentItems.push({
|
|
98
159
|
type: "image",
|
|
@@ -196,6 +257,33 @@ export async function handleMoveFile(args) {
|
|
|
196
257
|
return createErrorResponse(errorMessage);
|
|
197
258
|
}
|
|
198
259
|
}
|
|
260
|
+
/**
|
|
261
|
+
* Format a value for display, handling objects and arrays
|
|
262
|
+
*/
|
|
263
|
+
function formatValue(value, indent = '') {
|
|
264
|
+
if (value === null || value === undefined) {
|
|
265
|
+
return String(value);
|
|
266
|
+
}
|
|
267
|
+
if (Array.isArray(value)) {
|
|
268
|
+
if (value.length === 0)
|
|
269
|
+
return '[]';
|
|
270
|
+
// For arrays of objects (like sheets), format each item
|
|
271
|
+
const items = value.map((item, i) => {
|
|
272
|
+
if (typeof item === 'object' && item !== null) {
|
|
273
|
+
const props = Object.entries(item)
|
|
274
|
+
.map(([k, v]) => `${k}: ${v}`)
|
|
275
|
+
.join(', ');
|
|
276
|
+
return `${indent} [${i}] { ${props} }`;
|
|
277
|
+
}
|
|
278
|
+
return `${indent} [${i}] ${item}`;
|
|
279
|
+
});
|
|
280
|
+
return `\n${items.join('\n')}`;
|
|
281
|
+
}
|
|
282
|
+
if (typeof value === 'object') {
|
|
283
|
+
return JSON.stringify(value);
|
|
284
|
+
}
|
|
285
|
+
return String(value);
|
|
286
|
+
}
|
|
199
287
|
/**
|
|
200
288
|
* Handle get_file_info command
|
|
201
289
|
*/
|
|
@@ -203,12 +291,14 @@ export async function handleGetFileInfo(args) {
|
|
|
203
291
|
try {
|
|
204
292
|
const parsed = GetFileInfoArgsSchema.parse(args);
|
|
205
293
|
const info = await getFileInfo(parsed.path);
|
|
294
|
+
// Generic formatting for any file type
|
|
295
|
+
const formattedText = Object.entries(info)
|
|
296
|
+
.map(([key, value]) => `${key}: ${formatValue(value)}`)
|
|
297
|
+
.join('\n');
|
|
206
298
|
return {
|
|
207
299
|
content: [{
|
|
208
300
|
type: "text",
|
|
209
|
-
text:
|
|
210
|
-
.map(([key, value]) => `${key}: ${value}`)
|
|
211
|
-
.join('\n')
|
|
301
|
+
text: formattedText
|
|
212
302
|
}],
|
|
213
303
|
};
|
|
214
304
|
}
|
|
@@ -217,5 +307,21 @@ export async function handleGetFileInfo(args) {
|
|
|
217
307
|
return createErrorResponse(errorMessage);
|
|
218
308
|
}
|
|
219
309
|
}
|
|
220
|
-
// The listAllowedDirectories function has been removed
|
|
221
310
|
// Use get_config to retrieve the allowedDirectories configuration
|
|
311
|
+
/**
|
|
312
|
+
* Handle write_pdf command
|
|
313
|
+
*/
|
|
314
|
+
export async function handleWritePdf(args) {
|
|
315
|
+
try {
|
|
316
|
+
const parsed = WritePdfArgsSchema.parse(args);
|
|
317
|
+
await writePdf(parsed.path, parsed.content, parsed.outputPath, parsed.options);
|
|
318
|
+
const targetPath = parsed.outputPath || parsed.path;
|
|
319
|
+
return {
|
|
320
|
+
content: [{ type: "text", text: `Successfully wrote PDF to ${targetPath}${parsed.outputPath ? `\nOriginal file: ${parsed.path}` : ''}` }],
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
catch (error) {
|
|
324
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
325
|
+
return createErrorResponse(errorMessage);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { spawn } from 'child_process';
|
|
2
|
+
import fs from 'fs/promises';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
import { ExecuteNodeArgsSchema } from '../tools/schemas.js';
|
|
6
|
+
// Get the directory where the MCP is installed (for requiring packages like exceljs)
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
+
const __dirname = path.dirname(__filename);
|
|
9
|
+
const mcpRoot = path.resolve(__dirname, '..', '..');
|
|
10
|
+
/**
|
|
11
|
+
* Handle execute_node command
|
|
12
|
+
* Executes Node.js code using the same Node runtime as the MCP
|
|
13
|
+
*/
|
|
14
|
+
export async function handleExecuteNode(args) {
|
|
15
|
+
const parsed = ExecuteNodeArgsSchema.parse(args);
|
|
16
|
+
const { code, timeout_ms } = parsed;
|
|
17
|
+
// Create temp file IN THE MCP DIRECTORY so ES module imports resolve correctly
|
|
18
|
+
// (ES modules resolve packages relative to file location, not NODE_PATH or cwd)
|
|
19
|
+
const tempFile = path.join(mcpRoot, `.mcp-exec-${Date.now()}-${Math.random().toString(36).slice(2)}.mjs`);
|
|
20
|
+
// User code runs directly - imports will resolve from mcpRoot/node_modules
|
|
21
|
+
const wrappedCode = code;
|
|
22
|
+
try {
|
|
23
|
+
await fs.writeFile(tempFile, wrappedCode, 'utf8');
|
|
24
|
+
const result = await new Promise((resolve) => {
|
|
25
|
+
const proc = spawn(process.execPath, [tempFile], {
|
|
26
|
+
cwd: mcpRoot,
|
|
27
|
+
timeout: timeout_ms
|
|
28
|
+
});
|
|
29
|
+
let stdout = '';
|
|
30
|
+
let stderr = '';
|
|
31
|
+
proc.stdout.on('data', (data) => {
|
|
32
|
+
stdout += data.toString();
|
|
33
|
+
});
|
|
34
|
+
proc.stderr.on('data', (data) => {
|
|
35
|
+
stderr += data.toString();
|
|
36
|
+
});
|
|
37
|
+
proc.on('close', (exitCode) => {
|
|
38
|
+
resolve({ stdout, stderr, exitCode: exitCode ?? 1 });
|
|
39
|
+
});
|
|
40
|
+
proc.on('error', (err) => {
|
|
41
|
+
resolve({ stdout, stderr: stderr + '\n' + err.message, exitCode: 1 });
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
// Clean up temp file
|
|
45
|
+
await fs.unlink(tempFile).catch(() => { });
|
|
46
|
+
if (result.exitCode !== 0) {
|
|
47
|
+
return {
|
|
48
|
+
content: [{
|
|
49
|
+
type: "text",
|
|
50
|
+
text: `Execution failed (exit code ${result.exitCode}):\n${result.stderr}\n${result.stdout}`
|
|
51
|
+
}],
|
|
52
|
+
isError: true
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
content: [{
|
|
57
|
+
type: "text",
|
|
58
|
+
text: result.stdout || '(no output)'
|
|
59
|
+
}]
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
// Clean up temp file on error
|
|
64
|
+
await fs.unlink(tempFile).catch(() => { });
|
|
65
|
+
return {
|
|
66
|
+
content: [{
|
|
67
|
+
type: "text",
|
|
68
|
+
text: `Failed to execute Node.js code: ${error instanceof Error ? error.message : String(error)}`
|
|
69
|
+
}],
|
|
70
|
+
isError: true
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -31,6 +31,11 @@ async function runServer() {
|
|
|
31
31
|
}
|
|
32
32
|
// Set global flag for onboarding control
|
|
33
33
|
global.disableOnboarding = DISABLE_ONBOARDING;
|
|
34
|
+
// Create transport FIRST so all logging gets properly buffered
|
|
35
|
+
// This must happen before any code that might use logger.*
|
|
36
|
+
const transport = new FilteredStdioServerTransport();
|
|
37
|
+
// Export transport for use throughout the application
|
|
38
|
+
global.mcpTransport = transport;
|
|
34
39
|
try {
|
|
35
40
|
deferLog('info', 'Loading configuration...');
|
|
36
41
|
await configManager.loadConfig();
|
|
@@ -47,9 +52,6 @@ async function runServer() {
|
|
|
47
52
|
deferLog('warning', 'Continuing with in-memory configuration only');
|
|
48
53
|
// Continue anyway - we'll use an in-memory config
|
|
49
54
|
}
|
|
50
|
-
const transport = new FilteredStdioServerTransport();
|
|
51
|
-
// Export transport for use throughout the application
|
|
52
|
-
global.mcpTransport = transport;
|
|
53
55
|
// Handle uncaught exceptions
|
|
54
56
|
process.on('uncaughtException', async (error) => {
|
|
55
57
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
package/dist/search-manager.d.ts
CHANGED
|
@@ -83,6 +83,24 @@ export interface SearchSessionOptions {
|
|
|
83
83
|
runtime: number;
|
|
84
84
|
totalResults: number;
|
|
85
85
|
}>;
|
|
86
|
+
/**
|
|
87
|
+
* Search Excel files for content matches
|
|
88
|
+
* Called during content search to include Excel files alongside text files
|
|
89
|
+
* Searches ALL sheets in each Excel file (row-wise for cross-column matching)
|
|
90
|
+
*
|
|
91
|
+
* TODO: Refactor - Extract Excel search logic to separate module (src/utils/search/excel-search.ts)
|
|
92
|
+
* and inject into SearchManager, similar to how file handlers are structured in src/utils/files/
|
|
93
|
+
* This would allow adding other file type searches (PDF, etc.) without bloating search-manager.ts
|
|
94
|
+
*/
|
|
95
|
+
private searchExcelFiles;
|
|
96
|
+
/**
|
|
97
|
+
* Find all Excel files in a directory recursively
|
|
98
|
+
*/
|
|
99
|
+
private findExcelFiles;
|
|
100
|
+
/**
|
|
101
|
+
* Extract context around a match for display (show surrounding text)
|
|
102
|
+
*/
|
|
103
|
+
private getMatchContext;
|
|
86
104
|
/**
|
|
87
105
|
* Clean up completed sessions older than specified time
|
|
88
106
|
* Called automatically by cleanup interval
|
|
@@ -101,6 +119,13 @@ export interface SearchSessionOptions {
|
|
|
101
119
|
* Detect if pattern contains glob wildcards
|
|
102
120
|
*/
|
|
103
121
|
private isGlobPattern;
|
|
122
|
+
/**
|
|
123
|
+
* Determine if Excel search should be included based on context
|
|
124
|
+
* Only searches Excel files when:
|
|
125
|
+
* - filePattern explicitly targets Excel files (*.xlsx, *.xls, *.xlsm, *.xlsb)
|
|
126
|
+
* - or the rootPath itself is an Excel file
|
|
127
|
+
*/
|
|
128
|
+
private shouldIncludeExcelSearch;
|
|
104
129
|
private buildRipgrepArgs;
|
|
105
130
|
private setupProcessHandlers;
|
|
106
131
|
private processBufferedOutput;
|