@juspay/neurolink 7.14.2 → 7.14.4

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 (99) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/cli/commands/config.d.ts +66 -66
  3. package/dist/core/baseProvider.d.ts +12 -7
  4. package/dist/core/baseProvider.js +118 -125
  5. package/dist/core/constants.d.ts +5 -0
  6. package/dist/core/constants.js +6 -0
  7. package/dist/core/dynamicModels.d.ts +4 -4
  8. package/dist/core/factory.d.ts +2 -4
  9. package/dist/core/types.d.ts +8 -22
  10. package/dist/index.d.ts +1 -4
  11. package/dist/lib/core/baseProvider.d.ts +12 -7
  12. package/dist/lib/core/baseProvider.js +118 -125
  13. package/dist/lib/core/constants.d.ts +5 -0
  14. package/dist/lib/core/constants.js +6 -0
  15. package/dist/lib/core/dynamicModels.d.ts +8 -8
  16. package/dist/lib/core/factory.d.ts +2 -4
  17. package/dist/lib/core/types.d.ts +8 -22
  18. package/dist/lib/index.d.ts +1 -4
  19. package/dist/lib/mcp/contracts/mcpContract.d.ts +6 -19
  20. package/dist/lib/mcp/externalServerManager.d.ts +2 -4
  21. package/dist/lib/mcp/externalServerManager.js +7 -8
  22. package/dist/lib/mcp/factory.d.ts +61 -7
  23. package/dist/lib/mcp/factory.js +36 -23
  24. package/dist/lib/mcp/mcpClientFactory.d.ts +2 -1
  25. package/dist/lib/mcp/mcpClientFactory.js +73 -26
  26. package/dist/lib/mcp/registry.d.ts +1 -1
  27. package/dist/lib/mcp/toolDiscoveryService.d.ts +1 -1
  28. package/dist/lib/mcp/toolDiscoveryService.js +50 -19
  29. package/dist/lib/mcp/toolRegistry.d.ts +23 -1
  30. package/dist/lib/mcp/toolRegistry.js +108 -17
  31. package/dist/lib/models/modelResolver.js +2 -1
  32. package/dist/lib/neurolink.d.ts +12 -8
  33. package/dist/lib/neurolink.js +130 -134
  34. package/dist/lib/providers/amazonBedrock.d.ts +2 -2
  35. package/dist/lib/providers/anthropic.d.ts +3 -3
  36. package/dist/lib/providers/googleAiStudio.d.ts +2 -2
  37. package/dist/lib/providers/mistral.d.ts +3 -3
  38. package/dist/lib/providers/ollama.d.ts +2 -2
  39. package/dist/lib/providers/openAI.d.ts +3 -3
  40. package/dist/lib/providers/openaiCompatible.d.ts +2 -2
  41. package/dist/lib/providers/sagemaker/client.d.ts +2 -5
  42. package/dist/lib/providers/sagemaker/language-model.d.ts +4 -6
  43. package/dist/lib/providers/sagemaker/parsers.js +5 -4
  44. package/dist/lib/sdk/toolRegistration.d.ts +6 -6
  45. package/dist/lib/sdk/toolRegistration.js +17 -56
  46. package/dist/lib/types/generateTypes.d.ts +9 -9
  47. package/dist/lib/types/streamTypes.d.ts +4 -4
  48. package/dist/lib/types/tools.d.ts +15 -7
  49. package/dist/lib/types/typeAliases.d.ts +412 -0
  50. package/dist/lib/types/typeAliases.js +48 -0
  51. package/dist/lib/utils/factoryProcessing.d.ts +2 -1
  52. package/dist/lib/utils/factoryProcessing.js +4 -3
  53. package/dist/lib/utils/parameterValidation.d.ts +97 -0
  54. package/dist/lib/utils/parameterValidation.js +452 -0
  55. package/dist/lib/utils/transformationUtils.d.ts +204 -0
  56. package/dist/lib/utils/transformationUtils.js +334 -0
  57. package/dist/lib/utils/typeUtils.d.ts +77 -0
  58. package/dist/lib/utils/typeUtils.js +97 -0
  59. package/dist/mcp/contracts/mcpContract.d.ts +6 -19
  60. package/dist/mcp/externalServerManager.d.ts +2 -4
  61. package/dist/mcp/externalServerManager.js +7 -8
  62. package/dist/mcp/factory.d.ts +61 -7
  63. package/dist/mcp/factory.js +36 -23
  64. package/dist/mcp/mcpClientFactory.d.ts +2 -1
  65. package/dist/mcp/mcpClientFactory.js +73 -26
  66. package/dist/mcp/registry.d.ts +1 -1
  67. package/dist/mcp/toolDiscoveryService.d.ts +1 -1
  68. package/dist/mcp/toolDiscoveryService.js +50 -19
  69. package/dist/mcp/toolRegistry.d.ts +23 -1
  70. package/dist/mcp/toolRegistry.js +108 -17
  71. package/dist/models/modelResolver.js +2 -1
  72. package/dist/neurolink.d.ts +12 -8
  73. package/dist/neurolink.js +130 -134
  74. package/dist/providers/amazonBedrock.d.ts +2 -2
  75. package/dist/providers/anthropic.d.ts +3 -3
  76. package/dist/providers/googleAiStudio.d.ts +2 -2
  77. package/dist/providers/mistral.d.ts +3 -3
  78. package/dist/providers/ollama.d.ts +2 -2
  79. package/dist/providers/openAI.d.ts +3 -3
  80. package/dist/providers/openaiCompatible.d.ts +2 -2
  81. package/dist/providers/sagemaker/client.d.ts +2 -5
  82. package/dist/providers/sagemaker/language-model.d.ts +4 -6
  83. package/dist/providers/sagemaker/parsers.js +5 -4
  84. package/dist/sdk/toolRegistration.d.ts +6 -6
  85. package/dist/sdk/toolRegistration.js +17 -56
  86. package/dist/types/generateTypes.d.ts +9 -9
  87. package/dist/types/streamTypes.d.ts +4 -4
  88. package/dist/types/tools.d.ts +15 -7
  89. package/dist/types/typeAliases.d.ts +412 -0
  90. package/dist/types/typeAliases.js +48 -0
  91. package/dist/utils/factoryProcessing.d.ts +2 -1
  92. package/dist/utils/factoryProcessing.js +4 -3
  93. package/dist/utils/parameterValidation.d.ts +97 -0
  94. package/dist/utils/parameterValidation.js +452 -0
  95. package/dist/utils/transformationUtils.d.ts +204 -0
  96. package/dist/utils/transformationUtils.js +334 -0
  97. package/dist/utils/typeUtils.d.ts +77 -0
  98. package/dist/utils/typeUtils.js +97 -0
  99. package/package.json +1 -1
