fast-cxt-mcp 1.1.0 → 1.1.2
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 +2 -2
- package/src/core.mjs +8 -6
- package/src/executor.mjs +33 -3
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fast-cxt-mcp",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.2",
|
|
4
4
|
"description": "AI-driven semantic code search MCP server (Node.js)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/server.mjs",
|
|
7
7
|
"bin": {
|
|
8
|
-
"fast-
|
|
8
|
+
"fast-context-mcp": "src/server.mjs"
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
11
|
"src/",
|
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";
|
|
@@ -925,22 +925,24 @@ function getRepoMap(projectRoot, targetDepth = 3, excludePaths = []) {
|
|
|
925
925
|
function _parseAnswer(xmlText, projectRoot) {
|
|
926
926
|
const files = [];
|
|
927
927
|
const resolvedRoot = resolve(projectRoot);
|
|
928
|
-
const fileRegex = /<file\s+path="([^"]+)
|
|
928
|
+
const fileRegex = /<file\s+path=(["'])([^"']+)\1>([\s\S]*?)<\/file>/g;
|
|
929
929
|
let fm;
|
|
930
930
|
while ((fm = fileRegex.exec(xmlText)) !== null) {
|
|
931
|
-
const vpath = fm[
|
|
932
|
-
|
|
931
|
+
const vpath = fm[2];
|
|
932
|
+
let rel = vpath.replace(/^\/codebase[\/\\]?/, "");
|
|
933
|
+
rel = rel.replace(/^[\/\\]+/, "");
|
|
933
934
|
|
|
934
935
|
// Path safety: reject traversal attempts (../) and paths outside project root
|
|
935
936
|
const fullPath = resolve(projectRoot, rel);
|
|
936
|
-
|
|
937
|
+
const relToRoot = relative(resolvedRoot, fullPath);
|
|
938
|
+
if (relToRoot === ".." || relToRoot.startsWith(`..${sep}`) || isAbsolute(relToRoot)) {
|
|
937
939
|
continue;
|
|
938
940
|
}
|
|
939
941
|
|
|
940
942
|
const ranges = [];
|
|
941
943
|
const rangeRegex = /<range>(\d+)-(\d+)<\/range>/g;
|
|
942
944
|
let rm;
|
|
943
|
-
while ((rm = rangeRegex.exec(fm[
|
|
945
|
+
while ((rm = rangeRegex.exec(fm[3])) !== null) {
|
|
944
946
|
ranges.push([parseInt(rm[1], 10), parseInt(rm[2], 10)]);
|
|
945
947
|
}
|
|
946
948
|
|
package/src/executor.mjs
CHANGED
|
@@ -52,8 +52,12 @@ export class ToolExecutor {
|
|
|
52
52
|
* @returns {string}
|
|
53
53
|
*/
|
|
54
54
|
_real(virtual) {
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
// Guard against undefined/null from malformed AI responses
|
|
56
|
+
if (virtual == null || typeof virtual !== "string") {
|
|
57
|
+
return this.root;
|
|
58
|
+
}
|
|
59
|
+
if (virtual.startsWith("/codebase") || virtual.startsWith("\\codebase")) {
|
|
60
|
+
const rel = virtual.slice("/codebase".length).replace(/^[\/\\]+/, "");
|
|
57
61
|
return join(this.root, rel);
|
|
58
62
|
}
|
|
59
63
|
return virtual;
|
|
@@ -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,11 +366,16 @@ 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
|
|
347
378
|
const matches = [];
|
|
348
|
-
const fullPattern = join(rp, pattern).replace(/\\/g, "/");
|
|
349
379
|
|
|
350
380
|
try {
|
|
351
381
|
_globWalk(rp, pattern, matches, typeFilter);
|