start-vibing-stacks 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -12,6 +12,7 @@ import * as ui from './ui.js';
12
12
  import { detectProject, detectPhpFramework, detectNodeFramework } from './detector.js';
13
13
  import { autoInstall, installComposer, installClaudeCode } from './installer.js';
14
14
  import { loadStackConfig, setupProject } from './setup.js';
15
+ import { selectMcpServers, installMcpServers } from './mcp.js';
15
16
  // =============================================================================
16
17
  // CLI Arguments
17
18
  // =============================================================================
@@ -19,6 +20,7 @@ const args = process.argv.slice(2);
19
20
  const FLAGS = {
20
21
  force: args.includes('--force'),
21
22
  noClaude: args.includes('--no-claude'),
23
+ noMcp: args.includes('--no-mcp'),
22
24
  noInstall: args.includes('--no-install'),
23
25
  help: args.includes('--help') || args.includes('-h'),
24
26
  version: args.includes('--version') || args.includes('-v'),
@@ -36,6 +38,7 @@ if (FLAGS.help) {
36
38
  ${chalk.bold('Options:')}
37
39
  --force Overwrite existing configuration
38
40
  --no-claude Skip Claude Code installation
41
+ --no-mcp Skip MCP server selection
39
42
  --no-install Skip dependency installation
40
43
  --help, -h Show this help
41
44
  --version, -v Show version
@@ -155,6 +158,8 @@ async function main() {
155
158
  })),
156
159
  },
157
160
  ]);
161
+ // ─── Step 6b: MCP Servers ────────────────────────────────────────────
162
+ const selectedMcps = FLAGS.noMcp ? [] : await selectMcpServers(stackId, database);
158
163
  // ─── Step 7: Show Summary & Confirm ────────────────────────────────────
159
164
  const config = {
160
165
  name: projectName,
@@ -180,6 +185,7 @@ async function main() {
180
185
  'Skills': `${stackConfig.skills.length} stack + shared`,
181
186
  'Hooks': 'stop-validator + prompt-inject',
182
187
  'Gates': stackConfig.qualityGates.map((g) => g.name).join(' → '),
188
+ 'MCPs': selectedMcps.length > 0 ? `${selectedMcps.length} servers` : 'None',
183
189
  });
