@probelabs/probe 0.6.0-rc102 → 0.6.0-rc104
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/build/agent/ProbeAgent.js +330 -11
- package/build/agent/index.js +28142 -448
- package/build/agent/probeTool.js +23 -5
- package/build/agent/tools.js +57 -5
- package/build/mcp/index.js +23 -144
- package/build/mcp/index.ts +22 -161
- package/build/utils.js +1 -1
- package/cjs/agent/ProbeAgent.cjs +27948 -270
- package/cjs/index.cjs +27996 -318
- package/package.json +2 -1
- package/src/agent/ProbeAgent.js +330 -11
- package/src/agent/index.js +39 -45
- package/src/agent/probeTool.js +23 -5
- package/src/agent/tools.js +57 -5
- package/src/mcp/index.ts +22 -161
- package/src/utils.js +1 -1
package/build/agent/probeTool.js
CHANGED
|
@@ -207,14 +207,24 @@ export function createWrappedTools(baseTools) {
|
|
|
207
207
|
// Simple file listing tool
|
|
208
208
|
export const listFilesTool = {
|
|
209
209
|
execute: async (params) => {
|
|
210
|
-
const { directory = '.' } = params;
|
|
210
|
+
const { directory = '.', workingDirectory } = params;
|
|
211
|
+
|
|
212
|
+
// Use the provided working directory, or fall back to process.cwd()
|
|
213
|
+
const baseCwd = workingDirectory || process.cwd();
|
|
214
|
+
|
|
215
|
+
// Security: Validate path to prevent traversal attacks
|
|
216
|
+
const secureBaseDir = path.resolve(baseCwd);
|
|
217
|
+
const targetDir = path.resolve(secureBaseDir, directory);
|
|
218
|
+
if (!targetDir.startsWith(secureBaseDir + path.sep) && targetDir !== secureBaseDir) {
|
|
219
|
+
throw new Error('Path traversal attempt detected. Access denied.');
|
|
220
|
+
}
|
|
211
221
|
|
|
212
222
|
try {
|
|
213
223
|
const files = await listFilesByLevel({
|
|
214
|
-
directory,
|
|
224
|
+
directory: targetDir,
|
|
215
225
|
maxFiles: 100,
|
|
216
226
|
respectGitignore: !process.env.PROBE_NO_GITIGNORE || process.env.PROBE_NO_GITIGNORE === '',
|
|
217
|
-
cwd:
|
|
227
|
+
cwd: secureBaseDir
|
|
218
228
|
});
|
|
219
229
|
|
|
220
230
|
return files;
|
|
@@ -227,15 +237,23 @@ export const listFilesTool = {
|
|
|
227
237
|
// Simple file search tool
|
|
228
238
|
export const searchFilesTool = {
|
|
229
239
|
execute: async (params) => {
|
|
230
|
-
const { pattern, directory = '.', recursive = true } = params;
|
|
240
|
+
const { pattern, directory = '.', recursive = true, workingDirectory } = params;
|
|
231
241
|
|
|
232
242
|
if (!pattern) {
|
|
233
243
|
throw new Error('Pattern is required for file search');
|
|
234
244
|
}
|
|
235
245
|
|
|
246
|
+
// Security: Validate path to prevent traversal attacks
|
|
247
|
+
const baseCwd = workingDirectory || process.cwd();
|
|
248
|
+
const secureBaseDir = path.resolve(baseCwd);
|
|
249
|
+
const targetDir = path.resolve(secureBaseDir, directory);
|
|
250
|
+
if (!targetDir.startsWith(secureBaseDir + path.sep) && targetDir !== secureBaseDir) {
|
|
251
|
+
throw new Error('Path traversal attempt detected. Access denied.');
|
|
252
|
+
}
|
|
253
|
+
|
|
236
254
|
try {
|
|
237
255
|
const options = {
|
|
238
|
-
cwd:
|
|
256
|
+
cwd: targetDir,
|
|
239
257
|
ignore: ['node_modules/**', '.git/**'],
|
|
240
258
|
absolute: false
|
|
241
259
|
};
|
package/build/agent/tools.js
CHANGED
|
@@ -141,12 +141,45 @@ export function parseXmlToolCallWithThinking(xmlString, validTools) {
|
|
|
141
141
|
// Remove thinking tags and their content from the XML string
|
|
142
142
|
let cleanedXmlString = xmlString.replace(/<thinking>[\s\S]*?<\/thinking>/g, '').trim();
|
|
143
143
|
|
|
144
|
-
//
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
144
|
+
// Enhanced recovery logic for attempt_complete shorthand
|
|
145
|
+
// Check for various forms of attempt_complete tags:
|
|
146
|
+
// 1. Perfect shorthand: <attempt_complete>
|
|
147
|
+
// 2. Self-closing: <attempt_complete/>
|
|
148
|
+
// 3. Empty with closing tag: <attempt_complete></attempt_complete>
|
|
149
|
+
// 4. Incomplete: <attempt_complete (missing closing bracket)
|
|
150
|
+
// 5. With trailing content that should be ignored
|
|
151
|
+
const attemptCompletePatterns = [
|
|
152
|
+
// Standard shorthand with optional whitespace
|
|
153
|
+
/^<attempt_complete>\s*$/,
|
|
154
|
+
// Empty with proper closing tag (common case from the logs)
|
|
155
|
+
/^<attempt_complete>\s*<\/attempt_complete>\s*$/,
|
|
156
|
+
// Self-closing variant
|
|
157
|
+
/^<attempt_complete\s*\/>\s*$/,
|
|
158
|
+
// Incomplete opening tag (missing closing bracket)
|
|
159
|
+
/^<attempt_complete\s*$/,
|
|
160
|
+
// With trailing content (extract just the tag part) - must come after empty tag pattern
|
|
161
|
+
/^<attempt_complete>(.*)$/s,
|
|
162
|
+
// Self-closing with trailing content
|
|
163
|
+
/^<attempt_complete\s*\/>(.*)$/s
|
|
164
|
+
];
|
|
165
|
+
|
|
166
|
+
for (const pattern of attemptCompletePatterns) {
|
|
167
|
+
const match = cleanedXmlString.match(pattern);
|
|
168
|
+
if (match) {
|
|
169
|
+
// Convert any form of attempt_complete to the standard format
|
|
170
|
+
return {
|
|
171
|
+
toolName: 'attempt_completion',
|
|
172
|
+
params: { result: '__PREVIOUS_RESPONSE__' }
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Additional recovery: check if the string contains attempt_complete anywhere
|
|
178
|
+
// and treat the entire response as a completion signal if no other tool tags are found
|
|
179
|
+
if (cleanedXmlString.includes('<attempt_complete') && !hasOtherToolTags(cleanedXmlString, validTools)) {
|
|
180
|
+
// This handles malformed cases where attempt_complete appears but is broken
|
|
148
181
|
return {
|
|
149
|
-
toolName: 'attempt_completion',
|
|
182
|
+
toolName: 'attempt_completion',
|
|
150
183
|
params: { result: '__PREVIOUS_RESPONSE__' }
|
|
151
184
|
};
|
|
152
185
|
}
|
|
@@ -160,4 +193,23 @@ export function parseXmlToolCallWithThinking(xmlString, validTools) {
|
|
|
160
193
|
}
|
|
161
194
|
|
|
162
195
|
return parsedTool;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Helper function to check if the XML string contains other tool tags
|
|
200
|
+
* @param {string} xmlString - The XML string to check
|
|
201
|
+
* @param {string[]} validTools - List of valid tool names
|
|
202
|
+
* @returns {boolean} - True if other tool tags are found
|
|
203
|
+
*/
|
|
204
|
+
function hasOtherToolTags(xmlString, validTools = []) {
|
|
205
|
+
const defaultTools = ['search', 'query', 'extract', 'listFiles', 'searchFiles', 'implement', 'attempt_completion'];
|
|
206
|
+
const toolsToCheck = validTools.length > 0 ? validTools : defaultTools;
|
|
207
|
+
|
|
208
|
+
// Check for any tool tags other than attempt_complete variants
|
|
209
|
+
for (const tool of toolsToCheck) {
|
|
210
|
+
if (tool !== 'attempt_completion' && xmlString.includes(`<${tool}`)) {
|
|
211
|
+
return true;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return false;
|
|
163
215
|
}
|
package/build/mcp/index.js
CHANGED
|
@@ -8,7 +8,7 @@ import path from 'path';
|
|
|
8
8
|
import fs from 'fs-extra';
|
|
9
9
|
import { fileURLToPath } from 'url';
|
|
10
10
|
// Import from parent package
|
|
11
|
-
import { search,
|
|
11
|
+
import { search, extract } from '../index.js';
|
|
12
12
|
// Parse command-line arguments
|
|
13
13
|
function parseArgs() {
|
|
14
14
|
const args = process.argv.slice(2);
|
|
@@ -128,38 +128,22 @@ class ProbeServer {
|
|
|
128
128
|
},
|
|
129
129
|
query: {
|
|
130
130
|
type: 'string',
|
|
131
|
-
description: 'Elastic search query. Supports logical operators (AND, OR, NOT), and grouping with parentheses. Examples: "config", "(term1 OR term2) AND term3"
|
|
132
|
-
},
|
|
133
|
-
filesOnly: {
|
|
134
|
-
type: 'boolean',
|
|
135
|
-
description: 'Skip AST parsing and just output unique files',
|
|
136
|
-
},
|
|
137
|
-
ignore: {
|
|
138
|
-
type: 'array',
|
|
139
|
-
items: { type: 'string' },
|
|
140
|
-
description: 'Custom patterns to ignore (in addition to .gitignore and common patterns)'
|
|
141
|
-
},
|
|
142
|
-
excludeFilenames: {
|
|
143
|
-
type: 'boolean',
|
|
144
|
-
description: 'Exclude filenames from being used for matching'
|
|
131
|
+
description: 'Elastic search query. Supports logical operators (AND, OR, NOT), and grouping with parentheses. For exact matches of specific identifiers, use quotes: "MyFunction", "SpecificStruct", "exact_variable_name". Examples: "config", "(term1 OR term2) AND term3", "getUserData", "struct Config".',
|
|
145
132
|
},
|
|
146
133
|
exact: {
|
|
147
134
|
type: 'boolean',
|
|
148
|
-
description: '
|
|
135
|
+
description: 'When you exactly know what you are looking for, like known function name, struct name, or variable name, set this flag for precise matching'
|
|
149
136
|
},
|
|
150
137
|
allowTests: {
|
|
151
138
|
type: 'boolean',
|
|
152
|
-
description: 'Allow test files and test code blocks in results (
|
|
139
|
+
description: 'Allow test files and test code blocks in results (enabled by default)',
|
|
140
|
+
default: true
|
|
153
141
|
},
|
|
154
142
|
session: {
|
|
155
143
|
type: 'string',
|
|
156
144
|
description: 'Session identifier for caching. Set to "new" if unknown, or want to reset cache. Re-use session ID returned from previous searches',
|
|
157
145
|
default: "new",
|
|
158
146
|
},
|
|
159
|
-
timeout: {
|
|
160
|
-
type: 'number',
|
|
161
|
-
description: 'Timeout for the search operation in seconds (default: 30)',
|
|
162
|
-
},
|
|
163
147
|
noGitignore: {
|
|
164
148
|
type: 'boolean',
|
|
165
149
|
description: 'Skip .gitignore files (will use PROBE_NO_GITIGNORE environment variable if not set)',
|
|
@@ -168,50 +152,6 @@ class ProbeServer {
|
|
|
168
152
|
required: ['path', 'query']
|
|
169
153
|
},
|
|
170
154
|
},
|
|
171
|
-
{
|
|
172
|
-
name: 'query_code',
|
|
173
|
-
description: "Search code using ast-grep structural pattern matching. Use this tool to find specific code structures like functions, classes, or methods.",
|
|
174
|
-
inputSchema: {
|
|
175
|
-
type: 'object',
|
|
176
|
-
properties: {
|
|
177
|
-
path: {
|
|
178
|
-
type: 'string',
|
|
179
|
-
description: 'Absolute path to the directory to search in (e.g., "/Users/username/projects/myproject").',
|
|
180
|
-
},
|
|
181
|
-
pattern: {
|
|
182
|
-
type: 'string',
|
|
183
|
-
description: 'The ast-grep pattern to search for. Examples: "fn $NAME($$$PARAMS) $$$BODY" for Rust functions, "def $NAME($$$PARAMS): $$$BODY" for Python functions.',
|
|
184
|
-
},
|
|
185
|
-
language: {
|
|
186
|
-
type: 'string',
|
|
187
|
-
description: 'The programming language to search in. If not specified, the tool will try to infer the language from file extensions. Supported languages: rust, javascript, typescript, python, go, c, cpp, java, ruby, php, swift, csharp, yaml.',
|
|
188
|
-
},
|
|
189
|
-
ignore: {
|
|
190
|
-
type: 'array',
|
|
191
|
-
items: { type: 'string' },
|
|
192
|
-
description: 'Custom patterns to ignore (in addition to common patterns)',
|
|
193
|
-
},
|
|
194
|
-
maxResults: {
|
|
195
|
-
type: 'number',
|
|
196
|
-
description: 'Maximum number of results to return'
|
|
197
|
-
},
|
|
198
|
-
format: {
|
|
199
|
-
type: 'string',
|
|
200
|
-
enum: ['markdown', 'plain', 'json', 'color'],
|
|
201
|
-
description: 'Output format for the query results'
|
|
202
|
-
},
|
|
203
|
-
timeout: {
|
|
204
|
-
type: 'number',
|
|
205
|
-
description: 'Timeout for the query operation in seconds (default: 30)',
|
|
206
|
-
},
|
|
207
|
-
noGitignore: {
|
|
208
|
-
type: 'boolean',
|
|
209
|
-
description: 'Skip .gitignore files (will use PROBE_NO_GITIGNORE environment variable if not set)',
|
|
210
|
-
}
|
|
211
|
-
},
|
|
212
|
-
required: ['path', 'pattern']
|
|
213
|
-
},
|
|
214
|
-
},
|
|
215
155
|
{
|
|
216
156
|
name: 'extract_code',
|
|
217
157
|
description: "Extract code blocks from files based on line number, or symbol name. Fetch full file when line number is not provided.",
|
|
@@ -229,22 +169,8 @@ class ProbeServer {
|
|
|
229
169
|
},
|
|
230
170
|
allowTests: {
|
|
231
171
|
type: 'boolean',
|
|
232
|
-
description: 'Allow test files and test code blocks in results (
|
|
233
|
-
|
|
234
|
-
contextLines: {
|
|
235
|
-
type: 'number',
|
|
236
|
-
description: 'Number of context lines to include before and after the extracted block when AST parsing fails to find a suitable node',
|
|
237
|
-
default: 0
|
|
238
|
-
},
|
|
239
|
-
format: {
|
|
240
|
-
type: 'string',
|
|
241
|
-
enum: ['markdown', 'plain', 'json'],
|
|
242
|
-
description: 'Output format for the extracted code',
|
|
243
|
-
default: 'markdown'
|
|
244
|
-
},
|
|
245
|
-
timeout: {
|
|
246
|
-
type: 'number',
|
|
247
|
-
description: 'Timeout for the extract operation in seconds (default: 30)',
|
|
172
|
+
description: 'Allow test files and test code blocks in results (enabled by default)',
|
|
173
|
+
default: true
|
|
248
174
|
},
|
|
249
175
|
noGitignore: {
|
|
250
176
|
type: 'boolean',
|
|
@@ -257,8 +183,8 @@ class ProbeServer {
|
|
|
257
183
|
],
|
|
258
184
|
}));
|
|
259
185
|
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
260
|
-
if (request.params.name !== 'search_code' && request.params.name !== '
|
|
261
|
-
request.params.name !== 'probe' && request.params.name !== '
|
|
186
|
+
if (request.params.name !== 'search_code' && request.params.name !== 'extract_code' &&
|
|
187
|
+
request.params.name !== 'probe' && request.params.name !== 'extract') {
|
|
262
188
|
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${request.params.name}`);
|
|
263
189
|
}
|
|
264
190
|
try {
|
|
@@ -282,10 +208,6 @@ class ProbeServer {
|
|
|
282
208
|
}
|
|
283
209
|
result = await this.executeCodeSearch(args);
|
|
284
210
|
}
|
|
285
|
-
else if (request.params.name === 'query_code' || request.params.name === 'query') {
|
|
286
|
-
const args = request.params.arguments;
|
|
287
|
-
result = await this.executeCodeQuery(args);
|
|
288
|
-
}
|
|
289
211
|
else { // extract_code or extract
|
|
290
212
|
const args = request.params.arguments;
|
|
291
213
|
result = await this.executeCodeExtract(args);
|
|
@@ -331,20 +253,19 @@ class ProbeServer {
|
|
|
331
253
|
query: args.query
|
|
332
254
|
};
|
|
333
255
|
// Add optional parameters only if they exist
|
|
334
|
-
if (args.filesOnly !== undefined)
|
|
335
|
-
options.filesOnly = args.filesOnly;
|
|
336
|
-
if (args.ignore !== undefined)
|
|
337
|
-
options.ignore = args.ignore;
|
|
338
|
-
if (args.excludeFilenames !== undefined)
|
|
339
|
-
options.excludeFilenames = args.excludeFilenames;
|
|
340
256
|
if (args.exact !== undefined)
|
|
341
257
|
options.exact = args.exact;
|
|
342
258
|
if (args.maxResults !== undefined)
|
|
343
259
|
options.maxResults = args.maxResults;
|
|
344
260
|
if (args.maxTokens !== undefined)
|
|
345
261
|
options.maxTokens = args.maxTokens;
|
|
346
|
-
|
|
262
|
+
// Set allowTests to true by default if not specified
|
|
263
|
+
if (args.allowTests !== undefined) {
|
|
347
264
|
options.allowTests = args.allowTests;
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
options.allowTests = true;
|
|
268
|
+
}
|
|
348
269
|
// Use noGitignore from args, or fall back to PROBE_NO_GITIGNORE environment variable
|
|
349
270
|
if (args.noGitignore !== undefined) {
|
|
350
271
|
options.noGitignore = args.noGitignore;
|
|
@@ -358,13 +279,6 @@ class ProbeServer {
|
|
|
358
279
|
else {
|
|
359
280
|
options.session = "new";
|
|
360
281
|
}
|
|
361
|
-
// Use timeout from args, or fall back to instance default
|
|
362
|
-
if (args.timeout !== undefined) {
|
|
363
|
-
options.timeout = args.timeout;
|
|
364
|
-
}
|
|
365
|
-
else if (this.defaultTimeout !== undefined) {
|
|
366
|
-
options.timeout = this.defaultTimeout;
|
|
367
|
-
}
|
|
368
282
|
// Handle format options
|
|
369
283
|
if (this.defaultFormat === 'outline-xml') {
|
|
370
284
|
// For outline-xml format, we pass it as a format flag to the search command
|
|
@@ -394,45 +308,6 @@ class ProbeServer {
|
|
|
394
308
|
throw new McpError('MethodNotFound', `Error executing code search: ${error.message || String(error)}`);
|
|
395
309
|
}
|
|
396
310
|
}
|
|
397
|
-
async executeCodeQuery(args) {
|
|
398
|
-
try {
|
|
399
|
-
// Validate required parameters
|
|
400
|
-
if (!args.path) {
|
|
401
|
-
throw new Error("Path is required");
|
|
402
|
-
}
|
|
403
|
-
if (!args.pattern) {
|
|
404
|
-
throw new Error("Pattern is required");
|
|
405
|
-
}
|
|
406
|
-
// Create a single options object with both pattern and path
|
|
407
|
-
const options = {
|
|
408
|
-
path: args.path,
|
|
409
|
-
pattern: args.pattern,
|
|
410
|
-
language: args.language,
|
|
411
|
-
ignore: args.ignore,
|
|
412
|
-
allowTests: args.allowTests,
|
|
413
|
-
maxResults: args.maxResults,
|
|
414
|
-
format: args.format,
|
|
415
|
-
timeout: args.timeout || this.defaultTimeout
|
|
416
|
-
};
|
|
417
|
-
// Use noGitignore from args, or fall back to PROBE_NO_GITIGNORE environment variable
|
|
418
|
-
if (args.noGitignore !== undefined) {
|
|
419
|
-
options.noGitignore = args.noGitignore;
|
|
420
|
-
}
|
|
421
|
-
else if (process.env.PROBE_NO_GITIGNORE) {
|
|
422
|
-
options.noGitignore = process.env.PROBE_NO_GITIGNORE === 'true';
|
|
423
|
-
}
|
|
424
|
-
console.log("Executing query with options:", JSON.stringify({
|
|
425
|
-
path: options.path,
|
|
426
|
-
pattern: options.pattern
|
|
427
|
-
}));
|
|
428
|
-
const result = await query(options);
|
|
429
|
-
return result;
|
|
430
|
-
}
|
|
431
|
-
catch (error) {
|
|
432
|
-
console.error('Error executing code query:', error);
|
|
433
|
-
throw new McpError('MethodNotFound', `Error executing code query: ${error.message || String(error)}`);
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
311
|
async executeCodeExtract(args) {
|
|
437
312
|
try {
|
|
438
313
|
// Validate required parameters
|
|
@@ -446,11 +321,15 @@ class ProbeServer {
|
|
|
446
321
|
const options = {
|
|
447
322
|
files: args.files,
|
|
448
323
|
path: args.path,
|
|
449
|
-
|
|
450
|
-
contextLines: args.contextLines,
|
|
451
|
-
format: args.format,
|
|
452
|
-
timeout: args.timeout || this.defaultTimeout
|
|
324
|
+
format: 'xml'
|
|
453
325
|
};
|
|
326
|
+
// Set allowTests to true by default if not specified
|
|
327
|
+
if (args.allowTests !== undefined) {
|
|
328
|
+
options.allowTests = args.allowTests;
|
|
329
|
+
}
|
|
330
|
+
else {
|
|
331
|
+
options.allowTests = true;
|
|
332
|
+
}
|
|
454
333
|
// Use noGitignore from args, or fall back to PROBE_NO_GITIGNORE environment variable
|
|
455
334
|
if (args.noGitignore !== undefined) {
|
|
456
335
|
options.noGitignore = args.noGitignore;
|
package/build/mcp/index.ts
CHANGED
|
@@ -112,37 +112,19 @@ console.log(`Bin directory: ${binDir}`);
|
|
|
112
112
|
interface SearchCodeArgs {
|
|
113
113
|
path: string;
|
|
114
114
|
query: string | string[];
|
|
115
|
-
filesOnly?: boolean;
|
|
116
|
-
ignore?: string[];
|
|
117
|
-
excludeFilenames?: boolean;
|
|
118
115
|
exact?: boolean;
|
|
119
116
|
maxResults?: number;
|
|
120
117
|
maxTokens?: number;
|
|
121
118
|
allowTests?: boolean;
|
|
122
119
|
session?: string;
|
|
123
|
-
timeout?: number;
|
|
124
120
|
noGitignore?: boolean;
|
|
125
121
|
}
|
|
126
122
|
|
|
127
|
-
interface QueryCodeArgs {
|
|
128
|
-
path: string;
|
|
129
|
-
pattern: string;
|
|
130
|
-
language?: string;
|
|
131
|
-
ignore?: string[];
|
|
132
|
-
allowTests?: boolean;
|
|
133
|
-
maxResults?: number;
|
|
134
|
-
format?: 'markdown' | 'plain' | 'json' | 'color';
|
|
135
|
-
timeout?: number;
|
|
136
|
-
noGitignore?: boolean;
|
|
137
|
-
}
|
|
138
123
|
|
|
139
124
|
interface ExtractCodeArgs {
|
|
140
125
|
path: string;
|
|
141
126
|
files: string[];
|
|
142
127
|
allowTests?: boolean;
|
|
143
|
-
contextLines?: number;
|
|
144
|
-
format?: 'markdown' | 'plain' | 'json';
|
|
145
|
-
timeout?: number;
|
|
146
128
|
noGitignore?: boolean;
|
|
147
129
|
}
|
|
148
130
|
|
|
@@ -193,38 +175,22 @@ class ProbeServer {
|
|
|
193
175
|
},
|
|
194
176
|
query: {
|
|
195
177
|
type: 'string',
|
|
196
|
-
description: 'Elastic search query. Supports logical operators (AND, OR, NOT), and grouping with parentheses. Examples: "config", "(term1 OR term2) AND term3"
|
|
197
|
-
},
|
|
198
|
-
filesOnly: {
|
|
199
|
-
type: 'boolean',
|
|
200
|
-
description: 'Skip AST parsing and just output unique files',
|
|
201
|
-
},
|
|
202
|
-
ignore: {
|
|
203
|
-
type: 'array',
|
|
204
|
-
items: { type: 'string' },
|
|
205
|
-
description: 'Custom patterns to ignore (in addition to .gitignore and common patterns)'
|
|
206
|
-
},
|
|
207
|
-
excludeFilenames: {
|
|
208
|
-
type: 'boolean',
|
|
209
|
-
description: 'Exclude filenames from being used for matching'
|
|
178
|
+
description: 'Elastic search query. Supports logical operators (AND, OR, NOT), and grouping with parentheses. For exact matches of specific identifiers, use quotes: "MyFunction", "SpecificStruct", "exact_variable_name". Examples: "config", "(term1 OR term2) AND term3", "getUserData", "struct Config".',
|
|
210
179
|
},
|
|
211
180
|
exact: {
|
|
212
181
|
type: 'boolean',
|
|
213
|
-
description: '
|
|
182
|
+
description: 'When you exactly know what you are looking for, like known function name, struct name, or variable name, set this flag for precise matching'
|
|
214
183
|
},
|
|
215
184
|
allowTests: {
|
|
216
185
|
type: 'boolean',
|
|
217
|
-
description: 'Allow test files and test code blocks in results (
|
|
186
|
+
description: 'Allow test files and test code blocks in results (enabled by default)',
|
|
187
|
+
default: true
|
|
218
188
|
},
|
|
219
189
|
session: {
|
|
220
190
|
type: 'string',
|
|
221
191
|
description: 'Session identifier for caching. Set to "new" if unknown, or want to reset cache. Re-use session ID returned from previous searches',
|
|
222
192
|
default: "new",
|
|
223
193
|
},
|
|
224
|
-
timeout: {
|
|
225
|
-
type: 'number',
|
|
226
|
-
description: 'Timeout for the search operation in seconds (default: 30)',
|
|
227
|
-
},
|
|
228
194
|
noGitignore: {
|
|
229
195
|
type: 'boolean',
|
|
230
196
|
description: 'Skip .gitignore files (will use PROBE_NO_GITIGNORE environment variable if not set)',
|
|
@@ -233,50 +199,6 @@ class ProbeServer {
|
|
|
233
199
|
required: ['path', 'query']
|
|
234
200
|
},
|
|
235
201
|
},
|
|
236
|
-
{
|
|
237
|
-
name: 'query_code',
|
|
238
|
-
description: "Search code using ast-grep structural pattern matching. Use this tool to find specific code structures like functions, classes, or methods.",
|
|
239
|
-
inputSchema: {
|
|
240
|
-
type: 'object',
|
|
241
|
-
properties: {
|
|
242
|
-
path: {
|
|
243
|
-
type: 'string',
|
|
244
|
-
description: 'Absolute path to the directory to search in (e.g., "/Users/username/projects/myproject").',
|
|
245
|
-
},
|
|
246
|
-
pattern: {
|
|
247
|
-
type: 'string',
|
|
248
|
-
description: 'The ast-grep pattern to search for. Examples: "fn $NAME($$$PARAMS) $$$BODY" for Rust functions, "def $NAME($$$PARAMS): $$$BODY" for Python functions.',
|
|
249
|
-
},
|
|
250
|
-
language: {
|
|
251
|
-
type: 'string',
|
|
252
|
-
description: 'The programming language to search in. If not specified, the tool will try to infer the language from file extensions. Supported languages: rust, javascript, typescript, python, go, c, cpp, java, ruby, php, swift, csharp, yaml.',
|
|
253
|
-
},
|
|
254
|
-
ignore: {
|
|
255
|
-
type: 'array',
|
|
256
|
-
items: { type: 'string' },
|
|
257
|
-
description: 'Custom patterns to ignore (in addition to common patterns)',
|
|
258
|
-
},
|
|
259
|
-
maxResults: {
|
|
260
|
-
type: 'number',
|
|
261
|
-
description: 'Maximum number of results to return'
|
|
262
|
-
},
|
|
263
|
-
format: {
|
|
264
|
-
type: 'string',
|
|
265
|
-
enum: ['markdown', 'plain', 'json', 'color'],
|
|
266
|
-
description: 'Output format for the query results'
|
|
267
|
-
},
|
|
268
|
-
timeout: {
|
|
269
|
-
type: 'number',
|
|
270
|
-
description: 'Timeout for the query operation in seconds (default: 30)',
|
|
271
|
-
},
|
|
272
|
-
noGitignore: {
|
|
273
|
-
type: 'boolean',
|
|
274
|
-
description: 'Skip .gitignore files (will use PROBE_NO_GITIGNORE environment variable if not set)',
|
|
275
|
-
}
|
|
276
|
-
},
|
|
277
|
-
required: ['path', 'pattern']
|
|
278
|
-
},
|
|
279
|
-
},
|
|
280
202
|
{
|
|
281
203
|
name: 'extract_code',
|
|
282
204
|
description: "Extract code blocks from files based on line number, or symbol name. Fetch full file when line number is not provided.",
|
|
@@ -294,22 +216,8 @@ class ProbeServer {
|
|
|
294
216
|
},
|
|
295
217
|
allowTests: {
|
|
296
218
|
type: 'boolean',
|
|
297
|
-
description: 'Allow test files and test code blocks in results (
|
|
298
|
-
|
|
299
|
-
contextLines: {
|
|
300
|
-
type: 'number',
|
|
301
|
-
description: 'Number of context lines to include before and after the extracted block when AST parsing fails to find a suitable node',
|
|
302
|
-
default: 0
|
|
303
|
-
},
|
|
304
|
-
format: {
|
|
305
|
-
type: 'string',
|
|
306
|
-
enum: ['markdown', 'plain', 'json'],
|
|
307
|
-
description: 'Output format for the extracted code',
|
|
308
|
-
default: 'markdown'
|
|
309
|
-
},
|
|
310
|
-
timeout: {
|
|
311
|
-
type: 'number',
|
|
312
|
-
description: 'Timeout for the extract operation in seconds (default: 30)',
|
|
219
|
+
description: 'Allow test files and test code blocks in results (enabled by default)',
|
|
220
|
+
default: true
|
|
313
221
|
},
|
|
314
222
|
noGitignore: {
|
|
315
223
|
type: 'boolean',
|
|
@@ -323,8 +231,8 @@ class ProbeServer {
|
|
|
323
231
|
}));
|
|
324
232
|
|
|
325
233
|
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
326
|
-
if (request.params.name !== 'search_code' && request.params.name !== '
|
|
327
|
-
request.params.name !== 'probe' && request.params.name !== '
|
|
234
|
+
if (request.params.name !== 'search_code' && request.params.name !== 'extract_code' &&
|
|
235
|
+
request.params.name !== 'probe' && request.params.name !== 'extract') {
|
|
328
236
|
throw new McpError(
|
|
329
237
|
ErrorCode.MethodNotFound,
|
|
330
238
|
`Unknown tool: ${request.params.name}`
|
|
@@ -356,9 +264,6 @@ class ProbeServer {
|
|
|
356
264
|
}
|
|
357
265
|
|
|
358
266
|
result = await this.executeCodeSearch(args);
|
|
359
|
-
} else if (request.params.name === 'query_code' || request.params.name === 'query') {
|
|
360
|
-
const args = request.params.arguments as unknown as QueryCodeArgs;
|
|
361
|
-
result = await this.executeCodeQuery(args);
|
|
362
267
|
} else { // extract_code or extract
|
|
363
268
|
const args = request.params.arguments as unknown as ExtractCodeArgs;
|
|
364
269
|
result = await this.executeCodeExtract(args);
|
|
@@ -409,13 +314,15 @@ class ProbeServer {
|
|
|
409
314
|
};
|
|
410
315
|
|
|
411
316
|
// Add optional parameters only if they exist
|
|
412
|
-
if (args.filesOnly !== undefined) options.filesOnly = args.filesOnly;
|
|
413
|
-
if (args.ignore !== undefined) options.ignore = args.ignore;
|
|
414
|
-
if (args.excludeFilenames !== undefined) options.excludeFilenames = args.excludeFilenames;
|
|
415
317
|
if (args.exact !== undefined) options.exact = args.exact;
|
|
416
318
|
if (args.maxResults !== undefined) options.maxResults = args.maxResults;
|
|
417
319
|
if (args.maxTokens !== undefined) options.maxTokens = args.maxTokens;
|
|
418
|
-
|
|
320
|
+
// Set allowTests to true by default if not specified
|
|
321
|
+
if (args.allowTests !== undefined) {
|
|
322
|
+
options.allowTests = args.allowTests;
|
|
323
|
+
} else {
|
|
324
|
+
options.allowTests = true;
|
|
325
|
+
}
|
|
419
326
|
// Use noGitignore from args, or fall back to PROBE_NO_GITIGNORE environment variable
|
|
420
327
|
if (args.noGitignore !== undefined) {
|
|
421
328
|
options.noGitignore = args.noGitignore;
|
|
@@ -427,12 +334,6 @@ class ProbeServer {
|
|
|
427
334
|
} else {
|
|
428
335
|
options.session = "new";
|
|
429
336
|
}
|
|
430
|
-
// Use timeout from args, or fall back to instance default
|
|
431
|
-
if (args.timeout !== undefined) {
|
|
432
|
-
options.timeout = args.timeout;
|
|
433
|
-
} else if (this.defaultTimeout !== undefined) {
|
|
434
|
-
options.timeout = this.defaultTimeout;
|
|
435
|
-
}
|
|
436
337
|
|
|
437
338
|
// Handle format options
|
|
438
339
|
if (this.defaultFormat === 'outline-xml') {
|
|
@@ -467,50 +368,6 @@ class ProbeServer {
|
|
|
467
368
|
}
|
|
468
369
|
}
|
|
469
370
|
|
|
470
|
-
private async executeCodeQuery(args: QueryCodeArgs): Promise<string> {
|
|
471
|
-
try {
|
|
472
|
-
// Validate required parameters
|
|
473
|
-
if (!args.path) {
|
|
474
|
-
throw new Error("Path is required");
|
|
475
|
-
}
|
|
476
|
-
if (!args.pattern) {
|
|
477
|
-
throw new Error("Pattern is required");
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
// Create a single options object with both pattern and path
|
|
481
|
-
const options: any = {
|
|
482
|
-
path: args.path,
|
|
483
|
-
pattern: args.pattern,
|
|
484
|
-
language: args.language,
|
|
485
|
-
ignore: args.ignore,
|
|
486
|
-
allowTests: args.allowTests,
|
|
487
|
-
maxResults: args.maxResults,
|
|
488
|
-
format: args.format,
|
|
489
|
-
timeout: args.timeout || this.defaultTimeout
|
|
490
|
-
};
|
|
491
|
-
|
|
492
|
-
// Use noGitignore from args, or fall back to PROBE_NO_GITIGNORE environment variable
|
|
493
|
-
if (args.noGitignore !== undefined) {
|
|
494
|
-
options.noGitignore = args.noGitignore;
|
|
495
|
-
} else if (process.env.PROBE_NO_GITIGNORE) {
|
|
496
|
-
options.noGitignore = process.env.PROBE_NO_GITIGNORE === 'true';
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
console.log("Executing query with options:", JSON.stringify({
|
|
500
|
-
path: options.path,
|
|
501
|
-
pattern: options.pattern
|
|
502
|
-
}));
|
|
503
|
-
|
|
504
|
-
const result = await query(options);
|
|
505
|
-
return result;
|
|
506
|
-
} catch (error: any) {
|
|
507
|
-
console.error('Error executing code query:', error);
|
|
508
|
-
throw new McpError(
|
|
509
|
-
'MethodNotFound' as unknown as ErrorCode,
|
|
510
|
-
`Error executing code query: ${error.message || String(error)}`
|
|
511
|
-
);
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
371
|
|
|
515
372
|
private async executeCodeExtract(args: ExtractCodeArgs): Promise<string> {
|
|
516
373
|
try {
|
|
@@ -526,12 +383,16 @@ class ProbeServer {
|
|
|
526
383
|
const options: any = {
|
|
527
384
|
files: args.files,
|
|
528
385
|
path: args.path,
|
|
529
|
-
|
|
530
|
-
contextLines: args.contextLines,
|
|
531
|
-
format: args.format,
|
|
532
|
-
timeout: args.timeout || this.defaultTimeout
|
|
386
|
+
format: 'xml'
|
|
533
387
|
};
|
|
534
388
|
|
|
389
|
+
// Set allowTests to true by default if not specified
|
|
390
|
+
if (args.allowTests !== undefined) {
|
|
391
|
+
options.allowTests = args.allowTests;
|
|
392
|
+
} else {
|
|
393
|
+
options.allowTests = true;
|
|
394
|
+
}
|
|
395
|
+
|
|
535
396
|
// Use noGitignore from args, or fall back to PROBE_NO_GITIGNORE environment variable
|
|
536
397
|
if (args.noGitignore !== undefined) {
|
|
537
398
|
options.noGitignore = args.noGitignore;
|
package/build/utils.js
CHANGED
|
@@ -43,7 +43,7 @@ export async function getBinaryPath(options = {}) {
|
|
|
43
43
|
|
|
44
44
|
// Check bin directory
|
|
45
45
|
const isWindows = process.platform === 'win32';
|
|
46
|
-
const binaryName = isWindows ? 'probe.exe' : 'probe';
|
|
46
|
+
const binaryName = isWindows ? 'probe.exe' : 'probe-binary';
|
|
47
47
|
const binaryPath = path.join(binDir, binaryName);
|
|
48
48
|
|
|
49
49
|
if (fs.existsSync(binaryPath) && !forceDownload) {
|