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.
- package/README.md +24 -19
- package/package.json +1 -1
- package/src/agents/agent-loop.js +532 -0
- package/src/agents/agentic/code-generator.js +767 -0
- package/src/agents/agentic/code-reviewer.js +698 -0
- package/src/agents/agentic/index.js +43 -0
- package/src/agents/function-tool.js +432 -0
- package/src/agents/index.js +45 -0
- package/src/agents/schema-generator.js +514 -0
- package/src/analyzers/ast-extractor.js +870 -0
- package/src/analyzers/context-optimizer.js +681 -0
- package/src/analyzers/repository-map.js +692 -0
- package/src/integrations/index.js +7 -1
- package/src/integrations/mcp/index.js +175 -0
- package/src/integrations/mcp/mcp-context-provider.js +472 -0
- package/src/integrations/mcp/mcp-discovery.js +436 -0
- package/src/integrations/mcp/mcp-tool-registry.js +467 -0
- package/src/integrations/mcp-connector.js +818 -0
- package/src/integrations/tool-discovery.js +589 -0
- package/src/managers/index.js +7 -0
- package/src/managers/skill-tools.js +565 -0
- package/src/monitoring/cost-tracker.js +7 -0
- package/src/monitoring/incident-manager.js +10 -0
- package/src/monitoring/observability.js +10 -0
- package/src/monitoring/quality-dashboard.js +491 -0
- package/src/monitoring/release-manager.js +10 -0
- package/src/orchestration/agent-skill-binding.js +655 -0
- package/src/orchestration/error-handler.js +827 -0
- package/src/orchestration/index.js +235 -1
- package/src/orchestration/mcp-tool-adapters.js +896 -0
- package/src/orchestration/reasoning/index.js +58 -0
- package/src/orchestration/reasoning/planning-engine.js +831 -0
- package/src/orchestration/reasoning/reasoning-engine.js +710 -0
- package/src/orchestration/reasoning/self-correction.js +751 -0
- package/src/orchestration/skill-executor.js +665 -0
- package/src/orchestration/skill-registry.js +650 -0
- package/src/orchestration/workflow-examples.js +1072 -0
- package/src/orchestration/workflow-executor.js +779 -0
- package/src/phase4-integration.js +248 -0
- package/src/phase5-integration.js +402 -0
- package/src/steering/steering-auto-update.js +572 -0
- package/src/steering/steering-validator.js +547 -0
- package/src/templates/template-constraints.js +646 -0
- 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
|
+
};
|