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,436 @@
1
+ /**
2
+ * MUSUBI MCP Server Discovery
3
+ *
4
+ * Discovers and manages MCP servers from various configuration sources:
5
+ * - claude_desktop_config.json
6
+ * - .mcp/config.json (project-level)
7
+ * - Environment variables
8
+ * - Dynamic discovery via well-known paths
9
+ *
10
+ * @module integrations/mcp/mcp-discovery
11
+ */
12
+
13
+ 'use strict';
14
+
15
+ const fs = require('fs');
16
+ const path = require('path');
17
+ const os = require('os');
18
+ const EventEmitter = require('events');
19
+
20
+ /**
21
+ * @typedef {Object} MCPServerConfig
22
+ * @property {string} name - Server name
23
+ * @property {string} command - Command to start server
24
+ * @property {string[]} [args] - Command arguments
25
+ * @property {Object<string, string>} [env] - Environment variables
26
+ * @property {string} [cwd] - Working directory
27
+ * @property {string} [transport] - Transport type (stdio, sse, http)
28
+ * @property {string} [url] - URL for SSE/HTTP transports
29
+ * @property {Object} [metadata] - Additional metadata
30
+ */
31
+
32
+ /**
33
+ * @typedef {Object} DiscoveryResult
34
+ * @property {MCPServerConfig[]} servers - Discovered servers
35
+ * @property {string[]} sources - Config sources used
36
+ * @property {Object<string, Error>} errors - Errors per source
37
+ */
38
+
39
+ /**
40
+ * Known configuration file locations
41
+ */
42
+ const CONFIG_LOCATIONS = {
43
+ // Claude Desktop config
44
+ claudeDesktop: {
45
+ darwin: path.join(os.homedir(), 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json'),
46
+ win32: path.join(os.homedir(), 'AppData', 'Roaming', 'Claude', 'claude_desktop_config.json'),
47
+ linux: path.join(os.homedir(), '.config', 'claude', 'claude_desktop_config.json')
48
+ },
49
+ // Project-level config
50
+ project: [
51
+ '.mcp/config.json',
52
+ '.mcp.json',
53
+ 'mcp.config.json'
54
+ ],
55
+ // VS Code settings
56
+ vscode: '.vscode/mcp.json'
57
+ };
58
+
59
+ /**
60
+ * MCP Server Discovery class
61
+ */
62
+ class MCPDiscovery extends EventEmitter {
63
+ /**
64
+ * @param {Object} options
65
+ * @param {string} [options.projectRoot] - Project root directory
66
+ * @param {boolean} [options.includeGlobal=true] - Include global configs
67
+ * @param {boolean} [options.includeProject=true] - Include project configs
68
+ * @param {boolean} [options.includeEnv=true] - Include env var configs
69
+ */
70
+ constructor(options = {}) {
71
+ super();
72
+
73
+ this.projectRoot = options.projectRoot || process.cwd();
74
+ this.includeGlobal = options.includeGlobal ?? true;
75
+ this.includeProject = options.includeProject ?? true;
76
+ this.includeEnv = options.includeEnv ?? true;
77
+
78
+ /** @type {Map<string, MCPServerConfig>} */
79
+ this.servers = new Map();
80
+
81
+ /** @type {string[]} */
82
+ this.discoveredSources = [];
83
+
84
+ /** @type {Object<string, Error>} */
85
+ this.errors = {};
86
+ }
87
+
88
+ /**
89
+ * Discover all available MCP servers
90
+ * @returns {Promise<DiscoveryResult>}
91
+ */
92
+ async discover() {
93
+ this.servers.clear();
94
+ this.discoveredSources = [];
95
+ this.errors = {};
96
+
97
+ const promises = [];
98
+
99
+ // Global configs (Claude Desktop, etc.)
100
+ if (this.includeGlobal) {
101
+ promises.push(this.discoverGlobalConfigs());
102
+ }
103
+
104
+ // Project-level configs
105
+ if (this.includeProject) {
106
+ promises.push(this.discoverProjectConfigs());
107
+ }
108
+
109
+ // Environment variable configs
110
+ if (this.includeEnv) {
111
+ promises.push(this.discoverEnvConfigs());
112
+ }
113
+
114
+ await Promise.all(promises);
115
+
116
+ this.emit('discovery:complete', {
117
+ serverCount: this.servers.size,
118
+ sources: this.discoveredSources
119
+ });
120
+
121
+ return {
122
+ servers: Array.from(this.servers.values()),
123
+ sources: this.discoveredSources,
124
+ errors: this.errors
125
+ };
126
+ }
127
+
128
+ /**
129
+ * Discover global configuration files
130
+ */
131
+ async discoverGlobalConfigs() {
132
+ // Claude Desktop config
133
+ const platform = process.platform;
134
+ const claudeConfigPath = CONFIG_LOCATIONS.claudeDesktop[platform];
135
+
136
+ if (claudeConfigPath) {
137
+ await this.loadConfigFile(claudeConfigPath, 'claude-desktop');
138
+ }
139
+ }
140
+
141
+ /**
142
+ * Discover project-level configuration files
143
+ */
144
+ async discoverProjectConfigs() {
145
+ for (const configPath of CONFIG_LOCATIONS.project) {
146
+ const fullPath = path.join(this.projectRoot, configPath);
147
+ await this.loadConfigFile(fullPath, `project:${configPath}`);
148
+ }
149
+
150
+ // VS Code config
151
+ const vscodePath = path.join(this.projectRoot, CONFIG_LOCATIONS.vscode);
152
+ await this.loadConfigFile(vscodePath, 'vscode');
153
+ }
154
+
155
+ /**
156
+ * Discover servers from environment variables
157
+ */
158
+ async discoverEnvConfigs() {
159
+ // MCP_SERVERS environment variable (JSON array)
160
+ const envServers = process.env.MCP_SERVERS;
161
+ if (envServers) {
162
+ try {
163
+ const servers = JSON.parse(envServers);
164
+ if (Array.isArray(servers)) {
165
+ for (const server of servers) {
166
+ this.addServer(server, 'env:MCP_SERVERS');
167
+ }
168
+ }
169
+ } catch (error) {
170
+ this.errors['env:MCP_SERVERS'] = error;
171
+ }
172
+ }
173
+
174
+ // Individual MCP_SERVER_* environment variables
175
+ for (const [key, value] of Object.entries(process.env)) {
176
+ if (key.startsWith('MCP_SERVER_') && key !== 'MCP_SERVERS') {
177
+ try {
178
+ const server = JSON.parse(value);
179
+ const name = key.replace('MCP_SERVER_', '').toLowerCase();
180
+ this.addServer({ name, ...server }, `env:${key}`);
181
+ } catch (error) {
182
+ this.errors[`env:${key}`] = error;
183
+ }
184
+ }
185
+ }
186
+ }
187
+
188
+ /**
189
+ * Load configuration from a file
190
+ * @param {string} filePath - Path to config file
191
+ * @param {string} source - Source identifier
192
+ */
193
+ async loadConfigFile(filePath, source) {
194
+ try {
195
+ if (!fs.existsSync(filePath)) {
196
+ return;
197
+ }
198
+
199
+ const content = fs.readFileSync(filePath, 'utf-8');
200
+ const config = JSON.parse(content);
201
+
202
+ // Handle different config formats
203
+ if (config.mcpServers) {
204
+ // Claude Desktop format
205
+ for (const [name, serverConfig] of Object.entries(config.mcpServers)) {
206
+ this.addServer({ name, ...serverConfig }, source);
207
+ }
208
+ } else if (config.servers) {
209
+ // Alternative format with servers array
210
+ for (const server of config.servers) {
211
+ this.addServer(server, source);
212
+ }
213
+ } else if (Array.isArray(config)) {
214
+ // Direct array of servers
215
+ for (const server of config) {
216
+ this.addServer(server, source);
217
+ }
218
+ }
219
+
220
+ this.discoveredSources.push(source);
221
+ this.emit('config:loaded', { source, path: filePath });
222
+
223
+ } catch (error) {
224
+ this.errors[source] = error;
225
+ this.emit('config:error', { source, error });
226
+ }
227
+ }
228
+
229
+ /**
230
+ * Add a server to the registry
231
+ * @param {MCPServerConfig} serverConfig
232
+ * @param {string} source
233
+ */
234
+ addServer(serverConfig, source) {
235
+ if (!serverConfig.name) {
236
+ return;
237
+ }
238
+
239
+ const existing = this.servers.get(serverConfig.name);
240
+ if (existing) {
241
+ // Merge configurations, later sources override
242
+ this.servers.set(serverConfig.name, {
243
+ ...existing,
244
+ ...serverConfig,
245
+ metadata: {
246
+ ...existing.metadata,
247
+ ...serverConfig.metadata,
248
+ sources: [...(existing.metadata?.sources || []), source]
249
+ }
250
+ });
251
+ } else {
252
+ this.servers.set(serverConfig.name, {
253
+ ...serverConfig,
254
+ metadata: {
255
+ ...serverConfig.metadata,
256
+ sources: [source]
257
+ }
258
+ });
259
+ }
260
+
261
+ this.emit('server:discovered', { name: serverConfig.name, source });
262
+ }
263
+
264
+ /**
265
+ * Get a server by name
266
+ * @param {string} name
267
+ * @returns {MCPServerConfig|undefined}
268
+ */
269
+ getServer(name) {
270
+ return this.servers.get(name);
271
+ }
272
+
273
+ /**
274
+ * Get all servers
275
+ * @returns {MCPServerConfig[]}
276
+ */
277
+ getAllServers() {
278
+ return Array.from(this.servers.values());
279
+ }
280
+
281
+ /**
282
+ * Get servers by transport type
283
+ * @param {string} transport
284
+ * @returns {MCPServerConfig[]}
285
+ */
286
+ getServersByTransport(transport) {
287
+ return this.getAllServers().filter(s =>
288
+ (s.transport || 'stdio') === transport
289
+ );
290
+ }
291
+
292
+ /**
293
+ * Filter servers by criteria
294
+ * @param {Function} predicate
295
+ * @returns {MCPServerConfig[]}
296
+ */
297
+ filterServers(predicate) {
298
+ return this.getAllServers().filter(predicate);
299
+ }
300
+
301
+ /**
302
+ * Check if a server exists
303
+ * @param {string} name
304
+ * @returns {boolean}
305
+ */
306
+ hasServer(name) {
307
+ return this.servers.has(name);
308
+ }
309
+
310
+ /**
311
+ * Get server count
312
+ * @returns {number}
313
+ */
314
+ get serverCount() {
315
+ return this.servers.size;
316
+ }
317
+
318
+ /**
319
+ * Create a project configuration file
320
+ * @param {MCPServerConfig[]} servers - Servers to include
321
+ * @param {Object} [options]
322
+ * @param {string} [options.format='mcp'] - Config format
323
+ * @param {string} [options.path] - Custom path
324
+ * @returns {Promise<string>} Path to created file
325
+ */
326
+ async createProjectConfig(servers, options = {}) {
327
+ const format = options.format || 'mcp';
328
+ const configPath = options.path || path.join(this.projectRoot, '.mcp', 'config.json');
329
+
330
+ // Ensure directory exists
331
+ const dir = path.dirname(configPath);
332
+ if (!fs.existsSync(dir)) {
333
+ fs.mkdirSync(dir, { recursive: true });
334
+ }
335
+
336
+ let config;
337
+ if (format === 'claude') {
338
+ // Claude Desktop format
339
+ config = {
340
+ mcpServers: Object.fromEntries(
341
+ servers.map(s => [s.name, {
342
+ command: s.command,
343
+ args: s.args,
344
+ env: s.env
345
+ }])
346
+ )
347
+ };
348
+ } else {
349
+ // MUSUBI MCP format
350
+ config = {
351
+ version: '1.0',
352
+ servers: servers.map(s => ({
353
+ name: s.name,
354
+ command: s.command,
355
+ args: s.args,
356
+ env: s.env,
357
+ transport: s.transport || 'stdio',
358
+ metadata: s.metadata
359
+ }))
360
+ };
361
+ }
362
+
363
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
364
+ this.emit('config:created', { path: configPath });
365
+
366
+ return configPath;
367
+ }
368
+
369
+ /**
370
+ * Watch for configuration changes
371
+ * @param {Function} callback
372
+ * @returns {Function} Unwatch function
373
+ */
374
+ watch(callback) {
375
+ const watchers = [];
376
+
377
+ // Watch project configs
378
+ for (const configPath of CONFIG_LOCATIONS.project) {
379
+ const fullPath = path.join(this.projectRoot, configPath);
380
+ const dir = path.dirname(fullPath);
381
+
382
+ if (fs.existsSync(dir)) {
383
+ try {
384
+ const watcher = fs.watch(dir, (eventType, filename) => {
385
+ if (filename === path.basename(fullPath)) {
386
+ this.discover().then(result => callback(result));
387
+ }
388
+ });
389
+ watchers.push(watcher);
390
+ } catch (e) {
391
+ // Directory may not exist
392
+ }
393
+ }
394
+ }
395
+
396
+ // Return unwatch function
397
+ return () => {
398
+ for (const watcher of watchers) {
399
+ watcher.close();
400
+ }
401
+ };
402
+ }
403
+
404
+ /**
405
+ * Get discovery summary
406
+ * @returns {Object}
407
+ */
408
+ getSummary() {
409
+ return {
410
+ totalServers: this.servers.size,
411
+ sources: this.discoveredSources,
412
+ errors: Object.keys(this.errors),
413
+ servers: this.getAllServers().map(s => ({
414
+ name: s.name,
415
+ transport: s.transport || 'stdio',
416
+ sources: s.metadata?.sources || []
417
+ }))
418
+ };
419
+ }
420
+ }
421
+
422
+ /**
423
+ * Create a discovery instance and run discovery
424
+ * @param {Object} options
425
+ * @returns {Promise<DiscoveryResult>}
426
+ */
427
+ async function discoverMCPServers(options = {}) {
428
+ const discovery = new MCPDiscovery(options);
429
+ return discovery.discover();
430
+ }
431
+
432
+ module.exports = {
433
+ MCPDiscovery,
434
+ discoverMCPServers,
435
+ CONFIG_LOCATIONS
436
+ };