@juspay/neurolink 7.14.1 → 7.14.3
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/CHANGELOG.md +12 -0
- package/dist/cli/commands/config.d.ts +66 -66
- package/dist/core/baseProvider.d.ts +12 -7
- package/dist/core/baseProvider.js +118 -125
- package/dist/core/constants.d.ts +5 -0
- package/dist/core/constants.js +6 -0
- package/dist/core/dynamicModels.d.ts +4 -4
- package/dist/core/factory.d.ts +2 -4
- package/dist/core/types.d.ts +8 -22
- package/dist/index.d.ts +19 -4
- package/dist/index.js +21 -0
- package/dist/lib/core/baseProvider.d.ts +12 -7
- package/dist/lib/core/baseProvider.js +118 -125
- package/dist/lib/core/constants.d.ts +5 -0
- package/dist/lib/core/constants.js +6 -0
- package/dist/lib/core/dynamicModels.d.ts +8 -8
- package/dist/lib/core/factory.d.ts +2 -4
- package/dist/lib/core/types.d.ts +8 -22
- package/dist/lib/index.d.ts +19 -4
- package/dist/lib/index.js +21 -0
- package/dist/lib/mcp/contracts/mcpContract.d.ts +6 -19
- package/dist/lib/mcp/externalServerManager.d.ts +2 -4
- package/dist/lib/mcp/externalServerManager.js +7 -8
- package/dist/lib/mcp/factory.d.ts +61 -7
- package/dist/lib/mcp/factory.js +36 -23
- package/dist/lib/mcp/mcpClientFactory.d.ts +2 -1
- package/dist/lib/mcp/mcpClientFactory.js +73 -26
- package/dist/lib/mcp/registry.d.ts +1 -1
- package/dist/lib/mcp/toolDiscoveryService.d.ts +1 -1
- package/dist/lib/mcp/toolDiscoveryService.js +50 -19
- package/dist/lib/mcp/toolRegistry.d.ts +23 -1
- package/dist/lib/mcp/toolRegistry.js +108 -17
- package/dist/lib/models/modelResolver.js +2 -1
- package/dist/lib/neurolink.d.ts +12 -8
- package/dist/lib/neurolink.js +130 -134
- package/dist/lib/providers/amazonBedrock.d.ts +2 -2
- package/dist/lib/providers/anthropic.d.ts +3 -3
- package/dist/lib/providers/googleAiStudio.d.ts +2 -2
- package/dist/lib/providers/mistral.d.ts +3 -3
- package/dist/lib/providers/ollama.d.ts +2 -2
- package/dist/lib/providers/openAI.d.ts +3 -3
- package/dist/lib/providers/openaiCompatible.d.ts +2 -2
- package/dist/lib/providers/sagemaker/client.d.ts +2 -5
- package/dist/lib/providers/sagemaker/language-model.d.ts +4 -6
- package/dist/lib/providers/sagemaker/parsers.js +5 -4
- package/dist/lib/sdk/toolRegistration.d.ts +6 -6
- package/dist/lib/sdk/toolRegistration.js +17 -56
- package/dist/lib/types/generateTypes.d.ts +9 -9
- package/dist/lib/types/streamTypes.d.ts +4 -4
- package/dist/lib/types/tools.d.ts +15 -7
- package/dist/lib/types/typeAliases.d.ts +412 -0
- package/dist/lib/types/typeAliases.js +48 -0
- package/dist/lib/utils/factoryProcessing.d.ts +2 -1
- package/dist/lib/utils/factoryProcessing.js +4 -3
- package/dist/lib/utils/parameterValidation.d.ts +97 -0
- package/dist/lib/utils/parameterValidation.js +452 -0
- package/dist/lib/utils/transformationUtils.d.ts +204 -0
- package/dist/lib/utils/transformationUtils.js +334 -0
- package/dist/lib/utils/typeUtils.d.ts +77 -0
- package/dist/lib/utils/typeUtils.js +97 -0
- package/dist/mcp/contracts/mcpContract.d.ts +6 -19
- package/dist/mcp/externalServerManager.d.ts +2 -4
- package/dist/mcp/externalServerManager.js +7 -8
- package/dist/mcp/factory.d.ts +61 -7
- package/dist/mcp/factory.js +36 -23
- package/dist/mcp/mcpClientFactory.d.ts +2 -1
- package/dist/mcp/mcpClientFactory.js +73 -26
- package/dist/mcp/registry.d.ts +1 -1
- package/dist/mcp/toolDiscoveryService.d.ts +1 -1
- package/dist/mcp/toolDiscoveryService.js +50 -19
- package/dist/mcp/toolRegistry.d.ts +23 -1
- package/dist/mcp/toolRegistry.js +108 -17
- package/dist/models/modelResolver.js +2 -1
- package/dist/neurolink.d.ts +12 -8
- package/dist/neurolink.js +130 -134
- package/dist/providers/amazonBedrock.d.ts +2 -2
- package/dist/providers/anthropic.d.ts +3 -3
- package/dist/providers/googleAiStudio.d.ts +2 -2
- package/dist/providers/mistral.d.ts +3 -3
- package/dist/providers/ollama.d.ts +2 -2
- package/dist/providers/openAI.d.ts +3 -3
- package/dist/providers/openaiCompatible.d.ts +2 -2
- package/dist/providers/sagemaker/client.d.ts +2 -5
- package/dist/providers/sagemaker/language-model.d.ts +4 -6
- package/dist/providers/sagemaker/parsers.js +5 -4
- package/dist/sdk/toolRegistration.d.ts +6 -6
- package/dist/sdk/toolRegistration.js +17 -56
- package/dist/types/generateTypes.d.ts +9 -9
- package/dist/types/streamTypes.d.ts +4 -4
- package/dist/types/tools.d.ts +15 -7
- package/dist/types/typeAliases.d.ts +412 -0
- package/dist/types/typeAliases.js +48 -0
- package/dist/utils/factoryProcessing.d.ts +2 -1
- package/dist/utils/factoryProcessing.js +4 -3
- package/dist/utils/parameterValidation.d.ts +97 -0
- package/dist/utils/parameterValidation.js +452 -0
- package/dist/utils/transformationUtils.d.ts +204 -0
- package/dist/utils/transformationUtils.js +334 -0
- package/dist/utils/typeUtils.d.ts +77 -0
- package/dist/utils/typeUtils.js +97 -0
- 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 =
|
|
10
|
+
export interface ExecutionContext<T = StandardRecord> {
|
|
10
11
|
sessionId?: string;
|
|
11
12
|
userId?: string;
|
|
12
13
|
config?: T;
|
|
13
|
-
metadata?:
|
|
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?:
|
|
45
|
-
outputSchema?:
|
|
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 =
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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:
|
|
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 -
|
|
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
|
-
|
|
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
|
+
}>;
|
package/dist/lib/mcp/factory.js
CHANGED
|
@@ -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.
|
|
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
|
-
//
|
|
80
|
-
|
|
81
|
-
|
|
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
|
-
|
|
113
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
162
|
-
|
|
163
|
-
|
|
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
|
-
})
|
|
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
|
|
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
|
-
|
|
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
|
-
//
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
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
|
|
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<
|
|
19
|
+
plugins: Map<string, DiscoveredMcp<import("../types/typeAliases.js").StandardRecord>>;
|
|
20
20
|
/**
|
|
21
21
|
* Register a plugin
|
|
22
22
|
*/
|
|
@@ -5,7 +5,9 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { EventEmitter } from "events";
|
|
7
7
|
import { mcpLogger } from "../utils/logger.js";
|
|
8
|
-
import { globalCircuitBreakerManager
|
|
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
|
-
//
|
|
231
|
-
|
|
232
|
-
|
|
232
|
+
// Use centralized validation for name
|
|
233
|
+
const nameError = validateToolName(toolInfo.name);
|
|
234
|
+
if (nameError) {
|
|
235
|
+
errors.push(nameError.message);
|
|
233
236
|
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
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
|
-
//
|
|
447
|
-
if (
|
|
448
|
-
throw new Error("Tool returned
|
|
449
|
-
}
|
|
450
|
-
//
|
|
451
|
-
if (result
|
|
452
|
-
|
|
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
|
-
|
|
455
|
-
|
|
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
|
/**
|