chore: convert submodules to standard directories for true monorepo structure
This commit is contained in:
137
vibn-agent-runner/scripts/smoke-mcp.js
Normal file
137
vibn-agent-runner/scripts/smoke-mcp.js
Normal file
@@ -0,0 +1,137 @@
|
||||
#!/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);
|
||||
}
|
||||
})();
|
||||
Reference in New Issue
Block a user