@juspay/neurolink 9.15.0 → 9.16.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 (193) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/adapters/video/videoAnalyzer.d.ts +1 -1
  3. package/dist/adapters/video/videoAnalyzer.js +10 -8
  4. package/dist/cli/commands/setup-anthropic.js +1 -14
  5. package/dist/cli/commands/setup-azure.js +1 -12
  6. package/dist/cli/commands/setup-bedrock.js +1 -9
  7. package/dist/cli/commands/setup-google-ai.js +1 -12
  8. package/dist/cli/commands/setup-openai.js +1 -14
  9. package/dist/cli/commands/workflow.d.ts +27 -0
  10. package/dist/cli/commands/workflow.js +216 -0
  11. package/dist/cli/factories/commandFactory.js +79 -20
  12. package/dist/cli/index.js +0 -1
  13. package/dist/cli/parser.js +4 -1
  14. package/dist/cli/utils/maskCredential.d.ts +11 -0
  15. package/dist/cli/utils/maskCredential.js +23 -0
  16. package/dist/constants/contextWindows.js +107 -16
  17. package/dist/constants/enums.d.ts +99 -15
  18. package/dist/constants/enums.js +152 -22
  19. package/dist/context/budgetChecker.js +1 -1
  20. package/dist/context/contextCompactor.js +31 -4
  21. package/dist/context/emergencyTruncation.d.ts +21 -0
  22. package/dist/context/emergencyTruncation.js +88 -0
  23. package/dist/context/errorDetection.d.ts +16 -0
  24. package/dist/context/errorDetection.js +48 -1
  25. package/dist/context/errors.d.ts +19 -0
  26. package/dist/context/errors.js +21 -0
  27. package/dist/context/stages/slidingWindowTruncator.d.ts +6 -0
  28. package/dist/context/stages/slidingWindowTruncator.js +159 -24
  29. package/dist/core/baseProvider.js +306 -200
  30. package/dist/core/conversationMemoryManager.js +104 -61
  31. package/dist/core/evaluationProviders.js +16 -33
  32. package/dist/core/factory.js +237 -164
  33. package/dist/core/modules/GenerationHandler.js +175 -116
  34. package/dist/core/modules/MessageBuilder.js +222 -170
  35. package/dist/core/modules/StreamHandler.d.ts +1 -0
  36. package/dist/core/modules/StreamHandler.js +95 -27
  37. package/dist/core/modules/TelemetryHandler.d.ts +10 -1
  38. package/dist/core/modules/TelemetryHandler.js +25 -7
  39. package/dist/core/modules/ToolsManager.js +115 -191
  40. package/dist/core/redisConversationMemoryManager.js +418 -282
  41. package/dist/factories/providerRegistry.d.ts +5 -0
  42. package/dist/factories/providerRegistry.js +20 -2
  43. package/dist/index.d.ts +2 -2
  44. package/dist/index.js +4 -2
  45. package/dist/lib/adapters/video/videoAnalyzer.d.ts +1 -1
  46. package/dist/lib/adapters/video/videoAnalyzer.js +10 -8
  47. package/dist/lib/constants/contextWindows.js +107 -16
  48. package/dist/lib/constants/enums.d.ts +99 -15
  49. package/dist/lib/constants/enums.js +152 -22
  50. package/dist/lib/context/budgetChecker.js +1 -1
  51. package/dist/lib/context/contextCompactor.js +31 -4
  52. package/dist/lib/context/emergencyTruncation.d.ts +21 -0
  53. package/dist/lib/context/emergencyTruncation.js +89 -0
  54. package/dist/lib/context/errorDetection.d.ts +16 -0
  55. package/dist/lib/context/errorDetection.js +48 -1
  56. package/dist/lib/context/errors.d.ts +19 -0
  57. package/dist/lib/context/errors.js +22 -0
  58. package/dist/lib/context/stages/slidingWindowTruncator.d.ts +6 -0
  59. package/dist/lib/context/stages/slidingWindowTruncator.js +159 -24
  60. package/dist/lib/core/baseProvider.js +306 -200
  61. package/dist/lib/core/conversationMemoryManager.js +104 -61
  62. package/dist/lib/core/evaluationProviders.js +16 -33
  63. package/dist/lib/core/factory.js +237 -164
  64. package/dist/lib/core/modules/GenerationHandler.js +175 -116
  65. package/dist/lib/core/modules/MessageBuilder.js +222 -170
  66. package/dist/lib/core/modules/StreamHandler.d.ts +1 -0
  67. package/dist/lib/core/modules/StreamHandler.js +95 -27
  68. package/dist/lib/core/modules/TelemetryHandler.d.ts +10 -1
  69. package/dist/lib/core/modules/TelemetryHandler.js +25 -7
  70. package/dist/lib/core/modules/ToolsManager.js +115 -191
  71. package/dist/lib/core/redisConversationMemoryManager.js +418 -282
  72. package/dist/lib/factories/providerRegistry.d.ts +5 -0
  73. package/dist/lib/factories/providerRegistry.js +20 -2
  74. package/dist/lib/index.d.ts +2 -2
  75. package/dist/lib/index.js +4 -2
  76. package/dist/lib/mcp/externalServerManager.js +66 -0
  77. package/dist/lib/mcp/mcpCircuitBreaker.js +24 -0
  78. package/dist/lib/mcp/mcpClientFactory.js +16 -0
  79. package/dist/lib/mcp/toolDiscoveryService.js +32 -6
  80. package/dist/lib/mcp/toolRegistry.js +193 -123
  81. package/dist/lib/neurolink.d.ts +6 -0
  82. package/dist/lib/neurolink.js +1162 -646
  83. package/dist/lib/providers/amazonBedrock.d.ts +1 -1
  84. package/dist/lib/providers/amazonBedrock.js +521 -319
  85. package/dist/lib/providers/anthropic.js +73 -17
  86. package/dist/lib/providers/anthropicBaseProvider.js +77 -17
  87. package/dist/lib/providers/googleAiStudio.d.ts +1 -1
  88. package/dist/lib/providers/googleAiStudio.js +292 -227
  89. package/dist/lib/providers/googleVertex.d.ts +36 -1
  90. package/dist/lib/providers/googleVertex.js +553 -260
  91. package/dist/lib/providers/ollama.js +329 -278
  92. package/dist/lib/providers/openAI.js +77 -19
  93. package/dist/lib/providers/sagemaker/parsers.js +3 -3
  94. package/dist/lib/providers/sagemaker/streaming.js +3 -3
  95. package/dist/lib/proxy/proxyFetch.js +81 -48
  96. package/dist/lib/rag/ChunkerFactory.js +1 -1
  97. package/dist/lib/rag/chunkers/MarkdownChunker.d.ts +22 -0
  98. package/dist/lib/rag/chunkers/MarkdownChunker.js +213 -9
  99. package/dist/lib/rag/chunking/markdownChunker.d.ts +16 -0
  100. package/dist/lib/rag/chunking/markdownChunker.js +174 -2
  101. package/dist/lib/rag/pipeline/contextAssembly.js +2 -1
  102. package/dist/lib/rag/ragIntegration.d.ts +18 -1
  103. package/dist/lib/rag/ragIntegration.js +94 -14
  104. package/dist/lib/rag/retrieval/vectorQueryTool.js +21 -4
  105. package/dist/lib/server/abstract/baseServerAdapter.js +4 -1
  106. package/dist/lib/server/adapters/fastifyAdapter.js +35 -30
  107. package/dist/lib/services/server/ai/observability/instrumentation.d.ts +32 -0
  108. package/dist/lib/services/server/ai/observability/instrumentation.js +39 -0
  109. package/dist/lib/telemetry/attributes.d.ts +52 -0
  110. package/dist/lib/telemetry/attributes.js +61 -0
  111. package/dist/lib/telemetry/index.d.ts +3 -0
  112. package/dist/lib/telemetry/index.js +3 -0
  113. package/dist/lib/telemetry/telemetryService.d.ts +6 -0
  114. package/dist/lib/telemetry/telemetryService.js +6 -0
  115. package/dist/lib/telemetry/tracers.d.ts +15 -0
  116. package/dist/lib/telemetry/tracers.js +17 -0
  117. package/dist/lib/telemetry/withSpan.d.ts +9 -0
  118. package/dist/lib/telemetry/withSpan.js +35 -0
  119. package/dist/lib/types/contextTypes.d.ts +10 -0
  120. package/dist/lib/types/streamTypes.d.ts +14 -0
  121. package/dist/lib/utils/conversationMemory.js +121 -82
  122. package/dist/lib/utils/logger.d.ts +5 -0
  123. package/dist/lib/utils/logger.js +50 -2
  124. package/dist/lib/utils/messageBuilder.js +22 -42
  125. package/dist/lib/utils/modelDetection.js +3 -3
  126. package/dist/lib/utils/providerRetry.d.ts +41 -0
  127. package/dist/lib/utils/providerRetry.js +114 -0
  128. package/dist/lib/utils/retryability.d.ts +14 -0
  129. package/dist/lib/utils/retryability.js +23 -0
  130. package/dist/lib/utils/sanitizers/svg.js +4 -5
  131. package/dist/lib/utils/tokenEstimation.d.ts +11 -1
  132. package/dist/lib/utils/tokenEstimation.js +19 -4
  133. package/dist/lib/utils/videoAnalysisProcessor.js +7 -3
  134. package/dist/mcp/externalServerManager.js +66 -0
  135. package/dist/mcp/mcpCircuitBreaker.js +24 -0
  136. package/dist/mcp/mcpClientFactory.js +16 -0
  137. package/dist/mcp/toolDiscoveryService.js +32 -6
  138. package/dist/mcp/toolRegistry.js +193 -123
  139. package/dist/neurolink.d.ts +6 -0
  140. package/dist/neurolink.js +1162 -646
  141. package/dist/providers/amazonBedrock.d.ts +1 -1
  142. package/dist/providers/amazonBedrock.js +521 -319
  143. package/dist/providers/anthropic.js +73 -17
  144. package/dist/providers/anthropicBaseProvider.js +77 -17
  145. package/dist/providers/googleAiStudio.d.ts +1 -1
  146. package/dist/providers/googleAiStudio.js +292 -227
  147. package/dist/providers/googleVertex.d.ts +36 -1
  148. package/dist/providers/googleVertex.js +553 -260
  149. package/dist/providers/ollama.js +329 -278
  150. package/dist/providers/openAI.js +77 -19
  151. package/dist/providers/sagemaker/parsers.js +3 -3
  152. package/dist/providers/sagemaker/streaming.js +3 -3
  153. package/dist/proxy/proxyFetch.js +81 -48
  154. package/dist/rag/ChunkerFactory.js +1 -1
  155. package/dist/rag/chunkers/MarkdownChunker.d.ts +22 -0
  156. package/dist/rag/chunkers/MarkdownChunker.js +213 -9
  157. package/dist/rag/chunking/markdownChunker.d.ts +16 -0
  158. package/dist/rag/chunking/markdownChunker.js +174 -2
  159. package/dist/rag/pipeline/contextAssembly.js +2 -1
  160. package/dist/rag/ragIntegration.d.ts +18 -1
  161. package/dist/rag/ragIntegration.js +94 -14
  162. package/dist/rag/retrieval/vectorQueryTool.js +21 -4
  163. package/dist/server/abstract/baseServerAdapter.js +4 -1
  164. package/dist/server/adapters/fastifyAdapter.js +35 -30
  165. package/dist/services/server/ai/observability/instrumentation.d.ts +32 -0
  166. package/dist/services/server/ai/observability/instrumentation.js +39 -0
  167. package/dist/telemetry/attributes.d.ts +52 -0
  168. package/dist/telemetry/attributes.js +60 -0
  169. package/dist/telemetry/index.d.ts +3 -0
  170. package/dist/telemetry/index.js +3 -0
  171. package/dist/telemetry/telemetryService.d.ts +6 -0
  172. package/dist/telemetry/telemetryService.js +6 -0
  173. package/dist/telemetry/tracers.d.ts +15 -0
  174. package/dist/telemetry/tracers.js +16 -0
  175. package/dist/telemetry/withSpan.d.ts +9 -0
  176. package/dist/telemetry/withSpan.js +34 -0
  177. package/dist/types/contextTypes.d.ts +10 -0
  178. package/dist/types/streamTypes.d.ts +14 -0
  179. package/dist/utils/conversationMemory.js +121 -82
  180. package/dist/utils/logger.d.ts +5 -0
  181. package/dist/utils/logger.js +50 -2
  182. package/dist/utils/messageBuilder.js +22 -42
  183. package/dist/utils/modelDetection.js +3 -3
  184. package/dist/utils/providerRetry.d.ts +41 -0
  185. package/dist/utils/providerRetry.js +113 -0
  186. package/dist/utils/retryability.d.ts +14 -0
  187. package/dist/utils/retryability.js +22 -0
  188. package/dist/utils/sanitizers/svg.js +4 -5
  189. package/dist/utils/tokenEstimation.d.ts +11 -1
  190. package/dist/utils/tokenEstimation.js +19 -4
  191. package/dist/utils/videoAnalysisProcessor.js +7 -3
  192. package/dist/workflow/config.d.ts +26 -26
  193. package/package.json +1 -1
