This repository has been archived on 2026-06-07. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
master-ai/vibn-agent-runner/scripts/smoke-mcp.js

138 lines
4.8 KiB
JavaScript

#!/usr/bin/env node
/* eslint-disable */
// Generic stdio MCP smoke runner.
//
// Usage:
// node scripts/smoke-mcp.js <server-name> [--call toolName] [--args '{"foo":"bar"}']
//
// Server names map to dist/mcp/<name>-server.js. Dummy env vars are injected so
// the server boots without needing real credentials — we're exercising the
// protocol surface, not the upstream API.
const { spawn } = require('child_process');
const path = require('path');
const SERVER_DUMMY_ENV = {
coolify: { COOLIFY_API_URL: 'https://smoke.example', COOLIFY_API_TOKEN: 'smoke-token' },
gitea: { GITEA_API_URL: 'https://smoke.example', GITEA_API_TOKEN: 'smoke-token', GITEA_USERNAME: 'smoke' },
workspace: {
WORKSPACE_ROOT: path.resolve(__dirname, '..'),
GITEA_API_URL: 'https://smoke.example',
GITEA_API_TOKEN: 'smoke-token',
GITEA_USERNAME: 'smoke',
},
'vibn-platform': {
SESSION_KEY: 'smoke-session',
GITEA_API_URL: 'https://smoke.example',
GITEA_API_TOKEN: 'smoke-token',
},
agent: { AGENT_RUNNER_URL: 'http://127.0.0.1:65535' },
};
const DEFAULT_CALL = {
coolify: { name: 'coolify_list_projects', args: {} },
gitea: { name: 'list_repos', args: {} },
workspace: { name: 'list_directory', args: { path: '.' } },
'vibn-platform': {
name: 'save_memory',
args: { key: 'smoke_probe', type: 'note', value: 'hello from smoke test' },
},
agent: { name: 'get_job_status', args: { job_id: 'smoke-nonexistent' } },
};
const args = process.argv.slice(2);
const serverName = args[0];
if (!serverName) {
console.error('usage: node scripts/smoke-mcp.js <coolify|gitea|...> [--call name] [--args json]');
process.exit(2);
}
let callName = DEFAULT_CALL[serverName]?.name;
let callArgs = DEFAULT_CALL[serverName]?.args || {};
for (let i = 1; i < args.length; i++) {
if (args[i] === '--call') callName = args[++i];
else if (args[i] === '--args') callArgs = JSON.parse(args[++i]);
}
const serverPath = path.resolve(__dirname, '..', 'dist', 'mcp', `${serverName}-server.js`);
const env = { ...process.env, ...(SERVER_DUMMY_ENV[serverName] || {}) };
// Only inject dummies for keys that aren't already set by the real environment.
for (const [k, v] of Object.entries(SERVER_DUMMY_ENV[serverName] || {})) {
if (!process.env[k]) env[k] = v;
}
const child = spawn(process.execPath, [serverPath], { stdio: ['pipe', 'pipe', 'inherit'], env });
let buf = '';
const pending = new Map();
let nextId = 1;
function send(method, params) {
const id = nextId++;
return new Promise((resolve, reject) => {
pending.set(id, { resolve, reject });
child.stdin.write(JSON.stringify({ jsonrpc: '2.0', id, method, params }) + '\n');
});
}
function notify(method, params) {
child.stdin.write(JSON.stringify({ jsonrpc: '2.0', method, params }) + '\n');
}
child.stdout.on('data', (chunk) => {
buf += chunk.toString('utf8');
let idx;
while ((idx = buf.indexOf('\n')) !== -1) {
const line = buf.slice(0, idx).trim();
buf = buf.slice(idx + 1);
if (!line) continue;
try {
const msg = JSON.parse(line);
if (msg.id && pending.has(msg.id)) {
const { resolve, reject } = pending.get(msg.id);
pending.delete(msg.id);
if (msg.error) reject(new Error(msg.error.message));
else resolve(msg.result);
}
} catch {
console.error('[smoke] non-JSON line:', line);
}
}
});
child.on('error', (err) => { console.error('[smoke] spawn error:', err); process.exit(2); });
(async () => {
try {
const init = await send('initialize', {
protocolVersion: '2024-11-05',
capabilities: {},
clientInfo: { name: 'vibn-smoke', version: '0.0.0' },
});
console.log('initialize.serverInfo:', JSON.stringify(init.serverInfo));
notify('notifications/initialized', {});
const tools = await send('tools/list', {});
console.log(`\ntools/list returned ${tools.tools.length} tools:`);
for (const t of tools.tools) {
const required = t.inputSchema?.required ?? [];
console.log(` - ${t.name}${required.length ? ` (required: ${required.join(', ')})` : ''}`);
}
if (callName) {
const callResult = await send('tools/call', { name: callName, arguments: callArgs });
console.log(`\ntools/call ${callName}(${JSON.stringify(callArgs)}):`);
console.log(JSON.stringify(callResult, null, 2).slice(0, 500));
}
console.log('\n[smoke] ✓ MCP stdio round-trip successful');
child.kill();
process.exit(0);
} catch (err) {
console.error('[smoke] FAILED:', err.message);
child.kill();
process.exit(1);
}
})();