fast-context-mcp 1.1.1 → 1.1.3
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/package.json +1 -1
- package/src/core.mjs +3 -2
- package/src/executor.mjs +49 -10
package/package.json
CHANGED
package/src/core.mjs
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
import { readdirSync, existsSync, statSync } from "node:fs";
|
|
15
|
-
import { resolve, join, relative, sep } from "node:path";
|
|
15
|
+
import { resolve, join, relative, sep, isAbsolute } from "node:path";
|
|
16
16
|
import { gzipSync } from "node:zlib";
|
|
17
17
|
import { randomUUID } from "node:crypto";
|
|
18
18
|
import { platform, arch, release, version as osVersion, hostname, cpus, totalmem } from "node:os";
|
|
@@ -934,7 +934,8 @@ function _parseAnswer(xmlText, projectRoot) {
|
|
|
934
934
|
|
|
935
935
|
// Path safety: reject traversal attempts (../) and paths outside project root
|
|
936
936
|
const fullPath = resolve(projectRoot, rel);
|
|
937
|
-
|
|
937
|
+
const relToRoot = relative(resolvedRoot, fullPath);
|
|
938
|
+
if (relToRoot === ".." || relToRoot.startsWith(`..${sep}`) || isAbsolute(relToRoot)) {
|
|
938
939
|
continue;
|
|
939
940
|
}
|
|
940
941
|
|
package/src/executor.mjs
CHANGED
|
@@ -52,6 +52,10 @@ export class ToolExecutor {
|
|
|
52
52
|
* @returns {string}
|
|
53
53
|
*/
|
|
54
54
|
_real(virtual) {
|
|
55
|
+
// Guard against undefined/null from malformed AI responses
|
|
56
|
+
if (virtual == null || typeof virtual !== "string") {
|
|
57
|
+
return this.root;
|
|
58
|
+
}
|
|
55
59
|
if (virtual.startsWith("/codebase") || virtual.startsWith("\\codebase")) {
|
|
56
60
|
const rel = virtual.slice("/codebase".length).replace(/^[\/\\]+/, "");
|
|
57
61
|
return join(this.root, rel);
|
|
@@ -122,6 +126,12 @@ export class ToolExecutor {
|
|
|
122
126
|
* @returns {Promise<string>}
|
|
123
127
|
*/
|
|
124
128
|
async rgAsync(pattern, path, include = null, exclude = null) {
|
|
129
|
+
if (!pattern || typeof pattern !== "string") {
|
|
130
|
+
return "Error: missing or invalid pattern";
|
|
131
|
+
}
|
|
132
|
+
if (!path || typeof path !== "string") {
|
|
133
|
+
return "Error: missing or invalid path";
|
|
134
|
+
}
|
|
125
135
|
this.collectedRgPatterns.push(pattern);
|
|
126
136
|
const rp = this._real(path);
|
|
127
137
|
if (!existsSync(rp)) {
|
|
@@ -168,6 +178,12 @@ export class ToolExecutor {
|
|
|
168
178
|
* @returns {string}
|
|
169
179
|
*/
|
|
170
180
|
rg(pattern, path, include = null, exclude = null) {
|
|
181
|
+
if (!pattern || typeof pattern !== "string") {
|
|
182
|
+
return "Error: missing or invalid pattern";
|
|
183
|
+
}
|
|
184
|
+
if (!path || typeof path !== "string") {
|
|
185
|
+
return "Error: missing or invalid path";
|
|
186
|
+
}
|
|
171
187
|
this.collectedRgPatterns.push(pattern);
|
|
172
188
|
const rp = this._real(path);
|
|
173
189
|
if (!existsSync(rp)) {
|
|
@@ -215,6 +231,9 @@ export class ToolExecutor {
|
|
|
215
231
|
* @returns {string}
|
|
216
232
|
*/
|
|
217
233
|
readfile(file, startLine = null, endLine = null) {
|
|
234
|
+
if (!file || typeof file !== "string") {
|
|
235
|
+
return "Error: missing or invalid file path";
|
|
236
|
+
}
|
|
218
237
|
const rp = this._real(file);
|
|
219
238
|
try {
|
|
220
239
|
const stat = statSync(rp);
|
|
@@ -249,6 +268,9 @@ export class ToolExecutor {
|
|
|
249
268
|
* @returns {string}
|
|
250
269
|
*/
|
|
251
270
|
tree(path, levels = null) {
|
|
271
|
+
if (!path || typeof path !== "string") {
|
|
272
|
+
return "Error: missing or invalid path";
|
|
273
|
+
}
|
|
252
274
|
const rp = this._real(path);
|
|
253
275
|
try {
|
|
254
276
|
const stat = statSync(rp);
|
|
@@ -284,6 +306,9 @@ export class ToolExecutor {
|
|
|
284
306
|
* @returns {string}
|
|
285
307
|
*/
|
|
286
308
|
ls(path, longFormat = false, allFiles = false) {
|
|
309
|
+
if (!path || typeof path !== "string") {
|
|
310
|
+
return "Error: missing or invalid path";
|
|
311
|
+
}
|
|
287
312
|
const rp = this._real(path);
|
|
288
313
|
try {
|
|
289
314
|
const stat = statSync(rp);
|
|
@@ -341,6 +366,12 @@ export class ToolExecutor {
|
|
|
341
366
|
* @returns {string}
|
|
342
367
|
*/
|
|
343
368
|
glob(pattern, path, typeFilter = "all") {
|
|
369
|
+
if (!pattern || typeof pattern !== "string") {
|
|
370
|
+
return "Error: missing or invalid pattern";
|
|
371
|
+
}
|
|
372
|
+
if (!path || typeof path !== "string") {
|
|
373
|
+
return "Error: missing or invalid path";
|
|
374
|
+
}
|
|
344
375
|
const rp = this._real(path);
|
|
345
376
|
|
|
346
377
|
// Use recursive readdir + fnmatch since Node 22 globSync may not be available
|
|
@@ -378,6 +409,9 @@ export class ToolExecutor {
|
|
|
378
409
|
* @returns {Promise<string>}
|
|
379
410
|
*/
|
|
380
411
|
async execCommandAsync(cmd) {
|
|
412
|
+
if (!cmd || typeof cmd !== "object") {
|
|
413
|
+
return "Error: missing or invalid command";
|
|
414
|
+
}
|
|
381
415
|
const t = cmd.type || "";
|
|
382
416
|
switch (t) {
|
|
383
417
|
case "rg":
|
|
@@ -401,6 +435,9 @@ export class ToolExecutor {
|
|
|
401
435
|
* @returns {string}
|
|
402
436
|
*/
|
|
403
437
|
execCommand(cmd) {
|
|
438
|
+
if (!cmd || typeof cmd !== "object") {
|
|
439
|
+
return "Error: missing or invalid command";
|
|
440
|
+
}
|
|
404
441
|
const t = cmd.type || "";
|
|
405
442
|
switch (t) {
|
|
406
443
|
case "rg":
|
|
@@ -424,13 +461,14 @@ export class ToolExecutor {
|
|
|
424
461
|
* @returns {Promise<string>}
|
|
425
462
|
*/
|
|
426
463
|
async execToolCallAsync(args) {
|
|
464
|
+
if (!args || typeof args !== "object") {
|
|
465
|
+
return "Error: missing or invalid tool args";
|
|
466
|
+
}
|
|
427
467
|
const keys = Object.keys(args).filter((k) => k.startsWith("command")).sort();
|
|
428
|
-
const tasks = keys
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
return `<${key}_result>\n${output}\n</${key}_result>`;
|
|
433
|
-
});
|
|
468
|
+
const tasks = keys.map(async (key) => {
|
|
469
|
+
const output = await this.execCommandAsync(args[key]);
|
|
470
|
+
return `<${key}_result>\n${output}\n</${key}_result>`;
|
|
471
|
+
});
|
|
434
472
|
const results = await Promise.all(tasks);
|
|
435
473
|
return results.join("");
|
|
436
474
|
}
|
|
@@ -442,12 +480,13 @@ export class ToolExecutor {
|
|
|
442
480
|
*/
|
|
443
481
|
execToolCall(args) {
|
|
444
482
|
const parts = [];
|
|
483
|
+
if (!args || typeof args !== "object") {
|
|
484
|
+
return "Error: missing or invalid tool args";
|
|
485
|
+
}
|
|
445
486
|
const keys = Object.keys(args).filter((k) => k.startsWith("command")).sort();
|
|
446
487
|
for (const key of keys) {
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
parts.push(`<${key}_result>\n${output}\n</${key}_result>`);
|
|
450
|
-
}
|
|
488
|
+
const output = this.execCommand(args[key]);
|
|
489
|
+
parts.push(`<${key}_result>\n${output}\n</${key}_result>`);
|
|
451
490
|
}
|
|
452
491
|
return parts.join("");
|
|
453
492
|
}
|