@@ -2,15 +2,16 @@
2
2
  * MCP Contract - Core Type Definitions
3
3
  * Industry standard camelCase interfaces for maximum flexibility
4
4
  */
5
+ import type { StandardRecord } from "../../types/typeAliases.js";
5
6
  /**
6
7
  * Generic execution context for MCP operations
7
8
  * All properties optional for maximum flexibility
8
9
  */
9
- export interface ExecutionContext<T = Record<string, unknown>> {
10
+ export interface ExecutionContext<T = StandardRecord> {
10
11
  sessionId?: string;
11
12
  userId?: string;
12
13
  config?: T;
13
- metadata?: Record<string, unknown>;
14
+ metadata?: StandardRecord;
14
15
  cacheOptions?: CacheOptions;
15
16
  fallbackOptions?: FallbackOptions;
16
17
  timeoutMs?: number;
@@ -41,14 +42,14 @@ export interface ToolInfo {
41
42
  description?: string;
42
43
  category?: string;
43
44
  serverId?: string;
44
- inputSchema?: Record<string, unknown>;
45
- outputSchema?: Record<string, unknown>;
45
+ inputSchema?: StandardRecord;
46
+ outputSchema?: StandardRecord;
46
47
  [key: string]: unknown;
47
48
  }
48
49
  /**
49
50
  * Discovered MCP server/plugin definition
50
51
  */
51
- export interface DiscoveredMcp<TTools = Record<string, unknown>> {
52
+ export interface DiscoveredMcp<TTools = StandardRecord> {
52
53
  metadata: McpMetadata;
53
54
  tools?: TTools;
54
55
  capabilities?: string[];
@@ -68,20 +69,6 @@ export interface McpMetadata {
68
69
  repository?: string;
69
70
  category?: string;
70
71
  }
71
- /**
72
- * Tool definition schema
73
- */
74
- export interface ToolDefinition {
75
- description?: string;
76
- inputSchema?: Record<string, unknown>;
77
- outputSchema?: Record<string, unknown>;
78
- category?: string;
79
- examples?: Array<{
80
- input: Record<string, unknown>;
81
- output: Record<string, unknown>;
82
- description?: string;
83
- }>;
84
- }
85
72
  /**
86
73
  * Tool execution result
87
74
  */
@@ -11,6 +11,7 @@ import { ToolDiscoveryService } from "./toolDiscoveryService.js";
11
11
  import type { ExternalMCPServerInstance, ExternalMCPServerHealth, ExternalMCPConfigValidation, ExternalMCPOperationResult, ExternalMCPManagerConfig, ExternalMCPToolInfo } from "../types/externalMcp.js";
12
12
  import type { MCPServerInfo } from "../types/mcpTypes.js";
13
13
  import type { JsonObject } from "../types/common.js";
14
+ import type { ServerLoadResult } from "../types/typeAliases.js";
14
15
  export declare class ExternalServerManager extends EventEmitter {
15
16
  private servers;
16
17
  private config;
@@ -26,10 +27,7 @@ export declare class ExternalServerManager extends EventEmitter {
26
27
  * @param configPath Optional path to config file (defaults to .mcp-config.json in cwd)
27
28
  * @returns Promise resolving to number of servers loaded
28
29
  */
29
- loadMCPConfiguration(configPath?: string): Promise<{
30
- serversLoaded: number;
31
- errors: string[];
32
- }>;
30
+ loadMCPConfiguration(configPath?: string): Promise<ServerLoadResult>;
33
31
  /**
34
32
  * Validate external MCP server configuration
35
33
  */
@@ -12,11 +12,12 @@ import { MCPClientFactory } from "./mcpClientFactory.js";
12
12
  import { ToolDiscoveryService } from "./toolDiscoveryService.js";
13
13
  import { toolRegistry } from "./toolRegistry.js";
14
14
  import { detectCategory } from "../utils/mcpDefaults.js";
15
+ import { isObject, isNonNullObject } from "../utils/typeUtils.js";
15
16
  /**
16
17
  * Type guard to validate if an object can be safely used as Record<string, JsonValue>
17
18
  */
18
19
  function isValidJsonRecord(value) {
19
- if (typeof value !== "object" || value === null || Array.isArray(value)) {
20
+ if (!isObject(value)) {
20
21
  return false;
21
22
  }
22
23
  const record = value;
@@ -35,7 +36,7 @@ function isValidJsonRecord(value) {
35
36
  typeof item === "boolean" ||
36
37
  item === null);
37
38
  }
38
- if (typeof val === "object" && val !== null) {
39
+ if (isNonNullObject(val)) {
39
40
  return isValidJsonRecord(val);
40
41
  }
41
42
  return false;
@@ -51,14 +52,13 @@ function safeMetadataConversion(metadata) {
51
52
  * Type guard to validate external MCP server configuration
52
53
  */
53
54
  function isValidExternalMCPServerConfig(config) {
54
- if (typeof config !== "object" || config === null) {
55
+ if (!isNonNullObject(config)) {
55
56
  return false;
56
57
  }
57
58
  const record = config;
58
59
  return (typeof record.command === "string" &&
59
60
  (record.args === undefined || Array.isArray(record.args)) &&
60
- (record.env === undefined ||
61
- (typeof record.env === "object" && record.env !== null)) &&
61
+ (record.env === undefined || isNonNullObject(record.env)) &&
62
62
  (record.transport === undefined || typeof record.transport === "string") &&
63
63
  (record.timeout === undefined || typeof record.timeout === "number") &&
64
64
  (record.retries === undefined || typeof record.retries === "number") &&
@@ -68,8 +68,7 @@ function isValidExternalMCPServerConfig(config) {
68
68
  typeof record.autoRestart === "boolean") &&
69
69
  (record.cwd === undefined || typeof record.cwd === "string") &&
70
70
  (record.url === undefined || typeof record.url === "string") &&
71
- (record.metadata === undefined ||
72
- (typeof record.metadata === "object" && record.metadata !== null)));
71
+ (record.metadata === undefined || isNonNullObject(record.metadata)));
73
72
  }
74
73
  export class ExternalServerManager extends EventEmitter {
75
74
  servers = new Map();
@@ -150,7 +149,7 @@ export class ExternalServerManager extends EventEmitter {
150
149
  args: Array.isArray(serverConfig.args)
151
150
  ? serverConfig.args
152
151
  : [],
153
- env: typeof serverConfig.env === "object" && serverConfig.env !== null
152
+ env: isNonNullObject(serverConfig.env)
154
153
  ? serverConfig.env
155
154
  : {},
156
155
  timeout: typeof serverConfig.timeout === "number"
@@ -3,7 +3,6 @@
3
3
  * Factory-First Architecture: MCP servers create tools for internal orchestration
4
4
  * Compatible with MCP patterns for seamless integration
5
5
  */
6
- import { z } from "zod";
7
6
  import type { ExecutionContext } from "./contracts/mcpContract.js";
8
7
  /**
9
8
  * MCP Server Categories for organization and discovery
@@ -71,19 +70,64 @@ export interface ToolResult {
71
70
  };
72
71
  }
73
72
  /**
74
- * MCP Tool Interface - Standard compatible with NeuroLink enhancements
73
+ * MCP Tool Interface - Standalone definition to avoid confusion with ToolDefinition execute signature
74
+ */
75
+ /**
76
+ * NeuroLink MCP Tool Interface - Standardized tool definition for MCP integration
77
+ *
78
+ * This interface defines the contract for all tools in the NeuroLink ecosystem,
79
+ * ensuring consistent execution patterns and metadata handling across different
80
+ * MCP servers and tool implementations.
81
+ *
82
+ * Key features:
83
+ * - Promise-based execution with ToolResult return type
84
+ * - Rich context support for session management and permissions
85
+ * - Optional schema validation for input/output
86
+ * - Comprehensive metadata support for tool discovery
87
+ *
88
+ * @example
89
+ * ```typescript
90
+ * const calculatorTool: NeuroLinkMCPTool = {
91
+ * name: "calculator",
92
+ * description: "Performs basic arithmetic operations",
93
+ * category: "math",
94
+ * inputSchema: z.object({ a: z.number(), b: z.number(), op: z.string() }),
95
+ * async execute(params, context) {
96
+ * const { a, b, op } = params as { a: number; b: number; op: string };
97
+ * const result = op === "add" ? a + b : a - b;
98
+ * return { success: true, data: result };
99
+ * }
100
+ * };
101
+ * ```
75
102
  */
76
103
  export interface NeuroLinkMCPTool {
104
+ /** Unique tool identifier for MCP registration and execution */
77
105
  name: string;
106
+ /** Human-readable description of tool functionality */
78
107
  description: string;
79
- execute: (params: unknown, context: NeuroLinkExecutionContext) => Promise<ToolResult>;
80
- inputSchema?: z.ZodSchema;
81
- outputSchema?: z.ZodSchema;
82
- isImplemented?: boolean;
108
+ /** Optional category for tool organization and discovery */
83
109
  category?: string;
110
+ /** Optional input schema for parameter validation (Zod or JSON Schema) */
111
+ inputSchema?: unknown;
112
+ /** Optional output schema for result validation */
113
+ outputSchema?: unknown;
114
+ /** Implementation status flag for development tracking */
115
+ isImplemented?: boolean;
116
+ /** Required permissions for tool execution in secured environments */
84
117
  permissions?: string[];
118
+ /** Tool version for compatibility and update management */
85
119
  version?: string;
120
+ /** Additional metadata for tool information and capabilities */
86
121
  metadata?: Record<string, unknown>;
122
+ /**
123
+ * Tool execution function with standardized signature
124
+ *
125
+ * @param params - Input parameters for the tool (validated against inputSchema if provided)
126
+ * @param context - Execution context with session, user, and environment information
127
+ * @returns Promise resolving to ToolResult with success status, data, and metadata
128
+ * @throws ValidationError if parameters fail validation
129
+ */
130
+ execute: (params: unknown, context: NeuroLinkExecutionContext) => Promise<ToolResult>;
87
131
  }
88
132
  /**
89
133
  * MCP Server Interface - Standard compatible
@@ -145,7 +189,8 @@ export interface MCPServerConfig {
145
189
  */
146
190
  export declare function createMCPServer(config: MCPServerConfig): NeuroLinkMCPServer;
147
191
  /**
148
- * Utility function to validate tool interface
192
+ * Utility function to validate tool interface using centralized validation
193
+ * Ensures proper async patterns and type safety
149
194
  */
150
195
  export declare function validateTool(tool: NeuroLinkMCPTool): boolean;
151
196
  /**
@@ -159,3 +204,12 @@ export declare function getServerInfo(server: NeuroLinkMCPServer): {
159
204
  toolCount: number;
160
205
  capabilities: string[];
161
206
  };
207
+ /**
208
+ * Async utility function to validate all tools in a server
209
+ * Ensures all registered tools follow proper async patterns
210
+ */
211
+ export declare function validateServerTools(server: NeuroLinkMCPServer): Promise<{
212
+ isValid: boolean;
213
+ invalidTools: string[];
214
+ errors: string[];
215
+ }>;
@@ -4,6 +4,7 @@
4
4
  * Compatible with MCP patterns for seamless integration
5
5
  */
6
6
  import { z } from "zod";
7
+ import { validateMCPTool, ValidationError, createValidationSummary, } from "../utils/parameterValidation.js";
7
8
  /**
8
9
  * Input validation schemas
9
10
  */
@@ -27,7 +28,7 @@ const ServerConfigSchema = z.object({
27
28
  ])
28
29
  .optional(),
29
30
  visibility: z.enum(["public", "private", "organization"]).optional(),
30
- metadata: z.record(z.any()).optional(),
31
+ metadata: z.record(z.unknown()).optional(),
31
32
  dependencies: z.array(z.string()).optional(),
32
33
  capabilities: z.array(z.string()).optional(),
33
34
  });
@@ -76,9 +77,11 @@ export function createMCPServer(config) {
76
77
  tools: {},
77
78
  // Tool registration method
78
79
  registerTool(tool) {
79
- // Validate tool has required fields
80
- if (!tool.name || !tool.description || !tool.execute) {
81
- throw new Error(`Invalid tool: name, description, and execute are required`);
80
+ // Comprehensive tool validation using centralized utilities
81
+ const validation = validateMCPTool(tool);
82
+ if (!validation.isValid) {
83
+ const summary = createValidationSummary(validation);
84
+ throw new ValidationError(`Invalid tool '${tool.name}': ${summary}`, "tool", "VALIDATION_FAILED", validation.suggestions);
82
85
  }
83
86
  // Check for duplicate tool names
84
87
  if (this.tools[tool.name]) {
@@ -105,28 +108,13 @@ export function createMCPServer(config) {
105
108
  return server;
106
109
  }
107
110
  /**
108
- * Utility function to validate tool interface
111
+ * Utility function to validate tool interface using centralized validation
112
+ * Ensures proper async patterns and type safety
109
113
  */
110
114
  export function validateTool(tool) {
111
115
  try {
112
- // Check required fields
113
- if (!tool.name || typeof tool.name !== "string") {
114
- return false;
115
- }
116
- if (!tool.description || typeof tool.description !== "string") {
117
- return false;
118
- }
119
- if (!tool.execute || typeof tool.execute !== "function") {
120
- return false;
121
- }
122
- // Validate optional schemas if present
123
- if (tool.inputSchema && !(tool.inputSchema instanceof z.ZodSchema)) {
124
- return false;
125
- }
126
- if (tool.outputSchema && !(tool.outputSchema instanceof z.ZodSchema)) {
127
- return false;
128
- }
129
- return true;
116
+ const validation = validateMCPTool(tool);
117
+ return validation.isValid;
130
118
  }
131
119
  catch (error) {
132
120
  return false;
@@ -145,4 +133,29 @@ export function getServerInfo(server) {
145
133
  capabilities: server.capabilities || [],
146
134
  };
147
135
  }
136
+ /**
137
+ * Async utility function to validate all tools in a server
138
+ * Ensures all registered tools follow proper async patterns
139
+ */
140
+ export async function validateServerTools(server) {
141
+ const invalidTools = [];
142
+ const errors = [];
143
+ for (const [toolName, tool] of Object.entries(server.tools)) {
144
+ try {
145
+ if (!validateTool(tool)) {
146
+ invalidTools.push(toolName);
147
+ errors.push(`Tool '${toolName}' does not follow proper async patterns`);
148
+ }
149
+ }
150
+ catch (error) {
151
+ invalidTools.push(toolName);
152
+ errors.push(`Tool '${toolName}' validation failed: ${error instanceof Error ? error.message : String(error)}`);
153
+ }
154
+ }
155
+ return {
156
+ isValid: invalidTools.length === 0,
157
+ invalidTools,
158
+ errors,
159
+ };
160
+ }
148
161
  // Types are already exported above via export interface declarations
@@ -72,7 +72,8 @@ export declare class MCPClientFactory {
72
72
  */
73
73
  private static extractCapabilities;
74
74
  /**
75
- * Create a timeout promise
75
+ * Create a timeout promise with AbortController support
76
+ * Provides consistent async timeout patterns across the factory
76
77
  */
77
78
  private static createTimeoutPromise;
78
79
  /**
@@ -9,7 +9,7 @@ import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
9
9
  import { WebSocketClientTransport } from "@modelcontextprotocol/sdk/client/websocket.js";
10
10
  import { spawn } from "child_process";
11
11
  import { mcpLogger } from "../utils/logger.js";
12
- import { globalCircuitBreakerManager, } from "./mcpCircuitBreaker.js";
12
+ import { globalCircuitBreakerManager } from "./mcpCircuitBreaker.js";
13
13
  /**
14
14
  * MCPClientFactory
15
15
  * Factory class for creating MCP clients with different transports
@@ -73,7 +73,11 @@ export class MCPClientFactory {
73
73
  */
74
74
  static async createClientInternal(config, timeout) {
75
75
  // Create transport
76
- const { transport, process } = await this.createTransport(config);
76
+ const transportResult = await this.createTransport(config);
77
+ // Extract transport and process with necessary type assertions
78
+ // Note: Type assertions required due to TransportResult using 'unknown' to avoid circular imports
79
+ const transport = transportResult.transport;
80
+ const process = transportResult.process;
77
81
  try {
78
82
  // Create client
79
83
  const client = new Client(this.NEUROLINK_IMPLEMENTATION, {
@@ -140,10 +144,12 @@ export class MCPClientFactory {
140
144
  // Spawn the process
141
145
  const childProcess = spawn(config.command, config.args || [], {
142
146
  stdio: ["pipe", "pipe", "pipe"],
143
- env: {
147
+ env: Object.fromEntries(Object.entries({
144
148
  ...process.env,
145
149
  ...config.env,
146
- },
150
+ })
151
+ .filter(([, value]) => value !== undefined)
152
+ .map(([k, v]) => [k, String(v)])),
147
153
  cwd: config.cwd,
148
154
  });
149
155
  // Handle process errors
@@ -157,14 +163,32 @@ export class MCPClientFactory {
157
163
  }
158
164
  });
159
165
  });
160
- // Wait for process to be ready or fail
161
- await Promise.race([
162
- new Promise((resolve) => setTimeout(resolve, 1000)), // Give process time to start
163
- processErrorPromise,
164
- ]);
166
+ // Wait for process to be ready or fail using AbortController for better async patterns
167
+ const processStartupController = new AbortController();
168
+ const processStartupTimeout = setTimeout(() => {
169
+ processStartupController.abort();
170
+ }, 1000);
171
+ try {
172
+ await Promise.race([
173
+ new Promise((resolve) => {
174
+ const checkReady = () => {
175
+ if (processStartupController.signal.aborted) {
176
+ resolve(); // Timeout reached, continue
177
+ }
178
+ else {
179
+ setTimeout(checkReady, 100);
180
+ }
181
+ };
182
+ checkReady();
183
+ }),
184
+ processErrorPromise,
185
+ ]);
186
+ }
187
+ finally {
188
+ clearTimeout(processStartupTimeout);
189
+ }
165
190
  // Check if process is still running
166
- if (childProcess.killed ||
167
- childProcess.exitCode !== null) {
191
+ if (childProcess.killed || childProcess.exitCode !== null) {
168
192
  throw new Error("Process failed to start or exited immediately");
169
193
  }
170
194
  // Create transport
@@ -174,7 +198,9 @@ export class MCPClientFactory {
174
198
  env: Object.fromEntries(Object.entries({
175
199
  ...process.env,
176
200
  ...config.env,
177
- }).filter(([, value]) => value !== undefined)),
201
+ })
202
+ .filter(([, value]) => value !== undefined)
203
+ .map(([key, value]) => [key, String(value)])),
178
204
  cwd: config.cwd,
179
205
  });
180
206
  return { transport, process: childProcess };
@@ -250,7 +276,7 @@ export class MCPClientFactory {
250
276
  capabilities: this.DEFAULT_CAPABILITIES,
251
277
  };
252
278
  }
253
- catch (error) {
279
+ catch {
254
280
  // If listing tools fails, try a simpler ping
255
281
  mcpLogger.debug("[MCPClientFactory] Tool listing failed, server may not support tools yet");
256
282
  return {
@@ -267,17 +293,25 @@ export class MCPClientFactory {
267
293
  // This can be enhanced when MCP servers provide more detailed capability info
268
294
  return {
269
295
  ...this.DEFAULT_CAPABILITIES,
270
- tools: serverInfo.tools ? {} : undefined,
296
+ ...(serverInfo.tools ? { tools: {} } : {}),
271
297
  };
272
298
  }
273
299
  /**
274
- * Create a timeout promise
300
+ * Create a timeout promise with AbortController support
301
+ * Provides consistent async timeout patterns across the factory
275
302
  */
276
- static createTimeoutPromise(timeout, message) {
303
+ static createTimeoutPromise(timeout, message, abortSignal) {
277
304
  return new Promise((_, reject) => {
278
- setTimeout(() => {
305
+ const timeoutId = setTimeout(() => {
279
306
  reject(new Error(message));
280
307
  }, timeout);
308
+ // Support abortion for better async cleanup
309
+ if (abortSignal) {
310
+ abortSignal.addEventListener("abort", () => {
311
+ clearTimeout(timeoutId);
312
+ reject(new Error(`Operation aborted: ${message}`));
313
+ });
314
+ }
281
315
  });
282
316
  }
283
317
  /**
@@ -299,17 +333,30 @@ export class MCPClientFactory {
299
333
  catch (error) {
300
334
  errors.push(`Transport close error: ${error instanceof Error ? error.message : String(error)}`);
301
335
  }
302
- // Kill process if exists
336
+ // Kill process if exists with proper async cleanup
303
337
  if (process && !process.killed) {
304
338
  try {
305
339
  process.kill("SIGTERM");
306
- // Force kill after 5 seconds
307
- setTimeout(() => {
308
- if (!process.killed) {
309
- mcpLogger.warn("[MCPClientFactory] Force killing process");
310
- process.kill("SIGKILL");
311
- }
312
- }, 5000);
340
+ // Use Promise-based approach for force kill timeout
341
+ await new Promise((resolve) => {
342
+ const forceKillTimeout = setTimeout(() => {
343
+ if (!process.killed) {
344
+ mcpLogger.warn("[MCPClientFactory] Force killing process");
345
+ try {
346
+ process.kill("SIGKILL");
347
+ }
348
+ catch (killError) {
349
+ mcpLogger.debug("[MCPClientFactory] Error in force kill:", killError);
350
+ }
351
+ }
352
+ resolve();
353
+ }, 5000);
354
+ // If process exits gracefully before timeout, clear the force kill
355
+ process.on("exit", () => {
356
+ clearTimeout(forceKillTimeout);
357
+ resolve();
358
+ });
359
+ });
313
360
  }
314
361
  catch (error) {
315
362
  errors.push(`Process kill error: ${error instanceof Error ? error.message : String(error)}`);
@@ -339,7 +386,7 @@ export class MCPClientFactory {
339
386
  try {
340
387
  await client.listTools();
341
388
  }
342
- catch (error) {
389
+ catch {
343
390
  // Tool listing failure doesn't necessarily mean connection failure
344
391
  mcpLogger.debug("[MCPClientFactory] Tool listing failed during test, but connection may be valid");
345
392
  }
@@ -16,7 +16,7 @@ export interface McpRegistry {
16
16
  * Maintains backward compatibility with existing code
17
17
  */
18
18
  export declare class MCPRegistry implements McpRegistry {
19
- plugins: Map<string, DiscoveredMcp<Record<string, unknown>>>;
19
+ plugins: Map<string, DiscoveredMcp<import("../types/typeAliases.js").StandardRecord>>;
20
20
  /**
21
21
  * Register a plugin
22
22
  */
@@ -144,7 +144,7 @@ export declare class ToolDiscoveryService extends EventEmitter {
144
144
  */
145
145
  private validateParameterType;
146
146
  /**
147
- * Validate tool output
147
+ * Validate tool output with enhanced type safety
148
148
  */
149
149
  private validateToolOutput;
150
150
  /**
@@ -5,7 +5,9 @@
5
5
  */
6
6
  import { EventEmitter } from "events";
7
7
  import { mcpLogger } from "../utils/logger.js";
8
- import { globalCircuitBreakerManager, } from "./mcpCircuitBreaker.js";
8
+ import { globalCircuitBreakerManager } from "./mcpCircuitBreaker.js";
9
+ import { isObject, isString, isBoolean, isNullish, } from "../utils/typeUtils.js";
10
+ import { validateToolName, validateToolDescription, } from "../utils/parameterValidation.js";
9
11
  /**
10
12
  * ToolDiscoveryService
11
13
  * Handles automatic tool discovery and registration from external MCP servers
@@ -227,15 +229,15 @@ export class ToolDiscoveryService extends EventEmitter {
227
229
  validateTool(toolInfo) {
228
230
  const errors = [];
229
231
  const warnings = [];
230
- // Basic validation
231
- if (!toolInfo.name || toolInfo.name.trim().length === 0) {
232
- errors.push("Tool name is required");
232
+ // Use centralized validation for name
233
+ const nameError = validateToolName(toolInfo.name);
234
+ if (nameError) {
235
+ errors.push(nameError.message);
233
236
  }
234
- if (toolInfo.name && !/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(toolInfo.name)) {
235
- errors.push("Tool name must start with a letter and contain only letters, numbers, underscores, and hyphens");
236
- }
237
- if (!toolInfo.description || toolInfo.description.trim().length === 0) {
238
- warnings.push("Tool description is empty");
237
+ // Use centralized validation for description
238
+ const descriptionError = validateToolDescription(toolInfo.description);
239
+ if (descriptionError) {
240
+ warnings.push(descriptionError.message);
239
241
  }
240
242
  if (!toolInfo.serverId) {
241
243
  errors.push("Server ID is required");
@@ -440,19 +442,48 @@ export class ToolDiscoveryService extends EventEmitter {
440
442
  }
441
443
  }
442
444
  /**
443
- * Validate tool output
445
+ * Validate tool output with enhanced type safety
444
446
  */
445
447
  validateToolOutput(result) {
446
- // Basic output validation
447
- if (!result) {
448
- throw new Error("Tool returned no result");
449
- }
450
- // Check for error indicators
451
- if (result.error) {
452
- throw new Error(`Tool execution error: ${result.error}`);
448
+ // Check for null/undefined results
449
+ if (isNullish(result)) {
450
+ throw new Error("Tool returned null or undefined result");
451
+ }
452
+ // Enhanced error detection for object results
453
+ if (isObject(result)) {
454
+ // Check for explicit error property
455
+ if (result.error !== undefined) {
456
+ const errorMessage = isString(result.error)
457
+ ? result.error
458
+ : "Tool execution failed with error";
459
+ throw new Error(`Tool execution error: ${errorMessage}`);
460
+ }
461
+ // Check for boolean error flag
462
+ if (isBoolean(result.isError) && result.isError === true) {
463
+ const errorDetail = isString(result.message)
464
+ ? `: ${result.message}`
465
+ : "";
466
+ throw new Error(`Tool execution failed${errorDetail}`);
467
+ }
468
+ // Check for common error status patterns
469
+ if (isString(result.status) &&
470
+ (result.status === "error" || result.status === "failed")) {
471
+ const errorDetail = isString(result.message) || isString(result.reason)
472
+ ? `: ${result.message || result.reason}`
473
+ : "";
474
+ throw new Error(`Tool execution failed${errorDetail}`);
475
+ }
476
+ // Check for success: false pattern
477
+ if (isBoolean(result.success) && result.success === false) {
478
+ const errorDetail = isString(result.message) || isString(result.error)
479
+ ? `: ${result.message || result.error}`
480
+ : "";
481
+ throw new Error(`Tool execution unsuccessful${errorDetail}`);
482
+ }
453
483
  }
454
- if (result.isError === true) {
455
- throw new Error("Tool execution failed");
484
+ // Validate that string results are not empty
485
+ if (isString(result) && result.trim() === "") {
486
+ throw new Error("Tool returned empty string result");
456
487
  }
457
488
  }
458
489
  /**
@@ -43,7 +43,29 @@ export declare class MCPToolRegistry extends MCPRegistry {
43
43
  registerServer(serverInfo: MCPServerInfo, context?: ExecutionContext): Promise<void>;
44
44
  registerServer(serverId: string, serverConfig?: unknown, context?: ExecutionContext): Promise<void>;
45
45
  /**
46
- * Execute a tool with enhanced context
46
+ * Execute a tool with enhanced context and automatic result wrapping
47
+ *
48
+ * This method handles both raw return values and ToolResult objects:
49
+ * - Raw values (primitives, objects) are automatically wrapped in ToolResult format
50
+ * - Existing ToolResult objects are enhanced with execution metadata
51
+ * - All results include execution timing and context information
52
+ *
53
+ * @param toolName - Name of the tool to execute
54
+ * @param args - Parameters to pass to the tool execution function
55
+ * @param context - Execution context with session, user, and environment info
56
+ * @returns Promise resolving to ToolResult object with data, metadata, and usage info
57
+ * @throws Error if tool is not found or execution fails
58
+ *
59
+ * @example
60
+ * ```typescript
61
+ * // Tool that returns raw value
62
+ * const result = await toolRegistry.executeTool("calculator", { a: 5, b: 3, op: "add" });
63
+ * // result.data === 8, result.metadata contains execution info
64
+ *
65
+ * // Tool that returns ToolResult
66
+ * const result = await toolRegistry.executeTool("complexTool", { input: "test" });
67
+ * // result is enhanced ToolResult with additional metadata
68
+ * ```
47
69
  */
48
70
  executeTool<T = unknown>(toolName: string, args?: unknown, context?: ExecutionContext): Promise<T>;
49
71
  /**