UNPKG

@viberaven/mcp

Version:

MCP server for VibeRaven production-readiness: check_readiness, verify, heal, audit, and strict gate tools for Cursor, Claude Code, and Codex.

176 lines (162 loc) 5.66 kB
#!/usr/bin/env node const { spawn } = require('node:child_process'); const readline = require('node:readline'); const serverInfo = { name: 'viberaven-mcp', version: '0.1.0-beta.0', }; const tools = [ { name: 'viberaven_scan', description: 'Scan an AI-built app for launch-readiness gaps and write .viberaven artifacts.', inputSchema: { type: 'object', properties: { cwd: { type: 'string', description: 'Workspace directory to scan. Defaults to the MCP process cwd.' }, json: { type: 'boolean', description: 'Return CLI JSON output when supported.' }, }, }, }, { name: 'viberaven_next', description: 'Return the next recommended launch-readiness action from the last scan.', inputSchema: { type: 'object', properties: { cwd: { type: 'string', description: 'Workspace directory. Defaults to the MCP process cwd.' }, }, }, }, { name: 'viberaven_prompt', description: 'Return a copy-ready coding-agent prompt for a launch gap.', inputSchema: { type: 'object', properties: { cwd: { type: 'string', description: 'Workspace directory. Defaults to the MCP process cwd.' }, gap: { type: 'string', description: 'Gap id from .viberaven/last-scan.json or viberaven_next.' }, area: { type: 'string', description: 'Optional production area such as auth, database, payments, deployment.' }, provider: { type: 'string', description: 'Optional provider such as clerk, supabase, stripe, vercel.' }, }, }, }, { name: 'viberaven_status', description: 'Return VibeRaven account and scan quota status.', inputSchema: { type: 'object', properties: { cwd: { type: 'string', description: 'Workspace directory. Defaults to the MCP process cwd.' }, }, }, }, { name: 'viberaven_report', description: 'Rebuild .viberaven/report.html from the last scan without consuming scan quota.', inputSchema: { type: 'object', properties: { cwd: { type: 'string', description: 'Workspace directory. Defaults to the MCP process cwd.' }, }, }, }, ]; function write(message) { process.stdout.write(`${JSON.stringify(message)}\n`); } function runCli(args, cwd) { return new Promise((resolve) => { const child = spawn('npx', ['-y', '@viberaven/cli@beta', ...args], { cwd: cwd || process.cwd(), shell: process.platform === 'win32', env: process.env, }); let stdout = ''; let stderr = ''; child.stdout.on('data', (chunk) => { stdout += chunk.toString(); }); child.stderr.on('data', (chunk) => { stderr += chunk.toString(); }); child.on('close', (code) => { resolve({ code, stdout, stderr }); }); }); } function textResult(text, isError = false) { return { content: [{ type: 'text', text: text || '(no output)' }], isError, }; } async function callTool(name, input = {}) { const cwd = typeof input.cwd === 'string' && input.cwd.trim() ? input.cwd : undefined; if (name === 'viberaven_scan') { const args = ['scan']; if (input.json) args.push('--json'); const result = await runCli(args, cwd); return textResult(`${result.stdout}${result.stderr ? `\n${result.stderr}` : ''}`.trim(), result.code !== 0); } if (name === 'viberaven_next') { const result = await runCli(['next', '--json'], cwd); return textResult(`${result.stdout}${result.stderr ? `\n${result.stderr}` : ''}`.trim(), result.code !== 0); } if (name === 'viberaven_prompt') { const args = ['prompt', '--no-copy']; if (input.gap) args.push('--gap', String(input.gap)); if (input.area) args.push('--area', String(input.area)); if (input.provider) args.push('--provider', String(input.provider)); const result = await runCli(args, cwd); return textResult(`${result.stdout}${result.stderr ? `\n${result.stderr}` : ''}`.trim(), result.code !== 0); } if (name === 'viberaven_status') { const result = await runCli(['status', '--json'], cwd); return textResult(`${result.stdout}${result.stderr ? `\n${result.stderr}` : ''}`.trim(), result.code !== 0); } if (name === 'viberaven_report') { const result = await runCli(['report'], cwd); return textResult(`${result.stdout}${result.stderr ? `\n${result.stderr}` : ''}`.trim(), result.code !== 0); } return textResult(`Unknown VibeRaven MCP tool: ${name}`, true); } const rl = readline.createInterface({ input: process.stdin }); rl.on('line', async (line) => { if (!line.trim()) return; let request; try { request = JSON.parse(line); } catch { return; } if (request.method === 'initialize') { write({ jsonrpc: '2.0', id: request.id, result: { protocolVersion: '2024-11-05', capabilities: { tools: {} }, serverInfo, }, }); return; } if (request.method === 'notifications/initialized') { return; } if (request.method === 'tools/list') { write({ jsonrpc: '2.0', id: request.id, result: { tools } }); return; } if (request.method === 'tools/call') { const params = request.params || {}; const result = await callTool(params.name, params.arguments || {}); write({ jsonrpc: '2.0', id: request.id, result }); return; } write({ jsonrpc: '2.0', id: request.id, error: { code: -32601, message: `Method not found: ${request.method}` }, }); });