@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
JavaScript
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}` },
});
});