mcp-docs-service 0.3.10 → 0.3.11
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 +178 -54
- package/dist/cli/bin.d.ts +8 -0
- package/dist/cli/bin.js +133 -0
- package/dist/cli/bin.js.map +1 -0
- package/dist/handlers/docs.d.ts +26 -0
- package/dist/handlers/docs.js +513 -0
- package/dist/handlers/docs.js.map +1 -0
- package/dist/handlers/documents.js +282 -0
- package/dist/handlers/file.d.ts +32 -0
- package/dist/handlers/file.js +222 -0
- package/dist/handlers/file.js.map +1 -0
- package/dist/handlers/health.js +196 -0
- package/dist/handlers/index.d.ts +1 -0
- package/dist/handlers/index.js +8 -0
- package/dist/handlers/index.js.map +1 -0
- package/dist/handlers/navigation.js +128 -0
- package/dist/index.js +107 -549
- package/dist/schemas/index.d.ts +1 -0
- package/dist/schemas/index.js +1 -0
- package/dist/schemas/index.js.map +1 -0
- package/dist/schemas/tools.d.ts +164 -0
- package/dist/schemas/tools.js +47 -0
- package/dist/schemas/tools.js.map +1 -0
- package/dist/types/docs.d.ts +74 -0
- package/dist/types/docs.js +1 -0
- package/dist/types/docs.js.map +1 -0
- package/dist/types/file.d.ts +21 -0
- package/dist/types/file.js +2 -0
- package/dist/types/file.js.map +1 -0
- package/dist/types/index.d.ts +44 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/tools.d.ts +11 -0
- package/dist/types/tools.js +1 -0
- package/dist/types/tools.js.map +1 -0
- package/dist/utils/file.d.ts +24 -0
- package/dist/utils/file.js +94 -0
- package/dist/utils/file.js.map +1 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +2 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/logging.js +27 -0
- package/dist/utils/path.d.ts +16 -0
- package/dist/utils/path.js +69 -0
- package/dist/utils/path.js.map +1 -0
- package/package.json +4 -8
- package/cursor-wrapper.cjs +0 -111
- package/npx-wrapper.cjs +0 -160
@@ -0,0 +1,282 @@
|
|
1
|
+
/**
|
2
|
+
* Document handlers for the MCP Docs Service
|
3
|
+
*
|
4
|
+
* These handlers implement the document management operations.
|
5
|
+
*/
|
6
|
+
import fs from "fs/promises";
|
7
|
+
import path from "path";
|
8
|
+
import { glob } from "glob";
|
9
|
+
import { createTwoFilesPatch } from "diff";
|
10
|
+
// File editing and diffing utilities
|
11
|
+
function normalizeLineEndings(text) {
|
12
|
+
return text.replace(/\r\n/g, "\n");
|
13
|
+
}
|
14
|
+
function createUnifiedDiff(originalContent, newContent, filepath = "file") {
|
15
|
+
// Ensure consistent line endings for diff
|
16
|
+
const normalizedOriginal = normalizeLineEndings(originalContent);
|
17
|
+
const normalizedNew = normalizeLineEndings(newContent);
|
18
|
+
return createTwoFilesPatch(filepath, filepath, normalizedOriginal, normalizedNew, "original", "modified");
|
19
|
+
}
|
20
|
+
// Parse frontmatter from markdown content
|
21
|
+
export function parseFrontmatter(content) {
|
22
|
+
const frontmatterRegex = /^---\s*\n([\s\S]*?)\n---\s*\n/;
|
23
|
+
const match = content.match(frontmatterRegex);
|
24
|
+
if (!match) {
|
25
|
+
return { frontmatter: {}, content };
|
26
|
+
}
|
27
|
+
const frontmatterStr = match[1];
|
28
|
+
const contentWithoutFrontmatter = content.slice(match[0].length);
|
29
|
+
// Parse frontmatter as key-value pairs
|
30
|
+
const frontmatter = {};
|
31
|
+
const lines = frontmatterStr.split("\n");
|
32
|
+
for (const line of lines) {
|
33
|
+
const colonIndex = line.indexOf(":");
|
34
|
+
if (colonIndex !== -1) {
|
35
|
+
const key = line.slice(0, colonIndex).trim();
|
36
|
+
let value = line.slice(colonIndex + 1).trim();
|
37
|
+
// Handle quoted values
|
38
|
+
if (value.startsWith('"') && value.endsWith('"')) {
|
39
|
+
value = value.slice(1, -1);
|
40
|
+
}
|
41
|
+
// Handle arrays
|
42
|
+
if (value.startsWith("[") && value.endsWith("]")) {
|
43
|
+
try {
|
44
|
+
value = JSON.parse(value);
|
45
|
+
}
|
46
|
+
catch {
|
47
|
+
// Keep as string if parsing fails
|
48
|
+
}
|
49
|
+
}
|
50
|
+
frontmatter[key] = value;
|
51
|
+
}
|
52
|
+
}
|
53
|
+
return { frontmatter, content: contentWithoutFrontmatter };
|
54
|
+
}
|
55
|
+
export class DocumentHandler {
|
56
|
+
docsDir;
|
57
|
+
constructor(docsDir) {
|
58
|
+
this.docsDir = docsDir;
|
59
|
+
}
|
60
|
+
/**
|
61
|
+
* Validates that a path is within the docs directory
|
62
|
+
*/
|
63
|
+
async validatePath(requestedPath) {
|
64
|
+
// Resolve path relative to docs directory
|
65
|
+
const resolvedPath = path.isAbsolute(requestedPath)
|
66
|
+
? requestedPath
|
67
|
+
: path.join(this.docsDir, requestedPath);
|
68
|
+
const normalizedPath = path.normalize(resolvedPath);
|
69
|
+
// Check if path is within docs directory
|
70
|
+
if (!normalizedPath.startsWith(path.normalize(this.docsDir))) {
|
71
|
+
throw new Error(`Access denied - path outside docs directory: ${normalizedPath}`);
|
72
|
+
}
|
73
|
+
return normalizedPath;
|
74
|
+
}
|
75
|
+
/**
|
76
|
+
* Read a document from the docs directory
|
77
|
+
*/
|
78
|
+
async readDocument(docPath) {
|
79
|
+
try {
|
80
|
+
const validPath = await this.validatePath(docPath);
|
81
|
+
const content = await fs.readFile(validPath, "utf-8");
|
82
|
+
return {
|
83
|
+
content: [{ type: "text", text: content }],
|
84
|
+
metadata: {
|
85
|
+
path: docPath,
|
86
|
+
...parseFrontmatter(content).frontmatter,
|
87
|
+
},
|
88
|
+
};
|
89
|
+
}
|
90
|
+
catch (error) {
|
91
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
92
|
+
return {
|
93
|
+
content: [
|
94
|
+
{ type: "text", text: `Error reading document: ${errorMessage}` },
|
95
|
+
],
|
96
|
+
isError: true,
|
97
|
+
};
|
98
|
+
}
|
99
|
+
}
|
100
|
+
/**
|
101
|
+
* Write a document to the docs directory
|
102
|
+
*/
|
103
|
+
async writeDocument(docPath, content, createDirectories = true) {
|
104
|
+
try {
|
105
|
+
const validPath = await this.validatePath(docPath);
|
106
|
+
// Create parent directories if needed
|
107
|
+
if (createDirectories) {
|
108
|
+
const dirPath = path.dirname(validPath);
|
109
|
+
await fs.mkdir(dirPath, { recursive: true });
|
110
|
+
}
|
111
|
+
await fs.writeFile(validPath, content, "utf-8");
|
112
|
+
return {
|
113
|
+
content: [{ type: "text", text: `Successfully wrote to ${docPath}` }],
|
114
|
+
};
|
115
|
+
}
|
116
|
+
catch (error) {
|
117
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
118
|
+
return {
|
119
|
+
content: [
|
120
|
+
{ type: "text", text: `Error writing document: ${errorMessage}` },
|
121
|
+
],
|
122
|
+
isError: true,
|
123
|
+
};
|
124
|
+
}
|
125
|
+
}
|
126
|
+
/**
|
127
|
+
* Apply edits to a document
|
128
|
+
*/
|
129
|
+
async editDocument(docPath, edits, dryRun = false) {
|
130
|
+
try {
|
131
|
+
const validPath = await this.validatePath(docPath);
|
132
|
+
// Read file content and normalize line endings
|
133
|
+
const content = normalizeLineEndings(await fs.readFile(validPath, "utf-8"));
|
134
|
+
// Apply edits sequentially
|
135
|
+
let modifiedContent = content;
|
136
|
+
for (const edit of edits) {
|
137
|
+
const normalizedOld = normalizeLineEndings(edit.oldText);
|
138
|
+
const normalizedNew = normalizeLineEndings(edit.newText);
|
139
|
+
// If exact match exists, use it
|
140
|
+
if (modifiedContent.includes(normalizedOld)) {
|
141
|
+
modifiedContent = modifiedContent.replace(normalizedOld, normalizedNew);
|
142
|
+
continue;
|
143
|
+
}
|
144
|
+
// Otherwise, try line-by-line matching with flexibility for whitespace
|
145
|
+
const oldLines = normalizedOld.split("\n");
|
146
|
+
const contentLines = modifiedContent.split("\n");
|
147
|
+
let matchFound = false;
|
148
|
+
for (let i = 0; i <= contentLines.length - oldLines.length; i++) {
|
149
|
+
const potentialMatch = contentLines.slice(i, i + oldLines.length);
|
150
|
+
// Compare lines with normalized whitespace
|
151
|
+
const isMatch = oldLines.every((oldLine, j) => {
|
152
|
+
const contentLine = potentialMatch[j];
|
153
|
+
return oldLine.trim() === contentLine.trim();
|
154
|
+
});
|
155
|
+
if (isMatch) {
|
156
|
+
// Preserve original indentation of first line
|
157
|
+
const originalIndent = contentLines[i].match(/^\s*/)?.[0] || "";
|
158
|
+
const newLines = normalizedNew.split("\n").map((line, j) => {
|
159
|
+
if (j === 0)
|
160
|
+
return originalIndent + line.trimStart();
|
161
|
+
// For subsequent lines, try to preserve relative indentation
|
162
|
+
const oldIndent = oldLines[j]?.match(/^\s*/)?.[0] || "";
|
163
|
+
const newIndent = line.match(/^\s*/)?.[0] || "";
|
164
|
+
if (oldIndent && newIndent) {
|
165
|
+
const relativeIndent = newIndent.length - oldIndent.length;
|
166
|
+
return (originalIndent +
|
167
|
+
" ".repeat(Math.max(0, relativeIndent)) +
|
168
|
+
line.trimStart());
|
169
|
+
}
|
170
|
+
return line;
|
171
|
+
});
|
172
|
+
contentLines.splice(i, oldLines.length, ...newLines);
|
173
|
+
modifiedContent = contentLines.join("\n");
|
174
|
+
matchFound = true;
|
175
|
+
break;
|
176
|
+
}
|
177
|
+
}
|
178
|
+
if (!matchFound) {
|
179
|
+
throw new Error(`Could not find exact match for edit:\n${edit.oldText}`);
|
180
|
+
}
|
181
|
+
}
|
182
|
+
// Create unified diff
|
183
|
+
const diff = createUnifiedDiff(content, modifiedContent, docPath);
|
184
|
+
// Format diff with appropriate number of backticks
|
185
|
+
let numBackticks = 3;
|
186
|
+
while (diff.includes("`".repeat(numBackticks))) {
|
187
|
+
numBackticks++;
|
188
|
+
}
|
189
|
+
const formattedDiff = `${"`".repeat(numBackticks)}diff\n${diff}${"`".repeat(numBackticks)}\n\n`;
|
190
|
+
if (!dryRun) {
|
191
|
+
await fs.writeFile(validPath, modifiedContent, "utf-8");
|
192
|
+
}
|
193
|
+
return {
|
194
|
+
content: [{ type: "text", text: formattedDiff }],
|
195
|
+
};
|
196
|
+
}
|
197
|
+
catch (error) {
|
198
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
199
|
+
return {
|
200
|
+
content: [
|
201
|
+
{ type: "text", text: `Error editing document: ${errorMessage}` },
|
202
|
+
],
|
203
|
+
isError: true,
|
204
|
+
};
|
205
|
+
}
|
206
|
+
}
|
207
|
+
/**
|
208
|
+
* List documents in the docs directory
|
209
|
+
*/
|
210
|
+
async listDocuments(basePath = "", recursive = false) {
|
211
|
+
try {
|
212
|
+
const baseDir = path.join(this.docsDir, basePath);
|
213
|
+
const pattern = recursive
|
214
|
+
? path.join(baseDir, "**/*.md")
|
215
|
+
: path.join(baseDir, "*.md");
|
216
|
+
const files = await glob(pattern);
|
217
|
+
const relativePaths = files.map((file) => path.relative(this.docsDir, file));
|
218
|
+
return {
|
219
|
+
content: [{ type: "text", text: relativePaths.join("\n") }],
|
220
|
+
};
|
221
|
+
}
|
222
|
+
catch (error) {
|
223
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
224
|
+
return {
|
225
|
+
content: [
|
226
|
+
{ type: "text", text: `Error listing documents: ${errorMessage}` },
|
227
|
+
],
|
228
|
+
isError: true,
|
229
|
+
};
|
230
|
+
}
|
231
|
+
}
|
232
|
+
/**
|
233
|
+
* Search for documents containing a query
|
234
|
+
*/
|
235
|
+
async searchDocuments(query, basePath = "") {
|
236
|
+
try {
|
237
|
+
const baseDir = path.join(this.docsDir, basePath);
|
238
|
+
const pattern = path.join(baseDir, "**/*.md");
|
239
|
+
const files = await glob(pattern);
|
240
|
+
const results = [];
|
241
|
+
for (const file of files) {
|
242
|
+
const content = await fs.readFile(file, "utf-8");
|
243
|
+
if (content.toLowerCase().includes(query.toLowerCase())) {
|
244
|
+
results.push(path.relative(this.docsDir, file));
|
245
|
+
}
|
246
|
+
}
|
247
|
+
return {
|
248
|
+
content: [{ type: "text", text: results.join("\n") }],
|
249
|
+
};
|
250
|
+
}
|
251
|
+
catch (error) {
|
252
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
253
|
+
return {
|
254
|
+
content: [
|
255
|
+
{ type: "text", text: `Error searching documents: ${errorMessage}` },
|
256
|
+
],
|
257
|
+
isError: true,
|
258
|
+
};
|
259
|
+
}
|
260
|
+
}
|
261
|
+
/**
|
262
|
+
* Delete a document from the docs directory
|
263
|
+
*/
|
264
|
+
async deleteDocument(docPath) {
|
265
|
+
try {
|
266
|
+
const validPath = await this.validatePath(docPath);
|
267
|
+
await fs.unlink(validPath);
|
268
|
+
return {
|
269
|
+
content: [{ type: "text", text: `Successfully deleted ${docPath}` }],
|
270
|
+
};
|
271
|
+
}
|
272
|
+
catch (error) {
|
273
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
274
|
+
return {
|
275
|
+
content: [
|
276
|
+
{ type: "text", text: `Error deleting document: ${errorMessage}` },
|
277
|
+
],
|
278
|
+
isError: true,
|
279
|
+
};
|
280
|
+
}
|
281
|
+
}
|
282
|
+
}
|
@@ -0,0 +1,32 @@
|
|
1
|
+
import { ToolResponse } from "../types/tools.js";
|
2
|
+
/**
|
3
|
+
* Reads a file and returns its content
|
4
|
+
*/
|
5
|
+
export declare function readFile(filePath: string, allowedDirectories: string[]): Promise<ToolResponse>;
|
6
|
+
/**
|
7
|
+
* Writes content to a file
|
8
|
+
*/
|
9
|
+
export declare function writeFile(filePath: string, content: string, allowedDirectories: string[]): Promise<ToolResponse>;
|
10
|
+
/**
|
11
|
+
* Lists files in a directory
|
12
|
+
*/
|
13
|
+
export declare function listFiles(dirPath: string, allowedDirectories: string[]): Promise<ToolResponse>;
|
14
|
+
/**
|
15
|
+
* Gets information about a file
|
16
|
+
*/
|
17
|
+
export declare function getFileInfo(filePath: string, allowedDirectories: string[]): Promise<ToolResponse>;
|
18
|
+
/**
|
19
|
+
* Searches for files matching a pattern
|
20
|
+
*/
|
21
|
+
export declare function searchForFiles(rootPath: string, pattern: string, excludePatterns: string[] | undefined, allowedDirectories: string[]): Promise<ToolResponse>;
|
22
|
+
/**
|
23
|
+
* Applies edits to a file
|
24
|
+
*/
|
25
|
+
export declare function editFile(filePath: string, edits: Array<{
|
26
|
+
oldText: string;
|
27
|
+
newText: string;
|
28
|
+
}>, allowedDirectories: string[]): Promise<ToolResponse>;
|
29
|
+
/**
|
30
|
+
* Gets the directory structure as a tree
|
31
|
+
*/
|
32
|
+
export declare function getDirectoryTree(dirPath: string, allowedDirectories: string[]): Promise<ToolResponse>;
|
@@ -0,0 +1,222 @@
|
|
1
|
+
import fs from "fs/promises";
|
2
|
+
import path from "path";
|
3
|
+
import { validatePath } from "../utils/path.js";
|
4
|
+
import { getFileStats, searchFiles, applyFileEdits } from "../utils/file.js";
|
5
|
+
/**
|
6
|
+
* Reads a file and returns its content
|
7
|
+
*/
|
8
|
+
export async function readFile(filePath, allowedDirectories) {
|
9
|
+
try {
|
10
|
+
const normalizedPath = await validatePath(filePath, allowedDirectories);
|
11
|
+
const content = await fs.readFile(normalizedPath, "utf-8");
|
12
|
+
return {
|
13
|
+
content: [{ type: "text", text: "File read successfully" }],
|
14
|
+
metadata: {
|
15
|
+
path: filePath,
|
16
|
+
content,
|
17
|
+
},
|
18
|
+
};
|
19
|
+
}
|
20
|
+
catch (error) {
|
21
|
+
return {
|
22
|
+
content: [{ type: "text", text: `Error reading file: ${error.message}` }],
|
23
|
+
isError: true,
|
24
|
+
};
|
25
|
+
}
|
26
|
+
}
|
27
|
+
/**
|
28
|
+
* Writes content to a file
|
29
|
+
*/
|
30
|
+
export async function writeFile(filePath, content, allowedDirectories) {
|
31
|
+
try {
|
32
|
+
const normalizedPath = await validatePath(filePath, allowedDirectories);
|
33
|
+
// Ensure the directory exists
|
34
|
+
const dirPath = path.dirname(normalizedPath);
|
35
|
+
await fs.mkdir(dirPath, { recursive: true });
|
36
|
+
// Write the file
|
37
|
+
await fs.writeFile(normalizedPath, content);
|
38
|
+
return {
|
39
|
+
content: [{ type: "text", text: "File written successfully" }],
|
40
|
+
metadata: {
|
41
|
+
path: filePath,
|
42
|
+
},
|
43
|
+
};
|
44
|
+
}
|
45
|
+
catch (error) {
|
46
|
+
return {
|
47
|
+
content: [{ type: "text", text: `Error writing file: ${error.message}` }],
|
48
|
+
isError: true,
|
49
|
+
};
|
50
|
+
}
|
51
|
+
}
|
52
|
+
/**
|
53
|
+
* Lists files in a directory
|
54
|
+
*/
|
55
|
+
export async function listFiles(dirPath, allowedDirectories) {
|
56
|
+
try {
|
57
|
+
const normalizedPath = await validatePath(dirPath, allowedDirectories);
|
58
|
+
const entries = await fs.readdir(normalizedPath, { withFileTypes: true });
|
59
|
+
const files = entries.map((entry) => ({
|
60
|
+
name: entry.name,
|
61
|
+
isDirectory: entry.isDirectory(),
|
62
|
+
isFile: entry.isFile(),
|
63
|
+
}));
|
64
|
+
return {
|
65
|
+
content: [
|
66
|
+
{ type: "text", text: `Listed ${files.length} files in ${dirPath}` },
|
67
|
+
],
|
68
|
+
metadata: {
|
69
|
+
path: dirPath,
|
70
|
+
files,
|
71
|
+
},
|
72
|
+
};
|
73
|
+
}
|
74
|
+
catch (error) {
|
75
|
+
return {
|
76
|
+
content: [
|
77
|
+
{ type: "text", text: `Error listing files: ${error.message}` },
|
78
|
+
],
|
79
|
+
isError: true,
|
80
|
+
};
|
81
|
+
}
|
82
|
+
}
|
83
|
+
/**
|
84
|
+
* Gets information about a file
|
85
|
+
*/
|
86
|
+
export async function getFileInfo(filePath, allowedDirectories) {
|
87
|
+
try {
|
88
|
+
const normalizedPath = await validatePath(filePath, allowedDirectories);
|
89
|
+
const fileInfo = await getFileStats(normalizedPath);
|
90
|
+
return {
|
91
|
+
content: [
|
92
|
+
{ type: "text", text: "File information retrieved successfully" },
|
93
|
+
],
|
94
|
+
metadata: {
|
95
|
+
path: filePath,
|
96
|
+
info: fileInfo,
|
97
|
+
},
|
98
|
+
};
|
99
|
+
}
|
100
|
+
catch (error) {
|
101
|
+
return {
|
102
|
+
content: [
|
103
|
+
{ type: "text", text: `Error getting file info: ${error.message}` },
|
104
|
+
],
|
105
|
+
isError: true,
|
106
|
+
};
|
107
|
+
}
|
108
|
+
}
|
109
|
+
/**
|
110
|
+
* Searches for files matching a pattern
|
111
|
+
*/
|
112
|
+
export async function searchForFiles(rootPath, pattern, excludePatterns = [], allowedDirectories) {
|
113
|
+
try {
|
114
|
+
const normalizedPath = await validatePath(rootPath, allowedDirectories);
|
115
|
+
const files = await searchFiles(normalizedPath, pattern, excludePatterns);
|
116
|
+
return {
|
117
|
+
content: [
|
118
|
+
{
|
119
|
+
type: "text",
|
120
|
+
text: `Found ${files.length} files matching pattern "${pattern}"`,
|
121
|
+
},
|
122
|
+
],
|
123
|
+
metadata: {
|
124
|
+
rootPath,
|
125
|
+
pattern,
|
126
|
+
excludePatterns,
|
127
|
+
files,
|
128
|
+
},
|
129
|
+
};
|
130
|
+
}
|
131
|
+
catch (error) {
|
132
|
+
return {
|
133
|
+
content: [
|
134
|
+
{ type: "text", text: `Error searching files: ${error.message}` },
|
135
|
+
],
|
136
|
+
isError: true,
|
137
|
+
};
|
138
|
+
}
|
139
|
+
}
|
140
|
+
/**
|
141
|
+
* Applies edits to a file
|
142
|
+
*/
|
143
|
+
export async function editFile(filePath, edits, allowedDirectories) {
|
144
|
+
try {
|
145
|
+
const normalizedPath = await validatePath(filePath, allowedDirectories);
|
146
|
+
const diff = await applyFileEdits(normalizedPath, edits, false);
|
147
|
+
return {
|
148
|
+
content: [{ type: "text", text: "File edited successfully" }],
|
149
|
+
metadata: {
|
150
|
+
path: filePath,
|
151
|
+
diff,
|
152
|
+
},
|
153
|
+
};
|
154
|
+
}
|
155
|
+
catch (error) {
|
156
|
+
return {
|
157
|
+
content: [{ type: "text", text: `Error editing file: ${error.message}` }],
|
158
|
+
isError: true,
|
159
|
+
};
|
160
|
+
}
|
161
|
+
}
|
162
|
+
/**
|
163
|
+
* Gets the directory structure as a tree
|
164
|
+
*/
|
165
|
+
export async function getDirectoryTree(dirPath, allowedDirectories) {
|
166
|
+
try {
|
167
|
+
const normalizedPath = await validatePath(dirPath, allowedDirectories);
|
168
|
+
async function buildTree(currentPath) {
|
169
|
+
const entries = await fs.readdir(currentPath, { withFileTypes: true });
|
170
|
+
const result = [];
|
171
|
+
for (const entry of entries) {
|
172
|
+
const entryPath = path.join(currentPath, entry.name);
|
173
|
+
if (entry.isDirectory()) {
|
174
|
+
const children = await buildTree(entryPath);
|
175
|
+
result.push({
|
176
|
+
name: entry.name,
|
177
|
+
path: entryPath,
|
178
|
+
type: "directory",
|
179
|
+
children,
|
180
|
+
});
|
181
|
+
}
|
182
|
+
else {
|
183
|
+
result.push({
|
184
|
+
name: entry.name,
|
185
|
+
path: entryPath,
|
186
|
+
type: "file",
|
187
|
+
});
|
188
|
+
}
|
189
|
+
}
|
190
|
+
// Sort entries: directories first, then files, both alphabetically
|
191
|
+
result.sort((a, b) => {
|
192
|
+
if (a.type !== b.type) {
|
193
|
+
return a.type === "directory" ? -1 : 1;
|
194
|
+
}
|
195
|
+
return a.name.localeCompare(b.name);
|
196
|
+
});
|
197
|
+
return result;
|
198
|
+
}
|
199
|
+
const tree = await buildTree(normalizedPath);
|
200
|
+
return {
|
201
|
+
content: [
|
202
|
+
{ type: "text", text: "Directory tree retrieved successfully" },
|
203
|
+
],
|
204
|
+
metadata: {
|
205
|
+
path: dirPath,
|
206
|
+
tree,
|
207
|
+
},
|
208
|
+
};
|
209
|
+
}
|
210
|
+
catch (error) {
|
211
|
+
return {
|
212
|
+
content: [
|
213
|
+
{
|
214
|
+
type: "text",
|
215
|
+
text: `Error getting directory tree: ${error.message}`,
|
216
|
+
},
|
217
|
+
],
|
218
|
+
isError: true,
|
219
|
+
};
|
220
|
+
}
|
221
|
+
}
|
222
|
+
//# sourceMappingURL=file.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"file.js","sourceRoot":"","sources":["../../src/handlers/file.ts"],"names":[],"mappings":"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"}
|