claude-flow 1.0.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 (83) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +612 -0
  3. package/bin/claude-flow +0 -0
  4. package/bin/claude-flow-simple +0 -0
  5. package/bin/claude-flow-typecheck +0 -0
  6. package/deno.json +84 -0
  7. package/package.json +45 -0
  8. package/scripts/check-links.ts +274 -0
  9. package/scripts/check-performance-regression.ts +168 -0
  10. package/scripts/claude-sparc.sh +562 -0
  11. package/scripts/coverage-report.ts +692 -0
  12. package/scripts/demo-task-system.ts +224 -0
  13. package/scripts/install.js +72 -0
  14. package/scripts/test-batch-tasks.ts +29 -0
  15. package/scripts/test-coordination-features.ts +238 -0
  16. package/scripts/test-mcp.ts +251 -0
  17. package/scripts/test-runner.ts +571 -0
  18. package/scripts/validate-examples.ts +288 -0
  19. package/src/cli/cli-core.ts +273 -0
  20. package/src/cli/commands/agent.ts +83 -0
  21. package/src/cli/commands/config.ts +442 -0
  22. package/src/cli/commands/help.ts +765 -0
  23. package/src/cli/commands/index.ts +963 -0
  24. package/src/cli/commands/mcp.ts +191 -0
  25. package/src/cli/commands/memory.ts +74 -0
  26. package/src/cli/commands/monitor.ts +403 -0
  27. package/src/cli/commands/session.ts +595 -0
  28. package/src/cli/commands/start.ts +156 -0
  29. package/src/cli/commands/status.ts +345 -0
  30. package/src/cli/commands/task.ts +79 -0
  31. package/src/cli/commands/workflow.ts +763 -0
  32. package/src/cli/completion.ts +553 -0
  33. package/src/cli/formatter.ts +310 -0
  34. package/src/cli/index.ts +211 -0
  35. package/src/cli/main.ts +23 -0
  36. package/src/cli/repl.ts +1050 -0
  37. package/src/cli/simple-cli.js +211 -0
  38. package/src/cli/simple-cli.ts +211 -0
  39. package/src/coordination/README.md +400 -0
  40. package/src/coordination/advanced-scheduler.ts +487 -0
  41. package/src/coordination/circuit-breaker.ts +366 -0
  42. package/src/coordination/conflict-resolution.ts +490 -0
  43. package/src/coordination/dependency-graph.ts +475 -0
  44. package/src/coordination/index.ts +63 -0
  45. package/src/coordination/manager.ts +460 -0
  46. package/src/coordination/messaging.ts +290 -0
  47. package/src/coordination/metrics.ts +585 -0
  48. package/src/coordination/resources.ts +322 -0
  49. package/src/coordination/scheduler.ts +390 -0
  50. package/src/coordination/work-stealing.ts +224 -0
  51. package/src/core/config.ts +627 -0
  52. package/src/core/event-bus.ts +186 -0
  53. package/src/core/json-persistence.ts +183 -0
  54. package/src/core/logger.ts +262 -0
  55. package/src/core/orchestrator-fixed.ts +312 -0
  56. package/src/core/orchestrator.ts +1234 -0
  57. package/src/core/persistence.ts +276 -0
  58. package/src/mcp/auth.ts +438 -0
  59. package/src/mcp/claude-flow-tools.ts +1280 -0
  60. package/src/mcp/load-balancer.ts +510 -0
  61. package/src/mcp/router.ts +240 -0
  62. package/src/mcp/server.ts +548 -0
  63. package/src/mcp/session-manager.ts +418 -0
  64. package/src/mcp/tools.ts +180 -0
  65. package/src/mcp/transports/base.ts +21 -0
  66. package/src/mcp/transports/http.ts +457 -0
  67. package/src/mcp/transports/stdio.ts +254 -0
  68. package/src/memory/backends/base.ts +22 -0
  69. package/src/memory/backends/markdown.ts +283 -0
  70. package/src/memory/backends/sqlite.ts +329 -0
  71. package/src/memory/cache.ts +238 -0
  72. package/src/memory/indexer.ts +238 -0
  73. package/src/memory/manager.ts +572 -0
  74. package/src/terminal/adapters/base.ts +29 -0
  75. package/src/terminal/adapters/native.ts +504 -0
  76. package/src/terminal/adapters/vscode.ts +340 -0
  77. package/src/terminal/manager.ts +308 -0
  78. package/src/terminal/pool.ts +271 -0
  79. package/src/terminal/session.ts +250 -0
  80. package/src/terminal/vscode-bridge.ts +242 -0
  81. package/src/utils/errors.ts +231 -0
  82. package/src/utils/helpers.ts +476 -0
  83. package/src/utils/types.ts +493 -0
