@juspay/neurolink 9.15.0 → 9.17.0

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.
Files changed (196) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +22 -20
  3. package/dist/adapters/video/videoAnalyzer.d.ts +1 -1
  4. package/dist/adapters/video/videoAnalyzer.js +10 -8
  5. package/dist/cli/commands/setup-anthropic.js +1 -14
  6. package/dist/cli/commands/setup-azure.js +1 -12
  7. package/dist/cli/commands/setup-bedrock.js +1 -9
  8. package/dist/cli/commands/setup-google-ai.js +1 -12
  9. package/dist/cli/commands/setup-openai.js +1 -14
  10. package/dist/cli/commands/workflow.d.ts +27 -0
  11. package/dist/cli/commands/workflow.js +216 -0
  12. package/dist/cli/factories/commandFactory.js +79 -20
  13. package/dist/cli/index.js +0 -1
  14. package/dist/cli/parser.js +4 -1
  15. package/dist/cli/utils/maskCredential.d.ts +11 -0
  16. package/dist/cli/utils/maskCredential.js +23 -0
  17. package/dist/constants/contextWindows.js +107 -16
  18. package/dist/constants/enums.d.ts +99 -15
  19. package/dist/constants/enums.js +152 -22
  20. package/dist/context/budgetChecker.js +1 -1
  21. package/dist/context/contextCompactor.js +31 -4
  22. package/dist/context/emergencyTruncation.d.ts +21 -0
  23. package/dist/context/emergencyTruncation.js +88 -0
  24. package/dist/context/errorDetection.d.ts +16 -0
  25. package/dist/context/errorDetection.js +48 -1
  26. package/dist/context/errors.d.ts +19 -0
  27. package/dist/context/errors.js +21 -0
  28. package/dist/context/stages/slidingWindowTruncator.d.ts +6 -0
  29. package/dist/context/stages/slidingWindowTruncator.js +159 -24
  30. package/dist/context/stages/structuredSummarizer.js +2 -2
  31. package/dist/core/baseProvider.js +306 -200
  32. package/dist/core/conversationMemoryManager.js +104 -61
  33. package/dist/core/evaluationProviders.js +16 -33
  34. package/dist/core/factory.js +237 -164
  35. package/dist/core/modules/GenerationHandler.js +175 -116
  36. package/dist/core/modules/MessageBuilder.js +222 -170
  37. package/dist/core/modules/StreamHandler.d.ts +1 -0
  38. package/dist/core/modules/StreamHandler.js +95 -27
  39. package/dist/core/modules/TelemetryHandler.d.ts +10 -1
  40. package/dist/core/modules/TelemetryHandler.js +25 -7
  41. package/dist/core/modules/ToolsManager.js +115 -191
  42. package/dist/core/redisConversationMemoryManager.js +418 -282
  43. package/dist/factories/providerRegistry.d.ts +5 -0
  44. package/dist/factories/providerRegistry.js +20 -2
  45. package/dist/index.d.ts +2 -2
  46. package/dist/index.js +4 -2
  47. package/dist/lib/adapters/video/videoAnalyzer.d.ts +1 -1
  48. package/dist/lib/adapters/video/videoAnalyzer.js +10 -8
  49. package/dist/lib/constants/contextWindows.js +107 -16
  50. package/dist/lib/constants/enums.d.ts +99 -15
  51. package/dist/lib/constants/enums.js +152 -22
  52. package/dist/lib/context/budgetChecker.js +1 -1
  53. package/dist/lib/context/contextCompactor.js +31 -4
  54. package/dist/lib/context/emergencyTruncation.d.ts +21 -0
  55. package/dist/lib/context/emergencyTruncation.js +89 -0
  56. package/dist/lib/context/errorDetection.d.ts +16 -0
  57. package/dist/lib/context/errorDetection.js +48 -1
  58. package/dist/lib/context/errors.d.ts +19 -0
  59. package/dist/lib/context/errors.js +22 -0
  60. package/dist/lib/context/stages/slidingWindowTruncator.d.ts +6 -0
  61. package/dist/lib/context/stages/slidingWindowTruncator.js +159 -24
  62. package/dist/lib/context/stages/structuredSummarizer.js +2 -2
  63. package/dist/lib/core/baseProvider.js +306 -200
  64. package/dist/lib/core/conversationMemoryManager.js +104 -61
  65. package/dist/lib/core/evaluationProviders.js +16 -33
  66. package/dist/lib/core/factory.js +237 -164
  67. package/dist/lib/core/modules/GenerationHandler.js +175 -116
  68. package/dist/lib/core/modules/MessageBuilder.js +222 -170
  69. package/dist/lib/core/modules/StreamHandler.d.ts +1 -0
  70. package/dist/lib/core/modules/StreamHandler.js +95 -27
  71. package/dist/lib/core/modules/TelemetryHandler.d.ts +10 -1
  72. package/dist/lib/core/modules/TelemetryHandler.js +25 -7
  73. package/dist/lib/core/modules/ToolsManager.js +115 -191
  74. package/dist/lib/core/redisConversationMemoryManager.js +418 -282
  75. package/dist/lib/factories/providerRegistry.d.ts +5 -0
  76. package/dist/lib/factories/providerRegistry.js +20 -2
  77. package/dist/lib/index.d.ts +2 -2
  78. package/dist/lib/index.js +4 -2
  79. package/dist/lib/mcp/externalServerManager.js +66 -0
  80. package/dist/lib/mcp/mcpCircuitBreaker.js +24 -0
  81. package/dist/lib/mcp/mcpClientFactory.js +16 -0
  82. package/dist/lib/mcp/toolDiscoveryService.js +32 -6
  83. package/dist/lib/mcp/toolRegistry.js +193 -123
  84. package/dist/lib/neurolink.d.ts +6 -0
  85. package/dist/lib/neurolink.js +1162 -646
  86. package/dist/lib/providers/amazonBedrock.d.ts +1 -1
  87. package/dist/lib/providers/amazonBedrock.js +521 -319
  88. package/dist/lib/providers/anthropic.js +73 -17
  89. package/dist/lib/providers/anthropicBaseProvider.js +77 -17
  90. package/dist/lib/providers/googleAiStudio.d.ts +1 -1
  91. package/dist/lib/providers/googleAiStudio.js +292 -227
  92. package/dist/lib/providers/googleVertex.d.ts +36 -1
  93. package/dist/lib/providers/googleVertex.js +553 -260
  94. package/dist/lib/providers/ollama.js +329 -278
  95. package/dist/lib/providers/openAI.js +77 -19
  96. package/dist/lib/providers/sagemaker/parsers.js +3 -3
  97. package/dist/lib/providers/sagemaker/streaming.js +3 -3
  98. package/dist/lib/proxy/proxyFetch.js +81 -48
  99. package/dist/lib/rag/ChunkerFactory.js +1 -1
  100. package/dist/lib/rag/chunkers/MarkdownChunker.d.ts +22 -0
  101. package/dist/lib/rag/chunkers/MarkdownChunker.js +213 -9
  102. package/dist/lib/rag/chunking/markdownChunker.d.ts +16 -0
  103. package/dist/lib/rag/chunking/markdownChunker.js +174 -2
  104. package/dist/lib/rag/pipeline/contextAssembly.js +2 -1
  105. package/dist/lib/rag/ragIntegration.d.ts +18 -1
  106. package/dist/lib/rag/ragIntegration.js +94 -14
  107. package/dist/lib/rag/retrieval/vectorQueryTool.js +21 -4
  108. package/dist/lib/server/abstract/baseServerAdapter.js +4 -1
  109. package/dist/lib/server/adapters/fastifyAdapter.js +35 -30
  110. package/dist/lib/services/server/ai/observability/instrumentation.d.ts +32 -0
  111. package/dist/lib/services/server/ai/observability/instrumentation.js +39 -0
  112. package/dist/lib/telemetry/attributes.d.ts +52 -0
  113. package/dist/lib/telemetry/attributes.js +61 -0
  114. package/dist/lib/telemetry/index.d.ts +3 -0
  115. package/dist/lib/telemetry/index.js +3 -0
  116. package/dist/lib/telemetry/telemetryService.d.ts +6 -0
  117. package/dist/lib/telemetry/telemetryService.js +6 -0
  118. package/dist/lib/telemetry/tracers.d.ts +15 -0
  119. package/dist/lib/telemetry/tracers.js +17 -0
  120. package/dist/lib/telemetry/withSpan.d.ts +9 -0
  121. package/dist/lib/telemetry/withSpan.js +35 -0
  122. package/dist/lib/types/contextTypes.d.ts +10 -0
  123. package/dist/lib/types/streamTypes.d.ts +14 -0
  124. package/dist/lib/utils/conversationMemory.js +123 -84
  125. package/dist/lib/utils/logger.d.ts +5 -0
  126. package/dist/lib/utils/logger.js +50 -2
  127. package/dist/lib/utils/messageBuilder.js +22 -42
  128. package/dist/lib/utils/modelDetection.js +3 -3
  129. package/dist/lib/utils/providerRetry.d.ts +41 -0
  130. package/dist/lib/utils/providerRetry.js +114 -0
  131. package/dist/lib/utils/retryability.d.ts +14 -0
  132. package/dist/lib/utils/retryability.js +23 -0
  133. package/dist/lib/utils/sanitizers/svg.js +4 -5
  134. package/dist/lib/utils/tokenEstimation.d.ts +11 -1
  135. package/dist/lib/utils/tokenEstimation.js +19 -4
  136. package/dist/lib/utils/videoAnalysisProcessor.js +7 -3
  137. package/dist/mcp/externalServerManager.js +66 -0
  138. package/dist/mcp/mcpCircuitBreaker.js +24 -0
  139. package/dist/mcp/mcpClientFactory.js +16 -0
  140. package/dist/mcp/toolDiscoveryService.js +32 -6
  141. package/dist/mcp/toolRegistry.js +193 -123
  142. package/dist/neurolink.d.ts +6 -0
  143. package/dist/neurolink.js +1162 -646
  144. package/dist/providers/amazonBedrock.d.ts +1 -1
  145. package/dist/providers/amazonBedrock.js +521 -319
  146. package/dist/providers/anthropic.js +73 -17
  147. package/dist/providers/anthropicBaseProvider.js +77 -17
  148. package/dist/providers/googleAiStudio.d.ts +1 -1
  149. package/dist/providers/googleAiStudio.js +292 -227
  150. package/dist/providers/googleVertex.d.ts +36 -1
  151. package/dist/providers/googleVertex.js +553 -260
  152. package/dist/providers/ollama.js +329 -278
  153. package/dist/providers/openAI.js +77 -19
  154. package/dist/providers/sagemaker/parsers.js +3 -3
  155. package/dist/providers/sagemaker/streaming.js +3 -3
  156. package/dist/proxy/proxyFetch.js +81 -48
  157. package/dist/rag/ChunkerFactory.js +1 -1
  158. package/dist/rag/chunkers/MarkdownChunker.d.ts +22 -0
  159. package/dist/rag/chunkers/MarkdownChunker.js +213 -9
  160. package/dist/rag/chunking/markdownChunker.d.ts +16 -0
  161. package/dist/rag/chunking/markdownChunker.js +174 -2
  162. package/dist/rag/pipeline/contextAssembly.js +2 -1
  163. package/dist/rag/ragIntegration.d.ts +18 -1
  164. package/dist/rag/ragIntegration.js +94 -14
  165. package/dist/rag/retrieval/vectorQueryTool.js +21 -4
  166. package/dist/server/abstract/baseServerAdapter.js +4 -1
  167. package/dist/server/adapters/fastifyAdapter.js +35 -30
  168. package/dist/services/server/ai/observability/instrumentation.d.ts +32 -0
  169. package/dist/services/server/ai/observability/instrumentation.js +39 -0
  170. package/dist/telemetry/attributes.d.ts +52 -0
  171. package/dist/telemetry/attributes.js +60 -0
  172. package/dist/telemetry/index.d.ts +3 -0
  173. package/dist/telemetry/index.js +3 -0
  174. package/dist/telemetry/telemetryService.d.ts +6 -0
  175. package/dist/telemetry/telemetryService.js +6 -0
  176. package/dist/telemetry/tracers.d.ts +15 -0
  177. package/dist/telemetry/tracers.js +16 -0
  178. package/dist/telemetry/withSpan.d.ts +9 -0
  179. package/dist/telemetry/withSpan.js +34 -0
  180. package/dist/types/contextTypes.d.ts +10 -0
  181. package/dist/types/streamTypes.d.ts +14 -0
  182. package/dist/utils/conversationMemory.js +123 -84
  183. package/dist/utils/logger.d.ts +5 -0
  184. package/dist/utils/logger.js +50 -2
  185. package/dist/utils/messageBuilder.js +22 -42
  186. package/dist/utils/modelDetection.js +3 -3
  187. package/dist/utils/providerRetry.d.ts +41 -0
  188. package/dist/utils/providerRetry.js +113 -0
  189. package/dist/utils/retryability.d.ts +14 -0
  190. package/dist/utils/retryability.js +22 -0
  191. package/dist/utils/sanitizers/svg.js +4 -5
  192. package/dist/utils/tokenEstimation.d.ts +11 -1
  193. package/dist/utils/tokenEstimation.js +19 -4
  194. package/dist/utils/videoAnalysisProcessor.js +7 -3
  195. package/dist/workflow/config.d.ts +26 -26
  196. package/package.json +1 -1
