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 +11 -1
- package/dist/mcp.d.ts +13 -0
- package/dist/mcp.js +165 -0
- package/package.json +1 -1
- package/stacks/_shared/config/mcp-servers.json +191 -0
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
|
|
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
|
@@ -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
|
+
}
|