@@ -0,0 +1,418 @@
1
+ /**
2
+ * Session manager for MCP connections
3
+ */
4
+
5
+ import {
6
+ MCPSession,
7
+ MCPInitializeParams,
8
+ MCPProtocolVersion,
9
+ MCPCapabilities,
10
+ MCPAuthConfig,
11
+ MCPConfig,
12
+ } from '../utils/types.ts';
13
+ import { ILogger } from '../core/logger.ts';
14
+ import { MCPError } from '../utils/errors.ts';
15
+ import { createHash, timingSafeEqual } from 'node:crypto';
16
+
17
+ export interface ISessionManager {
18
+ createSession(transport: 'stdio' | 'http' | 'websocket'): MCPSession;
19
+ getSession(id: string): MCPSession | undefined;
20
+ initializeSession(sessionId: string, params: MCPInitializeParams): void;
21
+ authenticateSession(sessionId: string, credentials: unknown): boolean;
22
+ updateActivity(sessionId: string): void;
23
+ removeSession(sessionId: string): void;
24
+ getActiveSessions(): MCPSession[];
25
+ cleanupExpiredSessions(): void;
26
+ getSessionMetrics(): {
27
+ total: number;
28
+ active: number;
29
+ authenticated: number;
30
+ expired: number;
31
+ };
32
+ }
33
+
34
+ /**
35
+ * Session manager implementation
36
+ */
37
+ export class SessionManager implements ISessionManager {
38
+ private sessions = new Map<string, MCPSession>();
39
+ private authConfig: MCPAuthConfig;
40
+ private sessionTimeout: number;
41
+ private maxSessions: number;
42
+ private cleanupInterval?: number;
43
+
44
+ constructor(
45
+ private config: MCPConfig,
46
+ private logger: ILogger,
47
+ ) {
48
+ this.authConfig = config.auth || { enabled: false, method: 'token' };
49
+ this.sessionTimeout = config.sessionTimeout || 3600000; // 1 hour default
50
+ this.maxSessions = config.maxSessions || 100;
51
+
52
+ // Start cleanup timer
53
+ this.cleanupInterval = setInterval(() => {
54
+ this.cleanupExpiredSessions();
55
+ }, 60000); // Clean up every minute
56
+ }
57
+
58
+ createSession(transport: 'stdio' | 'http' | 'websocket'): MCPSession {
59
+ // Check session limit
60
+ if (this.sessions.size >= this.maxSessions) {
61
+ // Try to clean up expired sessions first
62
+ this.cleanupExpiredSessions();
63
+
64
+ if (this.sessions.size >= this.maxSessions) {
65
+ throw new MCPError('Maximum number of sessions reached');
66
+ }
67
+ }
68
+
69
+ const sessionId = this.generateSessionId();
70
+ const now = new Date();
71
+
72
+ const session: MCPSession = {
73
+ id: sessionId,
74
+ clientInfo: { name: 'unknown', version: 'unknown' },
75
+ protocolVersion: { major: 0, minor: 0, patch: 0 },
76
+ capabilities: {},
77
+ isInitialized: false,
78
+ createdAt: now,
79
+ lastActivity: now,
80
+ transport,
81
+ authenticated: !this.authConfig.enabled, // If auth disabled, session is authenticated
82
+ };
83
+
84
+ this.sessions.set(sessionId, session);
85
+
86
+ this.logger.info('Session created', {
87
+ sessionId,
88
+ transport,
89
+ totalSessions: this.sessions.size,
90
+ });
91
+
92
+ return session;
93
+ }
94
+
95
+ getSession(id: string): MCPSession | undefined {
96
+ const session = this.sessions.get(id);
97
+ if (session && this.isSessionExpired(session)) {
98
+ this.removeSession(id);
99
+ return undefined;
100
+ }
101
+ return session;
102
+ }
103
+
104
+ initializeSession(sessionId: string, params: MCPInitializeParams): void {
105
+ const session = this.getSession(sessionId);
106
+ if (!session) {
107
+ throw new MCPError(`Session not found: ${sessionId}`);
108
+ }
109
+
110
+ // Validate protocol version
111
+ this.validateProtocolVersion(params.protocolVersion);
112
+
113
+ // Update session with initialization params
114
+ session.clientInfo = params.clientInfo;
115
+ session.protocolVersion = params.protocolVersion;
116
+ session.capabilities = params.capabilities;
117
+ session.isInitialized = true;
118
+ session.lastActivity = new Date();
119
+
120
+ this.logger.info('Session initialized', {
121
+ sessionId,
122
+ clientInfo: params.clientInfo,
123
+ protocolVersion: params.protocolVersion,
124
+ });
125
+ }
126
+
127
+ authenticateSession(sessionId: string, credentials: unknown): boolean {
128
+ const session = this.getSession(sessionId);
129
+ if (!session) {
130
+ return false;
131
+ }
132
+
133
+ if (!this.authConfig.enabled) {
134
+ session.authenticated = true;
135
+ return true;
136
+ }
137
+
138
+ let authenticated = false;
139
+
140
+ switch (this.authConfig.method) {
141
+ case 'token':
142
+ authenticated = this.authenticateToken(credentials);
143
+ break;
144
+ case 'basic':
145
+ authenticated = this.authenticateBasic(credentials);
146
+ break;
147
+ case 'oauth':
148
+ authenticated = this.authenticateOAuth(credentials);
149
+ break;
150
+ default:
151
+ this.logger.warn('Unknown authentication method', {
152
+ method: this.authConfig.method,
153
+ });
154
+ return false;
155
+ }
156
+
157
+ if (authenticated) {
158
+ session.authenticated = true;
159
+ session.authData = this.extractAuthData(credentials);
160
+ session.lastActivity = new Date();
161
+
162
+ this.logger.info('Session authenticated', {
163
+ sessionId,
164
+ method: this.authConfig.method,
165
+ });
166
+ } else {
167
+ this.logger.warn('Session authentication failed', {
168
+ sessionId,
169
+ method: this.authConfig.method,
170
+ });
171
+ }
172
+
173
+ return authenticated;
174
+ }
175
+
176
+ updateActivity(sessionId: string): void {
177
+ const session = this.getSession(sessionId);
178
+ if (session) {
179
+ session.lastActivity = new Date();
180
+ }
181
+ }
182
+
183
+ removeSession(sessionId: string): void {
184
+ const session = this.sessions.get(sessionId);
185
+ if (session) {
186
+ this.sessions.delete(sessionId);
187
+ this.logger.info('Session removed', {
188
+ sessionId,
189
+ duration: Date.now() - session.createdAt.getTime(),
190
+ transport: session.transport,
191
+ });
192
+ }
193
+ }
194
+
195
+ getActiveSessions(): MCPSession[] {
196
+ const activeSessions: MCPSession[] = [];
197
+ for (const session of this.sessions.values()) {
198
+ if (!this.isSessionExpired(session)) {
199
+ activeSessions.push(session);
200
+ }
201
+ }
202
+ return activeSessions;
203
+ }
204
+
205
+ cleanupExpiredSessions(): void {
206
+ const expiredSessions: string[] = [];
207
+
208
+ for (const [sessionId, session] of this.sessions) {
209
+ if (this.isSessionExpired(session)) {
210
+ expiredSessions.push(sessionId);
211
+ }
212
+ }
213
+
214
+ for (const sessionId of expiredSessions) {
215
+ this.removeSession(sessionId);
216
+ }
217
+
218
+ if (expiredSessions.length > 0) {
219
+ this.logger.info('Cleaned up expired sessions', {
220
+ count: expiredSessions.length,
221
+ remainingSessions: this.sessions.size,
222
+ });
223
+ }
224
+ }
225
+
226
+ getSessionMetrics(): {
227
+ total: number;
228
+ active: number;
229
+ authenticated: number;
230
+ expired: number;
231
+ } {
232
+ let active = 0;
233
+ let authenticated = 0;
234
+ let expired = 0;
235
+
236
+ for (const session of this.sessions.values()) {
237
+ if (this.isSessionExpired(session)) {
238
+ expired++;
239
+ } else {
240
+ active++;
241
+ if (session.authenticated) {
242
+ authenticated++;
243
+ }
244
+ }
245
+ }
246
+
247
+ return {
248
+ total: this.sessions.size,
249
+ active,
250
+ authenticated,
251
+ expired,
252
+ };
253
+ }
254
+
255
+ destroy(): void {
256
+ if (this.cleanupInterval) {
257
+ clearInterval(this.cleanupInterval);
258
+ }
259
+ this.sessions.clear();
260
+ }
261
+
262
+ private generateSessionId(): string {
263
+ const timestamp = Date.now().toString(36);
264
+ const random = Math.random().toString(36).substr(2, 9);
265
+ return `session_${timestamp}_${random}`;
266
+ }
267
+
268
+ private isSessionExpired(session: MCPSession): boolean {
269
+ const now = Date.now();
270
+ const sessionAge = now - session.lastActivity.getTime();
271
+ return sessionAge > this.sessionTimeout;
272
+ }
273
+
274
+ private validateProtocolVersion(version: MCPProtocolVersion): void {
275
+ // Currently supporting MCP version 2024-11-05
276
+ const supportedVersions = [
277
+ { major: 2024, minor: 11, patch: 5 },
278
+ ];
279
+
280
+ const isSupported = supportedVersions.some(
281
+ (supported) =>
282
+ supported.major === version.major &&
283
+ supported.minor === version.minor &&
284
+ supported.patch === version.patch,
285
+ );
286
+
287
+ if (!isSupported) {
288
+ throw new MCPError(
289
+ `Unsupported protocol version: ${version.major}.${version.minor}.${version.patch}`,
290
+ { supportedVersions }
291
+ );
292
+ }
293
+ }
294
+
295
+ private authenticateToken(credentials: unknown): boolean {
296
+ if (!this.authConfig.tokens || this.authConfig.tokens.length === 0) {
297
+ return false;
298
+ }
299
+
300
+ const token = this.extractToken(credentials);
301
+ if (!token) {
302
+ return false;
303
+ }
304
+
305
+ // Use timing-safe comparison to prevent timing attacks
306
+ return this.authConfig.tokens.some((validToken) => {
307
+ const encoder = new TextEncoder();
308
+ const validTokenBytes = encoder.encode(validToken);
309
+ const providedTokenBytes = encoder.encode(token);
310
+
311
+ if (validTokenBytes.length !== providedTokenBytes.length) {
312
+ return false;
313
+ }
314
+
315
+ return timingSafeEqual(validTokenBytes, providedTokenBytes);
316
+ });
317
+ }
318
+
319
+ private authenticateBasic(credentials: unknown): boolean {
320
+ if (!this.authConfig.users || this.authConfig.users.length === 0) {
321
+ return false;
322
+ }
323
+
324
+ const { username, password } = this.extractBasicAuth(credentials);
325
+ if (!username || !password) {
326
+ return false;
327
+ }
328
+
329
+ const user = this.authConfig.users.find((u) => u.username === username);
330
+ if (!user) {
331
+ return false;
332
+ }
333
+
334
+ // Hash the provided password and compare
335
+ const hashedPassword = this.hashPassword(password);
336
+ const expectedHashedPassword = this.hashPassword(user.password);
337
+
338
+ const encoder = new TextEncoder();
339
+ const hashedPasswordBytes = encoder.encode(hashedPassword);
340
+ const expectedHashedPasswordBytes = encoder.encode(expectedHashedPassword);
341
+
342
+ if (hashedPasswordBytes.length !== expectedHashedPasswordBytes.length) {
343
+ return false;
344
+ }
345
+
346
+ return timingSafeEqual(hashedPasswordBytes, expectedHashedPasswordBytes);
347
+ }
348
+
349
+ private authenticateOAuth(credentials: unknown): boolean {
350
+ // TODO: Implement OAuth authentication
351
+ // This would typically involve validating JWT tokens
352
+ this.logger.warn('OAuth authentication not yet implemented');
353
+ return false;
354
+ }
355
+
356
+ private extractToken(credentials: unknown): string | null {
357
+ if (typeof credentials === 'string') {
358
+ return credentials;
359
+ }
360
+
361
+ if (typeof credentials === 'object' && credentials !== null) {
362
+ const creds = credentials as Record<string, unknown>;
363
+ if (typeof creds.token === 'string') {
364
+ return creds.token;
365
+ }
366
+ if (typeof creds.authorization === 'string') {
367
+ const match = creds.authorization.match(/^Bearer\s+(.+)$/);
368
+ return match ? match[1] : null;
369
+ }
370
+ }
371
+
372
+ return null;
373
+ }
374
+
375
+ private extractBasicAuth(credentials: unknown): { username?: string; password?: string } {
376
+ if (typeof credentials === 'object' && credentials !== null) {
377
+ const creds = credentials as Record<string, unknown>;
378
+
379
+ if (typeof creds.username === 'string' && typeof creds.password === 'string') {
380
+ return {
381
+ username: creds.username,
382
+ password: creds.password,
383
+ };
384
+ }
385
+
386
+ if (typeof creds.authorization === 'string') {
387
+ const match = creds.authorization.match(/^Basic\s+(.+)$/);
388
+ if (match) {
389
+ try {
390
+ const decoded = atob(match[1]);
391
+ const [username, password] = decoded.split(':', 2);
392
+ return { username, password };
393
+ } catch {
394
+ return {};
395
+ }
396
+ }
397
+ }
398
+ }
399
+
400
+ return {};
401
+ }
402
+
403
+ private extractAuthData(credentials: unknown): any {
404
+ if (typeof credentials === 'object' && credentials !== null) {
405
+ const creds = credentials as Record<string, unknown>;
406
+ return {
407
+ token: this.extractToken(credentials),
408
+ user: creds.username || creds.user,
409
+ permissions: creds.permissions || [],
410
+ };
411
+ }
412
+ return {};
413
+ }
414
+
415
+ private hashPassword(password: string): string {
416
+ return createHash('sha256').update(password).digest('hex');
417
+ }
418
+ }
@@ -0,0 +1,180 @@
1
+ /**
2
+ * Tool registry for MCP
3
+ */
4
+
5
+ import { MCPTool } from '../utils/types.ts';
6
+ import { ILogger } from '../core/logger.ts';
7
+ import { MCPError } from '../utils/errors.ts';
8
+
9
+ /**
10
+ * Tool registry implementation
11
+ */
12
+ export class ToolRegistry {
13
+ private tools = new Map<string, MCPTool>();
14
+
15
+ constructor(private logger: ILogger) {}
16
+
17
+ /**
18
+ * Registers a new tool
19
+ */
20
+ register(tool: MCPTool): void {
21
+ if (this.tools.has(tool.name)) {
22
+ throw new MCPError(`Tool already registered: ${tool.name}`);
23
+ }
24
+
25
+ // Validate tool schema
26
+ this.validateTool(tool);
27
+
28
+ this.tools.set(tool.name, tool);
29
+ this.logger.debug('Tool registered', { name: tool.name });
30
+ }
31
+
32
+ /**
33
+ * Unregisters a tool
34
+ */
35
+ unregister(name: string): void {
36
+ if (!this.tools.has(name)) {
37
+ throw new MCPError(`Tool not found: ${name}`);
38
+ }
39
+
40
+ this.tools.delete(name);
41
+ this.logger.debug('Tool unregistered', { name });
42
+ }
43
+
44
+ /**
45
+ * Gets a tool by name
46
+ */
47
+ getTool(name: string): MCPTool | undefined {
48
+ return this.tools.get(name);
49
+ }
50
+
51
+ /**
52
+ * Lists all registered tools
53
+ */
54
+ listTools(): Array<{ name: string; description: string }> {
55
+ return Array.from(this.tools.values()).map(tool => ({
56
+ name: tool.name,
57
+ description: tool.description,
58
+ }));
59
+ }
60
+
61
+ /**
62
+ * Gets the number of registered tools
63
+ */
64
+ getToolCount(): number {
65
+ return this.tools.size;
66
+ }
67
+
68
+ /**
69
+ * Executes a tool
70
+ */
71
+ async executeTool(name: string, input: unknown): Promise<unknown> {
72
+ const tool = this.tools.get(name);
73
+ if (!tool) {
74
+ throw new MCPError(`Tool not found: ${name}`);
75
+ }
76
+
77
+ this.logger.debug('Executing tool', { name, input });
78
+
79
+ try {
80
+ // Validate input against schema
81
+ this.validateInput(tool, input);
82
+
83
+ // Execute tool handler
84
+ const result = await tool.handler(input);
85
+
86
+ this.logger.debug('Tool executed successfully', { name });
87
+ return result;
88
+ } catch (error) {
89
+ this.logger.error('Tool execution failed', { name, error });
90
+ throw error;
91
+ }
92
+ }
93
+
94
+ /**
95
+ * Validates tool definition
96
+ */
97
+ private validateTool(tool: MCPTool): void {
98
+ if (!tool.name || typeof tool.name !== 'string') {
99
+ throw new MCPError('Tool name must be a non-empty string');
100
+ }
101
+
102
+ if (!tool.description || typeof tool.description !== 'string') {
103
+ throw new MCPError('Tool description must be a non-empty string');
104
+ }
105
+
106
+ if (typeof tool.handler !== 'function') {
107
+ throw new MCPError('Tool handler must be a function');
108
+ }
109
+
110
+ if (!tool.inputSchema || typeof tool.inputSchema !== 'object') {
111
+ throw new MCPError('Tool inputSchema must be an object');
112
+ }
113
+
114
+ // Validate tool name format (namespace/name)
115
+ if (!tool.name.includes('/')) {
116
+ throw new MCPError('Tool name must be in format: namespace/name');
117
+ }
118
+ }
119
+
120
+ /**
121
+ * Validates input against tool schema
122
+ */
123
+ private validateInput(tool: MCPTool, input: unknown): void {
124
+ // Simple validation - in production, use a JSON Schema validator
125
+ const schema = tool.inputSchema as any;
126
+
127
+ if (schema.type === 'object' && schema.properties) {
128
+ if (typeof input !== 'object' || input === null) {
129
+ throw new MCPError('Input must be an object');
130
+ }
131
+
132
+ const inputObj = input as Record<string, unknown>;
133
+
134
+ // Check required properties
135
+ if (schema.required && Array.isArray(schema.required)) {
136
+ for (const prop of schema.required) {
137
+ if (!(prop in inputObj)) {
138
+ throw new MCPError(`Missing required property: ${prop}`);
139
+ }
140
+ }
141
+ }
142
+
143
+ // Check property types
144
+ for (const [prop, propSchema] of Object.entries(schema.properties)) {
145
+ if (prop in inputObj) {
146
+ const value = inputObj[prop];
147
+ const expectedType = (propSchema as any).type;
148
+
149
+ if (expectedType && !this.checkType(value, expectedType)) {
150
+ throw new MCPError(
151
+ `Invalid type for property ${prop}: expected ${expectedType}`,
152
+ );
153
+ }
154
+ }
155
+ }
156
+ }
157
+ }
158
+
159
+ /**
160
+ * Checks if a value matches a JSON Schema type
161
+ */
162
+ private checkType(value: unknown, type: string): boolean {
163
+ switch (type) {
164
+ case 'string':
165
+ return typeof value === 'string';
166
+ case 'number':
167
+ return typeof value === 'number';
168
+ case 'boolean':
169
+ return typeof value === 'boolean';
170
+ case 'object':
171
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
172
+ case 'array':
173
+ return Array.isArray(value);
174
+ case 'null':
175
+ return value === null;
176
+ default:
177
+ return true;
178
+ }
179
+ }
180
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Base transport interface for MCP
3
+ */
4
+
5
+ import { MCPRequest, MCPResponse, MCPNotification } from '../../utils/types.ts';
6
+
7
+ export type RequestHandler = (request: MCPRequest) => Promise<MCPResponse>;
8
+ export type NotificationHandler = (notification: MCPNotification) => Promise<void>;
9
+
10
+ export interface ITransport {
11
+ start(): Promise<void>;
12
+ stop(): Promise<void>;
13
+ onRequest(handler: RequestHandler): void;
14
+ onNotification?(handler: NotificationHandler): void;
15
+ sendNotification?(notification: MCPNotification): Promise<void>;
16
+ getHealthStatus(): Promise<{
17
+ healthy: boolean;
18
+ error?: string;
19
+ metrics?: Record<string, number>;
20
+ }>;
21
+ }