@probelabs/probe 0.6.0-rc121 → 0.6.0-rc122

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@probelabs/probe",
3
- "version": "0.6.0-rc121",
3
+ "version": "0.6.0-rc122",
4
4
  "description": "Node.js wrapper for the probe code search tool",
5
5
  "main": "src/index.js",
6
6
  "module": "src/index.js",
@@ -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.log('[DEBUG] Using provided MCP config object');
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.log(`[DEBUG] Loaded MCP config from: ${this.mcpConfigPath}`);
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.warn('[DEBUG] Using deprecated mcpServers option. Consider using mcpConfig instead.');
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}
@@ -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 Error]', error);
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);
@@ -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(`[MCP] Found ${servers.length} enabled servers`);
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
- if (this.debug) {
147
- console.error(`[MCP] Successfully connected to ${connectedCount}/${servers.length} servers`);
148
- console.error(`[MCP] Total tools available: ${this.tools.size}`);
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] Registered tool: ${qualifiedName}`);
239
+ console.error(`[MCP DEBUG] Registered tool: ${qualifiedName}`);
209
240
  }
210
241
  }
211
242
  }
212
243
 
213
- if (this.debug) {
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
 
@@ -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.log(`[MCP] Configuration saved to: ${path}`);
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
- if (this.debug) {
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 (this.debug) {
175
- console.error(`[MCP] Loaded ${Object.keys(vercelTools).length} MCP tools from ${result.connected} server(s)`);
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} with params:`, params);
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,
@@ -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