@juspay/neurolink 7.13.0 → 7.14.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.
- package/CHANGELOG.md +6 -0
- package/README.md +89 -25
- package/dist/config/conversationMemoryConfig.js +2 -1
- package/dist/context/ContextManager.js +15 -4
- package/dist/context/config.js +5 -1
- package/dist/context/utils.js +1 -1
- package/dist/core/baseProvider.d.ts +16 -1
- package/dist/core/baseProvider.js +208 -9
- package/dist/core/conversationMemoryManager.js +3 -2
- package/dist/core/factory.js +13 -2
- package/dist/factories/providerFactory.js +5 -11
- package/dist/factories/providerRegistry.js +2 -2
- package/dist/lib/config/conversationMemoryConfig.js +2 -1
- package/dist/lib/context/ContextManager.js +15 -4
- package/dist/lib/context/config.js +5 -1
- package/dist/lib/context/utils.js +1 -1
- package/dist/lib/core/baseProvider.d.ts +16 -1
- package/dist/lib/core/baseProvider.js +208 -9
- package/dist/lib/core/conversationMemoryManager.js +3 -2
- package/dist/lib/core/factory.js +13 -2
- package/dist/lib/factories/providerFactory.js +5 -11
- package/dist/lib/factories/providerRegistry.js +2 -2
- package/dist/lib/mcp/externalServerManager.d.ts +115 -0
- package/dist/lib/mcp/externalServerManager.js +677 -0
- package/dist/lib/mcp/mcpCircuitBreaker.d.ts +184 -0
- package/dist/lib/mcp/mcpCircuitBreaker.js +338 -0
- package/dist/lib/mcp/mcpClientFactory.d.ts +104 -0
- package/dist/lib/mcp/mcpClientFactory.js +416 -0
- package/dist/lib/mcp/toolDiscoveryService.d.ts +192 -0
- package/dist/lib/mcp/toolDiscoveryService.js +578 -0
- package/dist/lib/neurolink.d.ts +111 -16
- package/dist/lib/neurolink.js +517 -50
- package/dist/lib/providers/googleVertex.d.ts +1 -1
- package/dist/lib/providers/googleVertex.js +23 -7
- package/dist/lib/types/externalMcp.d.ts +282 -0
- package/dist/lib/types/externalMcp.js +6 -0
- package/dist/lib/types/generateTypes.d.ts +0 -1
- package/dist/lib/types/index.d.ts +1 -0
- package/dist/mcp/externalServerManager.d.ts +115 -0
- package/dist/mcp/externalServerManager.js +677 -0
- package/dist/mcp/mcpCircuitBreaker.d.ts +184 -0
- package/dist/mcp/mcpCircuitBreaker.js +338 -0
- package/dist/mcp/mcpClientFactory.d.ts +104 -0
- package/dist/mcp/mcpClientFactory.js +416 -0
- package/dist/mcp/toolDiscoveryService.d.ts +192 -0
- package/dist/mcp/toolDiscoveryService.js +578 -0
- package/dist/neurolink.d.ts +111 -16
- package/dist/neurolink.js +517 -50
- package/dist/providers/googleVertex.d.ts +1 -1
- package/dist/providers/googleVertex.js +23 -7
- package/dist/types/externalMcp.d.ts +282 -0
- package/dist/types/externalMcp.js +6 -0
- package/dist/types/generateTypes.d.ts +0 -1
- package/dist/types/index.d.ts +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Circuit Breaker
|
|
3
|
+
* Implements circuit breaker pattern for external MCP operations
|
|
4
|
+
* Provides fault tolerance and prevents cascading failures
|
|
5
|
+
*/
|
|
6
|
+
import { EventEmitter } from "events";
|
|
7
|
+
/**
|
|
8
|
+
* Circuit breaker states
|
|
9
|
+
*/
|
|
10
|
+
export type CircuitBreakerState = "closed" | "open" | "half-open";
|
|
11
|
+
/**
|
|
12
|
+
* Circuit breaker configuration
|
|
13
|
+
*/
|
|
14
|
+
export interface CircuitBreakerConfig {
|
|
15
|
+
/** Number of failures before opening the circuit */
|
|
16
|
+
failureThreshold: number;
|
|
17
|
+
/** Time to wait before attempting reset (milliseconds) */
|
|
18
|
+
resetTimeout: number;
|
|
19
|
+
/** Maximum calls allowed in half-open state */
|
|
20
|
+
halfOpenMaxCalls: number;
|
|
21
|
+
/** Timeout for individual operations (milliseconds) */
|
|
22
|
+
operationTimeout: number;
|
|
23
|
+
/** Minimum number of calls before calculating failure rate */
|
|
24
|
+
minimumCallsBeforeCalculation: number;
|
|
25
|
+
/** Window size for calculating failure rate (milliseconds) */
|
|
26
|
+
statisticsWindowSize: number;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Circuit breaker statistics
|
|
30
|
+
*/
|
|
31
|
+
export interface CircuitBreakerStats {
|
|
32
|
+
/** Current state */
|
|
33
|
+
state: CircuitBreakerState;
|
|
34
|
+
/** Total number of calls */
|
|
35
|
+
totalCalls: number;
|
|
36
|
+
/** Number of successful calls */
|
|
37
|
+
successfulCalls: number;
|
|
38
|
+
/** Number of failed calls */
|
|
39
|
+
failedCalls: number;
|
|
40
|
+
/** Current failure rate (0-1) */
|
|
41
|
+
failureRate: number;
|
|
42
|
+
/** Calls in current time window */
|
|
43
|
+
windowCalls: number;
|
|
44
|
+
/** Last state change timestamp */
|
|
45
|
+
lastStateChange: Date;
|
|
46
|
+
/** Next retry time (for open state) */
|
|
47
|
+
nextRetryTime?: Date;
|
|
48
|
+
/** Half-open call count */
|
|
49
|
+
halfOpenCalls: number;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Circuit breaker events
|
|
53
|
+
*/
|
|
54
|
+
export interface CircuitBreakerEvents {
|
|
55
|
+
stateChange: {
|
|
56
|
+
oldState: CircuitBreakerState;
|
|
57
|
+
newState: CircuitBreakerState;
|
|
58
|
+
reason: string;
|
|
59
|
+
timestamp: Date;
|
|
60
|
+
};
|
|
61
|
+
callSuccess: {
|
|
62
|
+
duration: number;
|
|
63
|
+
timestamp: Date;
|
|
64
|
+
};
|
|
65
|
+
callFailure: {
|
|
66
|
+
error: string;
|
|
67
|
+
duration: number;
|
|
68
|
+
timestamp: Date;
|
|
69
|
+
};
|
|
70
|
+
circuitOpen: {
|
|
71
|
+
failureRate: number;
|
|
72
|
+
totalCalls: number;
|
|
73
|
+
timestamp: Date;
|
|
74
|
+
};
|
|
75
|
+
circuitHalfOpen: {
|
|
76
|
+
timestamp: Date;
|
|
77
|
+
};
|
|
78
|
+
circuitClosed: {
|
|
79
|
+
timestamp: Date;
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* MCPCircuitBreaker
|
|
84
|
+
* Implements circuit breaker pattern for fault tolerance
|
|
85
|
+
*/
|
|
86
|
+
export declare class MCPCircuitBreaker extends EventEmitter {
|
|
87
|
+
private name;
|
|
88
|
+
private state;
|
|
89
|
+
private config;
|
|
90
|
+
private callHistory;
|
|
91
|
+
private lastFailureTime;
|
|
92
|
+
private halfOpenCalls;
|
|
93
|
+
private lastStateChange;
|
|
94
|
+
constructor(name: string, config?: Partial<CircuitBreakerConfig>);
|
|
95
|
+
/**
|
|
96
|
+
* Execute an operation with circuit breaker protection
|
|
97
|
+
*/
|
|
98
|
+
execute<T>(operation: () => Promise<T>): Promise<T>;
|
|
99
|
+
/**
|
|
100
|
+
* Record a call in the history
|
|
101
|
+
*/
|
|
102
|
+
private recordCall;
|
|
103
|
+
/**
|
|
104
|
+
* Check if failure threshold is exceeded
|
|
105
|
+
*/
|
|
106
|
+
private checkFailureThreshold;
|
|
107
|
+
/**
|
|
108
|
+
* Change circuit breaker state
|
|
109
|
+
*/
|
|
110
|
+
private changeState;
|
|
111
|
+
/**
|
|
112
|
+
* Create a timeout promise
|
|
113
|
+
*/
|
|
114
|
+
private timeoutPromise;
|
|
115
|
+
/**
|
|
116
|
+
* Clean up old call records
|
|
117
|
+
*/
|
|
118
|
+
private cleanupCallHistory;
|
|
119
|
+
/**
|
|
120
|
+
* Get current statistics
|
|
121
|
+
*/
|
|
122
|
+
getStats(): CircuitBreakerStats;
|
|
123
|
+
/**
|
|
124
|
+
* Manually reset the circuit breaker
|
|
125
|
+
*/
|
|
126
|
+
reset(): void;
|
|
127
|
+
/**
|
|
128
|
+
* Force open the circuit breaker
|
|
129
|
+
*/
|
|
130
|
+
forceOpen(reason?: string): void;
|
|
131
|
+
/**
|
|
132
|
+
* Get circuit breaker name
|
|
133
|
+
*/
|
|
134
|
+
getName(): string;
|
|
135
|
+
/**
|
|
136
|
+
* Check if circuit is open
|
|
137
|
+
*/
|
|
138
|
+
isOpen(): boolean;
|
|
139
|
+
/**
|
|
140
|
+
* Check if circuit is closed
|
|
141
|
+
*/
|
|
142
|
+
isClosed(): boolean;
|
|
143
|
+
/**
|
|
144
|
+
* Check if circuit is half-open
|
|
145
|
+
*/
|
|
146
|
+
isHalfOpen(): boolean;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Circuit breaker manager for multiple circuit breakers
|
|
150
|
+
*/
|
|
151
|
+
export declare class CircuitBreakerManager {
|
|
152
|
+
private breakers;
|
|
153
|
+
/**
|
|
154
|
+
* Get or create a circuit breaker
|
|
155
|
+
*/
|
|
156
|
+
getBreaker(name: string, config?: Partial<CircuitBreakerConfig>): MCPCircuitBreaker;
|
|
157
|
+
/**
|
|
158
|
+
* Remove a circuit breaker
|
|
159
|
+
*/
|
|
160
|
+
removeBreaker(name: string): boolean;
|
|
161
|
+
/**
|
|
162
|
+
* Get all circuit breaker names
|
|
163
|
+
*/
|
|
164
|
+
getBreakerNames(): string[];
|
|
165
|
+
/**
|
|
166
|
+
* Get statistics for all circuit breakers
|
|
167
|
+
*/
|
|
168
|
+
getAllStats(): Record<string, CircuitBreakerStats>;
|
|
169
|
+
/**
|
|
170
|
+
* Reset all circuit breakers
|
|
171
|
+
*/
|
|
172
|
+
resetAll(): void;
|
|
173
|
+
/**
|
|
174
|
+
* Get health summary
|
|
175
|
+
*/
|
|
176
|
+
getHealthSummary(): {
|
|
177
|
+
totalBreakers: number;
|
|
178
|
+
closedBreakers: number;
|
|
179
|
+
openBreakers: number;
|
|
180
|
+
halfOpenBreakers: number;
|
|
181
|
+
unhealthyBreakers: string[];
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
export declare const globalCircuitBreakerManager: CircuitBreakerManager;
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Circuit Breaker
|
|
3
|
+
* Implements circuit breaker pattern for external MCP operations
|
|
4
|
+
* Provides fault tolerance and prevents cascading failures
|
|
5
|
+
*/
|
|
6
|
+
import { EventEmitter } from "events";
|
|
7
|
+
import { mcpLogger } from "../utils/logger.js";
|
|
8
|
+
/**
|
|
9
|
+
* MCPCircuitBreaker
|
|
10
|
+
* Implements circuit breaker pattern for fault tolerance
|
|
11
|
+
*/
|
|
12
|
+
export class MCPCircuitBreaker extends EventEmitter {
|
|
13
|
+
name;
|
|
14
|
+
state = "closed";
|
|
15
|
+
config;
|
|
16
|
+
callHistory = [];
|
|
17
|
+
lastFailureTime = 0;
|
|
18
|
+
halfOpenCalls = 0;
|
|
19
|
+
lastStateChange = new Date();
|
|
20
|
+
constructor(name, config = {}) {
|
|
21
|
+
super();
|
|
22
|
+
this.name = name;
|
|
23
|
+
// Set default configuration
|
|
24
|
+
this.config = {
|
|
25
|
+
failureThreshold: config.failureThreshold ?? 5,
|
|
26
|
+
resetTimeout: config.resetTimeout ?? 60000,
|
|
27
|
+
halfOpenMaxCalls: config.halfOpenMaxCalls ?? 3,
|
|
28
|
+
operationTimeout: config.operationTimeout ?? 30000,
|
|
29
|
+
minimumCallsBeforeCalculation: config.minimumCallsBeforeCalculation ?? 10,
|
|
30
|
+
statisticsWindowSize: config.statisticsWindowSize ?? 300000, // 5 minutes
|
|
31
|
+
};
|
|
32
|
+
// Clean up old call records periodically
|
|
33
|
+
setInterval(() => this.cleanupCallHistory(), 60000);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Execute an operation with circuit breaker protection
|
|
37
|
+
*/
|
|
38
|
+
async execute(operation) {
|
|
39
|
+
const startTime = Date.now();
|
|
40
|
+
try {
|
|
41
|
+
// Check if circuit is open
|
|
42
|
+
if (this.state === "open") {
|
|
43
|
+
if (Date.now() - this.lastFailureTime < this.config.resetTimeout) {
|
|
44
|
+
throw new Error(`Circuit breaker '${this.name}' is open. Next retry at ${new Date(this.lastFailureTime + this.config.resetTimeout)}`);
|
|
45
|
+
}
|
|
46
|
+
// Transition to half-open
|
|
47
|
+
this.changeState("half-open", "Reset timeout reached");
|
|
48
|
+
}
|
|
49
|
+
// Check half-open call limit
|
|
50
|
+
if (this.state === "half-open" &&
|
|
51
|
+
this.halfOpenCalls >= this.config.halfOpenMaxCalls) {
|
|
52
|
+
throw new Error(`Circuit breaker '${this.name}' is half-open but call limit reached`);
|
|
53
|
+
}
|
|
54
|
+
// Execute operation with timeout
|
|
55
|
+
const result = await Promise.race([
|
|
56
|
+
operation(),
|
|
57
|
+
this.timeoutPromise(this.config.operationTimeout),
|
|
58
|
+
]);
|
|
59
|
+
// Record successful call
|
|
60
|
+
this.recordCall(true, Date.now() - startTime);
|
|
61
|
+
// Handle half-open success
|
|
62
|
+
if (this.state === "half-open") {
|
|
63
|
+
this.halfOpenCalls++;
|
|
64
|
+
// If enough successful calls in half-open, close the circuit
|
|
65
|
+
if (this.halfOpenCalls >= this.config.halfOpenMaxCalls) {
|
|
66
|
+
this.changeState("closed", "Half-open test successful");
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return result;
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
// Record failed call
|
|
73
|
+
const duration = Date.now() - startTime;
|
|
74
|
+
this.recordCall(false, duration);
|
|
75
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
76
|
+
// Emit failure event
|
|
77
|
+
this.emit("callFailure", {
|
|
78
|
+
error: errorMessage,
|
|
79
|
+
duration,
|
|
80
|
+
timestamp: new Date(),
|
|
81
|
+
});
|
|
82
|
+
// Handle state transitions on failure
|
|
83
|
+
if (this.state === "half-open") {
|
|
84
|
+
// Failure in half-open immediately opens circuit
|
|
85
|
+
this.changeState("open", `Half-open test failed: ${errorMessage}`);
|
|
86
|
+
}
|
|
87
|
+
else if (this.state === "closed") {
|
|
88
|
+
// Check if we should open the circuit
|
|
89
|
+
this.checkFailureThreshold();
|
|
90
|
+
}
|
|
91
|
+
throw error;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Record a call in the history
|
|
96
|
+
*/
|
|
97
|
+
recordCall(success, duration) {
|
|
98
|
+
const now = Date.now();
|
|
99
|
+
this.callHistory.push({
|
|
100
|
+
timestamp: now,
|
|
101
|
+
success,
|
|
102
|
+
duration,
|
|
103
|
+
});
|
|
104
|
+
// Emit success event
|
|
105
|
+
if (success) {
|
|
106
|
+
this.emit("callSuccess", {
|
|
107
|
+
duration,
|
|
108
|
+
timestamp: new Date(),
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
// Update failure time
|
|
112
|
+
if (!success) {
|
|
113
|
+
this.lastFailureTime = now;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Check if failure threshold is exceeded
|
|
118
|
+
*/
|
|
119
|
+
checkFailureThreshold() {
|
|
120
|
+
const windowStart = Date.now() - this.config.statisticsWindowSize;
|
|
121
|
+
const windowCalls = this.callHistory.filter((call) => call.timestamp >= windowStart);
|
|
122
|
+
// Need minimum calls before calculating failure rate
|
|
123
|
+
if (windowCalls.length < this.config.minimumCallsBeforeCalculation) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
const failedCalls = windowCalls.filter((call) => !call.success).length;
|
|
127
|
+
const failureRate = failedCalls / windowCalls.length;
|
|
128
|
+
mcpLogger.debug(`[CircuitBreaker:${this.name}] Failure rate: ${(failureRate * 100).toFixed(1)}% (${failedCalls}/${windowCalls.length})`);
|
|
129
|
+
// Open circuit if failure rate exceeds threshold
|
|
130
|
+
if (failedCalls >= this.config.failureThreshold) {
|
|
131
|
+
this.changeState("open", `Failure threshold exceeded: ${failedCalls} failures`);
|
|
132
|
+
this.emit("circuitOpen", {
|
|
133
|
+
failureRate,
|
|
134
|
+
totalCalls: windowCalls.length,
|
|
135
|
+
timestamp: new Date(),
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Change circuit breaker state
|
|
141
|
+
*/
|
|
142
|
+
changeState(newState, reason) {
|
|
143
|
+
const oldState = this.state;
|
|
144
|
+
this.state = newState;
|
|
145
|
+
this.lastStateChange = new Date();
|
|
146
|
+
// Reset counters based on state
|
|
147
|
+
if (newState === "half-open") {
|
|
148
|
+
this.halfOpenCalls = 0;
|
|
149
|
+
this.emit("circuitHalfOpen", {
|
|
150
|
+
timestamp: new Date(),
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
else if (newState === "closed") {
|
|
154
|
+
this.halfOpenCalls = 0;
|
|
155
|
+
this.emit("circuitClosed", {
|
|
156
|
+
timestamp: new Date(),
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
mcpLogger.info(`[CircuitBreaker:${this.name}] State changed: ${oldState} -> ${newState} (${reason})`);
|
|
160
|
+
// Emit state change event
|
|
161
|
+
this.emit("stateChange", {
|
|
162
|
+
oldState,
|
|
163
|
+
newState,
|
|
164
|
+
reason,
|
|
165
|
+
timestamp: new Date(),
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Create a timeout promise
|
|
170
|
+
*/
|
|
171
|
+
timeoutPromise(timeout) {
|
|
172
|
+
return new Promise((_, reject) => {
|
|
173
|
+
setTimeout(() => {
|
|
174
|
+
reject(new Error(`Operation timed out after ${timeout}ms`));
|
|
175
|
+
}, timeout);
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Clean up old call records
|
|
180
|
+
*/
|
|
181
|
+
cleanupCallHistory() {
|
|
182
|
+
const cutoffTime = Date.now() - this.config.statisticsWindowSize;
|
|
183
|
+
const originalLength = this.callHistory.length;
|
|
184
|
+
this.callHistory = this.callHistory.filter((call) => call.timestamp >= cutoffTime);
|
|
185
|
+
const removed = originalLength - this.callHistory.length;
|
|
186
|
+
if (removed > 0) {
|
|
187
|
+
mcpLogger.debug(`[CircuitBreaker:${this.name}] Cleaned up ${removed} old call records`);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Get current statistics
|
|
192
|
+
*/
|
|
193
|
+
getStats() {
|
|
194
|
+
const windowStart = Date.now() - this.config.statisticsWindowSize;
|
|
195
|
+
const windowCalls = this.callHistory.filter((call) => call.timestamp >= windowStart);
|
|
196
|
+
const successfulCalls = windowCalls.filter((call) => call.success).length;
|
|
197
|
+
const failedCalls = windowCalls.length - successfulCalls;
|
|
198
|
+
const failureRate = windowCalls.length > 0 ? failedCalls / windowCalls.length : 0;
|
|
199
|
+
return {
|
|
200
|
+
state: this.state,
|
|
201
|
+
totalCalls: this.callHistory.length,
|
|
202
|
+
successfulCalls: this.callHistory.filter((call) => call.success).length,
|
|
203
|
+
failedCalls: this.callHistory.filter((call) => !call.success).length,
|
|
204
|
+
failureRate,
|
|
205
|
+
windowCalls: windowCalls.length,
|
|
206
|
+
lastStateChange: this.lastStateChange,
|
|
207
|
+
nextRetryTime: this.state === "open"
|
|
208
|
+
? new Date(this.lastFailureTime + this.config.resetTimeout)
|
|
209
|
+
: undefined,
|
|
210
|
+
halfOpenCalls: this.halfOpenCalls,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Manually reset the circuit breaker
|
|
215
|
+
*/
|
|
216
|
+
reset() {
|
|
217
|
+
this.changeState("closed", "Manual reset");
|
|
218
|
+
this.callHistory = [];
|
|
219
|
+
this.lastFailureTime = 0;
|
|
220
|
+
this.halfOpenCalls = 0;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Force open the circuit breaker
|
|
224
|
+
*/
|
|
225
|
+
forceOpen(reason = "Manual force open") {
|
|
226
|
+
this.changeState("open", reason);
|
|
227
|
+
this.lastFailureTime = Date.now();
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Get circuit breaker name
|
|
231
|
+
*/
|
|
232
|
+
getName() {
|
|
233
|
+
return this.name;
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Check if circuit is open
|
|
237
|
+
*/
|
|
238
|
+
isOpen() {
|
|
239
|
+
return this.state === "open";
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Check if circuit is closed
|
|
243
|
+
*/
|
|
244
|
+
isClosed() {
|
|
245
|
+
return this.state === "closed";
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Check if circuit is half-open
|
|
249
|
+
*/
|
|
250
|
+
isHalfOpen() {
|
|
251
|
+
return this.state === "half-open";
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Circuit breaker manager for multiple circuit breakers
|
|
256
|
+
*/
|
|
257
|
+
export class CircuitBreakerManager {
|
|
258
|
+
breakers = new Map();
|
|
259
|
+
/**
|
|
260
|
+
* Get or create a circuit breaker
|
|
261
|
+
*/
|
|
262
|
+
getBreaker(name, config) {
|
|
263
|
+
if (!this.breakers.has(name)) {
|
|
264
|
+
const breaker = new MCPCircuitBreaker(name, config);
|
|
265
|
+
this.breakers.set(name, breaker);
|
|
266
|
+
mcpLogger.debug(`[CircuitBreakerManager] Created circuit breaker: ${name}`);
|
|
267
|
+
}
|
|
268
|
+
return this.breakers.get(name);
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Remove a circuit breaker
|
|
272
|
+
*/
|
|
273
|
+
removeBreaker(name) {
|
|
274
|
+
const removed = this.breakers.delete(name);
|
|
275
|
+
if (removed) {
|
|
276
|
+
mcpLogger.debug(`[CircuitBreakerManager] Removed circuit breaker: ${name}`);
|
|
277
|
+
}
|
|
278
|
+
return removed;
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Get all circuit breaker names
|
|
282
|
+
*/
|
|
283
|
+
getBreakerNames() {
|
|
284
|
+
return Array.from(this.breakers.keys());
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Get statistics for all circuit breakers
|
|
288
|
+
*/
|
|
289
|
+
getAllStats() {
|
|
290
|
+
const stats = {};
|
|
291
|
+
for (const [name, breaker] of this.breakers) {
|
|
292
|
+
stats[name] = breaker.getStats();
|
|
293
|
+
}
|
|
294
|
+
return stats;
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Reset all circuit breakers
|
|
298
|
+
*/
|
|
299
|
+
resetAll() {
|
|
300
|
+
for (const breaker of this.breakers.values()) {
|
|
301
|
+
breaker.reset();
|
|
302
|
+
}
|
|
303
|
+
mcpLogger.info("[CircuitBreakerManager] Reset all circuit breakers");
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Get health summary
|
|
307
|
+
*/
|
|
308
|
+
getHealthSummary() {
|
|
309
|
+
let closedBreakers = 0;
|
|
310
|
+
let openBreakers = 0;
|
|
311
|
+
let halfOpenBreakers = 0;
|
|
312
|
+
const unhealthyBreakers = [];
|
|
313
|
+
for (const [name, breaker] of this.breakers) {
|
|
314
|
+
const stats = breaker.getStats();
|
|
315
|
+
switch (stats.state) {
|
|
316
|
+
case "closed":
|
|
317
|
+
closedBreakers++;
|
|
318
|
+
break;
|
|
319
|
+
case "open":
|
|
320
|
+
openBreakers++;
|
|
321
|
+
unhealthyBreakers.push(name);
|
|
322
|
+
break;
|
|
323
|
+
case "half-open":
|
|
324
|
+
halfOpenBreakers++;
|
|
325
|
+
break;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
return {
|
|
329
|
+
totalBreakers: this.breakers.size,
|
|
330
|
+
closedBreakers,
|
|
331
|
+
openBreakers,
|
|
332
|
+
halfOpenBreakers,
|
|
333
|
+
unhealthyBreakers,
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
// Global circuit breaker manager instance
|
|
338
|
+
export const globalCircuitBreakerManager = new CircuitBreakerManager();
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Client Factory
|
|
3
|
+
* Creates and manages MCP clients for external servers
|
|
4
|
+
* Supports stdio, SSE, and WebSocket transports
|
|
5
|
+
*/
|
|
6
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
7
|
+
import type { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
|
|
8
|
+
import type { ClientCapabilities } from "@modelcontextprotocol/sdk/types.js";
|
|
9
|
+
import { ChildProcess } from "child_process";
|
|
10
|
+
import type { ExternalMCPServerConfig, MCPTransportType } from "../types/externalMcp.js";
|
|
11
|
+
/**
|
|
12
|
+
* MCP client creation result
|
|
13
|
+
*/
|
|
14
|
+
export interface MCPClientResult {
|
|
15
|
+
/** Whether client creation was successful */
|
|
16
|
+
success: boolean;
|
|
17
|
+
/** Created client instance */
|
|
18
|
+
client?: Client;
|
|
19
|
+
/** Created transport instance */
|
|
20
|
+
transport?: Transport;
|
|
21
|
+
/** Created process (for stdio transport) */
|
|
22
|
+
process?: ChildProcess;
|
|
23
|
+
/** Error message if failed */
|
|
24
|
+
error?: string;
|
|
25
|
+
/** Creation duration in milliseconds */
|
|
26
|
+
duration: number;
|
|
27
|
+
/** Server capabilities reported during handshake */
|
|
28
|
+
capabilities?: ClientCapabilities;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* MCPClientFactory
|
|
32
|
+
* Factory class for creating MCP clients with different transports
|
|
33
|
+
*/
|
|
34
|
+
export declare class MCPClientFactory {
|
|
35
|
+
private static readonly NEUROLINK_IMPLEMENTATION;
|
|
36
|
+
private static readonly DEFAULT_CAPABILITIES;
|
|
37
|
+
/**
|
|
38
|
+
* Create an MCP client for the given server configuration
|
|
39
|
+
*/
|
|
40
|
+
static createClient(config: ExternalMCPServerConfig, timeout?: number): Promise<MCPClientResult>;
|
|
41
|
+
/**
|
|
42
|
+
* Internal client creation logic
|
|
43
|
+
*/
|
|
44
|
+
private static createClientInternal;
|
|
45
|
+
/**
|
|
46
|
+
* Create transport based on configuration
|
|
47
|
+
*/
|
|
48
|
+
private static createTransport;
|
|
49
|
+
/**
|
|
50
|
+
* Create stdio transport with process spawning
|
|
51
|
+
*/
|
|
52
|
+
private static createStdioTransport;
|
|
53
|
+
/**
|
|
54
|
+
* Create SSE transport
|
|
55
|
+
*/
|
|
56
|
+
private static createSSETransport;
|
|
57
|
+
/**
|
|
58
|
+
* Create WebSocket transport
|
|
59
|
+
*/
|
|
60
|
+
private static createWebSocketTransport;
|
|
61
|
+
/**
|
|
62
|
+
* Perform MCP handshake and get server capabilities
|
|
63
|
+
*/
|
|
64
|
+
private static performHandshake;
|
|
65
|
+
/**
|
|
66
|
+
* Get server information
|
|
67
|
+
*/
|
|
68
|
+
private static getServerInfo;
|
|
69
|
+
/**
|
|
70
|
+
* Extract capabilities from server info
|
|
71
|
+
*/
|
|
72
|
+
private static extractCapabilities;
|
|
73
|
+
/**
|
|
74
|
+
* Create a timeout promise
|
|
75
|
+
*/
|
|
76
|
+
private static createTimeoutPromise;
|
|
77
|
+
/**
|
|
78
|
+
* Close an MCP client and clean up resources
|
|
79
|
+
*/
|
|
80
|
+
static closeClient(client: Client, transport: Transport, process?: ChildProcess): Promise<void>;
|
|
81
|
+
/**
|
|
82
|
+
* Test connection to an MCP server
|
|
83
|
+
*/
|
|
84
|
+
static testConnection(config: ExternalMCPServerConfig, timeout?: number): Promise<{
|
|
85
|
+
success: boolean;
|
|
86
|
+
error?: string;
|
|
87
|
+
capabilities?: ClientCapabilities;
|
|
88
|
+
}>;
|
|
89
|
+
/**
|
|
90
|
+
* Validate MCP server configuration for client creation
|
|
91
|
+
*/
|
|
92
|
+
static validateClientConfig(config: ExternalMCPServerConfig): {
|
|
93
|
+
isValid: boolean;
|
|
94
|
+
errors: string[];
|
|
95
|
+
};
|
|
96
|
+
/**
|
|
97
|
+
* Get supported transport types
|
|
98
|
+
*/
|
|
99
|
+
static getSupportedTransports(): MCPTransportType[];
|
|
100
|
+
/**
|
|
101
|
+
* Get default client capabilities
|
|
102
|
+
*/
|
|
103
|
+
static getDefaultCapabilities(): ClientCapabilities;
|
|
104
|
+
}
|