claude-flow-novice 1.5.12 ā 1.5.14
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/.claude/agents/analysis/code-review/analyze-code-quality.md +160 -177
- package/.claude/agents/architecture/system-design/arch-system-design.md +118 -153
- package/.claude-flow-novice/dist/mcp/auth.js +347 -0
- package/.claude-flow-novice/dist/mcp/claude-code-wrapper.js +717 -0
- package/.claude-flow-novice/dist/mcp/claude-flow-tools.js +1365 -0
- package/.claude-flow-novice/dist/mcp/client.js +201 -0
- package/.claude-flow-novice/dist/mcp/index.js +192 -0
- package/.claude-flow-novice/dist/mcp/integrate-wrapper.js +85 -0
- package/.claude-flow-novice/dist/mcp/lifecycle-manager.js +348 -0
- package/.claude-flow-novice/dist/mcp/load-balancer.js +386 -0
- package/.claude-flow-novice/dist/mcp/mcp-config-manager.js +1362 -0
- package/.claude-flow-novice/dist/mcp/mcp-server-novice-simplified.js +583 -0
- package/.claude-flow-novice/dist/mcp/mcp-server-novice.js +723 -0
- package/.claude-flow-novice/dist/mcp/mcp-server-sdk.js +649 -0
- package/.claude-flow-novice/dist/mcp/mcp-server.js +2256 -0
- package/.claude-flow-novice/dist/mcp/orchestration-integration.js +800 -0
- package/.claude-flow-novice/dist/mcp/performance-monitor.js +489 -0
- package/.claude-flow-novice/dist/mcp/protocol-manager.js +376 -0
- package/.claude-flow-novice/dist/mcp/router.js +220 -0
- package/.claude-flow-novice/dist/mcp/ruv-swarm-tools.js +671 -0
- package/.claude-flow-novice/dist/mcp/ruv-swarm-wrapper.js +254 -0
- package/.claude-flow-novice/dist/mcp/server-with-wrapper.js +32 -0
- package/.claude-flow-novice/dist/mcp/server-wrapper-mode.js +26 -0
- package/.claude-flow-novice/dist/mcp/server.js +539 -0
- package/.claude-flow-novice/dist/mcp/session-manager.js +338 -0
- package/.claude-flow-novice/dist/mcp/sparc-modes.js +455 -0
- package/.claude-flow-novice/dist/mcp/swarm-tools.js +903 -0
- package/.claude-flow-novice/dist/mcp/tools.js +426 -0
- package/.claude-flow-novice/dist/src/cli/commands/swarm.js +23 -1
- package/.claude-flow-novice/dist/src/cli/commands/swarm.js.map +1 -1
- package/.claude-flow-novice/dist/src/cli/simple-commands/init/templates/CLAUDE.md +42 -102
- package/.claude-flow-novice/dist/src/config/web-portal-config.js +2 -1
- package/.claude-flow-novice/dist/src/config/web-portal-config.js.map +1 -1
- package/.claude-flow-novice/dist/src/coordination/swarm-coordinator-factory.js +36 -0
- package/.claude-flow-novice/dist/src/coordination/swarm-coordinator-factory.js.map +1 -0
- package/.claude-flow-novice/dist/src/preferences/user-preference-manager.js +371 -0
- package/.claude-flow-novice/dist/src/preferences/user-preference-manager.js.map +1 -0
- package/.claude-flow-novice/dist/src/validators/index.js +12 -0
- package/.claude-flow-novice/dist/src/validators/index.js.map +1 -0
- package/.claude-flow-novice/dist/src/validators/swarm-init-validator.js +261 -0
- package/.claude-flow-novice/dist/src/validators/swarm-init-validator.js.map +1 -0
- package/.claude-flow-novice/dist/src/validators/todowrite-batching-validator.js +204 -0
- package/.claude-flow-novice/dist/src/validators/todowrite-batching-validator.js.map +1 -0
- package/.claude-flow-novice/dist/src/validators/todowrite-integration.js +189 -0
- package/.claude-flow-novice/dist/src/validators/todowrite-integration.js.map +1 -0
- package/.claude-flow-novice/dist/src/web/portal-server.js +12 -5
- package/.claude-flow-novice/dist/src/web/portal-server.js.map +1 -1
- package/config/hooks/post-edit-pipeline.js +231 -10
- package/package.json +4 -2
- package/scripts/src/web/frontend/.claude-flow/metrics/agent-metrics.json +1 -0
- package/scripts/src/web/frontend/.claude-flow/metrics/performance.json +9 -0
- package/scripts/src/web/frontend/.claude-flow/metrics/task-metrics.json +10 -0
- package/src/cli/simple-commands/init/templates/CLAUDE.md +4 -1
|
@@ -0,0 +1,1362 @@
|
|
|
1
|
+
// Bulletproof MCP Configuration Manager
|
|
2
|
+
// Handles Claude Code MCP configuration with comprehensive error handling
|
|
3
|
+
// Automatically detects and fixes broken configurations with rollback capability
|
|
4
|
+
// Provides enhanced user experience and backwards compatibility
|
|
5
|
+
|
|
6
|
+
import { promises as fs } from 'fs';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import os from 'os';
|
|
9
|
+
import { execSync, spawn } from 'child_process';
|
|
10
|
+
import { existsSync } from 'fs';
|
|
11
|
+
import { printSuccess, printWarning, printError } from '../cli/utils.js';
|
|
12
|
+
|
|
13
|
+
export class McpConfigurationManager {
|
|
14
|
+
constructor(options = {}) {
|
|
15
|
+
this.localConfigPath = this.findClaudeConfigPath();
|
|
16
|
+
this.projectConfigPath = path.join(process.cwd(), '.mcp.json');
|
|
17
|
+
this.verbose = options.verbose || false;
|
|
18
|
+
this.autoFix = options.autoFix !== false; // Default to true for bulletproof operation
|
|
19
|
+
this.dryRun = options.dryRun || false;
|
|
20
|
+
this.backupPaths = new Map();
|
|
21
|
+
this.operationLog = [];
|
|
22
|
+
this.errorRecovery = [];
|
|
23
|
+
this.rollbackStack = [];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Find Claude Code configuration path with comprehensive search
|
|
28
|
+
*/
|
|
29
|
+
findClaudeConfigPath() {
|
|
30
|
+
const homeDir = os.homedir();
|
|
31
|
+
const primaryPath = path.join(homeDir, '.claude.json');
|
|
32
|
+
|
|
33
|
+
if (existsSync(primaryPath)) {
|
|
34
|
+
return primaryPath;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Search alternative locations
|
|
38
|
+
const alternativePaths = [
|
|
39
|
+
path.join(homeDir, '.config', 'claude.json'),
|
|
40
|
+
path.join(homeDir, '.claude', 'config.json'),
|
|
41
|
+
path.join(homeDir, 'AppData', 'Local', 'Claude', 'config.json'), // Windows
|
|
42
|
+
path.join(homeDir, 'Library', 'Application Support', 'Claude', 'config.json'), // macOS
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
for (const altPath of alternativePaths) {
|
|
46
|
+
if (existsSync(altPath)) {
|
|
47
|
+
this.log(`š Found Claude config at alternative location: ${altPath}`);
|
|
48
|
+
return altPath;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Return primary path even if it doesn't exist (for creation)
|
|
53
|
+
return primaryPath;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Enhanced logging with operation tracking
|
|
58
|
+
*/
|
|
59
|
+
log(message, level = 'info') {
|
|
60
|
+
const timestamp = new Date().toISOString();
|
|
61
|
+
const logEntry = { timestamp, level, message };
|
|
62
|
+
this.operationLog.push(logEntry);
|
|
63
|
+
|
|
64
|
+
if (this.verbose || level === 'error' || level === 'warn') {
|
|
65
|
+
const prefix = {
|
|
66
|
+
info: ' ā¹ļø',
|
|
67
|
+
warn: ' ā ļø',
|
|
68
|
+
error: ' ā',
|
|
69
|
+
success: ' ā
',
|
|
70
|
+
debug: ' š'
|
|
71
|
+
}[level] || ' Ā·';
|
|
72
|
+
|
|
73
|
+
console.log(`${prefix} ${message}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Add operation to rollback stack
|
|
79
|
+
*/
|
|
80
|
+
addRollbackOperation(operation) {
|
|
81
|
+
this.rollbackStack.push({
|
|
82
|
+
...operation,
|
|
83
|
+
timestamp: new Date().toISOString()
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Check if Claude Code CLI is available
|
|
89
|
+
*/
|
|
90
|
+
isClaudeCodeInstalled() {
|
|
91
|
+
try {
|
|
92
|
+
execSync('which claude', { stdio: 'ignore' });
|
|
93
|
+
return true;
|
|
94
|
+
} catch {
|
|
95
|
+
try {
|
|
96
|
+
execSync('claude --version', { stdio: 'ignore' });
|
|
97
|
+
return true;
|
|
98
|
+
} catch {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Create backup with enhanced error handling
|
|
106
|
+
*/
|
|
107
|
+
async createConfigBackup(configPath, label = 'auto') {
|
|
108
|
+
if (!existsSync(configPath)) {
|
|
109
|
+
this.log(`No config file to backup at: ${configPath}`, 'debug');
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
try {
|
|
114
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
115
|
+
const backupPath = `${configPath}.backup-${label}-${timestamp}`;
|
|
116
|
+
|
|
117
|
+
if (!this.dryRun) {
|
|
118
|
+
await fs.copyFile(configPath, backupPath);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
this.backupPaths.set(configPath, backupPath);
|
|
122
|
+
this.addRollbackOperation({
|
|
123
|
+
type: 'restore-backup',
|
|
124
|
+
source: backupPath,
|
|
125
|
+
target: configPath,
|
|
126
|
+
action: async () => {
|
|
127
|
+
if (existsSync(backupPath)) {
|
|
128
|
+
await fs.copyFile(backupPath, configPath);
|
|
129
|
+
this.log(`š Restored ${configPath} from backup`, 'success');
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
this.log(`š¾ Created backup: ${backupPath}`, 'success');
|
|
135
|
+
return backupPath;
|
|
136
|
+
} catch (error) {
|
|
137
|
+
this.log(`ā ļø Could not create backup: ${error.message}`, 'warn');
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Enhanced configuration state detection with comprehensive analysis
|
|
144
|
+
*/
|
|
145
|
+
async detectConfigurationState() {
|
|
146
|
+
const state = {
|
|
147
|
+
hasLocalConfig: false,
|
|
148
|
+
hasProjectConfig: false,
|
|
149
|
+
localServers: [],
|
|
150
|
+
projectServers: [],
|
|
151
|
+
conflictingServers: [],
|
|
152
|
+
brokenPaths: [],
|
|
153
|
+
recommendations: [],
|
|
154
|
+
claudeCodeInstalled: this.isClaudeCodeInstalled(),
|
|
155
|
+
configPaths: {
|
|
156
|
+
local: this.localConfigPath,
|
|
157
|
+
project: this.projectConfigPath
|
|
158
|
+
},
|
|
159
|
+
healthScore: 100,
|
|
160
|
+
criticalIssues: [],
|
|
161
|
+
warnings: []
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
try {
|
|
165
|
+
this.log('š Analyzing MCP configuration state...', 'info');
|
|
166
|
+
|
|
167
|
+
// Check local configuration with enhanced validation
|
|
168
|
+
if (await this.fileExists(this.localConfigPath)) {
|
|
169
|
+
state.hasLocalConfig = true;
|
|
170
|
+
const localConfig = await this.readLocalConfig();
|
|
171
|
+
if (localConfig) {
|
|
172
|
+
state.localServers = this.extractMcpServers(localConfig);
|
|
173
|
+
this.log(`š Found ${state.localServers.length} local MCP servers`, 'info');
|
|
174
|
+
} else {
|
|
175
|
+
state.warnings.push('Local config file exists but is invalid/corrupted');
|
|
176
|
+
state.healthScore -= 20;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Check project configuration
|
|
181
|
+
if (await this.fileExists(this.projectConfigPath)) {
|
|
182
|
+
state.hasProjectConfig = true;
|
|
183
|
+
const projectConfig = await this.readProjectConfig();
|
|
184
|
+
if (projectConfig) {
|
|
185
|
+
state.projectServers = this.extractMcpServers(projectConfig);
|
|
186
|
+
this.log(`š Found ${state.projectServers.length} project MCP servers`, 'info');
|
|
187
|
+
} else {
|
|
188
|
+
state.warnings.push('Project config file exists but is invalid/corrupted');
|
|
189
|
+
state.healthScore -= 10;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Enhanced analysis
|
|
194
|
+
state.conflictingServers = this.findConflictingServers(state.localServers, state.projectServers);
|
|
195
|
+
state.brokenPaths = await this.findBrokenServerPaths([...state.localServers, ...state.projectServers]);
|
|
196
|
+
state.recommendations = this.generateRecommendations(state);
|
|
197
|
+
|
|
198
|
+
// Calculate health score
|
|
199
|
+
if (state.conflictingServers.length > 0) {
|
|
200
|
+
state.healthScore -= (state.conflictingServers.length * 15);
|
|
201
|
+
state.warnings.push(`${state.conflictingServers.length} conflicting server configurations`);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (state.brokenPaths.length > 0) {
|
|
205
|
+
state.healthScore -= (state.brokenPaths.length * 25);
|
|
206
|
+
state.criticalIssues.push(`${state.brokenPaths.length} broken server paths detected`);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (!state.claudeCodeInstalled) {
|
|
210
|
+
state.healthScore -= 30;
|
|
211
|
+
state.warnings.push('Claude Code CLI not installed');
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
this.log(`š Configuration health score: ${state.healthScore}/100`,
|
|
215
|
+
state.healthScore >= 80 ? 'success' : state.healthScore >= 60 ? 'warn' : 'error');
|
|
216
|
+
|
|
217
|
+
return state;
|
|
218
|
+
} catch (error) {
|
|
219
|
+
this.log(`ā Failed to detect configuration state: ${error.message}`, 'error');
|
|
220
|
+
throw new Error(`Failed to detect configuration state: ${error.message}`);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Perform pre-initialization audit
|
|
226
|
+
*/
|
|
227
|
+
async performPreInitAudit() {
|
|
228
|
+
const state = await this.detectConfigurationState();
|
|
229
|
+
const audit = {
|
|
230
|
+
hasIssues: false,
|
|
231
|
+
autoFixable: [],
|
|
232
|
+
requiresConfirmation: [],
|
|
233
|
+
blocking: [],
|
|
234
|
+
warnings: []
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
// Check for broken local servers
|
|
238
|
+
for (const brokenServer of state.brokenPaths) {
|
|
239
|
+
if (brokenServer.serverName === 'claude-flow-novice') {
|
|
240
|
+
audit.hasIssues = true;
|
|
241
|
+
audit.autoFixable.push({
|
|
242
|
+
type: 'remove-broken-local-server',
|
|
243
|
+
description: `Remove broken local MCP server: ${brokenServer.serverName}`,
|
|
244
|
+
serverName: brokenServer.serverName,
|
|
245
|
+
reason: brokenServer.reason,
|
|
246
|
+
action: () => this.removeLocalServer(brokenServer.serverName)
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Check for conflicting servers
|
|
252
|
+
for (const conflict of state.conflictingServers) {
|
|
253
|
+
if (conflict.serverName === 'claude-flow-novice') {
|
|
254
|
+
audit.hasIssues = true;
|
|
255
|
+
audit.requiresConfirmation.push({
|
|
256
|
+
type: 'conflicting-server',
|
|
257
|
+
description: `Server '${conflict.serverName}' exists in both local and project configs`,
|
|
258
|
+
recommendation: 'Remove from local config to use project configuration',
|
|
259
|
+
action: () => this.removeLocalServer(conflict.serverName)
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Check if local config takes precedence over project config
|
|
265
|
+
if (state.hasLocalConfig && state.hasProjectConfig) {
|
|
266
|
+
const claudeFlowInLocal = state.localServers.some(s => s.name === 'claude-flow-novice');
|
|
267
|
+
const claudeFlowInProject = state.projectServers.some(s => s.name === 'claude-flow-novice');
|
|
268
|
+
|
|
269
|
+
if (claudeFlowInLocal && claudeFlowInProject) {
|
|
270
|
+
audit.warnings.push({
|
|
271
|
+
type: 'precedence-warning',
|
|
272
|
+
description: 'Local MCP config will override project config',
|
|
273
|
+
recommendation: 'Consider removing from local config for project-specific setup'
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return audit;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Bulletproof MCP setup with comprehensive error handling and rollback
|
|
283
|
+
*/
|
|
284
|
+
async executeBulletproofSetup(serverConfig, ux = null) {
|
|
285
|
+
this.log('š§ Executing bulletproof MCP setup...', 'info');
|
|
286
|
+
const startTime = Date.now();
|
|
287
|
+
|
|
288
|
+
try {
|
|
289
|
+
// Phase 1: System validation and pre-audit
|
|
290
|
+
this.log('š Phase 1: System validation and pre-audit...', 'info');
|
|
291
|
+
if (ux) ux.displaySetupProgress('pre-audit');
|
|
292
|
+
|
|
293
|
+
if (!this.isClaudeCodeInstalled()) {
|
|
294
|
+
const error = new Error('Claude Code CLI not installed');
|
|
295
|
+
error.recovery = [
|
|
296
|
+
'Install Claude Code: npm install -g @anthropic-ai/claude-code',
|
|
297
|
+
'Verify installation: claude --version',
|
|
298
|
+
'Re-run setup after installation'
|
|
299
|
+
];
|
|
300
|
+
throw error;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
const audit = await this.performPreInitAudit();
|
|
304
|
+
const state = await this.detectConfigurationState();
|
|
305
|
+
|
|
306
|
+
// Display comprehensive analysis
|
|
307
|
+
if (ux && this.verbose) {
|
|
308
|
+
ux.displayConfigurationAnalysis({ ...state, hasIssues: audit.hasIssues });
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Phase 2: Create safety backups
|
|
312
|
+
this.log('š¾ Phase 2: Creating safety backups...', 'info');
|
|
313
|
+
if (ux) ux.displaySetupProgress('backup');
|
|
314
|
+
|
|
315
|
+
if (state.hasLocalConfig) {
|
|
316
|
+
await this.createConfigBackup(this.localConfigPath, 'pre-bulletproof');
|
|
317
|
+
}
|
|
318
|
+
if (state.hasProjectConfig) {
|
|
319
|
+
await this.createConfigBackup(this.projectConfigPath, 'pre-bulletproof');
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// Phase 3: Issue remediation
|
|
323
|
+
if (audit.hasIssues || state.brokenPaths.length > 0) {
|
|
324
|
+
this.log('š§ Phase 3: Remediating configuration issues...', 'info');
|
|
325
|
+
if (ux) ux.displaySetupProgress('cleanup');
|
|
326
|
+
|
|
327
|
+
const remediationResult = await this.performAutomatedRemediation(audit, state, ux);
|
|
328
|
+
if (!remediationResult.success) {
|
|
329
|
+
throw new Error(`Remediation failed: ${remediationResult.error}`);
|
|
330
|
+
}
|
|
331
|
+
} else {
|
|
332
|
+
this.log('ā
No configuration issues detected', 'success');
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// Phase 4: Ensure optimal project configuration
|
|
336
|
+
this.log('āļø Phase 4: Ensuring optimal project configuration...', 'info');
|
|
337
|
+
if (ux) ux.displaySetupProgress('project-config');
|
|
338
|
+
await this.ensureProjectConfiguration(serverConfig);
|
|
339
|
+
|
|
340
|
+
// Phase 5: Comprehensive verification
|
|
341
|
+
this.log('ā
Phase 5: Comprehensive verification...', 'info');
|
|
342
|
+
if (ux) ux.displaySetupProgress('verification');
|
|
343
|
+
const verificationResult = await this.performComprehensiveVerification();
|
|
344
|
+
|
|
345
|
+
if (!verificationResult.success) {
|
|
346
|
+
this.log('ā ļø Verification failed, attempting rollback...', 'warn');
|
|
347
|
+
await this.performRollback();
|
|
348
|
+
throw new Error(`Setup verification failed: ${verificationResult.error}`);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// Phase 6: Success and cleanup
|
|
352
|
+
const duration = Date.now() - startTime;
|
|
353
|
+
this.log(`š Bulletproof MCP setup completed successfully in ${duration}ms`, 'success');
|
|
354
|
+
if (ux) ux.displaySetupProgress('complete');
|
|
355
|
+
|
|
356
|
+
return {
|
|
357
|
+
success: true,
|
|
358
|
+
duration,
|
|
359
|
+
details: {
|
|
360
|
+
issuesFixed: audit.autoFixable.length + audit.requiresConfirmation.length,
|
|
361
|
+
backupsCreated: this.backupPaths.size,
|
|
362
|
+
healthScore: state.healthScore,
|
|
363
|
+
verificationPassed: verificationResult.success,
|
|
364
|
+
operationsPerformed: this.operationLog.length
|
|
365
|
+
},
|
|
366
|
+
summary: this.generateSetupSummary()
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
} catch (error) {
|
|
370
|
+
this.log(`ā Bulletproof setup failed: ${error.message}`, 'error');
|
|
371
|
+
|
|
372
|
+
// Enhanced error recovery
|
|
373
|
+
const recoveryResult = await this.handleSetupFailure(error, ux);
|
|
374
|
+
|
|
375
|
+
return {
|
|
376
|
+
success: false,
|
|
377
|
+
error: error.message,
|
|
378
|
+
recovery: recoveryResult,
|
|
379
|
+
logs: this.operationLog,
|
|
380
|
+
rollbackAvailable: this.rollbackStack.length > 0
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Ensure project configuration is correct
|
|
387
|
+
*/
|
|
388
|
+
async ensureProjectConfiguration(serverConfig) {
|
|
389
|
+
const defaultConfig = {
|
|
390
|
+
mcpServers: {
|
|
391
|
+
'claude-flow-novice': {
|
|
392
|
+
command: 'npx',
|
|
393
|
+
args: ['claude-flow-novice', 'mcp', 'start'],
|
|
394
|
+
env: {
|
|
395
|
+
NODE_ENV: 'production',
|
|
396
|
+
CLAUDE_FLOW_NOVICE_MODE: 'novice'
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
|
|
402
|
+
const configToUse = serverConfig || defaultConfig;
|
|
403
|
+
|
|
404
|
+
if (!this.dryRun) {
|
|
405
|
+
await fs.writeFile(
|
|
406
|
+
this.projectConfigPath,
|
|
407
|
+
JSON.stringify(configToUse, null, 2)
|
|
408
|
+
);
|
|
409
|
+
this.log('ā
Created/updated .mcp.json configuration');
|
|
410
|
+
} else {
|
|
411
|
+
this.log('[DRY RUN] Would create/update .mcp.json configuration');
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Remove a server from local configuration
|
|
417
|
+
*/
|
|
418
|
+
async removeLocalServer(serverName) {
|
|
419
|
+
try {
|
|
420
|
+
// Use Claude CLI to remove server cleanly
|
|
421
|
+
execSync(`claude mcp remove ${serverName} -s local`, {
|
|
422
|
+
stdio: this.verbose ? 'inherit' : 'pipe'
|
|
423
|
+
});
|
|
424
|
+
this.log(`ā
Removed ${serverName} from local configuration`);
|
|
425
|
+
} catch (error) {
|
|
426
|
+
// Fallback: manual removal
|
|
427
|
+
await this.manuallyRemoveFromLocalConfig(serverName);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Manually remove server from local config (fallback)
|
|
433
|
+
*/
|
|
434
|
+
async manuallyRemoveFromLocalConfig(serverName) {
|
|
435
|
+
try {
|
|
436
|
+
const localConfig = await this.readLocalConfig();
|
|
437
|
+
if (localConfig?.mcpServers?.[serverName]) {
|
|
438
|
+
delete localConfig.mcpServers[serverName];
|
|
439
|
+
|
|
440
|
+
// Create backup
|
|
441
|
+
await this.createConfigBackup(this.localConfigPath);
|
|
442
|
+
|
|
443
|
+
// Write updated config
|
|
444
|
+
await fs.writeFile(
|
|
445
|
+
this.localConfigPath,
|
|
446
|
+
JSON.stringify(localConfig, null, 2)
|
|
447
|
+
);
|
|
448
|
+
|
|
449
|
+
this.log(`ā
Manually removed ${serverName} from local configuration`);
|
|
450
|
+
}
|
|
451
|
+
} catch (error) {
|
|
452
|
+
throw new Error(`Failed to manually remove server: ${error.message}`);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* Verify the setup is working
|
|
458
|
+
*/
|
|
459
|
+
async verifySetup() {
|
|
460
|
+
try {
|
|
461
|
+
// Check if Claude CLI can list servers
|
|
462
|
+
const output = execSync('claude mcp list', {
|
|
463
|
+
encoding: 'utf8',
|
|
464
|
+
stdio: 'pipe'
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
// Check if our server is properly configured
|
|
468
|
+
if (output.includes('claude-flow-novice')) {
|
|
469
|
+
this.log('ā
MCP server verification successful');
|
|
470
|
+
} else {
|
|
471
|
+
this.log('ā ļø MCP server not visible in claude mcp list');
|
|
472
|
+
}
|
|
473
|
+
} catch (error) {
|
|
474
|
+
this.log('ā ļø Could not verify MCP setup (Claude CLI may not be installed)');
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* Create configuration backup
|
|
480
|
+
*/
|
|
481
|
+
async createConfigBackup(configPath) {
|
|
482
|
+
const backupPath = `${configPath}.backup-${Date.now()}`;
|
|
483
|
+
try {
|
|
484
|
+
await fs.copyFile(configPath, backupPath);
|
|
485
|
+
this.log(`š¾ Created backup: ${backupPath}`);
|
|
486
|
+
} catch (error) {
|
|
487
|
+
this.log(`ā ļø Could not create backup: ${error.message}`);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* Helper methods
|
|
493
|
+
*/
|
|
494
|
+
async fileExists(filePath) {
|
|
495
|
+
try {
|
|
496
|
+
await fs.access(filePath);
|
|
497
|
+
return true;
|
|
498
|
+
} catch {
|
|
499
|
+
return false;
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
async readLocalConfig() {
|
|
504
|
+
try {
|
|
505
|
+
const content = await fs.readFile(this.localConfigPath, 'utf8');
|
|
506
|
+
return JSON.parse(content);
|
|
507
|
+
} catch {
|
|
508
|
+
return null;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
async readProjectConfig() {
|
|
513
|
+
try {
|
|
514
|
+
const content = await fs.readFile(this.projectConfigPath, 'utf8');
|
|
515
|
+
return JSON.parse(content);
|
|
516
|
+
} catch {
|
|
517
|
+
return null;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
extractMcpServers(config) {
|
|
522
|
+
if (!config?.mcpServers) return [];
|
|
523
|
+
|
|
524
|
+
return Object.entries(config.mcpServers).map(([name, serverConfig]) => ({
|
|
525
|
+
name,
|
|
526
|
+
...serverConfig
|
|
527
|
+
}));
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
findConflictingServers(localServers, projectServers) {
|
|
531
|
+
const conflicts = [];
|
|
532
|
+
const localNames = new Set(localServers.map(s => s.name));
|
|
533
|
+
|
|
534
|
+
for (const projectServer of projectServers) {
|
|
535
|
+
if (localNames.has(projectServer.name)) {
|
|
536
|
+
conflicts.push({
|
|
537
|
+
serverName: projectServer.name,
|
|
538
|
+
localConfig: localServers.find(s => s.name === projectServer.name),
|
|
539
|
+
projectConfig: projectServer
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
return conflicts;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* Enhanced broken path detection with comprehensive validation
|
|
549
|
+
*/
|
|
550
|
+
async findBrokenServerPaths(servers) {
|
|
551
|
+
const broken = [];
|
|
552
|
+
this.log(`š Checking ${servers.length} servers for broken paths...`, 'debug');
|
|
553
|
+
|
|
554
|
+
for (const server of servers) {
|
|
555
|
+
const issues = await this.checkServerPath(server);
|
|
556
|
+
if (issues.length > 0) {
|
|
557
|
+
broken.push({
|
|
558
|
+
serverName: server.name,
|
|
559
|
+
command: server.command,
|
|
560
|
+
args: server.args,
|
|
561
|
+
issues,
|
|
562
|
+
severity: this.calculateIssueSeverity(issues)
|
|
563
|
+
});
|
|
564
|
+
this.log(`š Found issues with '${server.name}': ${issues.join(', ')}`, 'warn');
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
return broken;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
/**
|
|
572
|
+
* Comprehensive server path validation
|
|
573
|
+
*/
|
|
574
|
+
async checkServerPath(server) {
|
|
575
|
+
const issues = [];
|
|
576
|
+
|
|
577
|
+
// Check for .claude-flow-novice directory references (common broken pattern)
|
|
578
|
+
if (server.command && server.command.includes('.claude-flow-novice')) {
|
|
579
|
+
issues.push('Command points to non-existent .claude-flow-novice directory');
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
if (server.args && server.args.some(arg => arg.includes('.claude-flow-novice'))) {
|
|
583
|
+
issues.push('Arguments reference non-existent .claude-flow-novice directory');
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
// Check if command executable exists (when not using npx)
|
|
587
|
+
if (server.command && server.command !== 'npx' && server.command !== 'node') {
|
|
588
|
+
try {
|
|
589
|
+
execSync(`which ${server.command}`, { stdio: 'ignore' });
|
|
590
|
+
} catch {
|
|
591
|
+
issues.push(`Command '${server.command}' not found in PATH`);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
// Check if node script files exist
|
|
596
|
+
if (server.command === 'node' && server.args && server.args[0]) {
|
|
597
|
+
const scriptPath = path.resolve(server.args[0]);
|
|
598
|
+
if (!existsSync(scriptPath)) {
|
|
599
|
+
issues.push(`Script file '${server.args[0]}' does not exist`);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
// Check for invalid configuration patterns
|
|
604
|
+
if (!server.command) {
|
|
605
|
+
issues.push('Missing command field');
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
if (server.command === 'node' && (!server.args || server.args.length === 0)) {
|
|
609
|
+
issues.push('Node command specified but no script path provided');
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
// Check for outdated patterns
|
|
613
|
+
if (server.name === 'claude-flow-novice' && server.command === 'node') {
|
|
614
|
+
issues.push('Using outdated node-based configuration (should use npx)');
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
return issues;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
/**
|
|
621
|
+
* Calculate issue severity for prioritization
|
|
622
|
+
*/
|
|
623
|
+
calculateIssueSeverity(issues) {
|
|
624
|
+
let severity = 'low';
|
|
625
|
+
|
|
626
|
+
for (const issue of issues) {
|
|
627
|
+
if (issue.includes('not found') || issue.includes('does not exist')) {
|
|
628
|
+
severity = 'critical';
|
|
629
|
+
break;
|
|
630
|
+
} else if (issue.includes('.claude-flow-novice') || issue.includes('outdated')) {
|
|
631
|
+
severity = 'high';
|
|
632
|
+
} else if (severity === 'low') {
|
|
633
|
+
severity = 'medium';
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
return severity;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
generateRecommendations(state) {
|
|
641
|
+
const recommendations = [];
|
|
642
|
+
|
|
643
|
+
if (state.brokenPaths.length > 0) {
|
|
644
|
+
recommendations.push({
|
|
645
|
+
type: 'fix-broken-paths',
|
|
646
|
+
priority: 'high',
|
|
647
|
+
description: 'Remove broken MCP server configurations',
|
|
648
|
+
action: 'Run with --auto-fix to automatically clean up'
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
if (state.conflictingServers.length > 0) {
|
|
653
|
+
recommendations.push({
|
|
654
|
+
type: 'resolve-conflicts',
|
|
655
|
+
priority: 'medium',
|
|
656
|
+
description: 'Resolve configuration conflicts between local and project scopes',
|
|
657
|
+
action: 'Remove from local config to use project-specific settings'
|
|
658
|
+
});
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
return recommendations;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
/**
|
|
665
|
+
* Perform automated remediation of issues
|
|
666
|
+
*/
|
|
667
|
+
async performAutomatedRemediation(audit, state, ux) {
|
|
668
|
+
const remediationResults = [];
|
|
669
|
+
|
|
670
|
+
try {
|
|
671
|
+
// Fix critical issues first (broken paths)
|
|
672
|
+
for (const brokenServer of state.brokenPaths) {
|
|
673
|
+
if (brokenServer.severity === 'critical') {
|
|
674
|
+
const result = await this.fixBrokenServer(brokenServer);
|
|
675
|
+
remediationResults.push(result);
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
// Auto-fix identified issues
|
|
680
|
+
for (const fix of audit.autoFixable) {
|
|
681
|
+
try {
|
|
682
|
+
if (!this.dryRun) {
|
|
683
|
+
await fix.action();
|
|
684
|
+
remediationResults.push({ success: true, description: fix.description });
|
|
685
|
+
this.log(`ā
Fixed: ${fix.description}`, 'success');
|
|
686
|
+
} else {
|
|
687
|
+
this.log(`[DRY RUN] Would fix: ${fix.description}`, 'info');
|
|
688
|
+
}
|
|
689
|
+
} catch (error) {
|
|
690
|
+
const failureResult = { success: false, description: fix.description, error: error.message };
|
|
691
|
+
remediationResults.push(failureResult);
|
|
692
|
+
this.log(`ā Failed to fix: ${fix.description} - ${error.message}`, 'error');
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
// Handle issues requiring confirmation
|
|
697
|
+
for (const fix of audit.requiresConfirmation) {
|
|
698
|
+
if (this.autoFix) {
|
|
699
|
+
try {
|
|
700
|
+
if (!this.dryRun) {
|
|
701
|
+
await fix.action();
|
|
702
|
+
remediationResults.push({ success: true, description: fix.description });
|
|
703
|
+
this.log(`ā
Auto-fixed: ${fix.description}`, 'success');
|
|
704
|
+
}
|
|
705
|
+
} catch (error) {
|
|
706
|
+
remediationResults.push({ success: false, description: fix.description, error: error.message });
|
|
707
|
+
this.log(`ā Failed to auto-fix: ${fix.description} - ${error.message}`, 'error');
|
|
708
|
+
}
|
|
709
|
+
} else if (ux) {
|
|
710
|
+
const confirmed = await ux.promptForConfirmation(fix.description, true);
|
|
711
|
+
if (confirmed && !this.dryRun) {
|
|
712
|
+
await fix.action();
|
|
713
|
+
remediationResults.push({ success: true, description: fix.description });
|
|
714
|
+
this.log(`ā
User-confirmed fix: ${fix.description}`, 'success');
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
const successCount = remediationResults.filter(r => r.success).length;
|
|
720
|
+
const failureCount = remediationResults.filter(r => !r.success).length;
|
|
721
|
+
|
|
722
|
+
return {
|
|
723
|
+
success: failureCount === 0,
|
|
724
|
+
successCount,
|
|
725
|
+
failureCount,
|
|
726
|
+
results: remediationResults
|
|
727
|
+
};
|
|
728
|
+
|
|
729
|
+
} catch (error) {
|
|
730
|
+
return {
|
|
731
|
+
success: false,
|
|
732
|
+
error: error.message,
|
|
733
|
+
results: remediationResults
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
/**
|
|
739
|
+
* Fix broken server configuration
|
|
740
|
+
*/
|
|
741
|
+
async fixBrokenServer(brokenServer) {
|
|
742
|
+
const { serverName, issues, severity } = brokenServer;
|
|
743
|
+
|
|
744
|
+
try {
|
|
745
|
+
if (serverName === 'claude-flow-novice') {
|
|
746
|
+
// Remove broken local configuration
|
|
747
|
+
await this.removeLocalServer(serverName);
|
|
748
|
+
return {
|
|
749
|
+
success: true,
|
|
750
|
+
description: `Removed broken claude-flow-novice configuration`,
|
|
751
|
+
action: 'removed-broken-local'
|
|
752
|
+
};
|
|
753
|
+
} else {
|
|
754
|
+
// For other servers, just remove if critically broken
|
|
755
|
+
if (severity === 'critical') {
|
|
756
|
+
await this.removeLocalServer(serverName);
|
|
757
|
+
return {
|
|
758
|
+
success: true,
|
|
759
|
+
description: `Removed critically broken server: ${serverName}`,
|
|
760
|
+
action: 'removed-broken-server'
|
|
761
|
+
};
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
return { success: true, description: `Server ${serverName} issues noted but not critical` };
|
|
766
|
+
} catch (error) {
|
|
767
|
+
return {
|
|
768
|
+
success: false,
|
|
769
|
+
description: `Failed to fix server ${serverName}`,
|
|
770
|
+
error: error.message
|
|
771
|
+
};
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
/**
|
|
776
|
+
* Comprehensive verification with detailed testing
|
|
777
|
+
*/
|
|
778
|
+
async performComprehensiveVerification() {
|
|
779
|
+
const verificationResults = {
|
|
780
|
+
success: true,
|
|
781
|
+
tests: [],
|
|
782
|
+
warnings: [],
|
|
783
|
+
errors: []
|
|
784
|
+
};
|
|
785
|
+
|
|
786
|
+
try {
|
|
787
|
+
this.log('š§Ŗ Running comprehensive verification tests...', 'info');
|
|
788
|
+
|
|
789
|
+
// Test 1: Claude CLI availability
|
|
790
|
+
const cliTest = await this.testClaudeCli();
|
|
791
|
+
verificationResults.tests.push(cliTest);
|
|
792
|
+
if (!cliTest.passed) {
|
|
793
|
+
verificationResults.success = false;
|
|
794
|
+
verificationResults.errors.push('Claude CLI not available');
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
// Test 2: MCP server listing
|
|
798
|
+
if (cliTest.passed) {
|
|
799
|
+
const listTest = await this.testMcpListing();
|
|
800
|
+
verificationResults.tests.push(listTest);
|
|
801
|
+
if (!listTest.passed) {
|
|
802
|
+
verificationResults.warnings.push('MCP server listing issues detected');
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
// Test 3: Project configuration validation
|
|
807
|
+
const configTest = await this.testProjectConfiguration();
|
|
808
|
+
verificationResults.tests.push(configTest);
|
|
809
|
+
if (!configTest.passed) {
|
|
810
|
+
verificationResults.success = false;
|
|
811
|
+
verificationResults.errors.push('Project configuration invalid');
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
// Test 4: Connection test (if possible)
|
|
815
|
+
if (cliTest.passed && !this.dryRun) {
|
|
816
|
+
const connectionTest = await this.testMcpConnection();
|
|
817
|
+
verificationResults.tests.push(connectionTest);
|
|
818
|
+
if (!connectionTest.passed) {
|
|
819
|
+
verificationResults.warnings.push('MCP connection test failed');
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
// Test 5: File permissions and accessibility
|
|
824
|
+
const permissionTest = await this.testFilePermissions();
|
|
825
|
+
verificationResults.tests.push(permissionTest);
|
|
826
|
+
if (!permissionTest.passed) {
|
|
827
|
+
verificationResults.warnings.push('File permission issues detected');
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
const passedTests = verificationResults.tests.filter(t => t.passed).length;
|
|
831
|
+
const totalTests = verificationResults.tests.length;
|
|
832
|
+
|
|
833
|
+
this.log(`š Verification complete: ${passedTests}/${totalTests} tests passed`,
|
|
834
|
+
passedTests === totalTests ? 'success' : 'warn');
|
|
835
|
+
|
|
836
|
+
return verificationResults;
|
|
837
|
+
|
|
838
|
+
} catch (error) {
|
|
839
|
+
verificationResults.success = false;
|
|
840
|
+
verificationResults.errors.push(`Verification failed: ${error.message}`);
|
|
841
|
+
this.log(`ā Verification failed: ${error.message}`, 'error');
|
|
842
|
+
return verificationResults;
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
async testClaudeCli() {
|
|
847
|
+
try {
|
|
848
|
+
const output = execSync('claude --version', { encoding: 'utf8', stdio: 'pipe' });
|
|
849
|
+
return {
|
|
850
|
+
name: 'Claude CLI Availability',
|
|
851
|
+
passed: true,
|
|
852
|
+
details: `Version: ${output.trim()}`
|
|
853
|
+
};
|
|
854
|
+
} catch (error) {
|
|
855
|
+
return {
|
|
856
|
+
name: 'Claude CLI Availability',
|
|
857
|
+
passed: false,
|
|
858
|
+
details: `Not available: ${error.message}`
|
|
859
|
+
};
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
async testMcpListing() {
|
|
864
|
+
try {
|
|
865
|
+
const output = execSync('claude mcp list', { encoding: 'utf8', stdio: 'pipe', timeout: 10000 });
|
|
866
|
+
const hasClaudeFlowNovice = output.includes('claude-flow-novice');
|
|
867
|
+
return {
|
|
868
|
+
name: 'MCP Server Listing',
|
|
869
|
+
passed: hasClaudeFlowNovice,
|
|
870
|
+
details: hasClaudeFlowNovice ? 'claude-flow-novice found in listing' : 'claude-flow-novice not in listing'
|
|
871
|
+
};
|
|
872
|
+
} catch (error) {
|
|
873
|
+
return {
|
|
874
|
+
name: 'MCP Server Listing',
|
|
875
|
+
passed: false,
|
|
876
|
+
details: `Failed to list servers: ${error.message}`
|
|
877
|
+
};
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
async testProjectConfiguration() {
|
|
882
|
+
try {
|
|
883
|
+
if (!existsSync(this.projectConfigPath)) {
|
|
884
|
+
return {
|
|
885
|
+
name: 'Project Configuration',
|
|
886
|
+
passed: false,
|
|
887
|
+
details: 'Project .mcp.json file does not exist'
|
|
888
|
+
};
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
const config = await this.readProjectConfig();
|
|
892
|
+
if (!config || !config.mcpServers || !config.mcpServers['claude-flow-novice']) {
|
|
893
|
+
return {
|
|
894
|
+
name: 'Project Configuration',
|
|
895
|
+
passed: false,
|
|
896
|
+
details: 'claude-flow-novice not properly configured in project config'
|
|
897
|
+
};
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
return {
|
|
901
|
+
name: 'Project Configuration',
|
|
902
|
+
passed: true,
|
|
903
|
+
details: 'Project configuration valid'
|
|
904
|
+
};
|
|
905
|
+
} catch (error) {
|
|
906
|
+
return {
|
|
907
|
+
name: 'Project Configuration',
|
|
908
|
+
passed: false,
|
|
909
|
+
details: `Configuration test failed: ${error.message}`
|
|
910
|
+
};
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
async testMcpConnection() {
|
|
915
|
+
try {
|
|
916
|
+
const result = await this.runCommandWithTimeout('claude', ['mcp', 'list'], 5000);
|
|
917
|
+
const connected = result.success && result.stdout.includes('ā Connected');
|
|
918
|
+
return {
|
|
919
|
+
name: 'MCP Connection Test',
|
|
920
|
+
passed: connected,
|
|
921
|
+
details: connected ? 'Connection successful' : 'Connection test inconclusive'
|
|
922
|
+
};
|
|
923
|
+
} catch (error) {
|
|
924
|
+
return {
|
|
925
|
+
name: 'MCP Connection Test',
|
|
926
|
+
passed: false,
|
|
927
|
+
details: `Connection test failed: ${error.message}`
|
|
928
|
+
};
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
async testFilePermissions() {
|
|
933
|
+
const tests = [];
|
|
934
|
+
|
|
935
|
+
// Test local config permissions
|
|
936
|
+
try {
|
|
937
|
+
if (existsSync(this.localConfigPath)) {
|
|
938
|
+
await fs.access(this.localConfigPath, fs.constants.R_OK | fs.constants.W_OK);
|
|
939
|
+
tests.push({ file: 'local config', accessible: true });
|
|
940
|
+
}
|
|
941
|
+
} catch {
|
|
942
|
+
tests.push({ file: 'local config', accessible: false });
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
// Test project config permissions
|
|
946
|
+
try {
|
|
947
|
+
if (existsSync(this.projectConfigPath)) {
|
|
948
|
+
await fs.access(this.projectConfigPath, fs.constants.R_OK | fs.constants.W_OK);
|
|
949
|
+
tests.push({ file: 'project config', accessible: true });
|
|
950
|
+
}
|
|
951
|
+
} catch {
|
|
952
|
+
tests.push({ file: 'project config', accessible: false });
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
const allAccessible = tests.every(t => t.accessible);
|
|
956
|
+
return {
|
|
957
|
+
name: 'File Permissions',
|
|
958
|
+
passed: allAccessible,
|
|
959
|
+
details: `${tests.filter(t => t.accessible).length}/${tests.length} files accessible`
|
|
960
|
+
};
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
/**
|
|
964
|
+
* Run command with timeout
|
|
965
|
+
*/
|
|
966
|
+
runCommandWithTimeout(command, args, timeout = 30000) {
|
|
967
|
+
return new Promise((resolve, reject) => {
|
|
968
|
+
const child = spawn(command, args, { stdio: 'pipe' });
|
|
969
|
+
let stdout = '';
|
|
970
|
+
let stderr = '';
|
|
971
|
+
|
|
972
|
+
const timer = setTimeout(() => {
|
|
973
|
+
child.kill('SIGTERM');
|
|
974
|
+
reject(new Error(`Command timed out after ${timeout}ms`));
|
|
975
|
+
}, timeout);
|
|
976
|
+
|
|
977
|
+
child.stdout.on('data', (data) => {
|
|
978
|
+
stdout += data.toString();
|
|
979
|
+
});
|
|
980
|
+
|
|
981
|
+
child.stderr.on('data', (data) => {
|
|
982
|
+
stderr += data.toString();
|
|
983
|
+
});
|
|
984
|
+
|
|
985
|
+
child.on('close', (code) => {
|
|
986
|
+
clearTimeout(timer);
|
|
987
|
+
resolve({
|
|
988
|
+
success: code === 0,
|
|
989
|
+
code,
|
|
990
|
+
stdout,
|
|
991
|
+
stderr
|
|
992
|
+
});
|
|
993
|
+
});
|
|
994
|
+
|
|
995
|
+
child.on('error', (error) => {
|
|
996
|
+
clearTimeout(timer);
|
|
997
|
+
reject(error);
|
|
998
|
+
});
|
|
999
|
+
});
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
/**
|
|
1003
|
+
* Perform complete rollback of all operations
|
|
1004
|
+
*/
|
|
1005
|
+
async performRollback() {
|
|
1006
|
+
this.log('š Performing rollback of all operations...', 'warn');
|
|
1007
|
+
let rollbackCount = 0;
|
|
1008
|
+
|
|
1009
|
+
// Execute rollback operations in reverse order
|
|
1010
|
+
for (let i = this.rollbackStack.length - 1; i >= 0; i--) {
|
|
1011
|
+
const operation = this.rollbackStack[i];
|
|
1012
|
+
try {
|
|
1013
|
+
if (!this.dryRun) {
|
|
1014
|
+
await operation.action();
|
|
1015
|
+
}
|
|
1016
|
+
rollbackCount++;
|
|
1017
|
+
this.log(`š Rolled back: ${operation.type}`, 'info');
|
|
1018
|
+
} catch (error) {
|
|
1019
|
+
this.log(`ā Rollback failed for ${operation.type}: ${error.message}`, 'error');
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
this.log(`š Rollback completed: ${rollbackCount} operations reversed`, 'info');
|
|
1024
|
+
return { success: rollbackCount > 0, operationsRolledBack: rollbackCount };
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
/**
|
|
1028
|
+
* Handle setup failure with comprehensive recovery
|
|
1029
|
+
*/
|
|
1030
|
+
async handleSetupFailure(error, ux) {
|
|
1031
|
+
this.log(`ā Setup failed: ${error.message}`, 'error');
|
|
1032
|
+
|
|
1033
|
+
const recovery = {
|
|
1034
|
+
rollbackPerformed: false,
|
|
1035
|
+
backupsAvailable: this.backupPaths.size > 0,
|
|
1036
|
+
errorAnalysis: this.analyzeError(error),
|
|
1037
|
+
recommendedActions: []
|
|
1038
|
+
};
|
|
1039
|
+
|
|
1040
|
+
// Attempt automatic rollback if we have operations to roll back
|
|
1041
|
+
if (this.rollbackStack.length > 0) {
|
|
1042
|
+
try {
|
|
1043
|
+
const rollbackResult = await this.performRollback();
|
|
1044
|
+
recovery.rollbackPerformed = rollbackResult.success;
|
|
1045
|
+
recovery.operationsRolledBack = rollbackResult.operationsRolledBack;
|
|
1046
|
+
} catch (rollbackError) {
|
|
1047
|
+
this.log(`ā Rollback also failed: ${rollbackError.message}`, 'error');
|
|
1048
|
+
recovery.rollbackError = rollbackError.message;
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
// Generate recovery recommendations
|
|
1053
|
+
recovery.recommendedActions = this.generateRecoveryActions(error, recovery);
|
|
1054
|
+
|
|
1055
|
+
// Display recovery information if UX is available
|
|
1056
|
+
if (ux) {
|
|
1057
|
+
ux.displayErrorRecovery(error, recovery);
|
|
1058
|
+
} else {
|
|
1059
|
+
this.displayBasicRecovery(error, recovery);
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
return recovery;
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
/**
|
|
1066
|
+
* Analyze error for better recovery recommendations
|
|
1067
|
+
*/
|
|
1068
|
+
analyzeError(error) {
|
|
1069
|
+
const analysis = {
|
|
1070
|
+
type: 'unknown',
|
|
1071
|
+
severity: 'medium',
|
|
1072
|
+
recoverable: true,
|
|
1073
|
+
category: 'general'
|
|
1074
|
+
};
|
|
1075
|
+
|
|
1076
|
+
const message = error.message.toLowerCase();
|
|
1077
|
+
|
|
1078
|
+
if (message.includes('permission denied') || message.includes('eacces')) {
|
|
1079
|
+
analysis.type = 'permission';
|
|
1080
|
+
analysis.severity = 'high';
|
|
1081
|
+
analysis.category = 'filesystem';
|
|
1082
|
+
} else if (message.includes('not found') || message.includes('enoent')) {
|
|
1083
|
+
analysis.type = 'missing-file';
|
|
1084
|
+
analysis.severity = 'high';
|
|
1085
|
+
analysis.category = 'filesystem';
|
|
1086
|
+
} else if (message.includes('claude') && message.includes('not installed')) {
|
|
1087
|
+
analysis.type = 'missing-dependency';
|
|
1088
|
+
analysis.severity = 'critical';
|
|
1089
|
+
analysis.category = 'environment';
|
|
1090
|
+
analysis.recoverable = false;
|
|
1091
|
+
} else if (message.includes('timeout') || message.includes('timed out')) {
|
|
1092
|
+
analysis.type = 'timeout';
|
|
1093
|
+
analysis.severity = 'medium';
|
|
1094
|
+
analysis.category = 'network';
|
|
1095
|
+
} else if (message.includes('json') || message.includes('parse')) {
|
|
1096
|
+
analysis.type = 'config-corruption';
|
|
1097
|
+
analysis.severity = 'high';
|
|
1098
|
+
analysis.category = 'configuration';
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
return analysis;
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
/**
|
|
1105
|
+
* Generate specific recovery actions based on error analysis
|
|
1106
|
+
*/
|
|
1107
|
+
generateRecoveryActions(error, recovery) {
|
|
1108
|
+
const actions = [];
|
|
1109
|
+
const analysis = recovery.errorAnalysis;
|
|
1110
|
+
|
|
1111
|
+
switch (analysis.type) {
|
|
1112
|
+
case 'permission':
|
|
1113
|
+
actions.push('Check file permissions on configuration files');
|
|
1114
|
+
actions.push('Run with appropriate user privileges');
|
|
1115
|
+
actions.push('Ensure home directory is accessible');
|
|
1116
|
+
break;
|
|
1117
|
+
|
|
1118
|
+
case 'missing-file':
|
|
1119
|
+
actions.push('Verify Claude Code installation');
|
|
1120
|
+
actions.push('Check if configuration files exist');
|
|
1121
|
+
actions.push('Re-run initialization if needed');
|
|
1122
|
+
break;
|
|
1123
|
+
|
|
1124
|
+
case 'missing-dependency':
|
|
1125
|
+
actions.push('Install Claude Code: npm install -g @anthropic-ai/claude-code');
|
|
1126
|
+
actions.push('Verify installation: claude --version');
|
|
1127
|
+
actions.push('Re-run setup after installation');
|
|
1128
|
+
break;
|
|
1129
|
+
|
|
1130
|
+
case 'timeout':
|
|
1131
|
+
actions.push('Check network connectivity');
|
|
1132
|
+
actions.push('Retry with increased timeout');
|
|
1133
|
+
actions.push('Check if Claude Code service is running');
|
|
1134
|
+
break;
|
|
1135
|
+
|
|
1136
|
+
case 'config-corruption':
|
|
1137
|
+
if (recovery.backupsAvailable) {
|
|
1138
|
+
actions.push('Restore from automatic backup');
|
|
1139
|
+
actions.push('Validate configuration file syntax');
|
|
1140
|
+
}
|
|
1141
|
+
actions.push('Remove corrupted configuration and reinitialize');
|
|
1142
|
+
break;
|
|
1143
|
+
|
|
1144
|
+
default:
|
|
1145
|
+
actions.push('Check the error log for specific details');
|
|
1146
|
+
actions.push('Try running with --verbose for more information');
|
|
1147
|
+
if (recovery.backupsAvailable) {
|
|
1148
|
+
actions.push('Restore from backup if needed');
|
|
1149
|
+
}
|
|
1150
|
+
actions.push('Contact support if the issue persists');
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
return actions;
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
/**
|
|
1157
|
+
* Display basic recovery information when UX module isn't available
|
|
1158
|
+
*/
|
|
1159
|
+
displayBasicRecovery(error, recovery) {
|
|
1160
|
+
console.log('\nā Setup Failed - Recovery Information:');
|
|
1161
|
+
console.log(` Error: ${error.message}`);
|
|
1162
|
+
console.log(` Type: ${recovery.errorAnalysis.type}`);
|
|
1163
|
+
console.log(` Severity: ${recovery.errorAnalysis.severity}`);
|
|
1164
|
+
|
|
1165
|
+
if (recovery.rollbackPerformed) {
|
|
1166
|
+
console.log(`\nš Automatic rollback completed (${recovery.operationsRolledBack} operations)`);
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
if (recovery.backupsAvailable) {
|
|
1170
|
+
console.log(`\nš¾ Backups available for manual recovery`);
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
console.log('\nš ļø Recommended actions:');
|
|
1174
|
+
recovery.recommendedActions.forEach((action, i) => {
|
|
1175
|
+
console.log(` ${i + 1}. ${action}`);
|
|
1176
|
+
});
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
/**
|
|
1180
|
+
* Generate comprehensive setup summary
|
|
1181
|
+
*/
|
|
1182
|
+
generateSetupSummary() {
|
|
1183
|
+
return {
|
|
1184
|
+
operationsPerformed: this.operationLog.length,
|
|
1185
|
+
backupsCreated: this.backupPaths.size,
|
|
1186
|
+
rollbackOperationsAvailable: this.rollbackStack.length,
|
|
1187
|
+
configurationPaths: {
|
|
1188
|
+
local: this.localConfigPath,
|
|
1189
|
+
project: this.projectConfigPath
|
|
1190
|
+
},
|
|
1191
|
+
timestamp: new Date().toISOString()
|
|
1192
|
+
};
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
/**
|
|
1197
|
+
* Enhanced MCP initialization with bulletproof error handling and recovery
|
|
1198
|
+
*/
|
|
1199
|
+
export async function enhancedMcpInit(options = {}) {
|
|
1200
|
+
const manager = new McpConfigurationManager({
|
|
1201
|
+
verbose: options.verbose || false,
|
|
1202
|
+
autoFix: options.autoFix !== false, // Default to true for bulletproof operation
|
|
1203
|
+
dryRun: options.dryRun || false
|
|
1204
|
+
});
|
|
1205
|
+
|
|
1206
|
+
console.log('š Starting enhanced MCP initialization...', options.dryRun ? ' (DRY RUN)' : '');
|
|
1207
|
+
|
|
1208
|
+
try {
|
|
1209
|
+
// Initialize UX module if available and requested
|
|
1210
|
+
let ux = null;
|
|
1211
|
+
if (options.enhancedUx !== false) {
|
|
1212
|
+
try {
|
|
1213
|
+
const { McpUserExperience } = await import('../cli/mcp-user-experience.js');
|
|
1214
|
+
ux = new McpUserExperience({
|
|
1215
|
+
verbose: options.verbose,
|
|
1216
|
+
interactive: options.interactive !== false
|
|
1217
|
+
});
|
|
1218
|
+
|
|
1219
|
+
// Show educational content for first-time users
|
|
1220
|
+
if (options.showEducation) {
|
|
1221
|
+
ux.displayMcpEducation();
|
|
1222
|
+
}
|
|
1223
|
+
} catch (uxError) {
|
|
1224
|
+
manager.log(`ā ļø UX module not available, using basic output: ${uxError.message}`, 'warn');
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
// Comprehensive state detection
|
|
1229
|
+
const state = await manager.detectConfigurationState();
|
|
1230
|
+
|
|
1231
|
+
// Display state information
|
|
1232
|
+
if (options.verbose || state.healthScore < 80) {
|
|
1233
|
+
console.log('\nš MCP Configuration Analysis:');
|
|
1234
|
+
console.log(` Health Score: ${state.healthScore}/100 ${state.healthScore >= 80 ? 'ā
' : state.healthScore >= 60 ? 'ā ļø' : 'ā'}`);
|
|
1235
|
+
console.log(` Claude Code: ${state.claudeCodeInstalled ? 'ā
Installed' : 'ā Not installed'}`);
|
|
1236
|
+
console.log(` Local config: ${state.hasLocalConfig ? 'ā
Found' : 'ā Not found'}`);
|
|
1237
|
+
console.log(` Project config: ${state.hasProjectConfig ? 'ā
Found' : 'ā Not found'}`);
|
|
1238
|
+
console.log(` Servers: ${state.localServers.length} local, ${state.projectServers.length} project`);
|
|
1239
|
+
console.log(` Issues: ${state.conflictingServers.length} conflicts, ${state.brokenPaths.length} broken paths`);
|
|
1240
|
+
|
|
1241
|
+
if (state.criticalIssues.length > 0) {
|
|
1242
|
+
console.log('\nā Critical Issues:');
|
|
1243
|
+
state.criticalIssues.forEach(issue => console.log(` ⢠${issue}`));
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
if (state.warnings.length > 0) {
|
|
1247
|
+
console.log('\nā ļø Warnings:');
|
|
1248
|
+
state.warnings.forEach(warning => console.log(` ⢠${warning}`));
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
// Execute bulletproof setup
|
|
1253
|
+
const result = await manager.executeBulletproofSetup(options.serverConfig, ux);
|
|
1254
|
+
|
|
1255
|
+
// Enhanced success reporting
|
|
1256
|
+
if (result.success) {
|
|
1257
|
+
console.log('\nš Enhanced MCP initialization completed successfully!');
|
|
1258
|
+
console.log(` Duration: ${result.duration}ms`);
|
|
1259
|
+
console.log(` Issues Fixed: ${result.details.issuesFixed}`);
|
|
1260
|
+
console.log(` Health Score: ${result.details.healthScore}/100`);
|
|
1261
|
+
console.log(` Operations: ${result.details.operationsPerformed}`);
|
|
1262
|
+
|
|
1263
|
+
if (result.details.backupsCreated > 0) {
|
|
1264
|
+
console.log(` Backups Created: ${result.details.backupsCreated}`);
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
// Show success summary if UX is available
|
|
1268
|
+
if (ux) {
|
|
1269
|
+
ux.displaySuccessSummary(result.details);
|
|
1270
|
+
}
|
|
1271
|
+
} else {
|
|
1272
|
+
console.log('\nā Enhanced MCP initialization failed');
|
|
1273
|
+
if (result.rollbackAvailable) {
|
|
1274
|
+
console.log(' š Rollback operations are available');
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
return result;
|
|
1279
|
+
|
|
1280
|
+
} catch (error) {
|
|
1281
|
+
console.log(`\nā Enhanced MCP init failed: ${error.message}`);
|
|
1282
|
+
|
|
1283
|
+
// Provide error context
|
|
1284
|
+
if (error.recovery) {
|
|
1285
|
+
console.log('\nš ļø Recovery suggestions:');
|
|
1286
|
+
error.recovery.forEach((suggestion, i) => {
|
|
1287
|
+
console.log(` ${i + 1}. ${suggestion}`);
|
|
1288
|
+
});
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
return {
|
|
1292
|
+
success: false,
|
|
1293
|
+
error: error.message,
|
|
1294
|
+
recovery: error.recovery || [],
|
|
1295
|
+
logs: manager.operationLog
|
|
1296
|
+
};
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
/**
|
|
1301
|
+
* Quick MCP health check for troubleshooting
|
|
1302
|
+
*/
|
|
1303
|
+
export async function quickMcpHealthCheck(options = {}) {
|
|
1304
|
+
const manager = new McpConfigurationManager({
|
|
1305
|
+
verbose: options.verbose || false,
|
|
1306
|
+
dryRun: true // Health check is always read-only
|
|
1307
|
+
});
|
|
1308
|
+
|
|
1309
|
+
console.log('š„ Quick MCP health check...');
|
|
1310
|
+
|
|
1311
|
+
try {
|
|
1312
|
+
const state = await manager.detectConfigurationState();
|
|
1313
|
+
const verification = await manager.performComprehensiveVerification();
|
|
1314
|
+
|
|
1315
|
+
console.log('\nš Health Report:');
|
|
1316
|
+
console.log(` Overall Health: ${state.healthScore}/100 ${state.healthScore >= 80 ? 'ā
' : state.healthScore >= 60 ? 'ā ļø' : 'ā'}`);
|
|
1317
|
+
console.log(` Claude Code: ${state.claudeCodeInstalled ? 'ā
' : 'ā'}`);
|
|
1318
|
+
console.log(` Configuration: ${state.hasLocalConfig || state.hasProjectConfig ? 'ā
' : 'ā'}`);
|
|
1319
|
+
console.log(` Verification: ${verification.tests.filter(t => t.passed).length}/${verification.tests.length} tests passed`);
|
|
1320
|
+
|
|
1321
|
+
if (state.brokenPaths.length > 0) {
|
|
1322
|
+
console.log('\nā Broken Configurations:');
|
|
1323
|
+
state.brokenPaths.forEach(broken => {
|
|
1324
|
+
console.log(` ⢠${broken.serverName}: ${broken.issues.join(', ')}`);
|
|
1325
|
+
});
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
if (state.conflictingServers.length > 0) {
|
|
1329
|
+
console.log('\nā ļø Configuration Conflicts:');
|
|
1330
|
+
state.conflictingServers.forEach(conflict => {
|
|
1331
|
+
console.log(` ⢠${conflict.serverName}: Local vs Project configuration`);
|
|
1332
|
+
});
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
console.log('\nš ļø Recommendations:');
|
|
1336
|
+
if (state.recommendations.length > 0) {
|
|
1337
|
+
state.recommendations.forEach(rec => {
|
|
1338
|
+
console.log(` ⢠${rec.description}`);
|
|
1339
|
+
});
|
|
1340
|
+
} else {
|
|
1341
|
+
console.log(' ⢠No immediate action required');
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
return {
|
|
1345
|
+
healthy: state.healthScore >= 80 && verification.success,
|
|
1346
|
+
healthScore: state.healthScore,
|
|
1347
|
+
state,
|
|
1348
|
+
verification,
|
|
1349
|
+
needsAttention: state.healthScore < 80 || state.brokenPaths.length > 0
|
|
1350
|
+
};
|
|
1351
|
+
|
|
1352
|
+
} catch (error) {
|
|
1353
|
+
console.log(`\nā Health check failed: ${error.message}`);
|
|
1354
|
+
return {
|
|
1355
|
+
healthy: false,
|
|
1356
|
+
error: error.message,
|
|
1357
|
+
needsAttention: true
|
|
1358
|
+
};
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1362
|
+
export default McpConfigurationManager;
|