start-vibing 2.0.11 → 2.0.13

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 (131) hide show
  1. package/README.md +177 -177
  2. package/dist/cli.js +19 -2
  3. package/package.json +42 -42
  4. package/template/.claude/CLAUDE.md +174 -174
  5. package/template/.claude/agents/01-orchestration/agent-selector.md +130 -130
  6. package/template/.claude/agents/01-orchestration/checkpoint-manager.md +142 -142
  7. package/template/.claude/agents/01-orchestration/context-manager.md +138 -138
  8. package/template/.claude/agents/01-orchestration/error-recovery.md +182 -182
  9. package/template/.claude/agents/01-orchestration/orchestrator.md +114 -114
  10. package/template/.claude/agents/01-orchestration/parallel-coordinator.md +141 -141
  11. package/template/.claude/agents/01-orchestration/task-decomposer.md +121 -121
  12. package/template/.claude/agents/01-orchestration/workflow-router.md +114 -114
  13. package/template/.claude/agents/02-typescript/bun-runtime-expert.md +197 -197
  14. package/template/.claude/agents/02-typescript/esm-resolver.md +193 -193
  15. package/template/.claude/agents/02-typescript/import-alias-enforcer.md +158 -158
  16. package/template/.claude/agents/02-typescript/ts-generics-helper.md +183 -183
  17. package/template/.claude/agents/02-typescript/ts-migration-helper.md +238 -238
  18. package/template/.claude/agents/02-typescript/ts-strict-checker.md +180 -180
  19. package/template/.claude/agents/02-typescript/ts-types-analyzer.md +199 -199
  20. package/template/.claude/agents/02-typescript/type-definition-writer.md +187 -187
  21. package/template/.claude/agents/02-typescript/zod-schema-designer.md +212 -212
  22. package/template/.claude/agents/02-typescript/zod-validator.md +158 -158
  23. package/template/.claude/agents/03-testing/playwright-assertions.md +265 -265
  24. package/template/.claude/agents/03-testing/playwright-e2e.md +247 -247
  25. package/template/.claude/agents/03-testing/playwright-fixtures.md +234 -234
  26. package/template/.claude/agents/03-testing/playwright-multi-viewport.md +256 -256
  27. package/template/.claude/agents/03-testing/playwright-page-objects.md +247 -247
  28. package/template/.claude/agents/03-testing/test-cleanup-manager.md +248 -248
  29. package/template/.claude/agents/03-testing/test-data-generator.md +254 -254
  30. package/template/.claude/agents/03-testing/tester-integration.md +278 -278
  31. package/template/.claude/agents/03-testing/tester-unit.md +207 -207
  32. package/template/.claude/agents/03-testing/vitest-config.md +287 -287
  33. package/template/.claude/agents/04-docker/container-health.md +255 -255
  34. package/template/.claude/agents/04-docker/deployment-validator.md +225 -225
  35. package/template/.claude/agents/04-docker/docker-compose-designer.md +281 -281
  36. package/template/.claude/agents/04-docker/docker-env-manager.md +235 -235
  37. package/template/.claude/agents/04-docker/docker-multi-stage.md +241 -241
  38. package/template/.claude/agents/04-docker/dockerfile-optimizer.md +208 -208
  39. package/template/.claude/agents/05-database/database-seeder.md +273 -273
  40. package/template/.claude/agents/05-database/mongodb-query-optimizer.md +230 -230
  41. package/template/.claude/agents/05-database/mongoose-aggregation.md +306 -306
  42. package/template/.claude/agents/05-database/mongoose-index-optimizer.md +182 -182
  43. package/template/.claude/agents/05-database/mongoose-schema-designer.md +267 -267
  44. package/template/.claude/agents/06-security/auth-session-validator.md +68 -68
  45. package/template/.claude/agents/06-security/input-sanitizer.md +80 -80
  46. package/template/.claude/agents/06-security/owasp-checker.md +97 -97
  47. package/template/.claude/agents/06-security/permission-auditor.md +100 -100
  48. package/template/.claude/agents/06-security/security-auditor.md +84 -84
  49. package/template/.claude/agents/06-security/sensitive-data-scanner.md +83 -83
  50. package/template/.claude/agents/07-documentation/api-documenter.md +136 -136
  51. package/template/.claude/agents/07-documentation/changelog-manager.md +105 -105
  52. package/template/.claude/agents/07-documentation/documenter.md +76 -76
  53. package/template/.claude/agents/07-documentation/domain-updater.md +81 -81
  54. package/template/.claude/agents/07-documentation/jsdoc-generator.md +114 -114
  55. package/template/.claude/agents/07-documentation/readme-generator.md +135 -135
  56. package/template/.claude/agents/08-git/branch-manager.md +58 -58
  57. package/template/.claude/agents/08-git/commit-manager.md +63 -63
  58. package/template/.claude/agents/08-git/pr-creator.md +76 -76
  59. package/template/.claude/agents/09-quality/code-reviewer.md +71 -71
  60. package/template/.claude/agents/09-quality/quality-checker.md +67 -67
  61. package/template/.claude/agents/10-research/best-practices-finder.md +89 -89
  62. package/template/.claude/agents/10-research/competitor-analyzer.md +106 -106
  63. package/template/.claude/agents/10-research/pattern-researcher.md +93 -93
  64. package/template/.claude/agents/10-research/research-cache-manager.md +76 -76
  65. package/template/.claude/agents/10-research/research-web.md +98 -98
  66. package/template/.claude/agents/10-research/tech-evaluator.md +101 -101
  67. package/template/.claude/agents/11-ui-ux/accessibility-auditor.md +136 -136
  68. package/template/.claude/agents/11-ui-ux/design-system-enforcer.md +125 -125
  69. package/template/.claude/agents/11-ui-ux/skeleton-generator.md +118 -118
  70. package/template/.claude/agents/11-ui-ux/ui-desktop.md +132 -132
  71. package/template/.claude/agents/11-ui-ux/ui-mobile.md +98 -98
  72. package/template/.claude/agents/11-ui-ux/ui-tablet.md +110 -110
  73. package/template/.claude/agents/12-performance/api-latency-analyzer.md +156 -156
  74. package/template/.claude/agents/12-performance/bundle-analyzer.md +113 -113
  75. package/template/.claude/agents/12-performance/memory-leak-detector.md +137 -137
  76. package/template/.claude/agents/12-performance/performance-profiler.md +115 -115
  77. package/template/.claude/agents/12-performance/query-optimizer.md +124 -124
  78. package/template/.claude/agents/12-performance/render-optimizer.md +154 -154
  79. package/template/.claude/agents/13-debugging/build-error-fixer.md +207 -207
  80. package/template/.claude/agents/13-debugging/debugger.md +149 -149
  81. package/template/.claude/agents/13-debugging/error-stack-analyzer.md +141 -141
  82. package/template/.claude/agents/13-debugging/network-debugger.md +208 -208
  83. package/template/.claude/agents/13-debugging/runtime-error-fixer.md +181 -181
  84. package/template/.claude/agents/13-debugging/type-error-resolver.md +185 -185
  85. package/template/.claude/agents/14-validation/final-validator.md +93 -93
  86. package/template/.claude/agents/_backup/analyzer.md +134 -134
  87. package/template/.claude/agents/_backup/code-reviewer.md +279 -279
  88. package/template/.claude/agents/_backup/commit-manager.md +219 -219
  89. package/template/.claude/agents/_backup/debugger.md +280 -280
  90. package/template/.claude/agents/_backup/documenter.md +237 -237
  91. package/template/.claude/agents/_backup/domain-updater.md +197 -197
  92. package/template/.claude/agents/_backup/final-validator.md +169 -169
  93. package/template/.claude/agents/_backup/orchestrator.md +149 -149
  94. package/template/.claude/agents/_backup/performance.md +232 -232
  95. package/template/.claude/agents/_backup/quality-checker.md +240 -240
  96. package/template/.claude/agents/_backup/research.md +315 -315
  97. package/template/.claude/agents/_backup/security-auditor.md +192 -192
  98. package/template/.claude/agents/_backup/tester.md +566 -566
  99. package/template/.claude/agents/_backup/ui-ux-reviewer.md +247 -247
  100. package/template/.claude/config/README.md +30 -30
  101. package/template/.claude/config/mcp-config.json +344 -344
  102. package/template/.claude/config/project-config.json +53 -53
  103. package/template/.claude/config/quality-gates.json +46 -46
  104. package/template/.claude/config/security-rules.json +45 -45
  105. package/template/.claude/config/testing-config.json +164 -164
  106. package/template/.claude/hooks/SETUP.md +126 -126
  107. package/template/.claude/hooks/run-hook.ts +176 -176
  108. package/template/.claude/hooks/stop-validator.ts +914 -824
  109. package/template/.claude/hooks/user-prompt-submit.ts +886 -886
  110. package/template/.claude/scripts/mcp-quick-install.ts +151 -151
  111. package/template/.claude/scripts/setup-mcps.ts +651 -651
  112. package/template/.claude/settings.json +275 -275
  113. package/template/.claude/skills/bun-runtime/SKILL.md +430 -430
  114. package/template/.claude/skills/codebase-knowledge/domains/claude-system.md +431 -431
  115. package/template/.claude/skills/codebase-knowledge/domains/mcp-integration.md +295 -295
  116. package/template/.claude/skills/debugging-patterns/SKILL.md +485 -485
  117. package/template/.claude/skills/docker-patterns/SKILL.md +555 -555
  118. package/template/.claude/skills/git-workflow/SKILL.md +454 -454
  119. package/template/.claude/skills/mongoose-patterns/SKILL.md +499 -499
  120. package/template/.claude/skills/nextjs-app-router/SKILL.md +327 -327
  121. package/template/.claude/skills/performance-patterns/SKILL.md +547 -547
  122. package/template/.claude/skills/playwright-automation/SKILL.md +438 -438
  123. package/template/.claude/skills/react-patterns/SKILL.md +389 -389
  124. package/template/.claude/skills/research-cache/SKILL.md +222 -222
  125. package/template/.claude/skills/shadcn-ui/SKILL.md +511 -511
  126. package/template/.claude/skills/tailwind-patterns/SKILL.md +465 -465
  127. package/template/.claude/skills/test-coverage/SKILL.md +467 -467
  128. package/template/.claude/skills/trpc-api/SKILL.md +434 -434
  129. package/template/.claude/skills/typescript-strict/SKILL.md +367 -367
  130. package/template/.claude/skills/zod-validation/SKILL.md +403 -403
  131. 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
+ });