musubi-sdd 3.10.0 → 5.1.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 (44) hide show
  1. package/README.md +24 -19
  2. package/package.json +1 -1
  3. package/src/agents/agent-loop.js +532 -0
  4. package/src/agents/agentic/code-generator.js +767 -0
  5. package/src/agents/agentic/code-reviewer.js +698 -0
  6. package/src/agents/agentic/index.js +43 -0
  7. package/src/agents/function-tool.js +432 -0
  8. package/src/agents/index.js +45 -0
  9. package/src/agents/schema-generator.js +514 -0
  10. package/src/analyzers/ast-extractor.js +870 -0
  11. package/src/analyzers/context-optimizer.js +681 -0
  12. package/src/analyzers/repository-map.js +692 -0
  13. package/src/integrations/index.js +7 -1
  14. package/src/integrations/mcp/index.js +175 -0
  15. package/src/integrations/mcp/mcp-context-provider.js +472 -0
  16. package/src/integrations/mcp/mcp-discovery.js +436 -0
  17. package/src/integrations/mcp/mcp-tool-registry.js +467 -0
  18. package/src/integrations/mcp-connector.js +818 -0
  19. package/src/integrations/tool-discovery.js +589 -0
  20. package/src/managers/index.js +7 -0
  21. package/src/managers/skill-tools.js +565 -0
  22. package/src/monitoring/cost-tracker.js +7 -0
  23. package/src/monitoring/incident-manager.js +10 -0
  24. package/src/monitoring/observability.js +10 -0
  25. package/src/monitoring/quality-dashboard.js +491 -0
  26. package/src/monitoring/release-manager.js +10 -0
  27. package/src/orchestration/agent-skill-binding.js +655 -0
  28. package/src/orchestration/error-handler.js +827 -0
  29. package/src/orchestration/index.js +235 -1
  30. package/src/orchestration/mcp-tool-adapters.js +896 -0
  31. package/src/orchestration/reasoning/index.js +58 -0
  32. package/src/orchestration/reasoning/planning-engine.js +831 -0
  33. package/src/orchestration/reasoning/reasoning-engine.js +710 -0
  34. package/src/orchestration/reasoning/self-correction.js +751 -0
  35. package/src/orchestration/skill-executor.js +665 -0
  36. package/src/orchestration/skill-registry.js +650 -0
  37. package/src/orchestration/workflow-examples.js +1072 -0
  38. package/src/orchestration/workflow-executor.js +779 -0
  39. package/src/phase4-integration.js +248 -0
  40. package/src/phase5-integration.js +402 -0
  41. package/src/steering/steering-auto-update.js +572 -0
  42. package/src/steering/steering-validator.js +547 -0
  43. package/src/templates/template-constraints.js +646 -0
  44. package/src/validators/advanced-validation.js +580 -0
