@probelabs/probe 0.6.0-rc121 → 0.6.0-rc123
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 +36 -2
- package/build/agent/ProbeAgent.js +50 -14
- package/build/agent/index.js +18689 -33570
- package/build/agent/mcp/client.js +69 -15
- package/build/agent/mcp/config.js +8 -8
- package/build/agent/mcp/xmlBridge.js +52 -7
- package/build/agent/schemaUtils.js +28 -15
- package/build/grep.js +152 -0
- package/build/index.js +2 -0
- package/build/mcp/index.js +105 -87
- package/build/mcp/index.ts +120 -97
- package/cjs/agent/ProbeAgent.cjs +13913 -2703
- package/cjs/index.cjs +23926 -12658
- package/index.d.ts +45 -0
- package/package.json +2 -2
- package/src/agent/ProbeAgent.js +50 -14
- package/src/agent/index.js +1 -1
- package/src/agent/mcp/client.js +69 -15
- package/src/agent/mcp/config.js +8 -8
- package/src/agent/mcp/xmlBridge.js +52 -7
- package/src/agent/schemaUtils.js +28 -15
- package/src/grep.js +152 -0
- package/src/index.js +2 -0
- package/src/mcp/index.ts +120 -97
package/index.d.ts
CHANGED
|
@@ -303,6 +303,51 @@ export declare function query(
|
|
|
303
303
|
}
|
|
304
304
|
): Promise<any>;
|
|
305
305
|
|
|
306
|
+
/**
|
|
307
|
+
* Standard grep-style search that works across multiple operating systems
|
|
308
|
+
*
|
|
309
|
+
* Use this for searching non-code files (logs, config files, text files, etc.)
|
|
310
|
+
* that are not supported by probe's semantic search. For code files, prefer
|
|
311
|
+
* using the search() function which provides AST-aware semantic search.
|
|
312
|
+
*
|
|
313
|
+
* @param options - Grep options
|
|
314
|
+
* @param options.pattern - Regular expression pattern to search for
|
|
315
|
+
* @param options.paths - Path or array of paths to search in
|
|
316
|
+
* @param options.ignoreCase - Case-insensitive search (-i flag)
|
|
317
|
+
* @param options.lineNumbers - Show line numbers in output (-n flag)
|
|
318
|
+
* @param options.count - Only show count of matches per file (-c flag)
|
|
319
|
+
* @param options.filesWithMatches - Only show filenames that contain matches (-l flag)
|
|
320
|
+
* @param options.filesWithoutMatches - Only show filenames that do not contain matches (-L flag)
|
|
321
|
+
* @param options.invertMatch - Invert match: show lines that do NOT match (-v flag)
|
|
322
|
+
* @param options.beforeContext - Number of lines of context before match (-B flag)
|
|
323
|
+
* @param options.afterContext - Number of lines of context after match (-A flag)
|
|
324
|
+
* @param options.context - Number of lines of context before and after match (-C flag)
|
|
325
|
+
* @param options.noGitignore - Do not respect .gitignore files (--no-gitignore flag)
|
|
326
|
+
* @param options.color - Colorize output: 'always', 'never', 'auto' (--color flag)
|
|
327
|
+
* @param options.maxCount - Stop reading a file after N matching lines (-m flag)
|
|
328
|
+
* @returns Promise resolving to grep results as string
|
|
329
|
+
*/
|
|
330
|
+
export declare function grep(options: {
|
|
331
|
+
pattern: string;
|
|
332
|
+
paths: string | string[];
|
|
333
|
+
ignoreCase?: boolean;
|
|
334
|
+
lineNumbers?: boolean;
|
|
335
|
+
count?: boolean;
|
|
336
|
+
filesWithMatches?: boolean;
|
|
337
|
+
filesWithoutMatches?: boolean;
|
|
338
|
+
invertMatch?: boolean;
|
|
339
|
+
beforeContext?: number;
|
|
340
|
+
afterContext?: number;
|
|
341
|
+
context?: number;
|
|
342
|
+
noGitignore?: boolean;
|
|
343
|
+
color?: 'always' | 'never' | 'auto';
|
|
344
|
+
maxCount?: number;
|
|
345
|
+
binaryOptions?: {
|
|
346
|
+
forceDownload?: boolean;
|
|
347
|
+
version?: string;
|
|
348
|
+
};
|
|
349
|
+
}): Promise<string>;
|
|
350
|
+
|
|
306
351
|
/**
|
|
307
352
|
* Extract code blocks from files
|
|
308
353
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@probelabs/probe",
|
|
3
|
-
"version": "0.6.0-
|
|
3
|
+
"version": "0.6.0-rc123",
|
|
4
4
|
"description": "Node.js wrapper for the probe code search tool",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"module": "src/index.js",
|
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
"@opentelemetry/sdk-node": "^0.203.0",
|
|
81
81
|
"@opentelemetry/sdk-trace-base": "^1.30.0",
|
|
82
82
|
"@opentelemetry/semantic-conventions": "^1.36.0",
|
|
83
|
-
"@probelabs/maid": "^0.0.
|
|
83
|
+
"@probelabs/maid": "^0.0.9",
|
|
84
84
|
"ai": "^5.0.0",
|
|
85
85
|
"axios": "^1.8.3",
|
|
86
86
|
"fs-extra": "^11.1.1",
|
package/src/agent/ProbeAgent.js
CHANGED
|
@@ -141,6 +141,7 @@ export class ProbeAgent {
|
|
|
141
141
|
this.mcpConfig = options.mcpConfig || null;
|
|
142
142
|
this.mcpServers = options.mcpServers || null; // Deprecated, keep for backward compatibility
|
|
143
143
|
this.mcpBridge = null;
|
|
144
|
+
this._mcpInitialized = false; // Track if MCP initialization has been attempted
|
|
144
145
|
|
|
145
146
|
// Initialize the AI model
|
|
146
147
|
this.initializeModel();
|
|
@@ -154,8 +155,9 @@ export class ProbeAgent {
|
|
|
154
155
|
* This method initializes MCP and merges MCP tools into the tool list
|
|
155
156
|
*/
|
|
156
157
|
async initialize() {
|
|
157
|
-
// Initialize MCP if enabled
|
|
158
|
-
if (this.enableMcp) {
|
|
158
|
+
// Initialize MCP if enabled and not already initialized
|
|
159
|
+
if (this.enableMcp && !this._mcpInitialized) {
|
|
160
|
+
this._mcpInitialized = true; // Prevent multiple initialization attempts
|
|
159
161
|
try {
|
|
160
162
|
await this.initializeMCP();
|
|
161
163
|
|
|
@@ -184,7 +186,10 @@ export class ProbeAgent {
|
|
|
184
186
|
console.error('[DEBUG] ========================================\n');
|
|
185
187
|
}
|
|
186
188
|
} catch (error) {
|
|
187
|
-
console.error('[MCP] Failed to initialize MCP:', error);
|
|
189
|
+
console.error('[MCP ERROR] Failed to initialize MCP:', error.message);
|
|
190
|
+
if (this.debug) {
|
|
191
|
+
console.error('[MCP DEBUG] Full error details:', error);
|
|
192
|
+
}
|
|
188
193
|
this.mcpBridge = null;
|
|
189
194
|
}
|
|
190
195
|
}
|
|
@@ -742,14 +747,14 @@ export class ProbeAgent {
|
|
|
742
747
|
// Direct config object provided (SDK usage)
|
|
743
748
|
mcpConfig = this.mcpConfig;
|
|
744
749
|
if (this.debug) {
|
|
745
|
-
console.
|
|
750
|
+
console.error('[MCP DEBUG] Using provided MCP config object');
|
|
746
751
|
}
|
|
747
752
|
} else if (this.mcpConfigPath) {
|
|
748
753
|
// Explicit config path provided
|
|
749
754
|
try {
|
|
750
755
|
mcpConfig = loadMCPConfigurationFromPath(this.mcpConfigPath);
|
|
751
756
|
if (this.debug) {
|
|
752
|
-
console.
|
|
757
|
+
console.error(`[MCP DEBUG] Loaded MCP config from: ${this.mcpConfigPath}`);
|
|
753
758
|
}
|
|
754
759
|
} catch (error) {
|
|
755
760
|
throw new Error(`Failed to load MCP config from ${this.mcpConfigPath}: ${error.message}`);
|
|
@@ -758,10 +763,17 @@ export class ProbeAgent {
|
|
|
758
763
|
// Backward compatibility: convert old mcpServers format
|
|
759
764
|
mcpConfig = { mcpServers: this.mcpServers };
|
|
760
765
|
if (this.debug) {
|
|
761
|
-
console.
|
|
766
|
+
console.error('[MCP DEBUG] Using deprecated mcpServers option. Consider using mcpConfig instead.');
|
|
762
767
|
}
|
|
768
|
+
} else {
|
|
769
|
+
// No explicit config provided - will attempt auto-discovery
|
|
770
|
+
// This is important for CLI usage where config files may exist
|
|
771
|
+
if (this.debug) {
|
|
772
|
+
console.error('[MCP DEBUG] No explicit MCP config provided, will attempt auto-discovery');
|
|
773
|
+
}
|
|
774
|
+
// Pass null to trigger auto-discovery in MCPXmlBridge
|
|
775
|
+
mcpConfig = null;
|
|
763
776
|
}
|
|
764
|
-
// Note: auto-discovery fallback is removed - user must explicitly provide config
|
|
765
777
|
|
|
766
778
|
// Initialize the MCP XML bridge
|
|
767
779
|
this.mcpBridge = new MCPXmlBridge({ debug: this.debug });
|
|
@@ -771,24 +783,27 @@ export class ProbeAgent {
|
|
|
771
783
|
const mcpToolCount = mcpToolNames.length;
|
|
772
784
|
if (mcpToolCount > 0) {
|
|
773
785
|
if (this.debug) {
|
|
774
|
-
console.error('\n[DEBUG] ========================================');
|
|
775
|
-
console.error(`[DEBUG] MCP Tools Initialized (${mcpToolCount} tools)`);
|
|
776
|
-
console.error('[DEBUG] Available MCP tools:');
|
|
786
|
+
console.error('\n[MCP DEBUG] ========================================');
|
|
787
|
+
console.error(`[MCP DEBUG] MCP Tools Initialized (${mcpToolCount} tools)`);
|
|
788
|
+
console.error('[MCP DEBUG] Available MCP tools:');
|
|
777
789
|
for (const toolName of mcpToolNames) {
|
|
778
|
-
console.error(`[DEBUG] - ${toolName}`);
|
|
790
|
+
console.error(`[MCP DEBUG] - ${toolName}`);
|
|
779
791
|
}
|
|
780
|
-
console.error('[DEBUG] ========================================\n');
|
|
792
|
+
console.error('[MCP DEBUG] ========================================\n');
|
|
781
793
|
}
|
|
782
794
|
} else {
|
|
783
795
|
// For backward compatibility: if no tools were loaded, set bridge to null
|
|
784
796
|
// This maintains the behavior expected by existing tests
|
|
785
797
|
if (this.debug) {
|
|
786
|
-
console.error('[DEBUG] No MCP tools loaded, setting bridge to null');
|
|
798
|
+
console.error('[MCP DEBUG] No MCP tools loaded, setting bridge to null');
|
|
787
799
|
}
|
|
788
800
|
this.mcpBridge = null;
|
|
789
801
|
}
|
|
790
802
|
} catch (error) {
|
|
791
|
-
console.error('[MCP] Error initializing MCP:', error);
|
|
803
|
+
console.error('[MCP ERROR] Error initializing MCP:', error.message);
|
|
804
|
+
if (this.debug) {
|
|
805
|
+
console.error('[MCP DEBUG] Full error details:', error);
|
|
806
|
+
}
|
|
792
807
|
this.mcpBridge = null;
|
|
793
808
|
}
|
|
794
809
|
}
|
|
@@ -797,6 +812,27 @@ export class ProbeAgent {
|
|
|
797
812
|
* Get the system message with instructions for the AI (XML Tool Format)
|
|
798
813
|
*/
|
|
799
814
|
async getSystemMessage() {
|
|
815
|
+
// Lazy initialize MCP if enabled but not yet initialized
|
|
816
|
+
if (this.enableMcp && !this.mcpBridge && !this._mcpInitialized) {
|
|
817
|
+
this._mcpInitialized = true; // Prevent multiple initialization attempts
|
|
818
|
+
try {
|
|
819
|
+
await this.initializeMCP();
|
|
820
|
+
|
|
821
|
+
// Merge MCP tools into toolImplementations for unified access
|
|
822
|
+
if (this.mcpBridge) {
|
|
823
|
+
const mcpTools = this.mcpBridge.mcpTools || {};
|
|
824
|
+
for (const [toolName, toolImpl] of Object.entries(mcpTools)) {
|
|
825
|
+
this.toolImplementations[toolName] = toolImpl;
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
} catch (error) {
|
|
829
|
+
console.error('[MCP ERROR] Failed to lazy-initialize MCP:', error.message);
|
|
830
|
+
if (this.debug) {
|
|
831
|
+
console.error('[MCP DEBUG] Full error details:', error);
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
|
|
800
836
|
// Build tool definitions
|
|
801
837
|
let toolDefinitions = `
|
|
802
838
|
${searchToolDefinition}
|
package/src/agent/index.js
CHANGED
|
@@ -305,7 +305,7 @@ class ProbeAgentMcpServer {
|
|
|
305
305
|
this.agent = null;
|
|
306
306
|
|
|
307
307
|
this.setupToolHandlers();
|
|
308
|
-
this.server.onerror = (error) => console.error('[MCP
|
|
308
|
+
this.server.onerror = (error) => console.error('[MCP ERROR]', error);
|
|
309
309
|
process.on('SIGINT', async () => {
|
|
310
310
|
await this.server.close();
|
|
311
311
|
process.exit(0);
|
package/src/agent/mcp/client.js
CHANGED
|
@@ -128,14 +128,30 @@ export class MCPClientManager {
|
|
|
128
128
|
this.config = config || loadMCPConfiguration();
|
|
129
129
|
const servers = parseEnabledServers(this.config);
|
|
130
130
|
|
|
131
|
+
// Always log the number of servers found
|
|
132
|
+
console.error(`[MCP INFO] Found ${servers.length} enabled MCP server${servers.length !== 1 ? 's' : ''}`);
|
|
133
|
+
|
|
134
|
+
if (servers.length === 0) {
|
|
135
|
+
console.error('[MCP INFO] No MCP servers configured or enabled');
|
|
136
|
+
console.error('[MCP INFO] 0 MCP tools available');
|
|
137
|
+
return {
|
|
138
|
+
connected: 0,
|
|
139
|
+
total: 0,
|
|
140
|
+
tools: []
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
131
144
|
if (this.debug) {
|
|
132
|
-
console.error(
|
|
145
|
+
console.error('[MCP DEBUG] Server details:');
|
|
146
|
+
servers.forEach(server => {
|
|
147
|
+
console.error(`[MCP DEBUG] - ${server.name} (${server.transport})`);
|
|
148
|
+
});
|
|
133
149
|
}
|
|
134
150
|
|
|
135
151
|
// Connect to each enabled server
|
|
136
152
|
const connectionPromises = servers.map(server =>
|
|
137
153
|
this.connectToServer(server).catch(error => {
|
|
138
|
-
console.error(`[MCP] Failed to connect to ${server.name}:`, error.message);
|
|
154
|
+
console.error(`[MCP ERROR] Failed to connect to ${server.name}:`, error.message);
|
|
139
155
|
return null;
|
|
140
156
|
})
|
|
141
157
|
);
|
|
@@ -143,9 +159,23 @@ export class MCPClientManager {
|
|
|
143
159
|
const results = await Promise.all(connectionPromises);
|
|
144
160
|
const connectedCount = results.filter(Boolean).length;
|
|
145
161
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
console.error(`[MCP]
|
|
162
|
+
// Always log connection results
|
|
163
|
+
if (connectedCount === 0) {
|
|
164
|
+
console.error(`[MCP ERROR] Failed to connect to all ${servers.length} server${servers.length !== 1 ? 's' : ''}`);
|
|
165
|
+
console.error('[MCP INFO] 0 MCP tools available');
|
|
166
|
+
} else if (connectedCount < servers.length) {
|
|
167
|
+
console.error(`[MCP INFO] Successfully connected to ${connectedCount}/${servers.length} servers`);
|
|
168
|
+
console.error(`[MCP INFO] ${this.tools.size} MCP tool${this.tools.size !== 1 ? 's' : ''} available`);
|
|
169
|
+
} else {
|
|
170
|
+
console.error(`[MCP INFO] Successfully connected to all ${connectedCount} server${connectedCount !== 1 ? 's' : ''}`);
|
|
171
|
+
console.error(`[MCP INFO] ${this.tools.size} MCP tool${this.tools.size !== 1 ? 's' : ''} available`);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (this.debug && this.tools.size > 0) {
|
|
175
|
+
console.error('[MCP DEBUG] Available tools:');
|
|
176
|
+
Array.from(this.tools.keys()).forEach(toolName => {
|
|
177
|
+
console.error(`[MCP DEBUG] - ${toolName}`);
|
|
178
|
+
});
|
|
149
179
|
}
|
|
150
180
|
|
|
151
181
|
return {
|
|
@@ -164,7 +194,7 @@ export class MCPClientManager {
|
|
|
164
194
|
|
|
165
195
|
try {
|
|
166
196
|
if (this.debug) {
|
|
167
|
-
console.error(`[MCP] Connecting to ${name} via ${serverConfig.transport}...`);
|
|
197
|
+
console.error(`[MCP DEBUG] Connecting to ${name} via ${serverConfig.transport}...`);
|
|
168
198
|
}
|
|
169
199
|
|
|
170
200
|
// Create transport
|
|
@@ -193,6 +223,7 @@ export class MCPClientManager {
|
|
|
193
223
|
|
|
194
224
|
// Fetch and register tools
|
|
195
225
|
const toolsResponse = await client.listTools();
|
|
226
|
+
const toolCount = toolsResponse?.tools?.length || 0;
|
|
196
227
|
|
|
197
228
|
if (toolsResponse && toolsResponse.tools) {
|
|
198
229
|
for (const tool of toolsResponse.tools) {
|
|
@@ -205,18 +236,19 @@ export class MCPClientManager {
|
|
|
205
236
|
});
|
|
206
237
|
|
|
207
238
|
if (this.debug) {
|
|
208
|
-
console.error(`[MCP]
|
|
239
|
+
console.error(`[MCP DEBUG] Registered tool: ${qualifiedName}`);
|
|
209
240
|
}
|
|
210
241
|
}
|
|
211
242
|
}
|
|
212
243
|
|
|
213
|
-
|
|
214
|
-
console.error(`[MCP] Connected to ${name} with ${toolsResponse?.tools?.length || 0} tools`);
|
|
215
|
-
}
|
|
244
|
+
console.error(`[MCP INFO] Connected to ${name}: ${toolCount} tool${toolCount !== 1 ? 's' : ''} loaded`);
|
|
216
245
|
|
|
217
246
|
return true;
|
|
218
247
|
} catch (error) {
|
|
219
|
-
console.error(`[MCP] Error connecting to ${name}:`, error.message);
|
|
248
|
+
console.error(`[MCP ERROR] Error connecting to ${name}:`, error.message);
|
|
249
|
+
if (this.debug) {
|
|
250
|
+
console.error(`[MCP DEBUG] Full error details:`, error);
|
|
251
|
+
}
|
|
220
252
|
return false;
|
|
221
253
|
}
|
|
222
254
|
}
|
|
@@ -239,7 +271,7 @@ export class MCPClientManager {
|
|
|
239
271
|
|
|
240
272
|
try {
|
|
241
273
|
if (this.debug) {
|
|
242
|
-
console.error(`[MCP] Calling ${toolName} with args:`, args);
|
|
274
|
+
console.error(`[MCP DEBUG] Calling ${toolName} with args:`, JSON.stringify(args, null, 2));
|
|
243
275
|
}
|
|
244
276
|
|
|
245
277
|
// Get timeout from config (default 30 seconds)
|
|
@@ -261,9 +293,16 @@ export class MCPClientManager {
|
|
|
261
293
|
timeoutPromise
|
|
262
294
|
]);
|
|
263
295
|
|
|
296
|
+
if (this.debug) {
|
|
297
|
+
console.error(`[MCP DEBUG] Tool ${toolName} executed successfully`);
|
|
298
|
+
}
|
|
299
|
+
|
|
264
300
|
return result;
|
|
265
301
|
} catch (error) {
|
|
266
|
-
console.error(`[MCP] Error calling tool ${toolName}:`, error);
|
|
302
|
+
console.error(`[MCP ERROR] Error calling tool ${toolName}:`, error.message);
|
|
303
|
+
if (this.debug) {
|
|
304
|
+
console.error(`[MCP DEBUG] Full error details:`, error);
|
|
305
|
+
}
|
|
267
306
|
throw error;
|
|
268
307
|
}
|
|
269
308
|
}
|
|
@@ -316,16 +355,27 @@ export class MCPClientManager {
|
|
|
316
355
|
async disconnect() {
|
|
317
356
|
const disconnectPromises = [];
|
|
318
357
|
|
|
358
|
+
if (this.clients.size === 0) {
|
|
359
|
+
if (this.debug) {
|
|
360
|
+
console.error('[MCP DEBUG] No MCP clients to disconnect');
|
|
361
|
+
}
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
if (this.debug) {
|
|
366
|
+
console.error(`[MCP DEBUG] Disconnecting from ${this.clients.size} MCP server${this.clients.size !== 1 ? 's' : ''}...`);
|
|
367
|
+
}
|
|
368
|
+
|
|
319
369
|
for (const [name, clientInfo] of this.clients.entries()) {
|
|
320
370
|
disconnectPromises.push(
|
|
321
371
|
clientInfo.client.close()
|
|
322
372
|
.then(() => {
|
|
323
373
|
if (this.debug) {
|
|
324
|
-
console.error(`[MCP] Disconnected from ${name}`);
|
|
374
|
+
console.error(`[MCP DEBUG] Disconnected from ${name}`);
|
|
325
375
|
}
|
|
326
376
|
})
|
|
327
377
|
.catch(error => {
|
|
328
|
-
console.error(`[MCP] Error disconnecting from ${name}:`, error);
|
|
378
|
+
console.error(`[MCP ERROR] Error disconnecting from ${name}:`, error.message);
|
|
329
379
|
})
|
|
330
380
|
);
|
|
331
381
|
}
|
|
@@ -333,6 +383,10 @@ export class MCPClientManager {
|
|
|
333
383
|
await Promise.all(disconnectPromises);
|
|
334
384
|
this.clients.clear();
|
|
335
385
|
this.tools.clear();
|
|
386
|
+
|
|
387
|
+
if (this.debug) {
|
|
388
|
+
console.error('[MCP DEBUG] All MCP connections closed');
|
|
389
|
+
}
|
|
336
390
|
}
|
|
337
391
|
}
|
|
338
392
|
|
package/src/agent/mcp/config.js
CHANGED
|
@@ -51,8 +51,8 @@ export function loadMCPConfigurationFromPath(configPath) {
|
|
|
51
51
|
const content = readFileSync(configPath, 'utf8');
|
|
52
52
|
const config = JSON.parse(content);
|
|
53
53
|
|
|
54
|
-
if (process.env.DEBUG === '1') {
|
|
55
|
-
console.error(`[MCP] Loaded configuration from: ${configPath}`);
|
|
54
|
+
if (process.env.DEBUG === '1' || process.env.DEBUG_MCP === '1') {
|
|
55
|
+
console.error(`[MCP DEBUG] Loaded configuration from: ${configPath}`);
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
// Merge with environment variable overrides
|
|
@@ -94,12 +94,12 @@ export function loadMCPConfiguration() {
|
|
|
94
94
|
try {
|
|
95
95
|
const content = readFileSync(configPath, 'utf8');
|
|
96
96
|
config = JSON.parse(content);
|
|
97
|
-
if (process.env.DEBUG === '1') {
|
|
98
|
-
console.error(`[MCP] Loaded configuration from: ${configPath}`);
|
|
97
|
+
if (process.env.DEBUG === '1' || process.env.DEBUG_MCP === '1') {
|
|
98
|
+
console.error(`[MCP DEBUG] Loaded configuration from: ${configPath}`);
|
|
99
99
|
}
|
|
100
100
|
break;
|
|
101
101
|
} catch (error) {
|
|
102
|
-
console.error(`[MCP] Failed to parse config from ${configPath}:`, error.message);
|
|
102
|
+
console.error(`[MCP ERROR] Failed to parse config from ${configPath}:`, error.message);
|
|
103
103
|
}
|
|
104
104
|
}
|
|
105
105
|
}
|
|
@@ -209,12 +209,12 @@ export function parseEnabledServers(config) {
|
|
|
209
209
|
// Validate required fields based on transport
|
|
210
210
|
if (server.transport === 'stdio') {
|
|
211
211
|
if (!server.command) {
|
|
212
|
-
console.error(`[MCP] Server ${name} missing required 'command' for stdio transport`);
|
|
212
|
+
console.error(`[MCP ERROR] Server ${name} missing required 'command' for stdio transport`);
|
|
213
213
|
continue;
|
|
214
214
|
}
|
|
215
215
|
} else if (['websocket', 'sse', 'http'].includes(server.transport)) {
|
|
216
216
|
if (!server.url) {
|
|
217
|
-
console.error(`[MCP] Server ${name} missing required 'url' for ${server.transport} transport`);
|
|
217
|
+
console.error(`[MCP ERROR] Server ${name} missing required 'url' for ${server.transport} transport`);
|
|
218
218
|
continue;
|
|
219
219
|
}
|
|
220
220
|
}
|
|
@@ -301,7 +301,7 @@ export function saveConfig(config, path) {
|
|
|
301
301
|
}
|
|
302
302
|
|
|
303
303
|
writeFileSync(path, JSON.stringify(config, null, 2), 'utf8');
|
|
304
|
-
console.
|
|
304
|
+
console.error(`[MCP INFO] Configuration saved to: ${path}`);
|
|
305
305
|
}
|
|
306
306
|
|
|
307
307
|
export default {
|
|
@@ -141,23 +141,44 @@ export class MCPXmlBridge {
|
|
|
141
141
|
|
|
142
142
|
if (!config) {
|
|
143
143
|
// No config provided - fall back to auto-discovery for backward compatibility
|
|
144
|
+
if (this.debug) {
|
|
145
|
+
console.error('[MCP DEBUG] No config provided, attempting auto-discovery...');
|
|
146
|
+
}
|
|
144
147
|
mcpConfigs = loadMCPConfiguration();
|
|
148
|
+
|
|
149
|
+
// Check if auto-discovery found anything
|
|
150
|
+
if (!mcpConfigs || !mcpConfigs.mcpServers || Object.keys(mcpConfigs.mcpServers).length === 0) {
|
|
151
|
+
console.error('[MCP WARNING] MCP enabled but no configuration found');
|
|
152
|
+
console.error('[MCP INFO] To use MCP, provide configuration via:');
|
|
153
|
+
console.error('[MCP INFO] - mcpConfig option when creating ProbeAgent');
|
|
154
|
+
console.error('[MCP INFO] - mcpConfigPath option pointing to a config file');
|
|
155
|
+
console.error('[MCP INFO] - Config file in standard locations (~/.mcp/config.json, etc.)');
|
|
156
|
+
console.error('[MCP INFO] - Environment variable MCP_CONFIG_PATH');
|
|
157
|
+
}
|
|
145
158
|
} else if (Array.isArray(config)) {
|
|
146
159
|
// Deprecated: Array of server configs (backward compatibility)
|
|
160
|
+
if (this.debug) {
|
|
161
|
+
console.error('[MCP DEBUG] Using deprecated array config format (consider using mcpConfig object)');
|
|
162
|
+
}
|
|
147
163
|
mcpConfigs = { mcpServers: config };
|
|
148
164
|
} else {
|
|
149
165
|
// New: Full config object provided directly
|
|
166
|
+
if (this.debug) {
|
|
167
|
+
console.error('[MCP DEBUG] Using provided MCP config object');
|
|
168
|
+
}
|
|
150
169
|
mcpConfigs = config;
|
|
151
170
|
}
|
|
152
171
|
|
|
153
172
|
if (!mcpConfigs || !mcpConfigs.mcpServers || Object.keys(mcpConfigs.mcpServers).length === 0) {
|
|
154
|
-
|
|
155
|
-
console.error('[MCP] No MCP servers configured');
|
|
156
|
-
}
|
|
173
|
+
console.error('[MCP INFO] 0 MCP tools available');
|
|
157
174
|
return;
|
|
158
175
|
}
|
|
159
176
|
|
|
160
177
|
try {
|
|
178
|
+
if (this.debug) {
|
|
179
|
+
console.error('[MCP DEBUG] Initializing MCP client manager...');
|
|
180
|
+
}
|
|
181
|
+
|
|
161
182
|
// Initialize the MCP client manager
|
|
162
183
|
this.mcpManager = new MCPClientManager({ debug: this.debug });
|
|
163
184
|
const result = await this.mcpManager.initialize(mcpConfigs);
|
|
@@ -165,17 +186,27 @@ export class MCPXmlBridge {
|
|
|
165
186
|
// Get tools from the manager
|
|
166
187
|
const vercelTools = this.mcpManager.getVercelTools();
|
|
167
188
|
this.mcpTools = vercelTools;
|
|
189
|
+
const toolCount = Object.keys(vercelTools).length;
|
|
168
190
|
|
|
169
191
|
// Generate XML definitions for all tools
|
|
170
192
|
for (const [name, tool] of Object.entries(vercelTools)) {
|
|
171
193
|
this.xmlDefinitions[name] = mcpToolToXmlDefinition(name, tool);
|
|
172
194
|
}
|
|
173
195
|
|
|
174
|
-
if (
|
|
175
|
-
console.error(
|
|
196
|
+
if (toolCount === 0) {
|
|
197
|
+
console.error('[MCP INFO] MCP initialization complete: 0 tools loaded');
|
|
198
|
+
} else {
|
|
199
|
+
console.error(`[MCP INFO] MCP initialization complete: ${toolCount} tool${toolCount !== 1 ? 's' : ''} loaded from ${result.connected} server${result.connected !== 1 ? 's' : ''}`);
|
|
200
|
+
|
|
201
|
+
if (this.debug) {
|
|
202
|
+
console.error('[MCP DEBUG] Tool definitions generated for XML bridge');
|
|
203
|
+
}
|
|
176
204
|
}
|
|
177
205
|
} catch (error) {
|
|
178
|
-
console.error('[MCP] Failed to initialize MCP connections:', error);
|
|
206
|
+
console.error('[MCP ERROR] Failed to initialize MCP connections:', error.message);
|
|
207
|
+
if (this.debug) {
|
|
208
|
+
console.error('[MCP DEBUG] Full error details:', error);
|
|
209
|
+
}
|
|
179
210
|
}
|
|
180
211
|
}
|
|
181
212
|
|
|
@@ -204,28 +235,42 @@ export class MCPXmlBridge {
|
|
|
204
235
|
const parsed = parseXmlMcpToolCall(xmlString, this.getToolNames());
|
|
205
236
|
|
|
206
237
|
if (!parsed) {
|
|
238
|
+
console.error('[MCP ERROR] No valid MCP tool call found in XML');
|
|
207
239
|
throw new Error('No valid MCP tool call found in XML');
|
|
208
240
|
}
|
|
209
241
|
|
|
210
242
|
const { toolName, params } = parsed;
|
|
211
243
|
|
|
212
244
|
if (this.debug) {
|
|
213
|
-
console.error(`[MCP] Executing MCP tool: ${toolName}
|
|
245
|
+
console.error(`[MCP DEBUG] Executing MCP tool: ${toolName}`);
|
|
246
|
+
console.error(`[MCP DEBUG] Parameters:`, JSON.stringify(params, null, 2));
|
|
214
247
|
}
|
|
215
248
|
|
|
216
249
|
const tool = this.mcpTools[toolName];
|
|
217
250
|
if (!tool) {
|
|
251
|
+
console.error(`[MCP ERROR] Unknown MCP tool: ${toolName}`);
|
|
252
|
+
console.error(`[MCP ERROR] Available tools: ${this.getToolNames().join(', ')}`);
|
|
218
253
|
throw new Error(`Unknown MCP tool: ${toolName}`);
|
|
219
254
|
}
|
|
220
255
|
|
|
221
256
|
try {
|
|
222
257
|
const result = await tool.execute(params);
|
|
258
|
+
|
|
259
|
+
if (this.debug) {
|
|
260
|
+
console.error(`[MCP DEBUG] Tool ${toolName} executed successfully`);
|
|
261
|
+
}
|
|
262
|
+
|
|
223
263
|
return {
|
|
224
264
|
success: true,
|
|
225
265
|
toolName,
|
|
226
266
|
result
|
|
227
267
|
};
|
|
228
268
|
} catch (error) {
|
|
269
|
+
console.error(`[MCP ERROR] Tool ${toolName} execution failed:`, error.message);
|
|
270
|
+
if (this.debug) {
|
|
271
|
+
console.error(`[MCP DEBUG] Full error details:`, error);
|
|
272
|
+
}
|
|
273
|
+
|
|
229
274
|
return {
|
|
230
275
|
success: false,
|
|
231
276
|
toolName,
|
package/src/agent/schemaUtils.js
CHANGED
|
@@ -40,7 +40,7 @@ export function decodeHtmlEntities(text) {
|
|
|
40
40
|
|
|
41
41
|
/**
|
|
42
42
|
* Clean AI response by extracting JSON content when response contains JSON
|
|
43
|
-
* Only processes responses that contain JSON structures { or [
|
|
43
|
+
* Only processes responses that contain JSON structures { or [
|
|
44
44
|
* @param {string} response - Raw AI response
|
|
45
45
|
* @returns {string} - Cleaned response with JSON boundaries extracted if applicable
|
|
46
46
|
*/
|
|
@@ -50,34 +50,47 @@ export function cleanSchemaResponse(response) {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
const trimmed = response.trim();
|
|
53
|
-
|
|
54
|
-
// First, look for JSON after code block markers
|
|
53
|
+
|
|
54
|
+
// First, look for JSON after code block markers - similar to mermaid extraction
|
|
55
|
+
// Try with json language specifier
|
|
56
|
+
const jsonBlockMatch = trimmed.match(/```json\s*\n([\s\S]*?)\n```/);
|
|
57
|
+
if (jsonBlockMatch) {
|
|
58
|
+
return jsonBlockMatch[1].trim();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Try any code block with JSON content
|
|
62
|
+
const anyBlockMatch = trimmed.match(/```\s*\n([{\[][\s\S]*?[}\]])\s*```/);
|
|
63
|
+
if (anyBlockMatch) {
|
|
64
|
+
return anyBlockMatch[1].trim();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Legacy patterns for more specific matching
|
|
55
68
|
const codeBlockPatterns = [
|
|
56
69
|
/```json\s*\n?([{\[][\s\S]*?[}\]])\s*\n?```/,
|
|
57
70
|
/```\s*\n?([{\[][\s\S]*?[}\]])\s*\n?```/,
|
|
58
71
|
/`([{\[][\s\S]*?[}\]])`/
|
|
59
72
|
];
|
|
60
|
-
|
|
73
|
+
|
|
61
74
|
for (const pattern of codeBlockPatterns) {
|
|
62
75
|
const match = trimmed.match(pattern);
|
|
63
76
|
if (match) {
|
|
64
77
|
return match[1].trim();
|
|
65
78
|
}
|
|
66
79
|
}
|
|
67
|
-
|
|
80
|
+
|
|
68
81
|
// Look for code block start followed immediately by JSON
|
|
69
82
|
const codeBlockStartPattern = /```(?:json)?\s*\n?\s*([{\[])/;
|
|
70
83
|
const codeBlockMatch = trimmed.match(codeBlockStartPattern);
|
|
71
|
-
|
|
84
|
+
|
|
72
85
|
if (codeBlockMatch) {
|
|
73
86
|
const startIndex = codeBlockMatch.index + codeBlockMatch[0].length - 1; // Position of the bracket
|
|
74
|
-
|
|
87
|
+
|
|
75
88
|
// Find the matching closing bracket
|
|
76
89
|
const openChar = codeBlockMatch[1];
|
|
77
90
|
const closeChar = openChar === '{' ? '}' : ']';
|
|
78
91
|
let bracketCount = 1;
|
|
79
92
|
let endIndex = startIndex + 1;
|
|
80
|
-
|
|
93
|
+
|
|
81
94
|
while (endIndex < trimmed.length && bracketCount > 0) {
|
|
82
95
|
const char = trimmed[endIndex];
|
|
83
96
|
if (char === openChar) {
|
|
@@ -87,36 +100,36 @@ export function cleanSchemaResponse(response) {
|
|
|
87
100
|
}
|
|
88
101
|
endIndex++;
|
|
89
102
|
}
|
|
90
|
-
|
|
103
|
+
|
|
91
104
|
if (bracketCount === 0) {
|
|
92
105
|
return trimmed.substring(startIndex, endIndex);
|
|
93
106
|
}
|
|
94
107
|
}
|
|
95
|
-
|
|
108
|
+
|
|
96
109
|
// Fallback: Find JSON boundaries anywhere in the text
|
|
97
110
|
const firstBracket = Math.min(
|
|
98
111
|
trimmed.indexOf('{') >= 0 ? trimmed.indexOf('{') : Infinity,
|
|
99
112
|
trimmed.indexOf('[') >= 0 ? trimmed.indexOf('[') : Infinity
|
|
100
113
|
);
|
|
101
|
-
|
|
114
|
+
|
|
102
115
|
const lastBracket = Math.max(
|
|
103
116
|
trimmed.lastIndexOf('}'),
|
|
104
117
|
trimmed.lastIndexOf(']')
|
|
105
118
|
);
|
|
106
|
-
|
|
119
|
+
|
|
107
120
|
// Only extract if we found valid JSON boundaries
|
|
108
121
|
if (firstBracket < Infinity && lastBracket >= 0 && firstBracket < lastBracket) {
|
|
109
122
|
// Check if the response likely starts with JSON (directly or after minimal content)
|
|
110
123
|
const beforeFirstBracket = trimmed.substring(0, firstBracket).trim();
|
|
111
|
-
|
|
124
|
+
|
|
112
125
|
// If there's minimal content before the first bracket, extract the JSON
|
|
113
|
-
if (beforeFirstBracket === '' ||
|
|
126
|
+
if (beforeFirstBracket === '' ||
|
|
114
127
|
beforeFirstBracket.match(/^```\w*$/) ||
|
|
115
128
|
beforeFirstBracket.split('\n').length <= 2) {
|
|
116
129
|
return trimmed.substring(firstBracket, lastBracket + 1);
|
|
117
130
|
}
|
|
118
131
|
}
|
|
119
|
-
|
|
132
|
+
|
|
120
133
|
return response; // Return original if no extractable JSON found
|
|
121
134
|
}
|
|
122
135
|
|