@@ -10,6 +10,7 @@ import { directAgentTools } from "../agent/directTools.js";
10
10
  import { detectCategory, createMCPServerInfo } from "../utils/mcpDefaults.js";
11
11
  import { FlexibleToolValidator } from "./flexibleToolValidator.js";
12
12
  import { HITLUserRejectedError, HITLTimeoutError } from "../hitl/hitlErrors.js";
13
+ import { withSpan, tracers, ATTR } from "../telemetry/index.js";
13
14
  export class MCPToolRegistry extends MCPRegistry {
14
15
  tools = new Map();
15
16
  toolImplementations = new Map(); // Store actual tool implementations
@@ -227,149 +228,218 @@ export class MCPToolRegistry extends MCPRegistry {
227
228
  */
228
229
  async executeTool(toolName, args, context) {
229
230
  const startTime = Date.now();
230
- try {
231
- registryLogger.info(`🔧 [TOOL_EXECUTION] Starting execution: ${toolName}`, { args, context });
232
- // Try to find the tool by fully-qualified name first
233
- let tool = this.tools.get(toolName);
234
- registryLogger.info(`🔍 [TOOL_LOOKUP] Direct lookup result for '${toolName}':`, !!tool);
235
- // If not found, search for tool by name across all entries (for backward compatibility)
236
- let toolId = toolName;
237
- if (!tool) {
238
- for (const [candidateToolId, toolInfo] of this.tools.entries()) {
239
- if (toolInfo.name === toolName) {
231
+ // Resolve serverId eagerly for span attributes
232
+ let preResolvedServerId;
233
+ const toolEntry = this.tools.get(toolName);
234
+ if (toolEntry) {
235
+ preResolvedServerId = toolEntry.serverId;
236
+ }
237
+ else {
238
+ for (const toolInfo of this.tools.values()) {
239
+ if (toolInfo.name === toolName) {
240
+ preResolvedServerId = toolInfo.serverId;
241
+ break;
242
+ }
243
+ }
244
+ }
245
+ return withSpan({
246
+ name: "neurolink.tool.execute",
247
+ tracer: tracers.mcp,
248
+ attributes: {
249
+ [ATTR.GEN_AI_TOOL_NAME]: toolName,
250
+ [ATTR.MCP_SERVER_ID]: preResolvedServerId || "builtin",
251
+ },
252
+ }, async (span) => {
253
+ try {
254
+ registryLogger.info(`🔧 [TOOL_EXECUTION] Starting execution: ${toolName}`, {
255
+ hasArgs: args !== undefined,
256
+ hasContext: context !== undefined,
257
+ sessionId: context?.sessionId,
258
+ });
259
+ // Try to find the tool by fully-qualified name first
260
+ let tool = this.tools.get(toolName);
261
+ registryLogger.info(`🔍 [TOOL_LOOKUP] Direct lookup result for '${toolName}':`, !!tool);
262
+ // If not found, search for tool by name across all entries (for backward compatibility)
263
+ let toolId = toolName;
264
+ if (!tool) {
265
+ const matches = Array.from(this.tools.entries()).filter(([, toolInfo]) => toolInfo.name === toolName);
266
+ if (matches.length > 1) {
267
+ throw new Error(`Ambiguous tool name '${toolName}'. Use fully-qualified name 'serverId.${toolName}'.`);
268
+ }
269
+ if (matches.length === 1) {
270
+ const [candidateToolId, toolInfo] = matches[0];
240
271
  tool = toolInfo;
241
272
  toolId = candidateToolId;
242
- break;
243
273
  }
244
274
  }
245
- }
246
- if (!tool) {
247
- throw new Error(`Tool '${toolName}' not found in registry`);
248
- }
249
- // Create execution context if not provided
250
- const execContext = {
251
- sessionId: context?.sessionId || randomUUID(),
252
- userId: context?.userId,
253
- ...context,
254
- };
255
- // Get the tool implementation using the resolved toolId
256
- const toolImpl = this.toolImplementations.get(toolId);
257
- registryLogger.debug(`Looking for tool '${toolName}' (toolId: '${toolId}'), found: ${!!toolImpl}, type: ${typeof toolImpl?.execute}`);
258
- registryLogger.debug(`Available tools:`, Array.from(this.toolImplementations.keys()));
259
- if (!toolImpl || typeof toolImpl?.execute !== "function") {
260
- throw new Error(`Tool '${toolName}' implementation not found or not executable`);
261
- }
262
- // HITL Safety Check: Request confirmation if required
263
- let finalArgs = args;
264
- if (this.hitlManager && this.hitlManager.isEnabled()) {
265
- const requiresConfirmation = this.hitlManager.requiresConfirmation(toolName, args);
266
- if (requiresConfirmation) {
267
- registryLogger.info(`Tool '${toolName}' requires HITL confirmation`);
268
- try {
269
- const confirmationResult = await this.hitlManager.requestConfirmation(toolName, args, {
270
- serverId: tool.serverId,
271
- sessionId: execContext.sessionId,
272
- userId: execContext.userId,
273
- });
274
- if (!confirmationResult.approved) {
275
- // User rejected the tool execution
276
- throw new HITLUserRejectedError(`Tool execution rejected by user: ${confirmationResult.reason || "No reason provided"}`, toolName, confirmationResult.reason);
275
+ if (!tool) {
276
+ throw new Error(`Tool '${toolName}' not found in registry`);
277
+ }
278
+ // Classify tool type for observability
279
+ const serverId = tool.serverId || "unknown";
280
+ const toolType = serverId === "direct"
281
+ ? "builtin"
282
+ : serverId.startsWith("custom-tool-")
283
+ ? "custom"
284
+ : "mcp";
285
+ span.setAttribute("tool.type", toolType);
286
+ span.setAttribute(ATTR.MCP_SERVER_ID, serverId);
287
+ // Create execution context if not provided
288
+ const execContext = {
289
+ ...context,
290
+ sessionId: context?.sessionId ?? randomUUID(),
291
+ userId: context?.userId,
292
+ };
293
+ // Get the tool implementation using the resolved toolId
294
+ const toolImpl = this.toolImplementations.get(toolId);
295
+ registryLogger.debug(`Looking for tool '${toolName}' (toolId: '${toolId}'), found: ${!!toolImpl}, type: ${typeof toolImpl?.execute}`);
296
+ registryLogger.debug(`Available tools:`, Array.from(this.toolImplementations.keys()));
297
+ if (!toolImpl || typeof toolImpl?.execute !== "function") {
298
+ throw new Error(`Tool '${toolName}' implementation not found or not executable`);
299
+ }
300
+ // Capture argument metadata (avoid logging raw values which may contain secrets)
301
+ let argsStr;
302
+ try {
303
+ argsStr = JSON.stringify(args).slice(0, 4096);
304
+ }
305
+ catch {
306
+ argsStr = "[unserializable]";
307
+ }
308
+ span.setAttribute("tool.arguments_present", args !== undefined);
309
+ span.setAttribute("tool.arguments_size", argsStr.length);
310
+ // HITL Safety Check: Request confirmation if required
311
+ let finalArgs = args;
312
+ if (this.hitlManager && this.hitlManager.isEnabled()) {
313
+ const requiresConfirmation = this.hitlManager.requiresConfirmation(toolName, args);
314
+ if (requiresConfirmation) {
315
+ registryLogger.info(`Tool '${toolName}' requires HITL confirmation`);
316
+ span.addEvent("tool.hitl_requested");
317
+ try {
318
+ const confirmationResult = await this.hitlManager.requestConfirmation(toolName, args, {
319
+ serverId: tool.serverId,
320
+ sessionId: execContext.sessionId,
321
+ userId: execContext.userId,
322
+ });
323
+ if (!confirmationResult.approved) {
324
+ // User rejected the tool execution
325
+ span.addEvent("tool.hitl_rejected");
326
+ throw new HITLUserRejectedError(`Tool execution rejected by user: ${confirmationResult.reason || "No reason provided"}`, toolName, confirmationResult.reason);
327
+ }
328
+ span.addEvent("tool.hitl_approved");
329
+ // User approved - use modified arguments if provided
330
+ if (confirmationResult.modifiedArguments !== undefined) {
331
+ finalArgs = confirmationResult.modifiedArguments;
332
+ registryLogger.info(`Tool '${toolName}' arguments modified by user`);
333
+ }
334
+ registryLogger.info(`Tool '${toolName}' approved for execution (response time: ${confirmationResult.responseTime}ms)`);
277
335
  }
278
- // User approved - use modified arguments if provided
279
- if (confirmationResult.modifiedArguments !== undefined) {
280
- finalArgs = confirmationResult.modifiedArguments;
281
- registryLogger.info(`Tool '${toolName}' arguments modified by user`);
336
+ catch (error) {
337
+ if (error instanceof HITLTimeoutError) {
338
+ // Timeout occurred - user didn't respond in time
339
+ registryLogger.warn(`Tool '${toolName}' execution timed out waiting for user confirmation`);
340
+ throw error;
341
+ }
342
+ else if (error instanceof HITLUserRejectedError) {
343
+ // User explicitly rejected
344
+ registryLogger.info(`Tool '${toolName}' execution rejected by user`);
345
+ throw error;
346
+ }
347
+ else {
348
+ // Other HITL error (configuration, system error, etc.)
349
+ registryLogger.error(`HITL confirmation failed for tool '${toolName}':`, error);
350
+ throw new Error(`HITL confirmation failed: ${error instanceof Error ? error.message : String(error)}`);
351
+ }
282
352
  }
283
- registryLogger.info(`Tool '${toolName}' approved for execution (response time: ${confirmationResult.responseTime}ms)`);
284
353
  }
285
- catch (error) {
286
- if (error instanceof HITLTimeoutError) {
287
- // Timeout occurred - user didn't respond in time
288
- registryLogger.warn(`Tool '${toolName}' execution timed out waiting for user confirmation`);
289
- throw error;
290
- }
291
- else if (error instanceof HITLUserRejectedError) {
292
- // User explicitly rejected
293
- registryLogger.info(`Tool '${toolName}' execution rejected by user`);
294
- throw error;
295
- }
296
- else {
297
- // Other HITL error (configuration, system error, etc.)
298
- registryLogger.error(`HITL confirmation failed for tool '${toolName}':`, error);
299
- throw new Error(`HITL confirmation failed: ${error instanceof Error ? error.message : String(error)}`);
300
- }
354
+ else {
355
+ registryLogger.debug(`Tool '${toolName}' does not require HITL confirmation`);
301
356
  }
302
357
  }
358
+ // Execute the actual tool (with potentially modified arguments)
359
+ registryLogger.debug(`Executing tool '${toolName}' with args:`, finalArgs);
360
+ const toolResult = await toolImpl.execute(finalArgs, execContext);
361
+ // Properly wrap raw results in ToolResult format
362
+ let result;
363
+ // Check if result is already a ToolResult object
364
+ if (toolResult &&
365
+ typeof toolResult === "object" &&
366
+ "success" in toolResult &&
367
+ typeof toolResult.success === "boolean") {
368
+ // Result is already a ToolResult, enhance with metadata
369
+ const toolResultObj = toolResult;
370
+ result = {
371
+ ...toolResultObj,
372
+ usage: {
373
+ ...(toolResultObj.usage || {}),
374
+ executionTime: Date.now() - startTime,
375
+ },
376
+ metadata: {
377
+ ...(toolResultObj.metadata || {}),
378
+ toolName,
379
+ serverId: tool.serverId,
380
+ sessionId: execContext.sessionId,
381
+ executionTime: Date.now() - startTime,
382
+ },
383
+ };
384
+ }
303
385
  else {
304
- registryLogger.debug(`Tool '${toolName}' does not require HITL confirmation`);
386
+ // Result is a raw value, wrap it in ToolResult format
387
+ result = {
388
+ success: true,
389
+ data: toolResult,
390
+ usage: {
391
+ executionTime: Date.now() - startTime,
392
+ },
393
+ metadata: {
394
+ toolName,
395
+ serverId: tool.serverId,
396
+ sessionId: execContext.sessionId,
397
+ executionTime: Date.now() - startTime,
398
+ },
399
+ };
305
400
  }
401
+ // Update statistics
402
+ const duration = Date.now() - startTime;
403
+ this.updateStats(toolName, duration);
404
+ // Record success on span
405
+ let resultStr;
406
+ try {
407
+ resultStr = JSON.stringify(result.data);
408
+ }
409
+ catch {
410
+ resultStr = "[unserializable]";
411
+ }
412
+ span.setAttribute("tool.result_length", resultStr.length);
413
+ span.setAttribute("tool.success", true);
414
+ registryLogger.debug(`Tool '${toolName}' executed successfully in ${duration}ms`);
415
+ return result;
306
416
  }
307
- // Execute the actual tool (with potentially modified arguments)
308
- registryLogger.debug(`Executing tool '${toolName}' with args:`, finalArgs);
309
- const toolResult = await toolImpl.execute(finalArgs, execContext);
310
- // Properly wrap raw results in ToolResult format
311
- let result;
312
- // Check if result is already a ToolResult object
313
- if (toolResult &&
314
- typeof toolResult === "object" &&
315
- "success" in toolResult &&
316
- typeof toolResult.success === "boolean") {
317
- // Result is already a ToolResult, enhance with metadata
318
- const toolResultObj = toolResult;
319
- result = {
320
- ...toolResultObj,
321
- usage: {
322
- ...(toolResultObj.usage || {}),
323
- executionTime: Date.now() - startTime,
324
- },
325
- metadata: {
326
- ...(toolResultObj.metadata || {}),
327
- toolName,
328
- serverId: tool.serverId,
329
- sessionId: execContext.sessionId,
330
- executionTime: Date.now() - startTime,
331
- },
332
- };
333
- }
334
- else {
335
- // Result is a raw value, wrap it in ToolResult format
336
- result = {
337
- success: true,
338
- data: toolResult,
417
+ catch (error) {
418
+ registryLogger.error(`Tool execution failed: ${toolName}`, error);
419
+ // Record failure on span
420
+ span.setAttribute("tool.success", false);
421
+ // Rethrow precondition errors (tool not found, not executable)
422
+ const errMsg = error instanceof Error ? error.message : String(error);
423
+ if (errMsg.includes("not found in registry") ||
424
+ errMsg.includes("not executable")) {
425
+ throw error;
426
+ }
427
+ // Return runtime execution errors in ToolResult format
428
+ const errorResult = {
429
+ success: false,
430
+ data: null,
431
+ error: error instanceof Error ? error.message : String(error),
339
432
  usage: {
340
433
  executionTime: Date.now() - startTime,
341
434
  },
342
435
  metadata: {
343
436
  toolName,
344
- serverId: tool.serverId,
345
- sessionId: execContext.sessionId,
346
- executionTime: Date.now() - startTime,
437
+ sessionId: context?.sessionId,
347
438
  },
348
439
  };
440
+ return errorResult;
349
441
  }
350
- // Update statistics
351
- const duration = Date.now() - startTime;
352
- this.updateStats(toolName, duration);
353
- registryLogger.debug(`Tool '${toolName}' executed successfully in ${duration}ms`);
354
- return result;
355
- }
356
- catch (error) {
357
- registryLogger.error(`Tool execution failed: ${toolName}`, error);
358
- // Return error in ToolResult format
359
- const errorResult = {
360
- success: false,
361
- data: null,
362
- error: error instanceof Error ? error.message : String(error),
363
- usage: {
364
- executionTime: Date.now() - startTime,
365
- },
366
- metadata: {
367
- toolName,
368
- sessionId: context?.sessionId,
369
- },
370
- };
371
- return errorResult;
372
- }
442
+ });
373
443
  }
374
444
  async listTools(filterOrContext) {
375
445
  // FIXED: Return unique tools (avoid duplicates from dual registration)
@@ -95,6 +95,7 @@ import type { BatchOperationResult } from "./types/typeAliases.js";
95
95
  */
96
96
  export declare class NeuroLink {
97
97
  private mcpInitialized;
98
+ private mcpInitPromise;
98
99
  private emitter;
99
100
  private toolRegistry;
100
101
  private autoDiscoveredServerInfos;
@@ -270,6 +271,11 @@ export declare class NeuroLink {
270
271
  * Uses isolated async context to prevent hanging
271
272
  */
272
273
  private initializeMCP;
274
+ /**
275
+ * Actual one-shot MCP initialization logic. Called at most once per
276
+ * NeuroLink instance lifetime (unless cleanup() resets the flag).
277
+ */
278
+ private performMCPInitializationOnce;
273
279
  /**
274
280
  * Import performance manager with error handling
275
281
  */