mirror of
https://github.com/ruvnet/RuView
synced 2026-07-20 17:03:24 +00:00
4001e9e178
A host-portable RuView agent harness minted via MetaHarness and hardened per ADR-182. Published as @ruvnet/ruview@0.1.0 (bare `ruview` blocked by npm's typosquat filter → scoped fallback). What it does: - 6 fail-closed `ruview.*` tools (onboard, claim_check, verify, node_monitor, calibrate, node_flash) exposed as CLI verbs + a dependency-free MCP stdio server. - The "prove everything" rule made executable: `ruview.claim_check` flags untagged accuracy claims and the retracted "100%" framing. - 5 host-neutral skills (onboard/provision-node/calibrate-room/ train-pose/verify) + bundled .claude/ config + provenance manifest. Validated: 17/17 unit tests, live MCP handshake, `ruview.verify` ran the real verify.py to VERDICT: PASS, clean `npx @ruvnet/ruview` from registry. Packs to 16.7 kB / 21 files; kernel+host are optionalDependencies so the operator tools install lightweight. README: documented as the portable, multi-host companion to the in-repo plugins/ruview/ Claude Code plugin (not a replacement).
69 lines
2.6 KiB
JavaScript
69 lines
2.6 KiB
JavaScript
// SPDX-License-Identifier: MIT
|
|
// RuView harness — minimal MCP stdio server (JSON-RPC 2.0 over stdin/stdout).
|
|
//
|
|
// Dependency-free on purpose: a published `npx ruview` must `mcp start` without
|
|
// pulling the full MCP SDK. Implements the subset hosts use: `initialize`,
|
|
// `tools/list`, `tools/call`, and the `notifications/initialized` ack. Logs go to
|
|
// stderr ONLY — stdout is the JSON-RPC channel and must stay clean.
|
|
|
|
import { createInterface } from 'node:readline';
|
|
import { listTools, runTool } from './tools.js';
|
|
|
|
const PROTOCOL_VERSION = '2024-11-05';
|
|
const SERVER_INFO = { name: 'ruview', version: '0.1.0' };
|
|
|
|
function send(msg) {
|
|
process.stdout.write(JSON.stringify(msg) + '\n');
|
|
}
|
|
function result(id, res) { send({ jsonrpc: '2.0', id, result: res }); }
|
|
function error(id, code, message) { send({ jsonrpc: '2.0', id, error: { code, message } }); }
|
|
function log(...a) { process.stderr.write('[ruview-mcp] ' + a.join(' ') + '\n'); }
|
|
|
|
function handle(msg) {
|
|
const { id, method, params } = msg;
|
|
switch (method) {
|
|
case 'initialize':
|
|
return result(id, {
|
|
protocolVersion: PROTOCOL_VERSION,
|
|
capabilities: { tools: { listChanged: false } },
|
|
serverInfo: SERVER_INFO,
|
|
instructions: 'RuView WiFi-sensing operator tools. All results are fail-closed; accuracy claims must pass ruview.claim_check.',
|
|
});
|
|
case 'notifications/initialized':
|
|
case 'initialized':
|
|
return; // notification — no response
|
|
case 'ping':
|
|
return result(id, {});
|
|
case 'tools/list':
|
|
return result(id, { tools: listTools() });
|
|
case 'tools/call': {
|
|
const name = params?.name;
|
|
const args = params?.arguments || {};
|
|
const out = runTool(name, args);
|
|
// MCP content envelope: text block with the JSON, isError reflects ok=false.
|
|
return result(id, {
|
|
content: [{ type: 'text', text: JSON.stringify(out, null, 2) }],
|
|
isError: out && out.ok === false,
|
|
});
|
|
}
|
|
default:
|
|
if (id !== undefined) error(id, -32601, `Method not found: ${method}`);
|
|
}
|
|
}
|
|
|
|
export function startMcpServer() {
|
|
log(`starting (protocol ${PROTOCOL_VERSION}, ${listTools().length} tools)`);
|
|
const rl = createInterface({ input: process.stdin, crlfDelay: Infinity });
|
|
rl.on('line', (line) => {
|
|
const s = line.trim();
|
|
if (!s) return;
|
|
let msg;
|
|
try { msg = JSON.parse(s); } catch { return log('bad JSON line dropped'); }
|
|
try { handle(msg); } catch (err) {
|
|
if (msg && msg.id !== undefined) error(msg.id, -32603, String(err && err.message || err));
|
|
log('handler error:', String(err));
|
|
}
|
|
});
|
|
rl.on('close', () => { log('stdin closed — exiting'); process.exit(0); });
|
|
}
|