@myspec/mcp-server 0.1.2-next.30 → 0.1.2-next.31
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/dist/index.js +79 -37
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2896,9 +2896,11 @@ var readFileInput = z7.object({
|
|
|
2896
2896
|
includeIgnored: z7.boolean().optional()
|
|
2897
2897
|
});
|
|
2898
2898
|
var grepInput = z7.object({
|
|
2899
|
-
pattern: z7.string()
|
|
2900
|
-
|
|
2901
|
-
|
|
2899
|
+
pattern: z7.string().describe(
|
|
2900
|
+
"Search pattern. Interpreted as a regular expression by default (ripgrep syntax), e.g. `foo|bar` or `[Rr]ole`. Set `isRegex: false` to match the pattern as an exact literal string instead."
|
|
2901
|
+
),
|
|
2902
|
+
path: z7.string().optional().describe("File or directory under --root to search. Defaults to the whole root."),
|
|
2903
|
+
isRegex: z7.boolean().optional().describe("Treat `pattern` as a regex (default true). Set false for literal-string search."),
|
|
2902
2904
|
caseSensitive: z7.boolean().optional(),
|
|
2903
2905
|
maxMatches: z7.number().int().positive().optional(),
|
|
2904
2906
|
contextLines: z7.number().int().nonnegative().optional(),
|
|
@@ -3081,6 +3083,12 @@ function paginateLines(lines, offsetArg, limitArg) {
|
|
|
3081
3083
|
...truncated ? { nextOffset: endIdx + 1 } : {}
|
|
3082
3084
|
};
|
|
3083
3085
|
}
|
|
3086
|
+
var InvalidRegexError = class extends Error {
|
|
3087
|
+
constructor(message) {
|
|
3088
|
+
super(message);
|
|
3089
|
+
this.name = "InvalidRegexError";
|
|
3090
|
+
}
|
|
3091
|
+
};
|
|
3084
3092
|
async function handleGrep(args, opts) {
|
|
3085
3093
|
const subpath = args.path ?? ".";
|
|
3086
3094
|
let realPath;
|
|
@@ -3089,9 +3097,15 @@ async function handleGrep(args, opts) {
|
|
|
3089
3097
|
} catch (err) {
|
|
3090
3098
|
return mapPathError(err);
|
|
3091
3099
|
}
|
|
3100
|
+
if (!args.includeIgnored) {
|
|
3101
|
+
const check = isIgnored(subpath);
|
|
3102
|
+
if (check.ignored) {
|
|
3103
|
+
return structuredError("path_ignored", `Path matches ignore rule ${check.rule ?? ""}`);
|
|
3104
|
+
}
|
|
3105
|
+
}
|
|
3092
3106
|
const maxMatches = args.maxMatches ?? GREP_MAX_MATCHES_DEFAULT;
|
|
3093
3107
|
const contextLines = args.contextLines ?? 0;
|
|
3094
|
-
const isRegex = args.isRegex ??
|
|
3108
|
+
const isRegex = args.isRegex ?? true;
|
|
3095
3109
|
const caseSensitive = args.caseSensitive ?? false;
|
|
3096
3110
|
const ripgrep = await tryRipgrep(realPath, opts.realRoot, args.pattern, {
|
|
3097
3111
|
isRegex,
|
|
@@ -3106,14 +3120,21 @@ async function handleGrep(args, opts) {
|
|
|
3106
3120
|
if (ripgrep.fatal) {
|
|
3107
3121
|
return structuredError("grep_failed", ripgrep.error);
|
|
3108
3122
|
}
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3123
|
+
try {
|
|
3124
|
+
return structuredOk(
|
|
3125
|
+
await nodeGrep(realPath, opts.realRoot, args.pattern, {
|
|
3126
|
+
isRegex,
|
|
3127
|
+
caseSensitive,
|
|
3128
|
+
maxMatches,
|
|
3129
|
+
includeIgnored: args.includeIgnored ?? false
|
|
3130
|
+
})
|
|
3131
|
+
);
|
|
3132
|
+
} catch (err) {
|
|
3133
|
+
if (err instanceof InvalidRegexError) {
|
|
3134
|
+
return structuredError("grep_failed", err.message);
|
|
3135
|
+
}
|
|
3136
|
+
throw err;
|
|
3137
|
+
}
|
|
3117
3138
|
}
|
|
3118
3139
|
async function tryRipgrep(searchRoot, realRoot, pattern, opts) {
|
|
3119
3140
|
return new Promise((resolveP) => {
|
|
@@ -3202,9 +3223,38 @@ function parseRipgrepLine(rawLine, realRoot) {
|
|
|
3202
3223
|
}
|
|
3203
3224
|
var NODE_GREP_FILE_SIZE_CAP = 10 * 1024 * 1024;
|
|
3204
3225
|
async function nodeGrep(searchRoot, realRoot, pattern, opts) {
|
|
3205
|
-
|
|
3226
|
+
let regex;
|
|
3227
|
+
try {
|
|
3228
|
+
regex = opts.isRegex ? new RegExp(pattern, opts.caseSensitive ? "" : "i") : new RegExp(escapeRegExp(pattern), opts.caseSensitive ? "" : "i");
|
|
3229
|
+
} catch (err) {
|
|
3230
|
+
throw new InvalidRegexError(errorMessage(err));
|
|
3231
|
+
}
|
|
3206
3232
|
const matches = [];
|
|
3207
3233
|
let truncated = false;
|
|
3234
|
+
async function grepOneFile(full) {
|
|
3235
|
+
try {
|
|
3236
|
+
const st = await fs4.stat(full);
|
|
3237
|
+
if (st.size > NODE_GREP_FILE_SIZE_CAP) return;
|
|
3238
|
+
const content = await fs4.readFile(full, "utf-8");
|
|
3239
|
+
const lines = content.split("\n");
|
|
3240
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
3241
|
+
if (matches.length >= opts.maxMatches) {
|
|
3242
|
+
truncated = true;
|
|
3243
|
+
return;
|
|
3244
|
+
}
|
|
3245
|
+
const line = lines[i];
|
|
3246
|
+
if (line === void 0) continue;
|
|
3247
|
+
if (regex.test(line)) {
|
|
3248
|
+
matches.push({
|
|
3249
|
+
file: relative(realRoot, full) || full,
|
|
3250
|
+
line: i + 1,
|
|
3251
|
+
text: line
|
|
3252
|
+
});
|
|
3253
|
+
}
|
|
3254
|
+
}
|
|
3255
|
+
} catch {
|
|
3256
|
+
}
|
|
3257
|
+
}
|
|
3208
3258
|
async function walk(dir, prefix) {
|
|
3209
3259
|
if (matches.length >= opts.maxMatches) {
|
|
3210
3260
|
truncated = true;
|
|
@@ -3229,31 +3279,23 @@ async function nodeGrep(searchRoot, realRoot, pattern, opts) {
|
|
|
3229
3279
|
continue;
|
|
3230
3280
|
}
|
|
3231
3281
|
if (!item.isFile()) continue;
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
line: i + 1,
|
|
3248
|
-
text: line
|
|
3249
|
-
});
|
|
3250
|
-
}
|
|
3251
|
-
}
|
|
3252
|
-
} catch {
|
|
3253
|
-
}
|
|
3282
|
+
await grepOneFile(full);
|
|
3283
|
+
}
|
|
3284
|
+
}
|
|
3285
|
+
let rootIsDir;
|
|
3286
|
+
try {
|
|
3287
|
+
rootIsDir = (await fs4.stat(searchRoot)).isDirectory();
|
|
3288
|
+
} catch {
|
|
3289
|
+
return { matches, truncated };
|
|
3290
|
+
}
|
|
3291
|
+
if (rootIsDir) {
|
|
3292
|
+
await walk(searchRoot, relative(realRoot, searchRoot));
|
|
3293
|
+
} else {
|
|
3294
|
+
const rel = relative(realRoot, searchRoot);
|
|
3295
|
+
if (opts.includeIgnored || !isIgnored(rel).ignored) {
|
|
3296
|
+
await grepOneFile(searchRoot);
|
|
3254
3297
|
}
|
|
3255
3298
|
}
|
|
3256
|
-
await walk(searchRoot, relative(realRoot, searchRoot));
|
|
3257
3299
|
return { matches, truncated };
|
|
3258
3300
|
}
|
|
3259
3301
|
function escapeRegExp(s) {
|
|
@@ -3306,7 +3348,7 @@ function registerLocalFsTools(server, opts) {
|
|
|
3306
3348
|
},
|
|
3307
3349
|
{
|
|
3308
3350
|
name: "local_fs_grep",
|
|
3309
|
-
description: "Search for a pattern under the granted --root (ripgrep with Node fallback).",
|
|
3351
|
+
description: "Search for a regex pattern under the granted --root (ripgrep with Node fallback). Like the standard Grep tool, `pattern` is a regular expression by default; pass `isRegex: false` for a literal-string search. Regex syntax follows ripgrep (Rust regex) when installed, falling back to JavaScript RegExp otherwise, so avoid engine-specific constructs (lookbehind, backreferences).",
|
|
3310
3352
|
inputSchema: jsonSchemaFromZod(grepInput),
|
|
3311
3353
|
handler: (args) => handleGrep(args, opts)
|
|
3312
3354
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@myspec/mcp-server",
|
|
3
|
-
"version": "0.1.2-next.
|
|
3
|
+
"version": "0.1.2-next.31",
|
|
4
4
|
"description": "MySpec MCP server — exposes MySpec platform projects, files and attachments to MCP-aware clients via OAuth-authenticated access tokens.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|