start-vibing 2.0.10 → 2.0.12

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 (130) hide show
  1. package/README.md +177 -177
  2. package/package.json +42 -42
  3. package/template/.claude/CLAUDE.md +174 -174
  4. package/template/.claude/agents/01-orchestration/agent-selector.md +130 -130
  5. package/template/.claude/agents/01-orchestration/checkpoint-manager.md +142 -142
  6. package/template/.claude/agents/01-orchestration/context-manager.md +138 -138
  7. package/template/.claude/agents/01-orchestration/error-recovery.md +182 -182
  8. package/template/.claude/agents/01-orchestration/orchestrator.md +114 -114
  9. package/template/.claude/agents/01-orchestration/parallel-coordinator.md +141 -141
  10. package/template/.claude/agents/01-orchestration/task-decomposer.md +121 -121
  11. package/template/.claude/agents/01-orchestration/workflow-router.md +114 -114
  12. package/template/.claude/agents/02-typescript/bun-runtime-expert.md +197 -197
  13. package/template/.claude/agents/02-typescript/esm-resolver.md +193 -193
  14. package/template/.claude/agents/02-typescript/import-alias-enforcer.md +158 -158
  15. package/template/.claude/agents/02-typescript/ts-generics-helper.md +183 -183
  16. package/template/.claude/agents/02-typescript/ts-migration-helper.md +238 -238
  17. package/template/.claude/agents/02-typescript/ts-strict-checker.md +180 -180
  18. package/template/.claude/agents/02-typescript/ts-types-analyzer.md +199 -199
  19. package/template/.claude/agents/02-typescript/type-definition-writer.md +187 -187
  20. package/template/.claude/agents/02-typescript/zod-schema-designer.md +212 -212
  21. package/template/.claude/agents/02-typescript/zod-validator.md +158 -158
  22. package/template/.claude/agents/03-testing/playwright-assertions.md +265 -265
  23. package/template/.claude/agents/03-testing/playwright-e2e.md +247 -247
  24. package/template/.claude/agents/03-testing/playwright-fixtures.md +234 -234
  25. package/template/.claude/agents/03-testing/playwright-multi-viewport.md +256 -256
  26. package/template/.claude/agents/03-testing/playwright-page-objects.md +247 -247
  27. package/template/.claude/agents/03-testing/test-cleanup-manager.md +248 -248
  28. package/template/.claude/agents/03-testing/test-data-generator.md +254 -254
  29. package/template/.claude/agents/03-testing/tester-integration.md +278 -278
  30. package/template/.claude/agents/03-testing/tester-unit.md +207 -207
  31. package/template/.claude/agents/03-testing/vitest-config.md +287 -287
  32. package/template/.claude/agents/04-docker/container-health.md +255 -255
  33. package/template/.claude/agents/04-docker/deployment-validator.md +225 -225
  34. package/template/.claude/agents/04-docker/docker-compose-designer.md +281 -281
  35. package/template/.claude/agents/04-docker/docker-env-manager.md +235 -235
  36. package/template/.claude/agents/04-docker/docker-multi-stage.md +241 -241
  37. package/template/.claude/agents/04-docker/dockerfile-optimizer.md +208 -208
  38. package/template/.claude/agents/05-database/database-seeder.md +273 -273
  39. package/template/.claude/agents/05-database/mongodb-query-optimizer.md +230 -230
  40. package/template/.claude/agents/05-database/mongoose-aggregation.md +306 -306
  41. package/template/.claude/agents/05-database/mongoose-index-optimizer.md +182 -182
  42. package/template/.claude/agents/05-database/mongoose-schema-designer.md +267 -267
  43. package/template/.claude/agents/06-security/auth-session-validator.md +68 -68
  44. package/template/.claude/agents/06-security/input-sanitizer.md +80 -80
  45. package/template/.claude/agents/06-security/owasp-checker.md +97 -97
  46. package/template/.claude/agents/06-security/permission-auditor.md +100 -100
  47. package/template/.claude/agents/06-security/security-auditor.md +84 -84
  48. package/template/.claude/agents/06-security/sensitive-data-scanner.md +83 -83
  49. package/template/.claude/agents/07-documentation/api-documenter.md +136 -136
  50. package/template/.claude/agents/07-documentation/changelog-manager.md +105 -105
  51. package/template/.claude/agents/07-documentation/documenter.md +76 -76
  52. package/template/.claude/agents/07-documentation/domain-updater.md +81 -81
  53. package/template/.claude/agents/07-documentation/jsdoc-generator.md +114 -114
  54. package/template/.claude/agents/07-documentation/readme-generator.md +135 -135
  55. package/template/.claude/agents/08-git/branch-manager.md +58 -58
  56. package/template/.claude/agents/08-git/commit-manager.md +63 -63
  57. package/template/.claude/agents/08-git/pr-creator.md +76 -76
  58. package/template/.claude/agents/09-quality/code-reviewer.md +71 -71
  59. package/template/.claude/agents/09-quality/quality-checker.md +67 -67
  60. package/template/.claude/agents/10-research/best-practices-finder.md +89 -89
  61. package/template/.claude/agents/10-research/competitor-analyzer.md +106 -106
  62. package/template/.claude/agents/10-research/pattern-researcher.md +93 -93
  63. package/template/.claude/agents/10-research/research-cache-manager.md +76 -76
  64. package/template/.claude/agents/10-research/research-web.md +98 -98
  65. package/template/.claude/agents/10-research/tech-evaluator.md +101 -101
  66. package/template/.claude/agents/11-ui-ux/accessibility-auditor.md +136 -136
  67. package/template/.claude/agents/11-ui-ux/design-system-enforcer.md +125 -125
  68. package/template/.claude/agents/11-ui-ux/skeleton-generator.md +118 -118
  69. package/template/.claude/agents/11-ui-ux/ui-desktop.md +132 -132
  70. package/template/.claude/agents/11-ui-ux/ui-mobile.md +98 -98
  71. package/template/.claude/agents/11-ui-ux/ui-tablet.md +110 -110
  72. package/template/.claude/agents/12-performance/api-latency-analyzer.md +156 -156
  73. package/template/.claude/agents/12-performance/bundle-analyzer.md +113 -113
  74. package/template/.claude/agents/12-performance/memory-leak-detector.md +137 -137
  75. package/template/.claude/agents/12-performance/performance-profiler.md +115 -115
  76. package/template/.claude/agents/12-performance/query-optimizer.md +124 -124
  77. package/template/.claude/agents/12-performance/render-optimizer.md +154 -154
  78. package/template/.claude/agents/13-debugging/build-error-fixer.md +207 -207
  79. package/template/.claude/agents/13-debugging/debugger.md +149 -149
  80. package/template/.claude/agents/13-debugging/error-stack-analyzer.md +141 -141
  81. package/template/.claude/agents/13-debugging/network-debugger.md +208 -208
  82. package/template/.claude/agents/13-debugging/runtime-error-fixer.md +181 -181
  83. package/template/.claude/agents/13-debugging/type-error-resolver.md +185 -185
  84. package/template/.claude/agents/14-validation/final-validator.md +93 -93
  85. package/template/.claude/agents/_backup/analyzer.md +134 -134
  86. package/template/.claude/agents/_backup/code-reviewer.md +279 -279
  87. package/template/.claude/agents/_backup/commit-manager.md +219 -219
  88. package/template/.claude/agents/_backup/debugger.md +280 -280
  89. package/template/.claude/agents/_backup/documenter.md +237 -237
  90. package/template/.claude/agents/_backup/domain-updater.md +197 -197
  91. package/template/.claude/agents/_backup/final-validator.md +169 -169
  92. package/template/.claude/agents/_backup/orchestrator.md +149 -149
  93. package/template/.claude/agents/_backup/performance.md +232 -232
  94. package/template/.claude/agents/_backup/quality-checker.md +240 -240
  95. package/template/.claude/agents/_backup/research.md +315 -315
  96. package/template/.claude/agents/_backup/security-auditor.md +192 -192
  97. package/template/.claude/agents/_backup/tester.md +566 -566
  98. package/template/.claude/agents/_backup/ui-ux-reviewer.md +247 -247
  99. package/template/.claude/config/README.md +30 -30
  100. package/template/.claude/config/mcp-config.json +344 -344
  101. package/template/.claude/config/project-config.json +53 -53
  102. package/template/.claude/config/quality-gates.json +46 -46
  103. package/template/.claude/config/security-rules.json +45 -45
  104. package/template/.claude/config/testing-config.json +164 -164
  105. package/template/.claude/hooks/SETUP.md +126 -126
  106. package/template/.claude/hooks/run-hook.ts +176 -176
  107. package/template/.claude/hooks/stop-validator.ts +852 -825
  108. package/template/.claude/hooks/user-prompt-submit.ts +886 -886
  109. package/template/.claude/scripts/mcp-quick-install.ts +151 -151
  110. package/template/.claude/scripts/setup-mcps.ts +651 -651
  111. package/template/.claude/settings.json +275 -275
  112. package/template/.claude/skills/bun-runtime/SKILL.md +430 -430
  113. package/template/.claude/skills/codebase-knowledge/domains/claude-system.md +431 -431
  114. package/template/.claude/skills/codebase-knowledge/domains/mcp-integration.md +295 -295
  115. package/template/.claude/skills/debugging-patterns/SKILL.md +485 -485
  116. package/template/.claude/skills/docker-patterns/SKILL.md +555 -555
  117. package/template/.claude/skills/git-workflow/SKILL.md +454 -454
  118. package/template/.claude/skills/mongoose-patterns/SKILL.md +499 -499
  119. package/template/.claude/skills/nextjs-app-router/SKILL.md +327 -327
  120. package/template/.claude/skills/performance-patterns/SKILL.md +547 -547
  121. package/template/.claude/skills/playwright-automation/SKILL.md +438 -438
  122. package/template/.claude/skills/react-patterns/SKILL.md +389 -389
  123. package/template/.claude/skills/research-cache/SKILL.md +222 -222
  124. package/template/.claude/skills/shadcn-ui/SKILL.md +511 -511
  125. package/template/.claude/skills/tailwind-patterns/SKILL.md +465 -465
  126. package/template/.claude/skills/test-coverage/SKILL.md +467 -467
  127. package/template/.claude/skills/trpc-api/SKILL.md +434 -434
  128. package/template/.claude/skills/typescript-strict/SKILL.md +367 -367
  129. package/template/.claude/skills/zod-validation/SKILL.md +403 -403
  130. package/template/CLAUDE.md +117 -117