@@ -0,0 +1,896 @@
1
+ /**
2
+ * MCP Tool Adapters - Bidirectional MCP-Skill Integration
3
+ * Sprint 3.3.5: MCP Tool Ecosystem
4
+ *
5
+ * Features:
6
+ * - MCP tool to MUSUBI skill conversion
7
+ * - MUSUBI skill to MCP tool export
8
+ * - Schema translation (JSON Schema ↔ MCP)
9
+ * - Capability mapping
10
+ * - Transport abstraction (stdio, sse, http)
11
+ */
12
+
13
+ const EventEmitter = require('events');
14
+
15
+ /**
16
+ * MCP Transport types
17
+ */
18
+ const MCPTransport = {
19
+ STDIO: 'stdio',
20
+ SSE: 'sse',
21
+ HTTP: 'http',
22
+ WEBSOCKET: 'websocket'
23
+ };
24
+
25
+ /**
26
+ * Adapter direction
27
+ */
28
+ const AdapterDirection = {
29
+ MCP_TO_SKILL: 'mcp-to-skill',
30
+ SKILL_TO_MCP: 'skill-to-mcp'
31
+ };
32
+
33
+ /**
34
+ * MCP Tool Definition (incoming format)
35
+ */
36
+ class MCPToolDefinition {
37
+ constructor(options = {}) {
38
+ this.name = options.name || '';
39
+ this.description = options.description || '';
40
+ this.inputSchema = options.inputSchema || { type: 'object', properties: {} };
41
+ this.annotations = options.annotations || {};
42
+ this.server = options.server || null;
43
+ this.transport = options.transport || MCPTransport.STDIO;
44
+ }
45
+
46
+ validate() {
47
+ const errors = [];
48
+
49
+ if (!this.name) {
50
+ errors.push('Tool name is required');
51
+ }
52
+
53
+ if (!this.inputSchema || this.inputSchema.type !== 'object') {
54
+ errors.push('Input schema must be an object type');
55
+ }
56
+
57
+ return {
58
+ valid: errors.length === 0,
59
+ errors
60
+ };
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Schema translator between MCP and MUSUBI formats
66
+ */
67
+ class SchemaTranslator {
68
+ /**
69
+ * Convert MCP input schema to MUSUBI skill schema
70
+ */
71
+ static mcpToMusubi(mcpSchema) {
72
+ if (!mcpSchema) {
73
+ return {
74
+ type: 'object',
75
+ properties: {},
76
+ required: []
77
+ };
78
+ }
79
+
80
+ const musubiSchema = {
81
+ type: mcpSchema.type || 'object',
82
+ properties: {},
83
+ required: mcpSchema.required || []
84
+ };
85
+
86
+ if (mcpSchema.properties) {
87
+ for (const [key, prop] of Object.entries(mcpSchema.properties)) {
88
+ musubiSchema.properties[key] = this._translateProperty(prop);
89
+ }
90
+ }
91
+
92
+ // Add MUSUBI-specific metadata
93
+ musubiSchema.$schema = 'https://musubi.dev/schemas/skill-input/v1';
94
+
95
+ return musubiSchema;
96
+ }
97
+
98
+ /**
99
+ * Convert MUSUBI skill schema to MCP format
100
+ */
101
+ static musubiToMcp(musubiSchema) {
102
+ if (!musubiSchema) {
103
+ return {
104
+ type: 'object',
105
+ properties: {}
106
+ };
107
+ }
108
+
109
+ const mcpSchema = {
110
+ type: musubiSchema.type || 'object',
111
+ properties: {}
112
+ };
113
+
114
+ if (musubiSchema.required && musubiSchema.required.length > 0) {
115
+ mcpSchema.required = musubiSchema.required;
116
+ }
117
+
118
+ if (musubiSchema.properties) {
119
+ for (const [key, prop] of Object.entries(musubiSchema.properties)) {
120
+ mcpSchema.properties[key] = this._translatePropertyToMcp(prop);
121
+ }
122
+ }
123
+
124
+ return mcpSchema;
125
+ }
126
+
127
+ /**
128
+ * Translate individual property from MCP to MUSUBI
129
+ */
130
+ static _translateProperty(prop) {
131
+ const result = {
132
+ type: prop.type || 'string'
133
+ };
134
+
135
+ if (prop.description) {
136
+ result.description = prop.description;
137
+ }
138
+
139
+ if (prop.default !== undefined) {
140
+ result.default = prop.default;
141
+ }
142
+
143
+ if (prop.enum) {
144
+ result.enum = prop.enum;
145
+ }
146
+
147
+ // Handle nested objects
148
+ if (prop.type === 'object' && prop.properties) {
149
+ result.properties = {};
150
+ for (const [key, nestedProp] of Object.entries(prop.properties)) {
151
+ result.properties[key] = this._translateProperty(nestedProp);
152
+ }
153
+ }
154
+
155
+ // Handle arrays
156
+ if (prop.type === 'array' && prop.items) {
157
+ result.items = this._translateProperty(prop.items);
158
+ }
159
+
160
+ // Handle anyOf/oneOf
161
+ if (prop.anyOf) {
162
+ result.anyOf = prop.anyOf.map(p => this._translateProperty(p));
163
+ }
164
+ if (prop.oneOf) {
165
+ result.oneOf = prop.oneOf.map(p => this._translateProperty(p));
166
+ }
167
+
168
+ return result;
169
+ }
170
+
171
+ /**
172
+ * Translate individual property from MUSUBI to MCP
173
+ */
174
+ static _translatePropertyToMcp(prop) {
175
+ const result = {
176
+ type: prop.type || 'string'
177
+ };
178
+
179
+ if (prop.description) {
180
+ result.description = prop.description;
181
+ }
182
+
183
+ if (prop.default !== undefined) {
184
+ result.default = prop.default;
185
+ }
186
+
187
+ if (prop.enum) {
188
+ result.enum = prop.enum;
189
+ }
190
+
191
+ // Handle nested objects
192
+ if (prop.type === 'object' && prop.properties) {
193
+ result.properties = {};
194
+ for (const [key, nestedProp] of Object.entries(prop.properties)) {
195
+ result.properties[key] = this._translatePropertyToMcp(nestedProp);
196
+ }
197
+ }
198
+
199
+ // Handle arrays
200
+ if (prop.type === 'array' && prop.items) {
201
+ result.items = this._translatePropertyToMcp(prop.items);
202
+ }
203
+
204
+ return result;
205
+ }
206
+ }
207
+
208
+ /**
209
+ * MCP to MUSUBI Skill Adapter
210
+ * Wraps MCP tools as MUSUBI skills
211
+ */
212
+ class MCPToSkillAdapter extends EventEmitter {
213
+ constructor(options = {}) {
214
+ super();
215
+ this.registry = options.registry || null;
216
+ this.adapters = new Map();
217
+ this.connections = new Map();
218
+ this.defaultTimeout = options.timeout || 30000;
219
+ this.retryConfig = options.retry || {
220
+ maxRetries: 3,
221
+ baseDelay: 1000,
222
+ maxDelay: 10000
223
+ };
224
+ }
225
+
226
+ /**
227
+ * Register an MCP server and import its tools as skills
228
+ */
229
+ async registerServer(serverConfig) {
230
+ const { id, transport, endpoint, tools } = serverConfig;
231
+
232
+ if (this.connections.has(id)) {
233
+ throw new Error(`Server ${id} is already registered`);
234
+ }
235
+
236
+ // Store connection config
237
+ this.connections.set(id, {
238
+ id,
239
+ transport,
240
+ endpoint,
241
+ status: 'connected',
242
+ registeredAt: new Date()
243
+ });
244
+
245
+ // Import tools as skills
246
+ const importedSkills = [];
247
+ for (const tool of tools || []) {
248
+ try {
249
+ const skill = await this.importTool(id, tool);
250
+ importedSkills.push(skill);
251
+ } catch (error) {
252
+ this.emit('import-error', { serverId: id, tool, error });
253
+ }
254
+ }
255
+
256
+ this.emit('server-registered', {
257
+ serverId: id,
258
+ skillCount: importedSkills.length
259
+ });
260
+
261
+ return importedSkills;
262
+ }
263
+
264
+ /**
265
+ * Import a single MCP tool as a MUSUBI skill
266
+ */
267
+ async importTool(serverId, mcpTool) {
268
+ const toolDef = new MCPToolDefinition(mcpTool);
269
+ const validation = toolDef.validate();
270
+
271
+ if (!validation.valid) {
272
+ throw new Error(`Invalid MCP tool: ${validation.errors.join(', ')}`);
273
+ }
274
+
275
+ // Generate skill ID from server and tool name
276
+ const skillId = `mcp.${serverId}.${mcpTool.name}`;
277
+
278
+ // Create skill definition
279
+ const skillDefinition = {
280
+ id: skillId,
281
+ name: mcpTool.name,
282
+ description: mcpTool.description || `MCP tool: ${mcpTool.name}`,
283
+ category: 'mcp-imported',
284
+ version: '1.0.0',
285
+ inputSchema: SchemaTranslator.mcpToMusubi(mcpTool.inputSchema),
286
+ outputSchema: {
287
+ type: 'object',
288
+ properties: {
289
+ content: { type: 'array' },
290
+ isError: { type: 'boolean' }
291
+ }
292
+ },
293
+ tags: ['mcp', `mcp-server:${serverId}`],
294
+ metadata: {
295
+ source: 'mcp',
296
+ serverId,
297
+ originalName: mcpTool.name,
298
+ transport: toolDef.transport,
299
+ annotations: mcpTool.annotations || {}
300
+ },
301
+ // Execution handler
302
+ handler: async (input) => {
303
+ return this.executeMCPTool(serverId, mcpTool.name, input);
304
+ }
305
+ };
306
+
307
+ // Store adapter mapping
308
+ this.adapters.set(skillId, {
309
+ skillId,
310
+ serverId,
311
+ toolName: mcpTool.name,
312
+ definition: toolDef
313
+ });
314
+
315
+ // Register with skill registry if available
316
+ if (this.registry) {
317
+ this.registry.register(skillDefinition);
318
+ }
319
+
320
+ this.emit('tool-imported', { skillId, serverId, toolName: mcpTool.name });
321
+
322
+ return skillDefinition;
323
+ }
324
+
325
+ /**
326
+ * Execute an MCP tool via its server
327
+ */
328
+ async executeMCPTool(serverId, toolName, input) {
329
+ const connection = this.connections.get(serverId);
330
+
331
+ if (!connection) {
332
+ throw new Error(`Server ${serverId} not found`);
333
+ }
334
+
335
+ if (connection.status !== 'connected') {
336
+ throw new Error(`Server ${serverId} is not connected (status: ${connection.status})`);
337
+ }
338
+
339
+ const startTime = Date.now();
340
+
341
+ try {
342
+ // Create MCP tool call request
343
+ const request = {
344
+ jsonrpc: '2.0',
345
+ method: 'tools/call',
346
+ params: {
347
+ name: toolName,
348
+ arguments: input
349
+ },
350
+ id: this._generateRequestId()
351
+ };
352
+
353
+ // Execute based on transport
354
+ const response = await this._sendRequest(connection, request);
355
+
356
+ const duration = Date.now() - startTime;
357
+ this.emit('tool-executed', {
358
+ serverId,
359
+ toolName,
360
+ duration,
361
+ success: !response.isError
362
+ });
363
+
364
+ return response;
365
+
366
+ } catch (error) {
367
+ this.emit('tool-error', { serverId, toolName, error });
368
+ throw error;
369
+ }
370
+ }
371
+
372
+ /**
373
+ * Send request to MCP server (transport abstraction)
374
+ */
375
+ async _sendRequest(connection, request) {
376
+ // Note: This is a simplified implementation
377
+ // Real implementation would handle actual transport protocols
378
+
379
+ switch (connection.transport) {
380
+ case MCPTransport.STDIO:
381
+ return this._sendStdioRequest(connection, request);
382
+
383
+ case MCPTransport.HTTP:
384
+ return this._sendHttpRequest(connection, request);
385
+
386
+ case MCPTransport.SSE:
387
+ return this._sendSseRequest(connection, request);
388
+
389
+ case MCPTransport.WEBSOCKET:
390
+ return this._sendWebSocketRequest(connection, request);
391
+
392
+ default:
393
+ throw new Error(`Unsupported transport: ${connection.transport}`);
394
+ }
395
+ }
396
+
397
+ /**
398
+ * Stdio transport (placeholder)
399
+ */
400
+ async _sendStdioRequest(connection, request) {
401
+ // Placeholder for stdio transport implementation
402
+ // In real implementation, this would:
403
+ // 1. Spawn child process
404
+ // 2. Send JSON-RPC request via stdin
405
+ // 3. Read response from stdout
406
+ return {
407
+ content: [{ type: 'text', text: 'Stdio transport result' }],
408
+ isError: false
409
+ };
410
+ }
411
+
412
+ /**
413
+ * HTTP transport (placeholder)
414
+ */
415
+ async _sendHttpRequest(connection, request) {
416
+ // Placeholder for HTTP transport implementation
417
+ return {
418
+ content: [{ type: 'text', text: 'HTTP transport result' }],
419
+ isError: false
420
+ };
421
+ }
422
+
423
+ /**
424
+ * SSE transport (placeholder)
425
+ */
426
+ async _sendSseRequest(connection, request) {
427
+ // Placeholder for SSE transport implementation
428
+ return {
429
+ content: [{ type: 'text', text: 'SSE transport result' }],
430
+ isError: false
431
+ };
432
+ }
433
+
434
+ /**
435
+ * WebSocket transport (placeholder)
436
+ */
437
+ async _sendWebSocketRequest(connection, request) {
438
+ // Placeholder for WebSocket transport implementation
439
+ return {
440
+ content: [{ type: 'text', text: 'WebSocket transport result' }],
441
+ isError: false
442
+ };
443
+ }
444
+
445
+ /**
446
+ * Generate unique request ID
447
+ */
448
+ _generateRequestId() {
449
+ return `mcp-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
450
+ }
451
+
452
+ /**
453
+ * Disconnect from an MCP server
454
+ */
455
+ async disconnectServer(serverId) {
456
+ const connection = this.connections.get(serverId);
457
+
458
+ if (!connection) {
459
+ return false;
460
+ }
461
+
462
+ // Remove all skills from this server
463
+ for (const [skillId, adapter] of this.adapters) {
464
+ if (adapter.serverId === serverId) {
465
+ if (this.registry) {
466
+ this.registry.unregister(skillId);
467
+ }
468
+ this.adapters.delete(skillId);
469
+ }
470
+ }
471
+
472
+ this.connections.delete(serverId);
473
+ this.emit('server-disconnected', { serverId });
474
+
475
+ return true;
476
+ }
477
+
478
+ /**
479
+ * Get all imported skills from a server
480
+ */
481
+ getServerSkills(serverId) {
482
+ const skills = [];
483
+
484
+ for (const [skillId, adapter] of this.adapters) {
485
+ if (adapter.serverId === serverId) {
486
+ skills.push(adapter);
487
+ }
488
+ }
489
+
490
+ return skills;
491
+ }
492
+
493
+ /**
494
+ * Get connection status
495
+ */
496
+ getConnectionStatus() {
497
+ const status = {};
498
+
499
+ for (const [id, conn] of this.connections) {
500
+ status[id] = {
501
+ status: conn.status,
502
+ transport: conn.transport,
503
+ skillCount: this.getServerSkills(id).length,
504
+ registeredAt: conn.registeredAt
505
+ };
506
+ }
507
+
508
+ return status;
509
+ }
510
+ }
511
+
512
+ /**
513
+ * MUSUBI Skill to MCP Tool Adapter
514
+ * Exports MUSUBI skills as MCP tools
515
+ */
516
+ class SkillToMCPAdapter extends EventEmitter {
517
+ constructor(options = {}) {
518
+ super();
519
+ this.registry = options.registry || null;
520
+ this.executor = options.executor || null;
521
+ this.exportedTools = new Map();
522
+ this.serverInfo = {
523
+ name: options.serverName || 'musubi-mcp-server',
524
+ version: options.version || '1.0.0',
525
+ protocolVersion: '2024-11-05'
526
+ };
527
+ }
528
+
529
+ /**
530
+ * Export a MUSUBI skill as MCP tool
531
+ */
532
+ exportSkill(skillId) {
533
+ if (!this.registry) {
534
+ throw new Error('Skill registry is required');
535
+ }
536
+
537
+ const skill = this.registry.getSkill(skillId);
538
+ if (!skill) {
539
+ throw new Error(`Skill ${skillId} not found`);
540
+ }
541
+
542
+ const mcpTool = {
543
+ name: this._sanitizeToolName(skillId),
544
+ description: skill.description || '',
545
+ inputSchema: SchemaTranslator.musubiToMcp(skill.inputSchema),
546
+ annotations: {
547
+ musubiSkillId: skillId,
548
+ category: skill.category,
549
+ version: skill.version,
550
+ tags: skill.tags
551
+ }
552
+ };
553
+
554
+ this.exportedTools.set(skillId, mcpTool);
555
+ this.emit('skill-exported', { skillId, toolName: mcpTool.name });
556
+
557
+ return mcpTool;
558
+ }
559
+
560
+ /**
561
+ * Export multiple skills
562
+ */
563
+ exportSkills(skillIds) {
564
+ const tools = [];
565
+
566
+ for (const skillId of skillIds) {
567
+ try {
568
+ tools.push(this.exportSkill(skillId));
569
+ } catch (error) {
570
+ this.emit('export-error', { skillId, error });
571
+ }
572
+ }
573
+
574
+ return tools;
575
+ }
576
+
577
+ /**
578
+ * Export all skills from a category
579
+ */
580
+ exportCategory(category) {
581
+ if (!this.registry) {
582
+ throw new Error('Skill registry is required');
583
+ }
584
+
585
+ const skills = this.registry.findByCategory(category);
586
+ return this.exportSkills(skills.map(s => s.id));
587
+ }
588
+
589
+ /**
590
+ * Handle MCP tools/list request
591
+ */
592
+ handleListTools() {
593
+ const tools = [];
594
+
595
+ for (const [skillId, tool] of this.exportedTools) {
596
+ tools.push(tool);
597
+ }
598
+
599
+ return { tools };
600
+ }
601
+
602
+ /**
603
+ * Handle MCP tools/call request
604
+ */
605
+ async handleCallTool(params) {
606
+ const { name, arguments: args } = params;
607
+
608
+ // Find skill by tool name
609
+ let skillId = null;
610
+ for (const [id, tool] of this.exportedTools) {
611
+ if (tool.name === name) {
612
+ skillId = id;
613
+ break;
614
+ }
615
+ }
616
+
617
+ if (!skillId) {
618
+ return {
619
+ content: [{ type: 'text', text: `Tool not found: ${name}` }],
620
+ isError: true
621
+ };
622
+ }
623
+
624
+ try {
625
+ // Execute via skill executor if available
626
+ if (this.executor) {
627
+ const result = await this.executor.execute(skillId, args);
628
+ return this._formatMCPResult(result);
629
+ }
630
+
631
+ // Direct execution via registry
632
+ const skill = this.registry.getSkill(skillId);
633
+ if (skill.handler) {
634
+ const result = await skill.handler(args);
635
+ return this._formatMCPResult(result);
636
+ }
637
+
638
+ return {
639
+ content: [{ type: 'text', text: 'No handler available for skill' }],
640
+ isError: true
641
+ };
642
+
643
+ } catch (error) {
644
+ this.emit('call-error', { toolName: name, skillId, error });
645
+ return {
646
+ content: [{ type: 'text', text: error.message }],
647
+ isError: true
648
+ };
649
+ }
650
+ }
651
+
652
+ /**
653
+ * Format skill result as MCP response
654
+ */
655
+ _formatMCPResult(result) {
656
+ if (result === null || result === undefined) {
657
+ return {
658
+ content: [{ type: 'text', text: 'null' }],
659
+ isError: false
660
+ };
661
+ }
662
+
663
+ if (typeof result === 'string') {
664
+ return {
665
+ content: [{ type: 'text', text: result }],
666
+ isError: false
667
+ };
668
+ }
669
+
670
+ if (typeof result === 'object') {
671
+ // Check if already in MCP format
672
+ if (result.content && Array.isArray(result.content)) {
673
+ return result;
674
+ }
675
+
676
+ // Convert to JSON text
677
+ return {
678
+ content: [{
679
+ type: 'text',
680
+ text: JSON.stringify(result, null, 2)
681
+ }],
682
+ isError: false
683
+ };
684
+ }
685
+
686
+ return {
687
+ content: [{ type: 'text', text: String(result) }],
688
+ isError: false
689
+ };
690
+ }
691
+
692
+ /**
693
+ * Sanitize skill ID to valid MCP tool name
694
+ */
695
+ _sanitizeToolName(skillId) {
696
+ // MCP tool names should be alphanumeric with underscores
697
+ return skillId.replace(/[^a-zA-Z0-9_]/g, '_');
698
+ }
699
+
700
+ /**
701
+ * Get MCP server manifest
702
+ */
703
+ getServerManifest() {
704
+ return {
705
+ ...this.serverInfo,
706
+ capabilities: {
707
+ tools: { listChanged: true }
708
+ }
709
+ };
710
+ }
711
+
712
+ /**
713
+ * Create MCP protocol handler
714
+ */
715
+ createProtocolHandler() {
716
+ return async (request) => {
717
+ const { method, params, id } = request;
718
+
719
+ switch (method) {
720
+ case 'initialize':
721
+ return {
722
+ jsonrpc: '2.0',
723
+ id,
724
+ result: this.getServerManifest()
725
+ };
726
+
727
+ case 'tools/list':
728
+ return {
729
+ jsonrpc: '2.0',
730
+ id,
731
+ result: this.handleListTools()
732
+ };
733
+
734
+ case 'tools/call':
735
+ const result = await this.handleCallTool(params);
736
+ return {
737
+ jsonrpc: '2.0',
738
+ id,
739
+ result
740
+ };
741
+
742
+ default:
743
+ return {
744
+ jsonrpc: '2.0',
745
+ id,
746
+ error: {
747
+ code: -32601,
748
+ message: `Method not found: ${method}`
749
+ }
750
+ };
751
+ }
752
+ };
753
+ }
754
+ }
755
+
756
+ /**
757
+ * Unified MCP Adapter Manager
758
+ * Manages both import and export adapters
759
+ */
760
+ class MCPAdapterManager extends EventEmitter {
761
+ constructor(options = {}) {
762
+ super();
763
+ this.registry = options.registry || null;
764
+ this.executor = options.executor || null;
765
+
766
+ this.importAdapter = new MCPToSkillAdapter({
767
+ registry: this.registry,
768
+ timeout: options.timeout,
769
+ retry: options.retry
770
+ });
771
+
772
+ this.exportAdapter = new SkillToMCPAdapter({
773
+ registry: this.registry,
774
+ executor: this.executor,
775
+ serverName: options.serverName,
776
+ version: options.version
777
+ });
778
+
779
+ // Forward events
780
+ this._setupEventForwarding();
781
+ }
782
+
783
+ /**
784
+ * Setup event forwarding from child adapters
785
+ */
786
+ _setupEventForwarding() {
787
+ const importEvents = [
788
+ 'server-registered',
789
+ 'server-disconnected',
790
+ 'tool-imported',
791
+ 'tool-executed',
792
+ 'tool-error',
793
+ 'import-error'
794
+ ];
795
+
796
+ const exportEvents = [
797
+ 'skill-exported',
798
+ 'export-error',
799
+ 'call-error'
800
+ ];
801
+
802
+ for (const event of importEvents) {
803
+ this.importAdapter.on(event, (data) => {
804
+ this.emit(`import:${event}`, data);
805
+ });
806
+ }
807
+
808
+ for (const event of exportEvents) {
809
+ this.exportAdapter.on(event, (data) => {
810
+ this.emit(`export:${event}`, data);
811
+ });
812
+ }
813
+ }
814
+
815
+ /**
816
+ * Register MCP server and import tools
817
+ */
818
+ async registerMCPServer(serverConfig) {
819
+ return this.importAdapter.registerServer(serverConfig);
820
+ }
821
+
822
+ /**
823
+ * Disconnect from MCP server
824
+ */
825
+ async disconnectMCPServer(serverId) {
826
+ return this.importAdapter.disconnectServer(serverId);
827
+ }
828
+
829
+ /**
830
+ * Execute imported MCP tool
831
+ */
832
+ async executeMCPTool(serverId, toolName, input) {
833
+ return this.importAdapter.executeMCPTool(serverId, toolName, input);
834
+ }
835
+
836
+ /**
837
+ * Export skill as MCP tool
838
+ */
839
+ exportSkill(skillId) {
840
+ return this.exportAdapter.exportSkill(skillId);
841
+ }
842
+
843
+ /**
844
+ * Export multiple skills
845
+ */
846
+ exportSkills(skillIds) {
847
+ return this.exportAdapter.exportSkills(skillIds);
848
+ }
849
+
850
+ /**
851
+ * Get MCP protocol handler for exported skills
852
+ */
853
+ getMCPHandler() {
854
+ return this.exportAdapter.createProtocolHandler();
855
+ }
856
+
857
+ /**
858
+ * Get comprehensive status
859
+ */
860
+ getStatus() {
861
+ return {
862
+ import: {
863
+ connections: this.importAdapter.getConnectionStatus(),
864
+ adapterCount: this.importAdapter.adapters.size
865
+ },
866
+ export: {
867
+ toolCount: this.exportAdapter.exportedTools.size,
868
+ serverInfo: this.exportAdapter.serverInfo
869
+ }
870
+ };
871
+ }
872
+
873
+ /**
874
+ * Get imported skill count
875
+ */
876
+ getImportedSkillCount() {
877
+ return this.importAdapter.adapters.size;
878
+ }
879
+
880
+ /**
881
+ * Get exported tool count
882
+ */
883
+ getExportedToolCount() {
884
+ return this.exportAdapter.exportedTools.size;
885
+ }
886
+ }
887
+
888
+ module.exports = {
889
+ MCPTransport,
890
+ AdapterDirection,
891
+ MCPToolDefinition,
892
+ SchemaTranslator,
893
+ MCPToSkillAdapter,
894
+ SkillToMCPAdapter,
895
+ MCPAdapterManager
896
+ };