@@ -5,11 +5,16 @@ import type { ProviderRegistryOptions } from "../types/index.js";
5
5
  */
6
6
  export declare class ProviderRegistry {
7
7
  private static registered;
8
+ private static registrationPromise;
8
9
  private static options;
9
10
  /**
10
11
  * Register all providers with the factory
11
12
  */
12
13
  static registerAllProviders(): Promise<void>;
14
+ /**
15
+ * Internal registration implementation
16
+ */
17
+ private static _doRegister;
13
18
  /**
14
19
  * Check if providers are registered
15
20
  */
@@ -7,6 +7,7 @@ import { AIProviderName, GoogleAIModels, OpenAIModels, AnthropicModels, VertexMo
7
7
  */
8
8
  export class ProviderRegistry {
9
9
  static registered = false;
10
+ static registrationPromise = null;
10
11
  static options = {
11
12
  enableManualMCP: false, // Default to disabled for safety
12
13
  };
@@ -17,6 +18,22 @@ export class ProviderRegistry {
17
18
  if (this.registered) {
18
19
  return;
19
20
  }
21
+ if (this.registrationPromise) {
22
+ return this.registrationPromise;
23
+ }
24
+ this.registrationPromise = this._doRegister();
25
+ try {
26
+ await this.registrationPromise;
27
+ }
28
+ catch (error) {
29
+ this.registrationPromise = null; // Allow retry on failure
30
+ throw error;
31
+ }
32
+ }
33
+ /**
34
+ * Internal registration implementation
35
+ */
36
+ static async _doRegister() {
20
37
  try {
21
38
  // Register providers with dynamic import factory functions
22
39
  const { ProviderFactory } = await import("./providerFactory.js");
@@ -34,7 +51,7 @@ export class ProviderRegistry {
34
51
  ProviderFactory.registerProvider(AIProviderName.ANTHROPIC, async (modelName, _providerName, sdk) => {
35
52
  const { AnthropicProvider } = await import("../providers/anthropic.js");
36
53
  return new AnthropicProvider(modelName, sdk);
37
- }, AnthropicModels.CLAUDE_SONNET_4_0, ["claude", "anthropic"]);
54
+ }, AnthropicModels.CLAUDE_SONNET_4_6, ["claude", "anthropic"]);
38
55
  // Register Amazon Bedrock provider
39
56
  ProviderFactory.registerProvider(AIProviderName.BEDROCK, async (modelName, _providerName, sdk, region) => {
40
57
  const { AmazonBedrockProvider } = await import("../providers/amazonBedrock.js");
@@ -54,7 +71,7 @@ export class ProviderRegistry {
54
71
  ProviderFactory.registerProvider(AIProviderName.VERTEX, async (modelName, providerName, sdk, region) => {
55
72
  const { GoogleVertexProvider } = await import("../providers/googleVertex.js");
56
73
  return new GoogleVertexProvider(modelName, providerName, sdk, region);
57
- }, VertexModels.CLAUDE_4_0_SONNET, ["vertex", "googleVertex"]);
74
+ }, VertexModels.CLAUDE_4_6_SONNET, ["vertex", "googleVertex"]);
58
75
  // Register Hugging Face provider (Unified Router implementation)
59
76
  ProviderFactory.registerProvider(AIProviderName.HUGGINGFACE, async (modelName) => {
60
77
  const { HuggingFaceProvider } = await import("../providers/huggingFace.js");
@@ -130,6 +147,7 @@ export class ProviderRegistry {
130
147
  static clearRegistrations() {
131
148
  ProviderFactory.clearRegistrations();
132
149
  this.registered = false;
150
+ this.registrationPromise = null;
133
151
  }
134
152
  /**
135
153
  * Set registry options (should be called before initialization)
@@ -49,9 +49,9 @@ export { NeuroLink };
49
49
  export type { MCPServerInfo } from "./types/mcpTypes.js";
50
50
  export type { LangfuseConfig, LangfuseSpanAttributes, ObservabilityConfig, OpenTelemetryConfig, TraceNameFormat, } from "./types/observability.js";
51
51
  export { buildObservabilityConfigFromEnv } from "./utils/observabilityHelpers.js";
52
- import { createContextEnricher, flushOpenTelemetry, getLangfuseContext, getLangfuseHealthStatus, getLangfuseSpanProcessor, getSpanProcessors, getTracer, getTracerProvider, initializeOpenTelemetry, isOpenTelemetryInitialized, isUsingExternalTracerProvider, setLangfuseContext, shutdownOpenTelemetry } from "./services/server/ai/observability/instrumentation.js";
52
+ import { createContextEnricher, flushOpenTelemetry, getLangfuseContext, getLangfuseHealthStatus, getLangfuseSpanProcessor, getSpanProcessors, getTracer, getTracerProvider, initializeOpenTelemetry, isOpenTelemetryInitialized, isUsingExternalTracerProvider, runWithCurrentLangfuseContext, setLangfuseContext, shutdownOpenTelemetry } from "./services/server/ai/observability/instrumentation.js";
53
53
  export type { LangfuseContext } from "./services/server/ai/observability/instrumentation.js";
54
- export { initializeOpenTelemetry, shutdownOpenTelemetry, flushOpenTelemetry, getLangfuseHealthStatus, setLangfuseContext, getLangfuseSpanProcessor, getTracerProvider, isOpenTelemetryInitialized, getSpanProcessors, createContextEnricher, isUsingExternalTracerProvider, getLangfuseContext, getTracer, };
54
+ export { initializeOpenTelemetry, shutdownOpenTelemetry, flushOpenTelemetry, getLangfuseHealthStatus, setLangfuseContext, getLangfuseSpanProcessor, getTracerProvider, isOpenTelemetryInitialized, getSpanProcessors, createContextEnricher, isUsingExternalTracerProvider, getLangfuseContext, getTracer, runWithCurrentLangfuseContext, };
55
55
  export { clearAnalyticsMetrics, createAnalyticsMiddleware, getAnalyticsMetrics, } from "./middleware/builtin/analytics.js";
56
56
  export { MiddlewareFactory } from "./middleware/factory.js";
57
57
  export type { MiddlewareConfig, MiddlewareContext, MiddlewareFactoryOptions, MiddlewarePreset, NeuroLinkMiddleware, } from "./types/middlewareTypes.js";
package/dist/lib/index.js CHANGED
@@ -60,13 +60,15 @@ import { createContextEnricher, flushOpenTelemetry,
60
60
  // Enhanced context and tracing
61
61
  getLangfuseContext, getLangfuseHealthStatus, getLangfuseSpanProcessor,
62
62
  // NEW EXPORTS - External TracerProvider Support
63
- getSpanProcessors, getTracer, getTracerProvider, initializeOpenTelemetry, isOpenTelemetryInitialized, isUsingExternalTracerProvider, setLangfuseContext, shutdownOpenTelemetry, } from "./services/server/ai/observability/instrumentation.js";
63
+ getSpanProcessors, getTracer, getTracerProvider, initializeOpenTelemetry, isOpenTelemetryInitialized, isUsingExternalTracerProvider, runWithCurrentLangfuseContext, setLangfuseContext, shutdownOpenTelemetry, } from "./services/server/ai/observability/instrumentation.js";
64
64
  import { getTelemetryStatus as getStatus, initializeTelemetry as init, } from "./telemetry/index.js";
65
65
  export { initializeOpenTelemetry, shutdownOpenTelemetry, flushOpenTelemetry, getLangfuseHealthStatus, setLangfuseContext, getLangfuseSpanProcessor, getTracerProvider, isOpenTelemetryInitialized,
66
66
  // NEW EXPORTS - External TracerProvider Support
67
67
  getSpanProcessors, createContextEnricher, isUsingExternalTracerProvider,
68
68
  // Enhanced context and tracing
69
- getLangfuseContext, getTracer, };
69
+ getLangfuseContext, getTracer,
70
+ // ALS context propagation helper
71
+ runWithCurrentLangfuseContext, };
70
72
  // Analytics Middleware exports
71
73
  export { clearAnalyticsMetrics, createAnalyticsMiddleware, getAnalyticsMetrics, } from "./middleware/builtin/analytics.js";
72
74
  export { MiddlewareFactory } from "./middleware/factory.js";
@@ -14,6 +14,9 @@ import { toolRegistry } from "./toolRegistry.js";
14
14
  import { HITLUserRejectedError, HITLTimeoutError } from "../hitl/hitlErrors.js";
15
15
  import { detectCategory } from "../utils/mcpDefaults.js";
16
16
  import { isObject, isNonNullObject } from "../utils/typeUtils.js";
17
+ import { TelemetryService } from "../telemetry/telemetryService.js";
18
+ import { tracers } from "../telemetry/tracers.js";
19
+ import { SpanStatusCode } from "@opentelemetry/api";
17
20
  /**
18
21
  * Recursively substitute environment variables in strings
19
22
  * Replaces ${VAR_NAME} with the value from process.env.VAR_NAME
@@ -745,6 +748,16 @@ export class ExternalServerManager extends EventEmitter {
745
748
  throw new Error(`Server '${serverId}' not found`);
746
749
  }
747
750
  const config = instance.config;
751
+ const span = tracers.mcp.startSpan("neurolink.mcp.server.start", {
752
+ attributes: {
753
+ "mcp.server_id": serverId,
754
+ "mcp.transport": config.transport,
755
+ "mcp.command_name": config.command
756
+ ? config.command.split(/[\\/]/).pop() || ""
757
+ : "",
758
+ "mcp.command_present": Boolean(config.command),
759
+ },
760
+ });
748
761
  try {
749
762
  this.updateServerStatus(serverId, "connecting");
750
763
  mcpLogger.debug(`[ExternalServerManager] Starting server: ${serverId}`, {
@@ -803,6 +816,8 @@ export class ExternalServerManager extends EventEmitter {
803
816
  toolCount: instance.toolsMap.size,
804
817
  timestamp: new Date(),
805
818
  });
819
+ span.setAttribute("mcp.tool_count", instance.toolsMap.size);
820
+ span.setStatus({ code: SpanStatusCode.OK });
806
821
  mcpLogger.info(`[ExternalServerManager] Server started successfully: ${serverId}`);
807
822
  }
808
823
  catch (error) {
@@ -810,8 +825,16 @@ export class ExternalServerManager extends EventEmitter {
810
825
  this.updateServerStatus(serverId, "failed");
811
826
  instance.lastError =
812
827
  error instanceof Error ? error.message : String(error);
828
+ span.recordException(error instanceof Error ? error : new Error(String(error)));
829
+ span.setStatus({
830
+ code: SpanStatusCode.ERROR,
831
+ message: error instanceof Error ? error.message : String(error),
832
+ });
813
833
  throw error;
814
834
  }
835
+ finally {
836
+ span.end();
837
+ }
815
838
  }
816
839
  /**
817
840
  * Stop an external MCP server
@@ -821,6 +844,11 @@ export class ExternalServerManager extends EventEmitter {
821
844
  if (!instance) {
822
845
  return;
823
846
  }
847
+ const span = tracers.mcp.startSpan("neurolink.mcp.server.stop", {
848
+ attributes: {
849
+ "mcp.server_id": serverId,
850
+ },
851
+ });
824
852
  try {
825
853
  this.updateServerStatus(serverId, "stopping");
826
854
  // Clear timers
@@ -851,11 +879,20 @@ export class ExternalServerManager extends EventEmitter {
851
879
  instance.process = null;
852
880
  }
853
881
  this.updateServerStatus(serverId, "stopped");
882
+ span.setStatus({ code: SpanStatusCode.OK });
854
883
  mcpLogger.info(`[ExternalServerManager] Server stopped: ${serverId}`);
855
884
  }
856
885
  catch (error) {
857
886
  mcpLogger.error(`[ExternalServerManager] Error stopping server ${serverId}:`, error);
858
887
  this.updateServerStatus(serverId, "failed");
888
+ span.recordException(error instanceof Error ? error : new Error(String(error)));
889
+ span.setStatus({
890
+ code: SpanStatusCode.ERROR,
891
+ message: error instanceof Error ? error.message : String(error),
892
+ });
893
+ }
894
+ finally {
895
+ span.end();
859
896
  }
860
897
  }
861
898
  /**
@@ -959,16 +996,32 @@ export class ExternalServerManager extends EventEmitter {
959
996
  return;
960
997
  } // already scheduled
961
998
  instance.restartTimer = setTimeout(async () => {
999
+ const restartSpan = tracers.mcp.startSpan("neurolink.mcp.server.restart", {
1000
+ attributes: {
1001
+ "mcp.server_id": serverId,
1002
+ "mcp.restart_attempt": instance.reconnectAttempts,
1003
+ "mcp.restart_delay_ms": delay,
1004
+ },
1005
+ });
962
1006
  try {
963
1007
  await this.stopServer(serverId);
964
1008
  await this.startServer(serverId);
965
1009
  // Reset restart attempts on successful restart
966
1010
  instance.reconnectAttempts = 0;
1011
+ restartSpan.setStatus({ code: SpanStatusCode.OK });
967
1012
  }
968
1013
  catch (error) {
969
1014
  mcpLogger.error(`[ExternalServerManager] Restart failed for ${serverId}:`, error);
1015
+ restartSpan.recordException(error instanceof Error ? error : new Error(String(error)));
1016
+ restartSpan.setStatus({
1017
+ code: SpanStatusCode.ERROR,
1018
+ message: error instanceof Error ? error.message : String(error),
1019
+ });
970
1020
  this.scheduleRestart(serverId); // Try again
971
1021
  }
1022
+ finally {
1023
+ restartSpan.end();
1024
+ }
972
1025
  }, delay);
973
1026
  }
974
1027
  /**
@@ -1376,6 +1429,12 @@ export class ExternalServerManager extends EventEmitter {
1376
1429
  mcpLogger.debug(`[ExternalServerManager] Tool executed successfully: ${toolName} on ${serverId}`, {
1377
1430
  duration,
1378
1431
  });
1432
+ try {
1433
+ TelemetryService.getInstance()?.recordMCPToolCall(toolName, duration, true);
1434
+ }
1435
+ catch {
1436
+ /* telemetry should not break execution */
1437
+ }
1379
1438
  return result.data;
1380
1439
  }
1381
1440
  else {
@@ -1384,6 +1443,13 @@ export class ExternalServerManager extends EventEmitter {
1384
1443
  }
1385
1444
  catch (error) {
1386
1445
  instance.metrics.totalErrors++;
1446
+ try {
1447
+ const errorDuration = Date.now() - startTime;
1448
+ TelemetryService.getInstance()?.recordMCPToolCall(toolName, errorDuration, false);
1449
+ }
1450
+ catch {
1451
+ /* telemetry should not break execution */
1452
+ }
1387
1453
  mcpLogger.error(`[ExternalServerManager] Tool execution failed: ${toolName} on ${serverId}`, error);
1388
1454
  throw error;
1389
1455
  }
@@ -4,6 +4,7 @@
4
4
  * Provides fault tolerance and prevents cascading failures
5
5
  */
6
6
  import { EventEmitter } from "events";
7
+ import { trace } from "@opentelemetry/api";
7
8
  import { mcpLogger } from "../utils/logger.js";
8
9
  /**
9
10
  * MCPCircuitBreaker
@@ -53,6 +54,17 @@ export class MCPCircuitBreaker extends EventEmitter {
53
54
  this.halfOpenCalls >= this.config.halfOpenMaxCalls) {
54
55
  throw new Error(`Circuit breaker '${this.name}' is half-open but call limit reached`);
55
56
  }
57
+ // NLK-GAP-009: Record half-open test event when executing in half-open state
58
+ if (this.state === "half-open") {
59
+ const activeSpan = trace.getActiveSpan();
60
+ if (activeSpan) {
61
+ activeSpan.addEvent("circuit.half_open_test", {
62
+ "circuit.name": this.name,
63
+ "circuit.half_open_call": this.halfOpenCalls + 1,
64
+ "circuit.half_open_max_calls": this.config.halfOpenMaxCalls,
65
+ });
66
+ }
67
+ }
56
68
  // Execute operation with timeout
57
69
  const result = await Promise.race([
58
70
  operation(),
@@ -145,6 +157,18 @@ export class MCPCircuitBreaker extends EventEmitter {
145
157
  const oldState = this.state;
146
158
  this.state = newState;
147
159
  this.lastStateChange = new Date();
160
+ // NLK-GAP-009: Record state transition on active OTel span
161
+ const activeSpan = trace.getActiveSpan();
162
+ if (activeSpan) {
163
+ activeSpan.addEvent("circuit.state_change", {
164
+ "circuit.name": this.name,
165
+ "circuit.from_state": oldState,
166
+ "circuit.to_state": newState,
167
+ "circuit.reason": reason.slice(0, 128),
168
+ "circuit.failure_count": this.callHistory.filter((c) => !c.success)
169
+ .length,
170
+ });
171
+ }
148
172
  // Reset counters based on state
149
173
  if (newState === "half-open") {
150
174
  this.halfOpenCalls = 0;
@@ -15,6 +15,8 @@ import { globalCircuitBreakerManager } from "./mcpCircuitBreaker.js";
15
15
  import { withHTTPRetry, DEFAULT_HTTP_RETRY_CONFIG, } from "./httpRetryHandler.js";
16
16
  import { globalRateLimiterManager } from "./httpRateLimiter.js";
17
17
  import { NeuroLinkOAuthProvider, InMemoryTokenStorage } from "./auth/index.js";
18
+ import { tracers } from "../telemetry/tracers.js";
19
+ import { SpanStatusCode } from "@opentelemetry/api";
18
20
  /**
19
21
  * MCPClientFactory
20
22
  * Factory class for creating MCP clients with different transports
@@ -36,6 +38,13 @@ export class MCPClientFactory {
36
38
  */
37
39
  static async createClient(config, timeout = 10000) {
38
40
  const startTime = Date.now();
41
+ const span = tracers.mcp.startSpan("neurolink.mcp.client.create", {
42
+ attributes: {
43
+ "mcp.server_id": config.id,
44
+ "mcp.transport": config.transport,
45
+ "mcp.timeout_ms": timeout,
46
+ },
47
+ });
39
48
  try {
40
49
  mcpLogger.info(`[MCPClientFactory] Creating client for ${config.id}`, {
41
50
  transport: config.transport,
@@ -94,6 +103,7 @@ export class MCPClientFactory {
94
103
  duration: Date.now() - startTime,
95
104
  capabilities: result.capabilities,
96
105
  });
106
+ span.setStatus({ code: SpanStatusCode.OK });
97
107
  return {
98
108
  ...result,
99
109
  success: true,
@@ -103,12 +113,18 @@ export class MCPClientFactory {
103
113
  catch (error) {
104
114
  const errorMessage = error instanceof Error ? error.message : String(error);
105
115
  mcpLogger.error(`[MCPClientFactory] Failed to create client for ${config.id}:`, error);
116
+ // NLK-GAP-004 fix: Record both exception AND error status on span
117
+ span.recordException(error instanceof Error ? error : new Error(errorMessage));
118
+ span.setStatus({ code: SpanStatusCode.ERROR, message: errorMessage });
106
119
  return {
107
120
  success: false,
108
121
  error: errorMessage,
109
122
  duration: Date.now() - startTime,
110
123
  };
111
124
  }
125
+ finally {
126
+ span.end();
127
+ }
112
128
  }
113
129
  /**
114
130
  * Internal client creation logic
@@ -8,6 +8,10 @@ import { mcpLogger } from "../utils/logger.js";
8
8
  import { globalCircuitBreakerManager } from "./mcpCircuitBreaker.js";
9
9
  import { isObject, isNullish } from "../utils/typeUtils.js";
10
10
  import { validateToolName, validateToolDescription, } from "../utils/parameterValidation.js";
11
+ import { withTimeout } from "../utils/errorHandling.js";
12
+ import { SpanKind, SpanStatusCode } from "@opentelemetry/api";
13
+ import { tracers } from "../telemetry/tracers.js";
14
+ const mcpTracer = tracers.mcp;
11
15
  /**
12
16
  * ToolDiscoveryService
13
17
  * Handles automatic tool discovery and registration from external MCP servers
@@ -336,13 +340,35 @@ export class ToolDiscoveryService extends EventEmitter {
336
340
  });
337
341
  // Execute tool with circuit breaker protection
338
342
  const result = await circuitBreaker.execute(async () => {
339
- const timeout = options.timeout || 30000;
340
- const executePromise = client.callTool({
341
- name: toolName,
342
- arguments: parameters,
343
+ return mcpTracer.startActiveSpan("neurolink.mcp.callTool", {
344
+ kind: SpanKind.CLIENT,
345
+ attributes: {
346
+ "mcp.server_id": serverId,
347
+ "mcp.tool_name": toolName,
348
+ "mcp.timeout_ms": options.timeout || 30000,
349
+ },
350
+ }, async (callSpan) => {
351
+ try {
352
+ const timeout = options.timeout || 30000;
353
+ const callResult = await withTimeout(client.callTool({
354
+ name: toolName,
355
+ arguments: parameters,
356
+ }), timeout, new Error(`Tool execution timeout: ${toolName}`));
357
+ callSpan.setStatus({ code: SpanStatusCode.OK });
358
+ return callResult;
359
+ }
360
+ catch (err) {
361
+ callSpan.setStatus({
362
+ code: SpanStatusCode.ERROR,
363
+ message: err.message,
364
+ });
365
+ callSpan.recordException(err);
366
+ throw err;
367
+ }
368
+ finally {
369
+ callSpan.end();
370
+ }
343
371
  });
344
- const timeoutPromise = this.createTimeoutPromise(timeout, `Tool execution timeout: ${toolName}`);
345
- return await Promise.race([executePromise, timeoutPromise]);
346
372
  });
347
373
  const duration = Date.now() - startTime;
348
374
  // Update tool statistics