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,467 @@
1
+ /**
2
+ * MUSUBI MCP Tool Registry
3
+ *
4
+ * Manages MCP tools from discovered servers and provides
5
+ * automatic registration with the Agent Loop and Skill System.
6
+ *
7
+ * @module integrations/mcp/mcp-tool-registry
8
+ */
9
+
10
+ 'use strict';
11
+
12
+ const EventEmitter = require('events');
13
+
14
+ /**
15
+ * @typedef {Object} MCPToolDefinition
16
+ * @property {string} name - Tool name
17
+ * @property {string} description - Tool description
18
+ * @property {Object} inputSchema - JSON Schema for input
19
+ * @property {string} serverName - Source MCP server
20
+ * @property {Object} [annotations] - Tool annotations
21
+ */
22
+
23
+ /**
24
+ * @typedef {Object} ToolInvocationResult
25
+ * @property {boolean} success - Whether invocation succeeded
26
+ * @property {*} result - Tool result
27
+ * @property {Error} [error] - Error if failed
28
+ * @property {number} duration - Execution time in ms
29
+ */
30
+
31
+ /**
32
+ * MCP Tool Registry for managing tools from MCP servers
33
+ */
34
+ class MCPToolRegistry extends EventEmitter {
35
+ /**
36
+ * @param {Object} options
37
+ * @param {Object} [options.connector] - MCP connector instance
38
+ * @param {Object} [options.skillRegistry] - MUSUBI skill registry
39
+ * @param {boolean} [options.autoRegister=true] - Auto-register with skills
40
+ */
41
+ constructor(options = {}) {
42
+ super();
43
+
44
+ this.connector = options.connector || null;
45
+ this.skillRegistry = options.skillRegistry || null;
46
+ this.autoRegister = options.autoRegister ?? true;
47
+
48
+ /** @type {Map<string, MCPToolDefinition>} */
49
+ this.tools = new Map();
50
+
51
+ /** @type {Map<string, string[]>} */
52
+ this.serverTools = new Map();
53
+
54
+ /** @type {Map<string, number>} */
55
+ this.invocationCounts = new Map();
56
+
57
+ /** @type {Map<string, number[]>} */
58
+ this.latencyHistory = new Map();
59
+ }
60
+
61
+ /**
62
+ * Set the MCP connector
63
+ * @param {Object} connector
64
+ */
65
+ setConnector(connector) {
66
+ this.connector = connector;
67
+ }
68
+
69
+ /**
70
+ * Set the skill registry for auto-registration
71
+ * @param {Object} skillRegistry
72
+ */
73
+ setSkillRegistry(skillRegistry) {
74
+ this.skillRegistry = skillRegistry;
75
+ }
76
+
77
+ /**
78
+ * Discover and register tools from an MCP server
79
+ * @param {string} serverName - Server name
80
+ * @param {Object} serverConfig - Server configuration
81
+ * @returns {Promise<MCPToolDefinition[]>}
82
+ */
83
+ async discoverTools(serverName, serverConfig) {
84
+ if (!this.connector) {
85
+ throw new Error('MCP connector not set');
86
+ }
87
+
88
+ try {
89
+ // Connect to server
90
+ await this.connector.connect(serverName, serverConfig);
91
+
92
+ // List tools
93
+ const toolsResponse = await this.connector.listTools(serverName);
94
+ const tools = toolsResponse.tools || [];
95
+
96
+ const registered = [];
97
+
98
+ for (const tool of tools) {
99
+ const toolDef = {
100
+ name: `${serverName}/${tool.name}`,
101
+ originalName: tool.name,
102
+ description: tool.description || '',
103
+ inputSchema: tool.inputSchema || { type: 'object', properties: {} },
104
+ serverName,
105
+ annotations: tool.annotations || {}
106
+ };
107
+
108
+ this.registerTool(toolDef);
109
+ registered.push(toolDef);
110
+ }
111
+
112
+ // Track tools per server
113
+ this.serverTools.set(serverName, registered.map(t => t.name));
114
+
115
+ this.emit('tools:discovered', {
116
+ serverName,
117
+ count: registered.length,
118
+ tools: registered.map(t => t.name)
119
+ });
120
+
121
+ return registered;
122
+
123
+ } catch (error) {
124
+ this.emit('tools:discovery:error', { serverName, error });
125
+ throw error;
126
+ }
127
+ }
128
+
129
+ /**
130
+ * Register a tool
131
+ * @param {MCPToolDefinition} toolDef
132
+ */
133
+ registerTool(toolDef) {
134
+ this.tools.set(toolDef.name, toolDef);
135
+ this.invocationCounts.set(toolDef.name, 0);
136
+ this.latencyHistory.set(toolDef.name, []);
137
+
138
+ // Auto-register with skill registry
139
+ if (this.autoRegister && this.skillRegistry) {
140
+ this.registerAsSkill(toolDef);
141
+ }
142
+
143
+ this.emit('tool:registered', { name: toolDef.name });
144
+ }
145
+
146
+ /**
147
+ * Register tool as a MUSUBI skill
148
+ * @param {MCPToolDefinition} toolDef
149
+ */
150
+ registerAsSkill(toolDef) {
151
+ if (!this.skillRegistry) return;
152
+
153
+ const skill = {
154
+ id: `mcp-${toolDef.name.replace('/', '-')}`,
155
+ name: toolDef.name,
156
+ category: 'mcp-tool',
157
+ description: toolDef.description,
158
+ metadata: {
159
+ serverName: toolDef.serverName,
160
+ originalName: toolDef.originalName,
161
+ inputSchema: toolDef.inputSchema,
162
+ source: 'mcp'
163
+ },
164
+ handler: async (input) => {
165
+ return this.invokeTool(toolDef.name, input);
166
+ }
167
+ };
168
+
169
+ try {
170
+ this.skillRegistry.registerSkill(skill);
171
+ this.emit('skill:registered', { skillId: skill.id, toolName: toolDef.name });
172
+ } catch (error) {
173
+ this.emit('skill:registration:error', { toolName: toolDef.name, error });
174
+ }
175
+ }
176
+
177
+ /**
178
+ * Invoke a tool
179
+ * @param {string} toolName - Full tool name (serverName/toolName)
180
+ * @param {Object} input - Tool input
181
+ * @returns {Promise<ToolInvocationResult>}
182
+ */
183
+ async invokeTool(toolName, input) {
184
+ const tool = this.tools.get(toolName);
185
+ if (!tool) {
186
+ throw new Error(`Tool not found: ${toolName}`);
187
+ }
188
+
189
+ if (!this.connector) {
190
+ throw new Error('MCP connector not set');
191
+ }
192
+
193
+ const startTime = Date.now();
194
+
195
+ try {
196
+ // Validate input
197
+ const validation = this.validateInput(tool, input);
198
+ if (!validation.valid) {
199
+ throw new Error(`Invalid input: ${validation.errors.join(', ')}`);
200
+ }
201
+
202
+ // Call tool via connector
203
+ const result = await this.connector.callTool(
204
+ tool.serverName,
205
+ tool.originalName,
206
+ input
207
+ );
208
+
209
+ const duration = Date.now() - startTime;
210
+ this.recordInvocation(toolName, duration, true);
211
+
212
+ this.emit('tool:invoked', {
213
+ name: toolName,
214
+ duration,
215
+ success: true
216
+ });
217
+
218
+ return {
219
+ success: true,
220
+ result: result.content || result,
221
+ duration
222
+ };
223
+
224
+ } catch (error) {
225
+ const duration = Date.now() - startTime;
226
+ this.recordInvocation(toolName, duration, false);
227
+
228
+ this.emit('tool:error', {
229
+ name: toolName,
230
+ error: error.message,
231
+ duration
232
+ });
233
+
234
+ return {
235
+ success: false,
236
+ error,
237
+ duration
238
+ };
239
+ }
240
+ }
241
+
242
+ /**
243
+ * Validate tool input against schema
244
+ * @param {MCPToolDefinition} tool
245
+ * @param {Object} input
246
+ * @returns {Object}
247
+ */
248
+ validateInput(tool, input) {
249
+ const errors = [];
250
+ const schema = tool.inputSchema;
251
+
252
+ if (!schema || !schema.properties) {
253
+ return { valid: true, errors: [] };
254
+ }
255
+
256
+ // Check required fields
257
+ if (schema.required) {
258
+ for (const field of schema.required) {
259
+ if (!(field in input)) {
260
+ errors.push(`Missing required field: ${field}`);
261
+ }
262
+ }
263
+ }
264
+
265
+ // Type checking
266
+ for (const [key, prop] of Object.entries(schema.properties)) {
267
+ if (key in input) {
268
+ const value = input[key];
269
+ const expectedType = prop.type;
270
+
271
+ if (expectedType && !this.checkType(value, expectedType)) {
272
+ errors.push(`Invalid type for ${key}: expected ${expectedType}`);
273
+ }
274
+ }
275
+ }
276
+
277
+ return {
278
+ valid: errors.length === 0,
279
+ errors
280
+ };
281
+ }
282
+
283
+ /**
284
+ * Check if value matches expected type
285
+ * @param {*} value
286
+ * @param {string} expectedType
287
+ * @returns {boolean}
288
+ */
289
+ checkType(value, expectedType) {
290
+ switch (expectedType) {
291
+ case 'string': return typeof value === 'string';
292
+ case 'number': return typeof value === 'number';
293
+ case 'integer': return Number.isInteger(value);
294
+ case 'boolean': return typeof value === 'boolean';
295
+ case 'array': return Array.isArray(value);
296
+ case 'object': return typeof value === 'object' && value !== null && !Array.isArray(value);
297
+ case 'null': return value === null;
298
+ default: return true;
299
+ }
300
+ }
301
+
302
+ /**
303
+ * Record invocation metrics
304
+ * @param {string} toolName
305
+ * @param {number} duration
306
+ * @param {boolean} success
307
+ */
308
+ recordInvocation(toolName, duration, success) {
309
+ const count = this.invocationCounts.get(toolName) || 0;
310
+ this.invocationCounts.set(toolName, count + 1);
311
+
312
+ const history = this.latencyHistory.get(toolName) || [];
313
+ history.push(duration);
314
+
315
+ // Keep last 100 latencies
316
+ if (history.length > 100) {
317
+ history.shift();
318
+ }
319
+ this.latencyHistory.set(toolName, history);
320
+ }
321
+
322
+ /**
323
+ * Get tool by name
324
+ * @param {string} name
325
+ * @returns {MCPToolDefinition|undefined}
326
+ */
327
+ getTool(name) {
328
+ return this.tools.get(name);
329
+ }
330
+
331
+ /**
332
+ * Get all tools
333
+ * @returns {MCPToolDefinition[]}
334
+ */
335
+ getAllTools() {
336
+ return Array.from(this.tools.values());
337
+ }
338
+
339
+ /**
340
+ * Get tools by server
341
+ * @param {string} serverName
342
+ * @returns {MCPToolDefinition[]}
343
+ */
344
+ getToolsByServer(serverName) {
345
+ const toolNames = this.serverTools.get(serverName) || [];
346
+ return toolNames.map(name => this.tools.get(name)).filter(Boolean);
347
+ }
348
+
349
+ /**
350
+ * Get tools in OpenAI format
351
+ * @returns {Object[]}
352
+ */
353
+ getOpenAITools() {
354
+ return this.getAllTools().map(tool => ({
355
+ type: 'function',
356
+ function: {
357
+ name: tool.name.replace('/', '_'),
358
+ description: tool.description,
359
+ parameters: tool.inputSchema
360
+ }
361
+ }));
362
+ }
363
+
364
+ /**
365
+ * Get tools in Anthropic format
366
+ * @returns {Object[]}
367
+ */
368
+ getAnthropicTools() {
369
+ return this.getAllTools().map(tool => ({
370
+ name: tool.name.replace('/', '_'),
371
+ description: tool.description,
372
+ input_schema: tool.inputSchema
373
+ }));
374
+ }
375
+
376
+ /**
377
+ * Get tool statistics
378
+ * @param {string} toolName
379
+ * @returns {Object}
380
+ */
381
+ getToolStats(toolName) {
382
+ const invocations = this.invocationCounts.get(toolName) || 0;
383
+ const latencies = this.latencyHistory.get(toolName) || [];
384
+
385
+ let avgLatency = 0;
386
+ let minLatency = 0;
387
+ let maxLatency = 0;
388
+
389
+ if (latencies.length > 0) {
390
+ avgLatency = latencies.reduce((a, b) => a + b, 0) / latencies.length;
391
+ minLatency = Math.min(...latencies);
392
+ maxLatency = Math.max(...latencies);
393
+ }
394
+
395
+ return {
396
+ invocations,
397
+ avgLatency: Math.round(avgLatency),
398
+ minLatency,
399
+ maxLatency,
400
+ recentLatencies: latencies.slice(-10)
401
+ };
402
+ }
403
+
404
+ /**
405
+ * Get registry statistics
406
+ * @returns {Object}
407
+ */
408
+ getStats() {
409
+ const tools = this.getAllTools();
410
+ let totalInvocations = 0;
411
+
412
+ for (const count of this.invocationCounts.values()) {
413
+ totalInvocations += count;
414
+ }
415
+
416
+ return {
417
+ totalTools: tools.length,
418
+ servers: this.serverTools.size,
419
+ totalInvocations,
420
+ toolsByServer: Object.fromEntries(
421
+ Array.from(this.serverTools.entries()).map(([server, tools]) => [server, tools.length])
422
+ )
423
+ };
424
+ }
425
+
426
+ /**
427
+ * Unregister tools from a server
428
+ * @param {string} serverName
429
+ */
430
+ unregisterServer(serverName) {
431
+ const toolNames = this.serverTools.get(serverName) || [];
432
+
433
+ for (const name of toolNames) {
434
+ this.tools.delete(name);
435
+ this.invocationCounts.delete(name);
436
+ this.latencyHistory.delete(name);
437
+
438
+ // Unregister from skill registry
439
+ if (this.skillRegistry) {
440
+ const skillId = `mcp-${name.replace('/', '-')}`;
441
+ try {
442
+ this.skillRegistry.unregisterSkill(skillId);
443
+ } catch (e) {
444
+ // Ignore if not found
445
+ }
446
+ }
447
+ }
448
+
449
+ this.serverTools.delete(serverName);
450
+ this.emit('server:unregistered', { serverName, toolCount: toolNames.length });
451
+ }
452
+
453
+ /**
454
+ * Clear all tools
455
+ */
456
+ clear() {
457
+ this.tools.clear();
458
+ this.serverTools.clear();
459
+ this.invocationCounts.clear();
460
+ this.latencyHistory.clear();
461
+ this.emit('registry:cleared');
462
+ }
463
+ }
464
+
465
+ module.exports = {
466
+ MCPToolRegistry
467
+ };