184
190
  const { confirmed } = await inquirer.prompt([
185
191
  {
@@ -218,7 +224,11 @@ async function main() {
218
224
  ui.error('Setup failed.');
219
225
  process.exit(1);
220
226
  }
221
- // ─── Step 10: Install Claude Code ──────────────────────────────────────
227
+ // ─── Step 10: Install MCP Servers ───────────────────────────────────
228
+ if (selectedMcps.length > 0) {
229
+ await installMcpServers(selectedMcps, projectDir);
230
+ }
231
+ // ─── Step 11: Install Claude Code ──────────────────────────────────────
222
232
  if (!FLAGS.noClaude) {
223
233
  console.log('');
224
234
  installClaudeCode();
package/dist/mcp.d.ts ADDED
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Start Vibing Stacks — MCP Server Manager
3
+ *
4
+ * Discovers, selects, and configures MCP servers based on stack.
5
+ */
6
+ /**
7
+ * Interactive MCP selection
8
+ */
9
+ export declare function selectMcpServers(stackId: string, database: string): Promise<string[]>;
10
+ /**
11
+ * Install selected MCP servers
12
+ */
13
+ export declare function installMcpServers(selectedIds: string[], projectDir: string): Promise<void>;
package/dist/mcp.js ADDED
@@ -0,0 +1,165 @@
1
+ /**
2
+ * Start Vibing Stacks — MCP Server Manager
3
+ *
4
+ * Discovers, selects, and configures MCP servers based on stack.
5
+ */
6
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
7
+ import { join, dirname, resolve } from 'path';
8
+ import { fileURLToPath } from 'url';
9
+ import { execSync } from 'child_process';
10
+ import inquirer from 'inquirer';
11
+ import ora from 'ora';
12
+ import chalk from 'chalk';
13
+ import * as ui from './ui.js';
14
+ const __filename = fileURLToPath(import.meta.url);
15
+ const __dirname = dirname(__filename);
16
+ const PACKAGE_ROOT = resolve(__dirname, '..');
17
+ /**
18
+ * Load MCP server registry
19
+ */
20
+ function loadRegistry() {
21
+ const registryPath = join(PACKAGE_ROOT, 'stacks', '_shared', 'config', 'mcp-servers.json');
22
+ return JSON.parse(readFileSync(registryPath, 'utf8'));
23
+ }
24
+ /**
25
+ * Get servers compatible with a stack + database
26
+ */
27
+ function getCompatibleServers(registry, stackId, database) {
28
+ const results = [];
29
+ for (const [id, server] of Object.entries(registry.servers)) {
30
+ if (!server.stacks.includes(stackId))
31
+ continue;
32
+ // Filter DB-specific MCPs
33
+ if (id === 'mongodb' && database !== 'mongodb')
34
+ continue;
35
+ if (id === 'mysql' && database !== 'mysql')
36
+ continue;
37
+ if (id === 'postgres' && database !== 'postgresql')
38
+ continue;
39
+ if (id === 'nextjs-devtools' && stackId !== 'nodejs')
40
+ continue;
41
+ const recommended = server.tier === 'core' ||
42
+ (id === 'mongodb' && database === 'mongodb') ||
43
+ (id === 'mysql' && database === 'mysql') ||
44
+ (id === 'postgres' && database === 'postgresql');
45
+ results.push({ id, server, recommended });
46
+ }
47
+ // Sort: core first, then stack-specific, then productivity
48
+ const tierOrder = { core: 0, 'stack-specific': 1, productivity: 2 };
49
+ results.sort((a, b) => {
50
+ const aOrder = tierOrder[a.server.tier] ?? 3;
51
+ const bOrder = tierOrder[b.server.tier] ?? 3;
52
+ return aOrder - bOrder;
53
+ });
54
+ return results;
55
+ }
56
+ /**
57
+ * Interactive MCP selection
58
+ */
59
+ export async function selectMcpServers(stackId, database) {
60
+ const registry = loadRegistry();
61
+ const compatible = getCompatibleServers(registry, stackId, database);
62
+ if (compatible.length === 0) {
63
+ ui.info('No MCP servers available for this stack');
64
+ return [];
65
+ }
66
+ ui.header('🔌 MCP Servers');
67
+ console.log(chalk.dim(' Model Context Protocol — extend Claude Code capabilities\n'));
68
+ const { selectedMcps } = await inquirer.prompt([
69
+ {
70
+ type: 'checkbox',
71
+ name: 'selectedMcps',
72
+ message: 'Select MCP servers to install:',
73
+ choices: compatible.map((c) => {
74
+ const envNote = c.server.envVars.length > 0
75
+ ? chalk.dim(` (needs: ${c.server.envVars.join(', ')})`)
76
+ : '';
77
+ const tierLabel = c.server.tier === 'core'
78
+ ? chalk.green(' [core]')
79
+ : c.server.tier === 'stack-specific'
80
+ ? chalk.yellow(' [stack]')
81
+ : chalk.dim(' [optional]');
82
+ return {
83
+ name: `${c.server.icon} ${c.server.name} — ${c.server.description}${tierLabel}${envNote}`,
84
+ value: c.id,
85
+ checked: c.recommended,
86
+ };
87
+ }),
88
+ },
89
+ ]);
90
+ return selectedMcps;
91
+ }
92
+ /**
93
+ * Install selected MCP servers
94
+ */
95
+ export async function installMcpServers(selectedIds, projectDir) {
96
+ if (selectedIds.length === 0)
97
+ return;
98
+ const registry = loadRegistry();
99
+ const spinner = ora('Configuring MCP servers...').start();
100
+ // Build Claude Code MCP config
101
+ const mcpConfig = {};
102
+ for (const id of selectedIds) {
103
+ const server = registry.servers[id];
104
+ if (!server)
105
+ continue;
106
+ mcpConfig[id] = server.config;
107
+ // Try to install stdio-based servers
108
+ if (server.install && server.config.command) {
109
+ spinner.text = `Pre-caching ${server.name}...`;
110
+ try {
111
+ execSync(`${server.install} --help 2>/dev/null || true`, {
112
+ stdio: 'pipe',
113
+ timeout: 30000,
114
+ });
115
+ }
116
+ catch {
117
+ // Non-critical — Claude Code will install on first use
118
+ }
119
+ }
120
+ }
121
+ // Write MCP config to .claude/config/
122
+ const configDir = join(projectDir, '.claude', 'config');
123
+ mkdirSync(configDir, { recursive: true });
124
+ writeFileSync(join(configDir, 'mcp-active.json'), JSON.stringify({ mcpServers: mcpConfig }, null, 2));
125
+ // Also write to Claude Code's settings if exists
126
+ const claudeSettingsPath = join(projectDir, '.claude', 'settings.json');
127
+ if (existsSync(claudeSettingsPath)) {
128
+ try {
129
+ const settings = JSON.parse(readFileSync(claudeSettingsPath, 'utf8'));
130
+ settings.mcpServers = mcpConfig;
131
+ writeFileSync(claudeSettingsPath, JSON.stringify(settings, null, '\t'));
132
+ }
133
+ catch { }
134
+ }
135
+ // Create .env.example with required env vars
136
+ const envVars = [];
137
+ for (const id of selectedIds) {
138
+ const server = registry.servers[id];
139
+ if (server?.envVars.length) {
140
+ for (const v of server.envVars) {
141
+ envVars.push(`# ${server.name}\n${v}=`);
142
+ }
143
+ }
144
+ }
145
+ if (envVars.length > 0) {
146
+ const envExamplePath = join(projectDir, '.env.mcp.example');
147
+ writeFileSync(envExamplePath, `# MCP Server Environment Variables\n# Copy to .env and fill in values\n\n${envVars.join('\n\n')}\n`);
148
+ spinner.text = 'Created .env.mcp.example for required API keys';
149
+ }
150
+ spinner.succeed(`${selectedIds.length} MCP servers configured`);
151
+ // Show env var warnings
152
+ const needsEnv = selectedIds.filter((id) => {
153
+ const server = registry.servers[id];
154
+ return server?.envVars.length > 0;
155
+ });
156
+ if (needsEnv.length > 0) {
157
+ console.log('');
158
+ ui.warn('Some MCPs need environment variables:');
159
+ for (const id of needsEnv) {
160
+ const server = registry.servers[id];
161
+ console.log(chalk.dim(` ${server.icon} ${server.name}: ${server.envVars.join(', ')}`));
162
+ }
163
+ ui.info('See .env.mcp.example for details');
164
+ }
165
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "start-vibing-stacks",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "AI-powered multi-stack dev workflow for Claude Code. Supports PHP, Node.js, Python and more.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,191 @@
1
+ {
2
+ "$comment": "MCP Server Registry — available servers for all stacks",
3
+ "servers": {
4
+ "context7": {
5
+ "name": "Context7",
6
+ "description": "Real-time library documentation",
7
+ "icon": "📚",
8
+ "tier": "core",
9
+ "stacks": ["php", "nodejs", "python", "go", "rust"],
10
+ "install": "npx -y @upstash/context7-mcp@latest",
11
+ "config": {
12
+ "command": "npx",
13
+ "args": ["-y", "@upstash/context7-mcp@latest"]
14
+ },
15
+ "envVars": [],
16
+ "publisher": "upstash"
17
+ },
18
+ "sequential-thinking": {
19
+ "name": "Sequential Thinking",
20
+ "description": "Structured reasoning for complex problems",
21
+ "icon": "🧠",
22
+ "tier": "core",
23
+ "stacks": ["php", "nodejs", "python", "go", "rust"],
24
+ "install": "npx -y @modelcontextprotocol/server-sequential-thinking",
25
+ "config": {
26
+ "command": "npx",
27
+ "args": ["-y", "@modelcontextprotocol/server-sequential-thinking"]
28
+ },
29
+ "envVars": [],
30
+ "publisher": "modelcontextprotocol"
31
+ },
32
+ "playwright": {
33
+ "name": "Playwright",
34
+ "description": "Browser automation & E2E testing",
35
+ "icon": "🎭",
36
+ "tier": "core",
37
+ "stacks": ["php", "nodejs", "python"],
38
+ "install": "npx -y @playwright/mcp@latest",
39
+ "config": {
40
+ "command": "npx",
41
+ "args": ["-y", "@playwright/mcp@latest", "--user-data-dir", "./.playwright-data"]
42
+ },
43
+ "envVars": [],
44
+ "publisher": "microsoft"
45
+ },
46
+ "memory": {
47
+ "name": "Memory",
48
+ "description": "Persistent knowledge graph across sessions",
49
+ "icon": "💾",
50
+ "tier": "core",
51
+ "stacks": ["php", "nodejs", "python", "go", "rust"],
52
+ "install": "npx -y @modelcontextprotocol/server-memory",
53
+ "config": {
54
+ "command": "npx",
55
+ "args": ["-y", "@modelcontextprotocol/server-memory"]
56
+ },
57
+ "envVars": [],
58
+ "publisher": "modelcontextprotocol"
59
+ },
60
+ "github": {
61
+ "name": "GitHub",
62
+ "description": "Repository management, issues, PRs, CI/CD",
63
+ "icon": "🐙",
64
+ "tier": "core",
65
+ "stacks": ["php", "nodejs", "python", "go", "rust"],
66
+ "install": null,
67
+ "config": {
68
+ "remote": {
69
+ "url": "https://api.githubcopilot.com/mcp/"
70
+ }
71
+ },
72
+ "envVars": ["GITHUB_PERSONAL_ACCESS_TOKEN"],
73
+ "publisher": "github"
74
+ },
75
+ "brave-search": {
76
+ "name": "Brave Search",
77
+ "description": "Privacy-first web search",
78
+ "icon": "🔍",
79
+ "tier": "productivity",
80
+ "stacks": ["php", "nodejs", "python", "go", "rust"],
81
+ "install": "npx -y @modelcontextprotocol/server-brave-search",
82
+ "config": {
83
+ "command": "npx",
84
+ "args": ["-y", "@modelcontextprotocol/server-brave-search"]
85
+ },
86
+ "envVars": ["BRAVE_API_KEY"],
87
+ "publisher": "modelcontextprotocol"
88
+ },
89
+ "nextjs-devtools": {
90
+ "name": "Next.js DevTools",
91
+ "description": "Next.js 16+ error detection and live state",
92
+ "icon": "▲",
93
+ "tier": "stack-specific",
94
+ "stacks": ["nodejs"],
95
+ "install": "npx -y next-devtools-mcp@latest",
96
+ "config": {
97
+ "command": "npx",
98
+ "args": ["-y", "next-devtools-mcp@latest"]
99
+ },
100
+ "envVars": [],
101
+ "publisher": "vercel"
102
+ },
103
+ "mongodb": {
104
+ "name": "MongoDB",
105
+ "description": "MongoDB database operations",
106
+ "icon": "🍃",
107
+ "tier": "stack-specific",
108
+ "stacks": ["php", "nodejs", "python"],
109
+ "install": "npx -y @mongodb-js/mongodb-mcp-server",
110
+ "config": {
111
+ "command": "npx",
112
+ "args": ["-y", "@mongodb-js/mongodb-mcp-server"]
113
+ },
114
+ "envVars": ["MONGODB_URI"],
115
+ "publisher": "mongodb-js"
116
+ },
117
+ "mysql": {
118
+ "name": "MySQL",
119
+ "description": "MySQL/MariaDB database operations",
120
+ "icon": "🐬",
121
+ "tier": "stack-specific",
122
+ "stacks": ["php", "nodejs", "python"],
123
+ "install": "npx -y @benborla29/mcp-server-mysql",
124
+ "config": {
125
+ "command": "npx",
126
+ "args": ["-y", "@benborla29/mcp-server-mysql"]
127
+ },
128
+ "envVars": ["MYSQL_HOST", "MYSQL_USER", "MYSQL_PASSWORD", "MYSQL_DATABASE"],
129
+ "publisher": "benborla29"
130
+ },
131
+ "postgres": {
132
+ "name": "PostgreSQL",
133
+ "description": "PostgreSQL database operations",
134
+ "icon": "🐘",
135
+ "tier": "stack-specific",
136
+ "stacks": ["php", "nodejs", "python"],
137
+ "install": "npx -y @modelcontextprotocol/server-postgres",
138
+ "config": {
139
+ "command": "npx",
140
+ "args": ["-y", "@modelcontextprotocol/server-postgres"]
141
+ },
142
+ "envVars": ["POSTGRES_CONNECTION_STRING"],
143
+ "publisher": "modelcontextprotocol"
144
+ },
145
+ "sentry": {
146
+ "name": "Sentry",
147
+ "description": "Error tracking & AI-powered debugging",
148
+ "icon": "🐛",
149
+ "tier": "productivity",
150
+ "stacks": ["php", "nodejs", "python"],
151
+ "install": null,
152
+ "config": {
153
+ "url": "https://mcp.sentry.dev/mcp"
154
+ },
155
+ "envVars": [],
156
+ "publisher": "getsentry"
157
+ },
158
+ "figma": {
159
+ "name": "Figma",
160
+ "description": "Design-to-code workflows",
161
+ "icon": "🎨",
162
+ "tier": "productivity",
163
+ "stacks": ["php", "nodejs"],
164
+ "install": null,
165
+ "config": {
166
+ "url": "https://mcp.figma.com/mcp"
167
+ },
168
+ "envVars": [],
169
+ "publisher": "figma"
170
+ },
171
+ "fetch": {
172
+ "name": "Fetch",
173
+ "description": "HTTP requests and web content retrieval",
174
+ "icon": "🌐",
175
+ "tier": "productivity",
176
+ "stacks": ["php", "nodejs", "python", "go", "rust"],
177
+ "install": "uvx mcp-server-fetch",
178
+ "config": {
179
+ "command": "uvx",
180
+ "args": ["mcp-server-fetch"]
181
+ },
182
+ "envVars": [],
183
+ "publisher": "modelcontextprotocol"
184
+ }
185
+ },
186
+ "tiers": {
187
+ "core": "Essential for daily development — recommended for all",
188
+ "stack-specific": "Depends on your stack and database choice",
189
+ "productivity": "Enhanced productivity — optional"
190
+ }
191
+ }