@@ -1,651 +1,651 @@
1
- #!/usr/bin/env bun
2
- /**
3
- * MCP Auto-Installer for Claude Code
4
- *
5
- * This script automatically installs and configures recommended MCP servers
6
- * based on the project's agent/skill architecture.
7
- *
8
- * Features:
9
- * - Parallel installation for speed
10
- * - Progress tracking with visual feedback
11
- * - Security validation before installation
12
- * - Automatic .mcp.json generation
13
- * - Environment variable setup guidance
14
- *
15
- * Usage:
16
- * bun .claude/scripts/setup-mcps.ts [options]
17
- *
18
- * Options:
19
- * --tier=core|productivity|infrastructure|all Install specific tier (default: core)
20
- * --dry-run Show what would be installed
21
- * --force Reinstall even if already configured
22
- * --interactive Prompt for each server
23
- */
24
-
25
- import { spawn, spawnSync } from 'child_process';
26
- import { existsSync, readFileSync, writeFileSync } from 'fs';
27
- import { join, dirname } from 'path';
28
- import { fileURLToPath } from 'url';
29
-
30
- // Types
31
- interface MCPServerConfig {
32
- name: string;
33
- description: string;
34
- tier: 'core' | 'productivity' | 'infrastructure';
35
- verified: boolean;
36
- publisher: string;
37
- repository: string;
38
- transport: 'stdio' | 'http';
39
- config: {
40
- command?: string;
41
- args?: string[];
42
- url?: string;
43
- options?: Record<string, unknown>;
44
- remote?: { url: string };
45
- local?: { command: string; args: string[] };
46
- };
47
- envVars: string[];
48
- requiredPermissions: string[];
49
- agentMappings: string[];
50
- securityNotes: string;
51
- }
52
-
53
- interface MCPConfig {
54
- metadata: {
55
- version: string;
56
- lastUpdated: string;
57
- researchSources: string[];
58
- };
59
- tiers: Record<string, { description: string; servers: string[] }>;
60
- servers: Record<string, MCPServerConfig>;
61
- security: {
62
- guidelines: string[];
63
- redFlags: string[];
64
- trustedPublishers: string[];
65
- };
66
- installation: {
67
- parallelLimit: number;
68
- timeout: number;
69
- retryAttempts: number;
70
- scope: string;
71
- };
72
- }
73
-
74
- interface InstallResult {
75
- server: string;
76
- success: boolean;
77
- message: string;
78
- duration: number;
79
- }
80
-
81
- interface MCPJsonEntry {
82
- command?: string;
83
- args?: string[];
84
- url?: string;
85
- env?: Record<string, string>;
86
- }
87
-
88
- // Console colors (ANSI escape codes)
89
- const colors = {
90
- reset: '\x1b[0m',
91
- bright: '\x1b[1m',
92
- dim: '\x1b[2m',
93
- red: '\x1b[31m',
94
- green: '\x1b[32m',
95
- yellow: '\x1b[33m',
96
- blue: '\x1b[34m',
97
- magenta: '\x1b[35m',
98
- cyan: '\x1b[36m',
99
- white: '\x1b[37m',
100
- bgGreen: '\x1b[42m',
101
- bgRed: '\x1b[41m',
102
- bgYellow: '\x1b[43m',
103
- };
104
-
105
- // Get script directory
106
- const getScriptDir = (): string => {
107
- try {
108
- if (typeof import.meta.url !== 'undefined') {
109
- return dirname(fileURLToPath(import.meta.url));
110
- }
111
- } catch {
112
- // Fallback
113
- }
114
- return process.cwd();
115
- };
116
-
117
- const SCRIPT_DIR = getScriptDir();
118
- const PROJECT_ROOT = join(SCRIPT_DIR, '..', '..');
119
- const CONFIG_PATH = join(SCRIPT_DIR, '..', 'config', 'mcp-config.json');
120
- const MCP_JSON_PATH = join(PROJECT_ROOT, '.mcp.json');
121
-
122
- // Utility functions
123
- function log(message: string, type: 'info' | 'success' | 'warning' | 'error' = 'info'): void {
124
- const prefix = {
125
- info: `${colors.blue}[INFO]${colors.reset}`,
126
- success: `${colors.green}[OK]${colors.reset}`,
127
- warning: `${colors.yellow}[WARN]${colors.reset}`,
128
- error: `${colors.red}[ERROR]${colors.reset}`,
129
- };
130
- console.log(`${prefix[type]} ${message}`);
131
- }
132
-
133
- function header(text: string): void {
134
- const line = '='.repeat(60);
135
- console.log(`\n${colors.cyan}${line}${colors.reset}`);
136
- console.log(`${colors.bright}${colors.cyan} ${text}${colors.reset}`);
137
- console.log(`${colors.cyan}${line}${colors.reset}\n`);
138
- }
139
-
140
- function subheader(text: string): void {
141
- console.log(`\n${colors.magenta}>> ${text}${colors.reset}`);
142
- }
143
-
144
- function progressBar(current: number, total: number, width = 40): string {
145
- const percent = Math.round((current / total) * 100);
146
- const filled = Math.round((current / total) * width);
147
- const empty = width - filled;
148
- const bar = `[${'█'.repeat(filled)}${'░'.repeat(empty)}]`;
149
- return `${bar} ${percent}% (${current}/${total})`;
150
- }
151
-
152
- // Load configuration
153
- function loadConfig(): MCPConfig {
154
- if (!existsSync(CONFIG_PATH)) {
155
- throw new Error(`MCP config not found at: ${CONFIG_PATH}`);
156
- }
157
- const content = readFileSync(CONFIG_PATH, 'utf8');
158
- return JSON.parse(content) as MCPConfig;
159
- }
160
-
161
- // Load existing .mcp.json
162
- function loadMcpJson(): Record<string, MCPJsonEntry> {
163
- if (!existsSync(MCP_JSON_PATH)) {
164
- return {};
165
- }
166
- try {
167
- const content = readFileSync(MCP_JSON_PATH, 'utf8');
168
- return JSON.parse(content) as Record<string, MCPJsonEntry>;
169
- } catch {
170
- return {};
171
- }
172
- }
173
-
174
- // Save .mcp.json
175
- function saveMcpJson(config: Record<string, MCPJsonEntry>): void {
176
- writeFileSync(MCP_JSON_PATH, JSON.stringify(config, null, 2));
177
- }
178
-
179
- // Check if a command exists
180
- function commandExists(cmd: string): boolean {
181
- try {
182
- const result = spawnSync(process.platform === 'win32' ? 'where' : 'which', [cmd], {
183
- stdio: 'pipe',
184
- shell: true,
185
- });
186
- return result.status === 0;
187
- } catch {
188
- return false;
189
- }
190
- }
191
-
192
- // Validate security
193
- function validateSecurity(
194
- server: MCPServerConfig,
195
- config: MCPConfig
196
- ): { valid: boolean; warnings: string[] } {
197
- const warnings: string[] = [];
198
-
199
- // Check if publisher is trusted
200
- if (!config.security.trustedPublishers.includes(server.publisher)) {
201
- warnings.push(`Publisher '${server.publisher}' is not in trusted list`);
202
- }
203
-
204
- // Check if verified
205
- if (!server.verified) {
206
- warnings.push('Server is not verified');
207
- }
208
-
209
- // Check for missing repository
210
- if (!server.repository) {
211
- warnings.push('No repository URL provided');
212
- }
213
-
214
- return {
215
- valid: warnings.length === 0,
216
- warnings,
217
- };
218
- }
219
-
220
- // Install a single MCP server using claude mcp add
221
- async function installServer(
222
- serverId: string,
223
- server: MCPServerConfig,
224
- scope: string,
225
- dryRun: boolean
226
- ): Promise<InstallResult> {
227
- const startTime = Date.now();
228
-
229
- try {
230
- // Check if claude CLI is available
231
- if (!commandExists('claude')) {
232
- return {
233
- server: serverId,
234
- success: false,
235
- message: 'Claude CLI not found. Please install Claude Code first.',
236
- duration: Date.now() - startTime,
237
- };
238
- }
239
-
240
- // Build the command based on transport type
241
- let args: string[];
242
-
243
- if (server.transport === 'http' && server.config.url) {
244
- // Remote HTTP server
245
- args = ['mcp', 'add', '--transport', 'http', '-s', scope, serverId, server.config.url];
246
- } else if (server.transport === 'http' && server.config.remote?.url) {
247
- // Remote server with remote config
248
- args = [
249
- 'mcp',
250
- 'add',
251
- '--transport',
252
- 'http',
253
- '-s',
254
- scope,
255
- serverId,
256
- server.config.remote.url,
257
- ];
258
- } else if (server.config.command && server.config.args) {
259
- // Local stdio server
260
- args = [
261
- 'mcp',
262
- 'add',
263
- '-s',
264
- scope,
265
- serverId,
266
- '--',
267
- server.config.command,
268
- ...server.config.args,
269
- ];
270
- } else {
271
- return {
272
- server: serverId,
273
- success: false,
274
- message: 'Invalid server configuration',
275
- duration: Date.now() - startTime,
276
- };
277
- }
278
-
279
- if (dryRun) {
280
- return {
281
- server: serverId,
282
- success: true,
283
- message: `Would run: claude ${args.join(' ')}`,
284
- duration: Date.now() - startTime,
285
- };
286
- }
287
-
288
- // Execute the command
289
- return new Promise((resolve) => {
290
- const proc = spawn('claude', args, {
291
- shell: true,
292
- stdio: 'pipe',
293
- });
294
-
295
- let stdout = '';
296
- let stderr = '';
297
-
298
- proc.stdout?.on('data', (data) => {
299
- stdout += data.toString();
300
- });
301
-
302
- proc.stderr?.on('data', (data) => {
303
- stderr += data.toString();
304
- });
305
-
306
- proc.on('close', (code) => {
307
- const duration = Date.now() - startTime;
308
- if (code === 0) {
309
- resolve({
310
- server: serverId,
311
- success: true,
312
- message: 'Installed successfully',
313
- duration,
314
- });
315
- } else {
316
- resolve({
317
- server: serverId,
318
- success: false,
319
- message: stderr || stdout || `Exit code: ${code}`,
320
- duration,
321
- });
322
- }
323
- });
324
-
325
- proc.on('error', (err) => {
326
- resolve({
327
- server: serverId,
328
- success: false,
329
- message: err.message,
330
- duration: Date.now() - startTime,
331
- });
332
- });
333
-
334
- // Timeout
335
- setTimeout(() => {
336
- proc.kill();
337
- resolve({
338
- server: serverId,
339
- success: false,
340
- message: 'Installation timed out',
341
- duration: Date.now() - startTime,
342
- });
343
- }, 60000);
344
- });
345
- } catch (err) {
346
- return {
347
- server: serverId,
348
- success: false,
349
- message: err instanceof Error ? err.message : 'Unknown error',
350
- duration: Date.now() - startTime,
351
- };
352
- }
353
- }
354
-
355
- // Install servers in parallel with limit
356
- async function installServersParallel(
357
- servers: Array<{ id: string; config: MCPServerConfig }>,
358
- scope: string,
359
- parallelLimit: number,
360
- dryRun: boolean
361
- ): Promise<InstallResult[]> {
362
- const results: InstallResult[] = [];
363
- let completed = 0;
364
-
365
- // Process in batches
366
- for (let i = 0; i < servers.length; i += parallelLimit) {
367
- const batch = servers.slice(i, i + parallelLimit);
368
-
369
- const batchResults = await Promise.all(
370
- batch.map(async ({ id, config: serverConfig }) => {
371
- const result = await installServer(id, serverConfig, scope, dryRun);
372
- completed++;
373
-
374
- // Update progress
375
- const status = result.success
376
- ? `${colors.green}OK${colors.reset}`
377
- : `${colors.red}FAIL${colors.reset}`;
378
- console.log(` ${progressBar(completed, servers.length)} ${id}: ${status}`);
379
-
380
- return result;
381
- })
382
- );
383
-
384
- results.push(...batchResults);
385
- }
386
-
387
- return results;
388
- }
389
-
390
- // Generate .mcp.json for project sharing
391
- function generateMcpJson(
392
- servers: Array<{ id: string; config: MCPServerConfig }>,
393
- existingConfig: Record<string, MCPJsonEntry>
394
- ): Record<string, MCPJsonEntry> {
395
- const mcpJson = { ...existingConfig };
396
-
397
- for (const { id, config: server } of servers) {
398
- if (server.transport === 'stdio' && server.config.command && server.config.args) {
399
- const entry: MCPJsonEntry = {
400
- command: server.config.command,
401
- args: server.config.args,
402
- };
403
-
404
- // Add environment variables
405
- if (server.envVars.length > 0) {
406
- entry.env = {};
407
- for (const envVar of server.envVars) {
408
- entry.env[envVar] = `\${${envVar}}`;
409
- }
410
- }
411
-
412
- mcpJson[id] = entry;
413
- }
414
- }
415
-
416
- return mcpJson;
417
- }
418
-
419
- // Main function
420
- async function main(): Promise<void> {
421
- const args = process.argv.slice(2);
422
- const options = {
423
- tier: 'core' as string,
424
- dryRun: false,
425
- force: false,
426
- interactive: false,
427
- };
428
-
429
- // Parse arguments
430
- for (const arg of args) {
431
- if (arg.startsWith('--tier=')) {
432
- options.tier = arg.split('=')[1] || 'core';
433
- } else if (arg === '--dry-run') {
434
- options.dryRun = true;
435
- } else if (arg === '--force') {
436
- options.force = true;
437
- } else if (arg === '--interactive') {
438
- options.interactive = true;
439
- } else if (arg === '--help' || arg === '-h') {
440
- console.log(`
441
- ${colors.bright}MCP Auto-Installer for Claude Code${colors.reset}
442
-
443
- Usage: bun .claude/scripts/setup-mcps.ts [options]
444
-
445
- Options:
446
- --tier=<tier> Install specific tier: core, productivity, infrastructure, all
447
- (default: core)
448
- --dry-run Show what would be installed without actually installing
449
- --force Reinstall even if already configured
450
- --interactive Prompt before each installation
451
- --help, -h Show this help message
452
-
453
- Examples:
454
- bun .claude/scripts/setup-mcps.ts # Install core MCPs
455
- bun .claude/scripts/setup-mcps.ts --tier=all # Install all MCPs
456
- bun .claude/scripts/setup-mcps.ts --dry-run # Preview installation
457
- bun .claude/scripts/setup-mcps.ts --tier=productivity --force
458
- `);
459
- process.exit(0);
460
- }
461
- }
462
-
463
- header('MCP Auto-Installer for Claude Code');
464
-
465
- // Load configuration
466
- log('Loading MCP configuration...');
467
- let config: MCPConfig;
468
- try {
469
- config = loadConfig();
470
- log(`Found ${Object.keys(config.servers).length} configured MCP servers`, 'success');
471
- } catch (err) {
472
- log(
473
- `Failed to load config: ${err instanceof Error ? err.message : 'Unknown error'}`,
474
- 'error'
475
- );
476
- process.exit(1);
477
- }
478
-
479
- // Check prerequisites
480
- subheader('Checking Prerequisites');
481
-
482
- if (!commandExists('claude')) {
483
- log('Claude CLI not found. Please install Claude Code first.', 'error');
484
- log('Visit: https://claude.ai/code', 'info');
485
- process.exit(1);
486
- }
487
- log('Claude CLI found', 'success');
488
-
489
- if (!commandExists('npx')) {
490
- log('npx not found. Please install Node.js.', 'error');
491
- process.exit(1);
492
- }
493
- log('npx found', 'success');
494
-
495
- // Determine which servers to install
496
- subheader('Selecting Servers');
497
-
498
- const tiersToInstall = options.tier === 'all' ? Object.keys(config.tiers) : [options.tier];
499
-
500
- const serversToInstall: Array<{ id: string; config: MCPServerConfig }> = [];
501
-
502
- for (const tier of tiersToInstall) {
503
- const tierConfig = config.tiers[tier];
504
- if (!tierConfig) {
505
- log(`Unknown tier: ${tier}`, 'warning');
506
- continue;
507
- }
508
-
509
- log(`${colors.bright}Tier: ${tier}${colors.reset} - ${tierConfig.description}`);
510
-
511
- for (const serverId of tierConfig.servers) {
512
- const server = config.servers[serverId];
513
- if (!server) {
514
- log(` Server not found: ${serverId}`, 'warning');
515
- continue;
516
- }
517
-
518
- console.log(` ${colors.cyan}${serverId}${colors.reset}: ${server.description}`);
519
- serversToInstall.push({ id: serverId, config: server });
520
- }
521
- }
522
-
523
- if (serversToInstall.length === 0) {
524
- log('No servers selected for installation', 'warning');
525
- process.exit(0);
526
- }
527
-
528
- log(`\nTotal servers to install: ${serversToInstall.length}`, 'info');
529
-
530
- // Security validation
531
- subheader('Security Validation');
532
-
533
- const securityIssues: Array<{ server: string; warnings: string[] }> = [];
534
-
535
- for (const { id, config: server } of serversToInstall) {
536
- const validation = validateSecurity(server, config);
537
- if (!validation.valid) {
538
- securityIssues.push({ server: id, warnings: validation.warnings });
539
- }
540
- }
541
-
542
- if (securityIssues.length > 0) {
543
- log('Security warnings found:', 'warning');
544
- for (const issue of securityIssues) {
545
- console.log(` ${colors.yellow}${issue.server}${colors.reset}:`);
546
- for (const warning of issue.warnings) {
547
- console.log(` - ${warning}`);
548
- }
549
- }
550
- console.log();
551
- } else {
552
- log('All servers passed security validation', 'success');
553
- }
554
-
555
- // Environment variables check
556
- subheader('Environment Variables');
557
-
558
- const requiredEnvVars = new Set<string>();
559
- for (const { config: server } of serversToInstall) {
560
- for (const envVar of server.envVars) {
561
- requiredEnvVars.add(envVar);
562
- }
563
- }
564
-
565
- if (requiredEnvVars.size > 0) {
566
- log('The following environment variables are required:', 'info');
567
- for (const envVar of requiredEnvVars) {
568
- const isSet = !!process.env[envVar];
569
- const status = isSet
570
- ? `${colors.green}SET${colors.reset}`
571
- : `${colors.yellow}NOT SET${colors.reset}`;
572
- console.log(` ${envVar}: ${status}`);
573
- }
574
- console.log();
575
- log('Tip: Set these in your .env file or system environment', 'info');
576
- } else {
577
- log('No environment variables required for selected servers', 'success');
578
- }
579
-
580
- // Installation
581
- subheader(`${options.dryRun ? 'Dry Run - ' : ''}Installing MCP Servers`);
582
-
583
- console.log();
584
- const results = await installServersParallel(
585
- serversToInstall,
586
- config.installation.scope,
587
- config.installation.parallelLimit,
588
- options.dryRun
589
- );
590
-
591
- // Generate .mcp.json for team sharing
592
- if (!options.dryRun) {
593
- subheader('Generating .mcp.json');
594
-
595
- const existingMcpJson = loadMcpJson();
596
- const newMcpJson = generateMcpJson(serversToInstall, existingMcpJson);
597
- saveMcpJson(newMcpJson);
598
- log(`Generated ${MCP_JSON_PATH} for team sharing`, 'success');
599
- }
600
-
601
- // Summary
602
- header('Installation Summary');
603
-
604
- const successful = results.filter((r) => r.success);
605
- const failed = results.filter((r) => !r.success);
606
-
607
- console.log(`${colors.green}Successful: ${successful.length}${colors.reset}`);
608
- for (const result of successful) {
609
- console.log(` ${colors.green}✓${colors.reset} ${result.server} (${result.duration}ms)`);
610
- }
611
-
612
- if (failed.length > 0) {
613
- console.log(`\n${colors.red}Failed: ${failed.length}${colors.reset}`);
614
- for (const result of failed) {
615
- console.log(` ${colors.red}✗${colors.reset} ${result.server}: ${result.message}`);
616
- }
617
- }
618
-
619
- // Next steps
620
- if (!options.dryRun) {
621
- subheader('Next Steps');
622
-
623
- console.log(`
624
- 1. ${colors.cyan}Verify installation:${colors.reset}
625
- claude mcp list
626
-
627
- 2. ${colors.cyan}Test a server:${colors.reset}
628
- claude mcp get <server-name>
629
-
630
- 3. ${colors.cyan}Set missing environment variables:${colors.reset}
631
- Create a .env file with required variables
632
-
633
- 4. ${colors.cyan}Share with team:${colors.reset}
634
- Commit .mcp.json to version control
635
-
636
- 5. ${colors.cyan}Debug issues:${colors.reset}
637
- claude --mcp-debug
638
- `);
639
- }
640
-
641
- // Exit code
642
- if (failed.length > 0 && !options.dryRun) {
643
- process.exit(1);
644
- }
645
- }
646
-
647
- // Run
648
- main().catch((err) => {
649
- console.error(`${colors.red}Fatal error:${colors.reset}`, err);
650
- process.exit(1);
651
- });
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * MCP Auto-Installer for Claude Code
4
+ *
5
+ * This script automatically installs and configures recommended MCP servers
6
+ * based on the project's agent/skill architecture.
7
+ *
8
+ * Features:
9
+ * - Parallel installation for speed
10
+ * - Progress tracking with visual feedback
11
+ * - Security validation before installation
12
+ * - Automatic .mcp.json generation
13
+ * - Environment variable setup guidance
14
+ *
15
+ * Usage:
16
+ * bun .claude/scripts/setup-mcps.ts [options]
17
+ *
18
+ * Options:
19
+ * --tier=core|productivity|infrastructure|all Install specific tier (default: core)
20
+ * --dry-run Show what would be installed
21
+ * --force Reinstall even if already configured
22
+ * --interactive Prompt for each server
23
+ */
24
+
25
+ import { spawn, spawnSync } from 'child_process';
26
+ import { existsSync, readFileSync, writeFileSync } from 'fs';
27
+ import { join, dirname } from 'path';
28
+ import { fileURLToPath } from 'url';
29
+
30
+ // Types
31
+ interface MCPServerConfig {
32
+ name: string;
33
+ description: string;
34
+ tier: 'core' | 'productivity' | 'infrastructure';
35
+ verified: boolean;
36
+ publisher: string;
37
+ repository: string;
38
+ transport: 'stdio' | 'http';
39
+ config: {
40
+ command?: string;
41
+ args?: string[];
42
+ url?: string;
43
+ options?: Record<string, unknown>;
44
+ remote?: { url: string };
45
+ local?: { command: string; args: string[] };
46
+ };
47
+ envVars: string[];
48
+ requiredPermissions: string[];
49
+ agentMappings: string[];
50
+ securityNotes: string;
51
+ }
52
+
53
+ interface MCPConfig {
54
+ metadata: {
55
+ version: string;
56
+ lastUpdated: string;
57
+ researchSources: string[];
58
+ };
59
+ tiers: Record<string, { description: string; servers: string[] }>;
60
+ servers: Record<string, MCPServerConfig>;
61
+ security: {
62
+ guidelines: string[];
63
+ redFlags: string[];
64
+ trustedPublishers: string[];
65
+ };
66
+ installation: {
67
+ parallelLimit: number;
68
+ timeout: number;
69
+ retryAttempts: number;
70
+ scope: string;
71
+ };
72
+ }
73
+
74
+ interface InstallResult {
75
+ server: string;
76
+ success: boolean;
77
+ message: string;
78
+ duration: number;
79
+ }
80
+
81
+ interface MCPJsonEntry {
82
+ command?: string;
83
+ args?: string[];
84
+ url?: string;
85
+ env?: Record<string, string>;
86
+ }
87
+
88
+ // Console colors (ANSI escape codes)
89
+ const colors = {
90
+ reset: '\x1b[0m',
91
+ bright: '\x1b[1m',
92
+ dim: '\x1b[2m',
93
+ red: '\x1b[31m',
94
+ green: '\x1b[32m',
95
+ yellow: '\x1b[33m',
96
+ blue: '\x1b[34m',
97
+ magenta: '\x1b[35m',
98
+ cyan: '\x1b[36m',
99
+ white: '\x1b[37m',
100
+ bgGreen: '\x1b[42m',
101
+ bgRed: '\x1b[41m',
102
+ bgYellow: '\x1b[43m',
103
+ };
104
+
105
+ // Get script directory
106
+ const getScriptDir = (): string => {
107
+ try {
108
+ if (typeof import.meta.url !== 'undefined') {
109
+ return dirname(fileURLToPath(import.meta.url));
110
+ }
111
+ } catch {
112
+ // Fallback
113
+ }
114
+ return process.cwd();
115
+ };
116
+
117
+ const SCRIPT_DIR = getScriptDir();
118
+ const PROJECT_ROOT = join(SCRIPT_DIR, '..', '..');
119
+ const CONFIG_PATH = join(SCRIPT_DIR, '..', 'config', 'mcp-config.json');
120
+ const MCP_JSON_PATH = join(PROJECT_ROOT, '.mcp.json');
121
+
122
+ // Utility functions
123
+ function log(message: string, type: 'info' | 'success' | 'warning' | 'error' = 'info'): void {
124
+ const prefix = {
125
+ info: `${colors.blue}[INFO]${colors.reset}`,
126
+ success: `${colors.green}[OK]${colors.reset}`,
127
+ warning: `${colors.yellow}[WARN]${colors.reset}`,
128
+ error: `${colors.red}[ERROR]${colors.reset}`,
129
+ };
130
+ console.log(`${prefix[type]} ${message}`);
131
+ }
132
+
133
+ function header(text: string): void {
134
+ const line = '='.repeat(60);
135
+ console.log(`\n${colors.cyan}${line}${colors.reset}`);
136
+ console.log(`${colors.bright}${colors.cyan} ${text}${colors.reset}`);
137
+ console.log(`${colors.cyan}${line}${colors.reset}\n`);
138
+ }
139
+
140
+ function subheader(text: string): void {
141
+ console.log(`\n${colors.magenta}>> ${text}${colors.reset}`);
142
+ }
143
+
144
+ function progressBar(current: number, total: number, width = 40): string {
145
+ const percent = Math.round((current / total) * 100);
146
+ const filled = Math.round((current / total) * width);
147
+ const empty = width - filled;
148
+ const bar = `[${'█'.repeat(filled)}${'░'.repeat(empty)}]`;
149
+ return `${bar} ${percent}% (${current}/${total})`;
150
+ }
151
+
152
+ // Load configuration
153
+ function loadConfig(): MCPConfig {
154
+ if (!existsSync(CONFIG_PATH)) {
155
+ throw new Error(`MCP config not found at: ${CONFIG_PATH}`);
156
+ }
157
+ const content = readFileSync(CONFIG_PATH, 'utf8');
158
+ return JSON.parse(content) as MCPConfig;
159
+ }
160
+
161
+ // Load existing .mcp.json
162
+ function loadMcpJson(): Record<string, MCPJsonEntry> {
163
+ if (!existsSync(MCP_JSON_PATH)) {
164
+ return {};
165
+ }
166
+ try {
167
+ const content = readFileSync(MCP_JSON_PATH, 'utf8');
168
+ return JSON.parse(content) as Record<string, MCPJsonEntry>;
169
+ } catch {
170
+ return {};
171
+ }
172
+ }
173
+
174
+ // Save .mcp.json
175
+ function saveMcpJson(config: Record<string, MCPJsonEntry>): void {
176
+ writeFileSync(MCP_JSON_PATH, JSON.stringify(config, null, 2));
177
+ }
178
+
179
+ // Check if a command exists
180
+ function commandExists(cmd: string): boolean {
181
+ try {
182
+ const result = spawnSync(process.platform === 'win32' ? 'where' : 'which', [cmd], {
183
+ stdio: 'pipe',
184
+ shell: true,
185
+ });
186
+ return result.status === 0;
187
+ } catch {
188
+ return false;
189
+ }
190
+ }
191
+
192
+ // Validate security
193
+ function validateSecurity(
194
+ server: MCPServerConfig,
195
+ config: MCPConfig
196
+ ): { valid: boolean; warnings: string[] } {
197
+ const warnings: string[] = [];
198
+
199
+ // Check if publisher is trusted
200
+ if (!config.security.trustedPublishers.includes(server.publisher)) {
201
+ warnings.push(`Publisher '${server.publisher}' is not in trusted list`);
202
+ }
203
+
204
+ // Check if verified
205
+ if (!server.verified) {
206
+ warnings.push('Server is not verified');
207
+ }
208
+
209
+ // Check for missing repository
210
+ if (!server.repository) {
211
+ warnings.push('No repository URL provided');
212
+ }
213
+
214
+ return {
215
+ valid: warnings.length === 0,
216
+ warnings,
217
+ };
218
+ }
219
+
220
+ // Install a single MCP server using claude mcp add
221
+ async function installServer(
222
+ serverId: string,
223
+ server: MCPServerConfig,
224
+ scope: string,
225
+ dryRun: boolean
226
+ ): Promise<InstallResult> {
227
+ const startTime = Date.now();
228
+
229
+ try {
230
+ // Check if claude CLI is available
231
+ if (!commandExists('claude')) {
232
+ return {
233
+ server: serverId,
234
+ success: false,
235
+ message: 'Claude CLI not found. Please install Claude Code first.',
236
+ duration: Date.now() - startTime,
237
+ };
238
+ }
239
+
240
+ // Build the command based on transport type
241
+ let args: string[];
242
+
243
+ if (server.transport === 'http' && server.config.url) {
244
+ // Remote HTTP server
245
+ args = ['mcp', 'add', '--transport', 'http', '-s', scope, serverId, server.config.url];
246
+ } else if (server.transport === 'http' && server.config.remote?.url) {
247
+ // Remote server with remote config
248
+ args = [
249
+ 'mcp',
250
+ 'add',
251
+ '--transport',
252
+ 'http',
253
+ '-s',
254
+ scope,
255
+ serverId,
256
+ server.config.remote.url,
257
+ ];
258
+ } else if (server.config.command && server.config.args) {
259
+ // Local stdio server
260
+ args = [
261
+ 'mcp',
262
+ 'add',
263
+ '-s',
264
+ scope,
265
+ serverId,
266
+ '--',
267
+ server.config.command,
268
+ ...server.config.args,
269
+ ];
270
+ } else {
271
+ return {
272
+ server: serverId,
273
+ success: false,
274
+ message: 'Invalid server configuration',
275
+ duration: Date.now() - startTime,
276
+ };
277
+ }
278
+
279
+ if (dryRun) {
280
+ return {
281
+ server: serverId,
282
+ success: true,
283
+ message: `Would run: claude ${args.join(' ')}`,
284
+ duration: Date.now() - startTime,
285
+ };
286
+ }
287
+
288
+ // Execute the command
289
+ return new Promise((resolve) => {
290
+ const proc = spawn('claude', args, {
291
+ shell: true,
292
+ stdio: 'pipe',
293
+ });
294
+
295
+ let stdout = '';
296
+ let stderr = '';
297
+
298
+ proc.stdout?.on('data', (data) => {
299
+ stdout += data.toString();
300
+ });
301
+
302
+ proc.stderr?.on('data', (data) => {
303
+ stderr += data.toString();
304
+ });
305
+
306
+ proc.on('close', (code) => {
307
+ const duration = Date.now() - startTime;
308
+ if (code === 0) {
309
+ resolve({
310
+ server: serverId,
311
+ success: true,
312
+ message: 'Installed successfully',
313
+ duration,
314
+ });
315
+ } else {
316
+ resolve({
317
+ server: serverId,
318
+ success: false,
319
+ message: stderr || stdout || `Exit code: ${code}`,
320
+ duration,
321
+ });
322
+ }
323
+ });
324
+
325
+ proc.on('error', (err) => {
326
+ resolve({
327
+ server: serverId,
328
+ success: false,
329
+ message: err.message,
330
+ duration: Date.now() - startTime,
331
+ });
332
+ });
333
+
334
+ // Timeout
335
+ setTimeout(() => {
336
+ proc.kill();
337
+ resolve({
338
+ server: serverId,
339
+ success: false,
340
+ message: 'Installation timed out',
341
+ duration: Date.now() - startTime,
342
+ });
343
+ }, 60000);
344
+ });
345
+ } catch (err) {
346
+ return {
347
+ server: serverId,
348
+ success: false,
349
+ message: err instanceof Error ? err.message : 'Unknown error',
350
+ duration: Date.now() - startTime,
351
+ };
352
+ }
353
+ }
354
+
355
+ // Install servers in parallel with limit
356
+ async function installServersParallel(
357
+ servers: Array<{ id: string; config: MCPServerConfig }>,
358
+ scope: string,
359
+ parallelLimit: number,
360
+ dryRun: boolean
361
+ ): Promise<InstallResult[]> {
362
+ const results: InstallResult[] = [];
363
+ let completed = 0;
364
+
365
+ // Process in batches
366
+ for (let i = 0; i < servers.length; i += parallelLimit) {
367
+ const batch = servers.slice(i, i + parallelLimit);
368
+
369
+ const batchResults = await Promise.all(
370
+ batch.map(async ({ id, config: serverConfig }) => {
371
+ const result = await installServer(id, serverConfig, scope, dryRun);
372
+ completed++;
373
+
374
+ // Update progress
375
+ const status = result.success
376
+ ? `${colors.green}OK${colors.reset}`
377
+ : `${colors.red}FAIL${colors.reset}`;
378
+ console.log(` ${progressBar(completed, servers.length)} ${id}: ${status}`);
379
+
380
+ return result;
381
+ })
382
+ );
383
+
384
+ results.push(...batchResults);
385
+ }
386
+
387
+ return results;
388
+ }
389
+
390
+ // Generate .mcp.json for project sharing
391
+ function generateMcpJson(
392
+ servers: Array<{ id: string; config: MCPServerConfig }>,
393
+ existingConfig: Record<string, MCPJsonEntry>
394
+ ): Record<string, MCPJsonEntry> {
395
+ const mcpJson = { ...existingConfig };
396
+
397
+ for (const { id, config: server } of servers) {
398
+ if (server.transport === 'stdio' && server.config.command && server.config.args) {
399
+ const entry: MCPJsonEntry = {
400
+ command: server.config.command,
401
+ args: server.config.args,
402
+ };
403
+
404
+ // Add environment variables
405
+ if (server.envVars.length > 0) {
406
+ entry.env = {};
407
+ for (const envVar of server.envVars) {
408
+ entry.env[envVar] = `\${${envVar}}`;
409
+ }
410
+ }
411
+
412
+ mcpJson[id] = entry;
413
+ }
414
+ }
415
+
416
+ return mcpJson;
417
+ }
418
+
419
+ // Main function
420
+ async function main(): Promise<void> {
421
+ const args = process.argv.slice(2);
422
+ const options = {
423
+ tier: 'core' as string,
424
+ dryRun: false,
425
+ force: false,
426
+ interactive: false,
427
+ };
428
+
429
+ // Parse arguments
430
+ for (const arg of args) {
431
+ if (arg.startsWith('--tier=')) {
432
+ options.tier = arg.split('=')[1] || 'core';
433
+ } else if (arg === '--dry-run') {
434
+ options.dryRun = true;
435
+ } else if (arg === '--force') {
436
+ options.force = true;
437
+ } else if (arg === '--interactive') {
438
+ options.interactive = true;
439
+ } else if (arg === '--help' || arg === '-h') {
440
+ console.log(`
441
+ ${colors.bright}MCP Auto-Installer for Claude Code${colors.reset}
442
+
443
+ Usage: bun .claude/scripts/setup-mcps.ts [options]
444
+
445
+ Options:
446
+ --tier=<tier> Install specific tier: core, productivity, infrastructure, all
447
+ (default: core)
448
+ --dry-run Show what would be installed without actually installing
449
+ --force Reinstall even if already configured
450
+ --interactive Prompt before each installation
451
+ --help, -h Show this help message
452
+
453
+ Examples:
454
+ bun .claude/scripts/setup-mcps.ts # Install core MCPs
455
+ bun .claude/scripts/setup-mcps.ts --tier=all # Install all MCPs
456
+ bun .claude/scripts/setup-mcps.ts --dry-run # Preview installation
457
+ bun .claude/scripts/setup-mcps.ts --tier=productivity --force
458
+ `);
459
+ process.exit(0);
460
+ }
461
+ }
462
+
463
+ header('MCP Auto-Installer for Claude Code');
464
+
465
+ // Load configuration
466
+ log('Loading MCP configuration...');
467
+ let config: MCPConfig;
468
+ try {
469
+ config = loadConfig();
470
+ log(`Found ${Object.keys(config.servers).length} configured MCP servers`, 'success');
471
+ } catch (err) {
472
+ log(
473
+ `Failed to load config: ${err instanceof Error ? err.message : 'Unknown error'}`,
474
+ 'error'
475
+ );
476
+ process.exit(1);
477
+ }
478
+
479
+ // Check prerequisites
480
+ subheader('Checking Prerequisites');
481
+
482
+ if (!commandExists('claude')) {
483
+ log('Claude CLI not found. Please install Claude Code first.', 'error');
484
+ log('Visit: https://claude.ai/code', 'info');
485
+ process.exit(1);
486
+ }
487
+ log('Claude CLI found', 'success');
488
+
489
+ if (!commandExists('npx')) {
490
+ log('npx not found. Please install Node.js.', 'error');
491
+ process.exit(1);
492
+ }
493
+ log('npx found', 'success');
494
+
495
+ // Determine which servers to install
496
+ subheader('Selecting Servers');
497
+
498
+ const tiersToInstall = options.tier === 'all' ? Object.keys(config.tiers) : [options.tier];
499
+
500
+ const serversToInstall: Array<{ id: string; config: MCPServerConfig }> = [];
501
+
502
+ for (const tier of tiersToInstall) {
503
+ const tierConfig = config.tiers[tier];
504
+ if (!tierConfig) {
505
+ log(`Unknown tier: ${tier}`, 'warning');
506
+ continue;
507
+ }
508
+
509
+ log(`${colors.bright}Tier: ${tier}${colors.reset} - ${tierConfig.description}`);
510
+
511
+ for (const serverId of tierConfig.servers) {
512
+ const server = config.servers[serverId];
513
+ if (!server) {
514
+ log(` Server not found: ${serverId}`, 'warning');
515
+ continue;
516
+ }
517
+
518
+ console.log(` ${colors.cyan}${serverId}${colors.reset}: ${server.description}`);
519
+ serversToInstall.push({ id: serverId, config: server });
520
+ }
521
+ }
522
+
523
+ if (serversToInstall.length === 0) {
524
+ log('No servers selected for installation', 'warning');
525
+ process.exit(0);
526
+ }
527
+
528
+ log(`\nTotal servers to install: ${serversToInstall.length}`, 'info');
529
+
530
+ // Security validation
531
+ subheader('Security Validation');
532
+
533
+ const securityIssues: Array<{ server: string; warnings: string[] }> = [];
534
+
535
+ for (const { id, config: server } of serversToInstall) {
536
+ const validation = validateSecurity(server, config);
537
+ if (!validation.valid) {
538
+ securityIssues.push({ server: id, warnings: validation.warnings });
539
+ }
540
+ }
541
+
542
+ if (securityIssues.length > 0) {
543
+ log('Security warnings found:', 'warning');
544
+ for (const issue of securityIssues) {
545
+ console.log(` ${colors.yellow}${issue.server}${colors.reset}:`);
546
+ for (const warning of issue.warnings) {
547
+ console.log(` - ${warning}`);
548
+ }
549
+ }
550
+ console.log();
551
+ } else {
552
+ log('All servers passed security validation', 'success');
553
+ }
554
+
555
+ // Environment variables check
556
+ subheader('Environment Variables');
557
+
558
+ const requiredEnvVars = new Set<string>();
559
+ for (const { config: server } of serversToInstall) {
560
+ for (const envVar of server.envVars) {
561
+ requiredEnvVars.add(envVar);
562
+ }
563
+ }
564
+
565
+ if (requiredEnvVars.size > 0) {
566
+ log('The following environment variables are required:', 'info');
567
+ for (const envVar of requiredEnvVars) {
568
+ const isSet = !!process.env[envVar];
569
+ const status = isSet
570
+ ? `${colors.green}SET${colors.reset}`
571
+ : `${colors.yellow}NOT SET${colors.reset}`;
572
+ console.log(` ${envVar}: ${status}`);
573
+ }
574
+ console.log();
575
+ log('Tip: Set these in your .env file or system environment', 'info');
576
+ } else {
577
+ log('No environment variables required for selected servers', 'success');
578
+ }
579
+
580
+ // Installation
581
+ subheader(`${options.dryRun ? 'Dry Run - ' : ''}Installing MCP Servers`);
582
+
583
+ console.log();
584
+ const results = await installServersParallel(
585
+ serversToInstall,
586
+ config.installation.scope,
587
+ config.installation.parallelLimit,
588
+ options.dryRun
589
+ );
590
+
591
+ // Generate .mcp.json for team sharing
592
+ if (!options.dryRun) {
593
+ subheader('Generating .mcp.json');
594
+
595
+ const existingMcpJson = loadMcpJson();
596
+ const newMcpJson = generateMcpJson(serversToInstall, existingMcpJson);
597
+ saveMcpJson(newMcpJson);
598
+ log(`Generated ${MCP_JSON_PATH} for team sharing`, 'success');
599
+ }
600
+
601
+ // Summary
602
+ header('Installation Summary');
603
+
604
+ const successful = results.filter((r) => r.success);
605
+ const failed = results.filter((r) => !r.success);
606
+
607
+ console.log(`${colors.green}Successful: ${successful.length}${colors.reset}`);
608
+ for (const result of successful) {
609
+ console.log(` ${colors.green}✓${colors.reset} ${result.server} (${result.duration}ms)`);
610
+ }
611
+
612
+ if (failed.length > 0) {
613
+ console.log(`\n${colors.red}Failed: ${failed.length}${colors.reset}`);
614
+ for (const result of failed) {
615
+ console.log(` ${colors.red}✗${colors.reset} ${result.server}: ${result.message}`);
616
+ }
617
+ }
618
+
619
+ // Next steps
620
+ if (!options.dryRun) {
621
+ subheader('Next Steps');
622
+
623
+ console.log(`
624
+ 1. ${colors.cyan}Verify installation:${colors.reset}
625
+ claude mcp list
626
+
627
+ 2. ${colors.cyan}Test a server:${colors.reset}
628
+ claude mcp get <server-name>
629
+
630
+ 3. ${colors.cyan}Set missing environment variables:${colors.reset}
631
+ Create a .env file with required variables
632
+
633
+ 4. ${colors.cyan}Share with team:${colors.reset}
634
+ Commit .mcp.json to version control
635
+
636
+ 5. ${colors.cyan}Debug issues:${colors.reset}
637
+ claude --mcp-debug
638
+ `);
639
+ }
640
+
641
+ // Exit code
642
+ if (failed.length > 0 && !options.dryRun) {
643
+ process.exit(1);
644
+ }
645
+ }
646
+
647
+ // Run
648
+ main().catch((err) => {
649
+ console.error(`${colors.red}Fatal error:${colors.reset}`, err);
650
+ process.exit(1);
651
+ });