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,175 @@
1
+ /**
2
+ * MUSUBI MCP Integration
3
+ *
4
+ * Unified exports for MCP (Model Context Protocol) integration.
5
+ * Provides discovery, tool registration, and context management.
6
+ *
7
+ * @module integrations/mcp
8
+ */
9
+
10
+ 'use strict';
11
+
12
+ const { MCPDiscovery, discoverMCPServers, CONFIG_LOCATIONS } = require('./mcp-discovery');
13
+ const { MCPToolRegistry } = require('./mcp-tool-registry');
14
+ const { MCPContextProvider } = require('./mcp-context-provider');
15
+
16
+ /**
17
+ * @typedef {Object} MCPIntegrationOptions
18
+ * @property {Object} [discovery] - Discovery options
19
+ * @property {Object} [registry] - Registry options
20
+ * @property {Object} [context] - Context provider options
21
+ * @property {Object} [connector] - MCP connector instance
22
+ */
23
+
24
+ /**
25
+ * Create a discovery instance
26
+ * @param {Object} options
27
+ * @returns {MCPDiscovery}
28
+ */
29
+ function createMCPDiscovery(options = {}) {
30
+ return new MCPDiscovery(options);
31
+ }
32
+
33
+ /**
34
+ * Create a tool registry instance
35
+ * @param {Object} options
36
+ * @returns {MCPToolRegistry}
37
+ */
38
+ function createMCPToolRegistry(options = {}) {
39
+ return new MCPToolRegistry(options);
40
+ }
41
+
42
+ /**
43
+ * Create context provider instance
44
+ * @param {Object} options
45
+ * @returns {MCPContextProvider}
46
+ */
47
+ function createMCPContextProvider(options = {}) {
48
+ return new MCPContextProvider(options);
49
+ }
50
+
51
+ /**
52
+ * Create a fully integrated MCP system
53
+ * @param {MCPIntegrationOptions} options
54
+ * @returns {Object}
55
+ */
56
+ function createMCPIntegration(options = {}) {
57
+ const discovery = createMCPDiscovery(options.discovery);
58
+ const registry = createMCPToolRegistry(options.registry);
59
+ const context = new MCPContextProvider(options.context);
60
+
61
+ // Set connector if provided
62
+ if (options.connector) {
63
+ registry.setConnector(options.connector);
64
+ context.setConnector(options.connector);
65
+ }
66
+
67
+ // Wire up discovery to registry
68
+ discovery.on('server:discovered', async ({ server }) => {
69
+ try {
70
+ if (options.connector) {
71
+ await registry.registerFromServer(server.name);
72
+ }
73
+ } catch (error) {
74
+ // Silently handle - server might not be running
75
+ }
76
+ });
77
+
78
+ return {
79
+ discovery,
80
+ registry,
81
+ context,
82
+
83
+ /**
84
+ * Initialize the MCP integration
85
+ * @param {Object} [initOptions]
86
+ * @param {boolean} [initOptions.autoDiscover=true] - Auto-discover servers
87
+ * @param {boolean} [initOptions.watchChanges=false] - Watch for config changes
88
+ */
89
+ async initialize(initOptions = {}) {
90
+ const autoDiscover = initOptions.autoDiscover ?? true;
91
+ const watchChanges = initOptions.watchChanges ?? false;
92
+
93
+ if (autoDiscover) {
94
+ await discovery.discover();
95
+ }
96
+
97
+ if (watchChanges && discovery.watch) {
98
+ discovery.watch();
99
+ }
100
+
101
+ return this.getStatus();
102
+ },
103
+
104
+ /**
105
+ * Get all tools from the registry
106
+ * @returns {Object[]}
107
+ */
108
+ getTools() {
109
+ return registry.getAllTools();
110
+ },
111
+
112
+ /**
113
+ * Get tool definitions in Agent Loop format
114
+ * @returns {Object[]}
115
+ */
116
+ getToolDefinitions() {
117
+ return registry.getAllTools().map(tool => ({
118
+ name: tool.name,
119
+ description: tool.description,
120
+ inputSchema: tool.inputSchema
121
+ }));
122
+ },
123
+
124
+ /**
125
+ * Build context for a request
126
+ * @param {Object} contextOptions
127
+ * @returns {Promise<Object>}
128
+ */
129
+ async buildContext(contextOptions) {
130
+ return context.buildContext(contextOptions);
131
+ },
132
+
133
+ /**
134
+ * Get integration status
135
+ * @returns {Object}
136
+ */
137
+ getStatus() {
138
+ return {
139
+ discovery: discovery.getSummary(),
140
+ registry: registry.getStats(),
141
+ context: context.getStats()
142
+ };
143
+ },
144
+
145
+ /**
146
+ * Clean up resources
147
+ */
148
+ cleanup() {
149
+ if (discovery.stopWatching) {
150
+ discovery.stopWatching();
151
+ }
152
+ registry.clear();
153
+ context.clear();
154
+ }
155
+ };
156
+ }
157
+
158
+ module.exports = {
159
+ // Discovery
160
+ MCPDiscovery,
161
+ createMCPDiscovery,
162
+ discoverMCPServers,
163
+ CONFIG_LOCATIONS,
164
+
165
+ // Tool Registry
166
+ MCPToolRegistry,
167
+ createMCPToolRegistry,
168
+
169
+ // Context Provider
170
+ MCPContextProvider,
171
+ createMCPContextProvider,
172
+
173
+ // Integration
174
+ createMCPIntegration
175
+ };
@@ -0,0 +1,472 @@
1
+ /**
2
+ * MUSUBI MCP Context Provider
3
+ *
4
+ * Provides context information from MCP resources to the Agent Loop.
5
+ * Manages resources, prompts, and context from MCP servers.
6
+ *
7
+ * @module integrations/mcp/mcp-context-provider
8
+ */
9
+
10
+ 'use strict';
11
+
12
+ const EventEmitter = require('events');
13
+
14
+ /**
15
+ * @typedef {Object} MCPResource
16
+ * @property {string} uri - Resource URI
17
+ * @property {string} name - Resource name
18
+ * @property {string} [description] - Resource description
19
+ * @property {string} [mimeType] - MIME type
20
+ * @property {string} serverName - Source server
21
+ */
22
+
23
+ /**
24
+ * @typedef {Object} MCPPrompt
25
+ * @property {string} name - Prompt name
26
+ * @property {string} [description] - Prompt description
27
+ * @property {Object[]} [arguments] - Prompt arguments
28
+ * @property {string} serverName - Source server
29
+ */
30
+
31
+ /**
32
+ * @typedef {Object} ContextData
33
+ * @property {string} type - Context type (resource, prompt, sampling)
34
+ * @property {string} source - Source identifier
35
+ * @property {*} content - Context content
36
+ * @property {Object} metadata - Additional metadata
37
+ */
38
+
39
+ /**
40
+ * MCP Context Provider for managing context from MCP servers
41
+ */
42
+ class MCPContextProvider extends EventEmitter {
43
+ /**
44
+ * @param {Object} options
45
+ * @param {Object} [options.connector] - MCP connector instance
46
+ * @param {number} [options.cacheTTL=300000] - Cache TTL in ms (5 min)
47
+ * @param {number} [options.maxCacheSize=100] - Max cached items
48
+ */
49
+ constructor(options = {}) {
50
+ super();
51
+
52
+ this.connector = options.connector || null;
53
+ this.cacheTTL = options.cacheTTL ?? 300000;
54
+ this.maxCacheSize = options.maxCacheSize ?? 100;
55
+
56
+ /** @type {Map<string, MCPResource[]>} */
57
+ this.resources = new Map();
58
+
59
+ /** @type {Map<string, MCPPrompt[]>} */
60
+ this.prompts = new Map();
61
+
62
+ /** @type {Map<string, { data: *, timestamp: number }>} */
63
+ this.cache = new Map();
64
+
65
+ /** @type {Map<string, Function>} */
66
+ this.subscriptions = new Map();
67
+ }
68
+
69
+ /**
70
+ * Set the MCP connector
71
+ * @param {Object} connector
72
+ */
73
+ setConnector(connector) {
74
+ this.connector = connector;
75
+ }
76
+
77
+ /**
78
+ * Discover resources from an MCP server
79
+ * @param {string} serverName
80
+ * @returns {Promise<MCPResource[]>}
81
+ */
82
+ async discoverResources(serverName) {
83
+ if (!this.connector) {
84
+ throw new Error('MCP connector not set');
85
+ }
86
+
87
+ try {
88
+ const response = await this.connector.listResources(serverName);
89
+ const resources = (response.resources || []).map(r => ({
90
+ ...r,
91
+ serverName
92
+ }));
93
+
94
+ this.resources.set(serverName, resources);
95
+
96
+ this.emit('resources:discovered', {
97
+ serverName,
98
+ count: resources.length
99
+ });
100
+
101
+ return resources;
102
+
103
+ } catch (error) {
104
+ this.emit('resources:error', { serverName, error });
105
+ throw error;
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Discover prompts from an MCP server
111
+ * @param {string} serverName
112
+ * @returns {Promise<MCPPrompt[]>}
113
+ */
114
+ async discoverPrompts(serverName) {
115
+ if (!this.connector) {
116
+ throw new Error('MCP connector not set');
117
+ }
118
+
119
+ try {
120
+ const response = await this.connector.listPrompts(serverName);
121
+ const prompts = (response.prompts || []).map(p => ({
122
+ ...p,
123
+ serverName
124
+ }));
125
+
126
+ this.prompts.set(serverName, prompts);
127
+
128
+ this.emit('prompts:discovered', {
129
+ serverName,
130
+ count: prompts.length
131
+ });
132
+
133
+ return prompts;
134
+
135
+ } catch (error) {
136
+ this.emit('prompts:error', { serverName, error });
137
+ throw error;
138
+ }
139
+ }
140
+
141
+ /**
142
+ * Read a resource
143
+ * @param {string} serverName
144
+ * @param {string} uri
145
+ * @param {Object} [options]
146
+ * @param {boolean} [options.useCache=true]
147
+ * @returns {Promise<ContextData>}
148
+ */
149
+ async readResource(serverName, uri, options = {}) {
150
+ const cacheKey = `resource:${serverName}:${uri}`;
151
+ const useCache = options.useCache ?? true;
152
+
153
+ // Check cache
154
+ if (useCache) {
155
+ const cached = this.getFromCache(cacheKey);
156
+ if (cached) {
157
+ return cached;
158
+ }
159
+ }
160
+
161
+ if (!this.connector) {
162
+ throw new Error('MCP connector not set');
163
+ }
164
+
165
+ const response = await this.connector.readResource(serverName, uri);
166
+
167
+ const contextData = {
168
+ type: 'resource',
169
+ source: `${serverName}:${uri}`,
170
+ content: response.contents,
171
+ metadata: {
172
+ uri,
173
+ serverName,
174
+ mimeType: response.mimeType
175
+ }
176
+ };
177
+
178
+ this.addToCache(cacheKey, contextData);
179
+ this.emit('resource:read', { serverName, uri });
180
+
181
+ return contextData;
182
+ }
183
+
184
+ /**
185
+ * Get a prompt with arguments
186
+ * @param {string} serverName
187
+ * @param {string} promptName
188
+ * @param {Object} [args]
189
+ * @returns {Promise<ContextData>}
190
+ */
191
+ async getPrompt(serverName, promptName, args = {}) {
192
+ if (!this.connector) {
193
+ throw new Error('MCP connector not set');
194
+ }
195
+
196
+ const response = await this.connector.getPrompt(serverName, promptName, args);
197
+
198
+ const contextData = {
199
+ type: 'prompt',
200
+ source: `${serverName}:${promptName}`,
201
+ content: response.messages,
202
+ metadata: {
203
+ promptName,
204
+ serverName,
205
+ arguments: args,
206
+ description: response.description
207
+ }
208
+ };
209
+
210
+ this.emit('prompt:retrieved', { serverName, promptName });
211
+
212
+ return contextData;
213
+ }
214
+
215
+ /**
216
+ * Subscribe to resource updates
217
+ * @param {string} serverName
218
+ * @param {string} uri
219
+ * @param {Function} callback
220
+ * @returns {Function} Unsubscribe function
221
+ */
222
+ subscribeToResource(serverName, uri, callback) {
223
+ const key = `${serverName}:${uri}`;
224
+
225
+ // Store callback
226
+ this.subscriptions.set(key, callback);
227
+
228
+ // Setup MCP subscription if connector supports it
229
+ if (this.connector && this.connector.subscribe) {
230
+ this.connector.subscribe(serverName, uri, (update) => {
231
+ // Invalidate cache
232
+ this.cache.delete(`resource:${serverName}:${uri}`);
233
+
234
+ // Call callback
235
+ callback({
236
+ type: 'resource:updated',
237
+ source: key,
238
+ content: update
239
+ });
240
+ });
241
+ }
242
+
243
+ this.emit('subscription:created', { serverName, uri });
244
+
245
+ // Return unsubscribe function
246
+ return () => {
247
+ this.subscriptions.delete(key);
248
+ if (this.connector && this.connector.unsubscribe) {
249
+ this.connector.unsubscribe(serverName, uri);
250
+ }
251
+ this.emit('subscription:removed', { serverName, uri });
252
+ };
253
+ }
254
+
255
+ /**
256
+ * Get all resources
257
+ * @returns {MCPResource[]}
258
+ */
259
+ getAllResources() {
260
+ const all = [];
261
+ for (const resources of this.resources.values()) {
262
+ all.push(...resources);
263
+ }
264
+ return all;
265
+ }
266
+
267
+ /**
268
+ * Get resources by server
269
+ * @param {string} serverName
270
+ * @returns {MCPResource[]}
271
+ */
272
+ getResourcesByServer(serverName) {
273
+ return this.resources.get(serverName) || [];
274
+ }
275
+
276
+ /**
277
+ * Get all prompts
278
+ * @returns {MCPPrompt[]}
279
+ */
280
+ getAllPrompts() {
281
+ const all = [];
282
+ for (const prompts of this.prompts.values()) {
283
+ all.push(...prompts);
284
+ }
285
+ return all;
286
+ }
287
+
288
+ /**
289
+ * Get prompts by server
290
+ * @param {string} serverName
291
+ * @returns {MCPPrompt[]}
292
+ */
293
+ getPromptsByServer(serverName) {
294
+ return this.prompts.get(serverName) || [];
295
+ }
296
+
297
+ /**
298
+ * Build context for Agent Loop
299
+ * @param {Object} options
300
+ * @param {string[]} [options.resources] - Resource URIs to include
301
+ * @param {string[]} [options.prompts] - Prompt names to include
302
+ * @param {Object} [options.promptArgs] - Arguments for prompts
303
+ * @returns {Promise<Object>}
304
+ */
305
+ async buildContext(options = {}) {
306
+ const context = {
307
+ resources: [],
308
+ prompts: [],
309
+ metadata: {
310
+ builtAt: new Date().toISOString(),
311
+ sources: []
312
+ }
313
+ };
314
+
315
+ // Gather resources
316
+ if (options.resources) {
317
+ for (const resourceSpec of options.resources) {
318
+ const [serverName, uri] = this.parseResourceSpec(resourceSpec);
319
+ try {
320
+ const data = await this.readResource(serverName, uri);
321
+ context.resources.push(data);
322
+ context.metadata.sources.push(data.source);
323
+ } catch (error) {
324
+ this.emit('context:resource:error', { resourceSpec, error });
325
+ }
326
+ }
327
+ }
328
+
329
+ // Gather prompts
330
+ if (options.prompts) {
331
+ for (const promptSpec of options.prompts) {
332
+ const [serverName, promptName] = this.parseResourceSpec(promptSpec);
333
+ const args = options.promptArgs?.[promptSpec] || {};
334
+ try {
335
+ const data = await this.getPrompt(serverName, promptName, args);
336
+ context.prompts.push(data);
337
+ context.metadata.sources.push(data.source);
338
+ } catch (error) {
339
+ this.emit('context:prompt:error', { promptSpec, error });
340
+ }
341
+ }
342
+ }
343
+
344
+ this.emit('context:built', {
345
+ resourceCount: context.resources.length,
346
+ promptCount: context.prompts.length
347
+ });
348
+
349
+ return context;
350
+ }
351
+
352
+ /**
353
+ * Parse resource specification (serverName:uri or serverName/name)
354
+ * @param {string} spec
355
+ * @returns {[string, string]}
356
+ */
357
+ parseResourceSpec(spec) {
358
+ if (spec.includes(':')) {
359
+ const [serverName, ...rest] = spec.split(':');
360
+ return [serverName, rest.join(':')];
361
+ } else if (spec.includes('/')) {
362
+ const [serverName, ...rest] = spec.split('/');
363
+ return [serverName, rest.join('/')];
364
+ }
365
+ return [spec, ''];
366
+ }
367
+
368
+ /**
369
+ * Add item to cache
370
+ * @param {string} key
371
+ * @param {*} data
372
+ */
373
+ addToCache(key, data) {
374
+ // Evict old entries if at capacity
375
+ if (this.cache.size >= this.maxCacheSize) {
376
+ const oldest = this.findOldestCacheEntry();
377
+ if (oldest) {
378
+ this.cache.delete(oldest);
379
+ }
380
+ }
381
+
382
+ this.cache.set(key, {
383
+ data,
384
+ timestamp: Date.now()
385
+ });
386
+ }
387
+
388
+ /**
389
+ * Get item from cache
390
+ * @param {string} key
391
+ * @returns {*|null}
392
+ */
393
+ getFromCache(key) {
394
+ const entry = this.cache.get(key);
395
+ if (!entry) return null;
396
+
397
+ // Check TTL
398
+ if (Date.now() - entry.timestamp > this.cacheTTL) {
399
+ this.cache.delete(key);
400
+ return null;
401
+ }
402
+
403
+ return entry.data;
404
+ }
405
+
406
+ /**
407
+ * Find oldest cache entry
408
+ * @returns {string|null}
409
+ */
410
+ findOldestCacheEntry() {
411
+ let oldest = null;
412
+ let oldestTime = Infinity;
413
+
414
+ for (const [key, entry] of this.cache) {
415
+ if (entry.timestamp < oldestTime) {
416
+ oldestTime = entry.timestamp;
417
+ oldest = key;
418
+ }
419
+ }
420
+
421
+ return oldest;
422
+ }
423
+
424
+ /**
425
+ * Clear cache
426
+ */
427
+ clearCache() {
428
+ this.cache.clear();
429
+ this.emit('cache:cleared');
430
+ }
431
+
432
+ /**
433
+ * Get provider statistics
434
+ * @returns {Object}
435
+ */
436
+ getStats() {
437
+ return {
438
+ resources: {
439
+ total: this.getAllResources().length,
440
+ byServer: Object.fromEntries(
441
+ Array.from(this.resources.entries()).map(([s, r]) => [s, r.length])
442
+ )
443
+ },
444
+ prompts: {
445
+ total: this.getAllPrompts().length,
446
+ byServer: Object.fromEntries(
447
+ Array.from(this.prompts.entries()).map(([s, p]) => [s, p.length])
448
+ )
449
+ },
450
+ cache: {
451
+ size: this.cache.size,
452
+ maxSize: this.maxCacheSize
453
+ },
454
+ subscriptions: this.subscriptions.size
455
+ };
456
+ }
457
+
458
+ /**
459
+ * Clear all data
460
+ */
461
+ clear() {
462
+ this.resources.clear();
463
+ this.prompts.clear();
464
+ this.cache.clear();
465
+ this.subscriptions.clear();
466
+ this.emit('provider:cleared');
467
+ }
468
+ }
469
+
470
+ module.exports = {
471
+ MCPContextProvider
472
+ };