@claude-flow/mcp 3.0.0-alpha.1

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 (92) hide show
  1. package/.agentic-flow/intelligence.json +16 -0
  2. package/README.md +428 -0
  3. package/__tests__/integration.test.ts +449 -0
  4. package/__tests__/mcp.test.ts +641 -0
  5. package/dist/connection-pool.d.ts +36 -0
  6. package/dist/connection-pool.d.ts.map +1 -0
  7. package/dist/connection-pool.js +273 -0
  8. package/dist/connection-pool.js.map +1 -0
  9. package/dist/index.d.ts +75 -0
  10. package/dist/index.d.ts.map +1 -0
  11. package/dist/index.js +85 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/oauth.d.ts +146 -0
  14. package/dist/oauth.d.ts.map +1 -0
  15. package/dist/oauth.js +318 -0
  16. package/dist/oauth.js.map +1 -0
  17. package/dist/prompt-registry.d.ts +90 -0
  18. package/dist/prompt-registry.d.ts.map +1 -0
  19. package/dist/prompt-registry.js +209 -0
  20. package/dist/prompt-registry.js.map +1 -0
  21. package/dist/rate-limiter.d.ts +86 -0
  22. package/dist/rate-limiter.d.ts.map +1 -0
  23. package/dist/rate-limiter.js +197 -0
  24. package/dist/rate-limiter.js.map +1 -0
  25. package/dist/resource-registry.d.ts +144 -0
  26. package/dist/resource-registry.d.ts.map +1 -0
  27. package/dist/resource-registry.js +405 -0
  28. package/dist/resource-registry.js.map +1 -0
  29. package/dist/sampling.d.ts +102 -0
  30. package/dist/sampling.d.ts.map +1 -0
  31. package/dist/sampling.js +268 -0
  32. package/dist/sampling.js.map +1 -0
  33. package/dist/schema-validator.d.ts +30 -0
  34. package/dist/schema-validator.d.ts.map +1 -0
  35. package/dist/schema-validator.js +182 -0
  36. package/dist/schema-validator.js.map +1 -0
  37. package/dist/server.d.ts +122 -0
  38. package/dist/server.d.ts.map +1 -0
  39. package/dist/server.js +829 -0
  40. package/dist/server.js.map +1 -0
  41. package/dist/session-manager.d.ts +55 -0
  42. package/dist/session-manager.d.ts.map +1 -0
  43. package/dist/session-manager.js +252 -0
  44. package/dist/session-manager.js.map +1 -0
  45. package/dist/task-manager.d.ts +81 -0
  46. package/dist/task-manager.d.ts.map +1 -0
  47. package/dist/task-manager.js +337 -0
  48. package/dist/task-manager.js.map +1 -0
  49. package/dist/tool-registry.d.ts +88 -0
  50. package/dist/tool-registry.d.ts.map +1 -0
  51. package/dist/tool-registry.js +353 -0
  52. package/dist/tool-registry.js.map +1 -0
  53. package/dist/transport/http.d.ts +55 -0
  54. package/dist/transport/http.d.ts.map +1 -0
  55. package/dist/transport/http.js +446 -0
  56. package/dist/transport/http.js.map +1 -0
  57. package/dist/transport/index.d.ts +50 -0
  58. package/dist/transport/index.d.ts.map +1 -0
  59. package/dist/transport/index.js +181 -0
  60. package/dist/transport/index.js.map +1 -0
  61. package/dist/transport/stdio.d.ts +43 -0
  62. package/dist/transport/stdio.d.ts.map +1 -0
  63. package/dist/transport/stdio.js +194 -0
  64. package/dist/transport/stdio.js.map +1 -0
  65. package/dist/transport/websocket.d.ts +65 -0
  66. package/dist/transport/websocket.d.ts.map +1 -0
  67. package/dist/transport/websocket.js +314 -0
  68. package/dist/transport/websocket.js.map +1 -0
  69. package/dist/types.d.ts +473 -0
  70. package/dist/types.d.ts.map +1 -0
  71. package/dist/types.js +40 -0
  72. package/dist/types.js.map +1 -0
  73. package/package.json +42 -0
  74. package/src/connection-pool.ts +344 -0
  75. package/src/index.ts +253 -0
  76. package/src/oauth.ts +447 -0
  77. package/src/prompt-registry.ts +296 -0
  78. package/src/rate-limiter.ts +266 -0
  79. package/src/resource-registry.ts +530 -0
  80. package/src/sampling.ts +363 -0
  81. package/src/schema-validator.ts +213 -0
  82. package/src/server.ts +1134 -0
  83. package/src/session-manager.ts +339 -0
  84. package/src/task-manager.ts +427 -0
  85. package/src/tool-registry.ts +475 -0
  86. package/src/transport/http.ts +532 -0
  87. package/src/transport/index.ts +233 -0
  88. package/src/transport/stdio.ts +252 -0
  89. package/src/transport/websocket.ts +396 -0
  90. package/src/types.ts +664 -0
  91. package/tsconfig.json +20 -0
  92. package/vitest.config.ts +13 -0
@@ -0,0 +1,339 @@
1
+ /**
2
+ * @claude-flow/mcp - Session Manager
3
+ *
4
+ * MCP session lifecycle management
5
+ */
6
+
7
+ import { EventEmitter } from 'events';
8
+ import type {
9
+ MCPSession,
10
+ SessionState,
11
+ SessionMetrics,
12
+ MCPInitializeParams,
13
+ TransportType,
14
+ ILogger,
15
+ } from './types.js';
16
+
17
+ export interface SessionConfig {
18
+ maxSessions?: number;
19
+ sessionTimeout?: number;
20
+ cleanupInterval?: number;
21
+ enableMetrics?: boolean;
22
+ }
23
+
24
+ const DEFAULT_SESSION_CONFIG: Required<SessionConfig> = {
25
+ maxSessions: 100,
26
+ sessionTimeout: 30 * 60 * 1000,
27
+ cleanupInterval: 60 * 1000,
28
+ enableMetrics: true,
29
+ };
30
+
31
+ export class SessionManager extends EventEmitter {
32
+ private readonly sessions: Map<string, MCPSession> = new Map();
33
+ private readonly config: Required<SessionConfig>;
34
+ private cleanupTimer?: NodeJS.Timeout;
35
+ private sessionCounter = 0;
36
+
37
+ private totalCreated = 0;
38
+ private totalClosed = 0;
39
+ private totalExpired = 0;
40
+
41
+ constructor(
42
+ private readonly logger: ILogger,
43
+ config: SessionConfig = {}
44
+ ) {
45
+ super();
46
+ this.config = { ...DEFAULT_SESSION_CONFIG, ...config };
47
+ this.startCleanupTimer();
48
+ }
49
+
50
+ createSession(transport: TransportType): MCPSession {
51
+ if (this.sessions.size >= this.config.maxSessions) {
52
+ throw new Error(`Maximum sessions (${this.config.maxSessions}) reached`);
53
+ }
54
+
55
+ const id = this.generateSessionId();
56
+ const now = new Date();
57
+
58
+ const session: MCPSession = {
59
+ id,
60
+ state: 'created',
61
+ transport,
62
+ createdAt: now,
63
+ lastActivityAt: now,
64
+ isInitialized: false,
65
+ isAuthenticated: false,
66
+ };
67
+
68
+ this.sessions.set(id, session);
69
+ this.totalCreated++;
70
+
71
+ this.logger.debug('Session created', { id, transport });
72
+ this.emit('session:created', session);
73
+
74
+ return session;
75
+ }
76
+
77
+ initializeSession(
78
+ sessionId: string,
79
+ params: MCPInitializeParams
80
+ ): MCPSession | undefined {
81
+ const session = this.sessions.get(sessionId);
82
+ if (!session) {
83
+ this.logger.warn('Session not found for initialization', { sessionId });
84
+ return undefined;
85
+ }
86
+
87
+ session.state = 'ready';
88
+ session.isInitialized = true;
89
+ session.clientInfo = params.clientInfo;
90
+ session.protocolVersion = params.protocolVersion;
91
+ session.capabilities = params.capabilities;
92
+ session.lastActivityAt = new Date();
93
+
94
+ this.logger.info('Session initialized', {
95
+ sessionId,
96
+ clientInfo: params.clientInfo,
97
+ protocolVersion: params.protocolVersion,
98
+ });
99
+
100
+ this.emit('session:initialized', session);
101
+ return session;
102
+ }
103
+
104
+ authenticateSession(sessionId: string): boolean {
105
+ const session = this.sessions.get(sessionId);
106
+ if (!session) {
107
+ return false;
108
+ }
109
+
110
+ session.isAuthenticated = true;
111
+ session.lastActivityAt = new Date();
112
+
113
+ this.logger.debug('Session authenticated', { sessionId });
114
+ this.emit('session:authenticated', session);
115
+
116
+ return true;
117
+ }
118
+
119
+ getSession(sessionId: string): MCPSession | undefined {
120
+ return this.sessions.get(sessionId);
121
+ }
122
+
123
+ hasSession(sessionId: string): boolean {
124
+ return this.sessions.has(sessionId);
125
+ }
126
+
127
+ getActiveSessions(): MCPSession[] {
128
+ return Array.from(this.sessions.values()).filter(
129
+ (s) => s.state === 'ready' || s.state === 'created' || s.state === 'initializing'
130
+ );
131
+ }
132
+
133
+ updateActivity(sessionId: string): boolean {
134
+ const session = this.sessions.get(sessionId);
135
+ if (!session) {
136
+ return false;
137
+ }
138
+
139
+ session.lastActivityAt = new Date();
140
+ return true;
141
+ }
142
+
143
+ setState(sessionId: string, state: SessionState): boolean {
144
+ const session = this.sessions.get(sessionId);
145
+ if (!session) {
146
+ return false;
147
+ }
148
+
149
+ const oldState = session.state;
150
+ session.state = state;
151
+ session.lastActivityAt = new Date();
152
+
153
+ this.logger.debug('Session state changed', {
154
+ sessionId,
155
+ oldState,
156
+ newState: state,
157
+ });
158
+
159
+ this.emit('session:stateChanged', { session, oldState, newState: state });
160
+ return true;
161
+ }
162
+
163
+ setMetadata(sessionId: string, key: string, value: unknown): boolean {
164
+ const session = this.sessions.get(sessionId);
165
+ if (!session) {
166
+ return false;
167
+ }
168
+
169
+ if (!session.metadata) {
170
+ session.metadata = {};
171
+ }
172
+ session.metadata[key] = value;
173
+ session.lastActivityAt = new Date();
174
+
175
+ return true;
176
+ }
177
+
178
+ getMetadata(sessionId: string, key: string): unknown {
179
+ const session = this.sessions.get(sessionId);
180
+ return session?.metadata?.[key];
181
+ }
182
+
183
+ closeSession(sessionId: string, reason?: string): boolean {
184
+ const session = this.sessions.get(sessionId);
185
+ if (!session) {
186
+ return false;
187
+ }
188
+
189
+ session.state = 'closed';
190
+ this.sessions.delete(sessionId);
191
+ this.totalClosed++;
192
+
193
+ this.logger.info('Session closed', { sessionId, reason });
194
+ this.emit('session:closed', { session, reason });
195
+
196
+ return true;
197
+ }
198
+
199
+ removeSession(sessionId: string): boolean {
200
+ return this.closeSession(sessionId);
201
+ }
202
+
203
+ getSessionMetrics(): SessionMetrics {
204
+ let authenticated = 0;
205
+ let active = 0;
206
+
207
+ for (const session of this.sessions.values()) {
208
+ if (session.isAuthenticated) authenticated++;
209
+ if (session.state === 'ready') active++;
210
+ }
211
+
212
+ return {
213
+ total: this.sessions.size,
214
+ active,
215
+ authenticated,
216
+ expired: this.totalExpired,
217
+ };
218
+ }
219
+
220
+ getStats(): {
221
+ total: number;
222
+ byState: Record<SessionState, number>;
223
+ byTransport: Record<TransportType, number>;
224
+ totalCreated: number;
225
+ totalClosed: number;
226
+ totalExpired: number;
227
+ oldestSession?: Date;
228
+ newestSession?: Date;
229
+ } {
230
+ const byState: Record<string, number> = {
231
+ created: 0,
232
+ initializing: 0,
233
+ ready: 0,
234
+ closing: 0,
235
+ closed: 0,
236
+ error: 0,
237
+ };
238
+
239
+ const byTransport: Record<string, number> = {
240
+ stdio: 0,
241
+ http: 0,
242
+ websocket: 0,
243
+ 'in-process': 0,
244
+ };
245
+
246
+ let oldest: Date | undefined;
247
+ let newest: Date | undefined;
248
+
249
+ for (const session of this.sessions.values()) {
250
+ byState[session.state] = (byState[session.state] || 0) + 1;
251
+ byTransport[session.transport] = (byTransport[session.transport] || 0) + 1;
252
+
253
+ if (!oldest || session.createdAt < oldest) {
254
+ oldest = session.createdAt;
255
+ }
256
+ if (!newest || session.createdAt > newest) {
257
+ newest = session.createdAt;
258
+ }
259
+ }
260
+
261
+ return {
262
+ total: this.sessions.size,
263
+ byState: byState as Record<SessionState, number>,
264
+ byTransport: byTransport as Record<TransportType, number>,
265
+ totalCreated: this.totalCreated,
266
+ totalClosed: this.totalClosed,
267
+ totalExpired: this.totalExpired,
268
+ oldestSession: oldest,
269
+ newestSession: newest,
270
+ };
271
+ }
272
+
273
+ cleanupExpiredSessions(): number {
274
+ const now = Date.now();
275
+ const expired: string[] = [];
276
+
277
+ for (const [id, session] of this.sessions) {
278
+ const inactiveTime = now - session.lastActivityAt.getTime();
279
+ if (inactiveTime > this.config.sessionTimeout) {
280
+ expired.push(id);
281
+ }
282
+ }
283
+
284
+ for (const id of expired) {
285
+ const session = this.sessions.get(id);
286
+ if (session) {
287
+ session.state = 'closed';
288
+ this.sessions.delete(id);
289
+ this.totalExpired++;
290
+ this.logger.info('Session expired', { sessionId: id });
291
+ this.emit('session:expired', session);
292
+ }
293
+ }
294
+
295
+ if (expired.length > 0) {
296
+ this.logger.info('Cleaned up expired sessions', { count: expired.length });
297
+ }
298
+
299
+ return expired.length;
300
+ }
301
+
302
+ private startCleanupTimer(): void {
303
+ this.cleanupTimer = setInterval(() => {
304
+ this.cleanupExpiredSessions();
305
+ }, this.config.cleanupInterval);
306
+ }
307
+
308
+ private stopCleanupTimer(): void {
309
+ if (this.cleanupTimer) {
310
+ clearInterval(this.cleanupTimer);
311
+ this.cleanupTimer = undefined;
312
+ }
313
+ }
314
+
315
+ private generateSessionId(): string {
316
+ return `session-${++this.sessionCounter}-${Date.now()}-${Math.random().toString(36).substring(2, 8)}`;
317
+ }
318
+
319
+ clearAll(): void {
320
+ for (const id of this.sessions.keys()) {
321
+ this.closeSession(id, 'Session manager cleared');
322
+ }
323
+ this.logger.info('All sessions cleared');
324
+ }
325
+
326
+ destroy(): void {
327
+ this.stopCleanupTimer();
328
+ this.clearAll();
329
+ this.removeAllListeners();
330
+ this.logger.info('Session manager destroyed');
331
+ }
332
+ }
333
+
334
+ export function createSessionManager(
335
+ logger: ILogger,
336
+ config?: SessionConfig
337
+ ): SessionManager {
338
+ return new SessionManager(logger, config);
339
+ }