snapkitty
developer-tools
python
ortho32-mcp / tests /protocol.test.mjs
SNAPKITTYWEST's picture
push from SNAPKITTYWEST/ortho32-mcp
bbe7300 verified
Raw History Blame Contribute Delete
4.13 kB
import { spawnSync } from "node:child_process";
import { existsSync } from "node:fs";
import { join } from "node:path";
const root = new URL("..", import.meta.url).pathname.replace(/^\/([A-Za-z]:)/, "$1");
const script = join(root, "bin", "ortho32-mcp.awk");
const awk = process.env.AWK || [
"awk",
"gawk",
"C:\\Program Files\\Git\\usr\\bin\\awk.exe",
"C:\\Program Files\\Git\\usr\\bin\\gawk.exe",
].find((candidate) => {
if (candidate.includes(":")) return existsSync(candidate);
const probe = spawnSync(candidate, ["--version"], { encoding: "utf8" });
return probe.status === 0 || probe.stderr || probe.stdout;
});
if (!awk) {
console.error("FAIL: no awk executable found. Set AWK=/path/to/awk.");
process.exit(1);
}
function request(id, method, params) {
return JSON.stringify({ jsonrpc: "2.0", id, method, ...(params === undefined ? {} : { params }) });
}
function notification(method, params) {
return JSON.stringify({ jsonrpc: "2.0", method, ...(params === undefined ? {} : { params }) });
}
function call(id, name, args) {
return request(id, "tools/call", { name, arguments: args });
}
function run(lines) {
const input = `${lines.join("\n")}\n`;
const res = spawnSync(awk, ["-f", script], { input, encoding: "utf8" });
if (res.error) throw res.error;
if (res.status !== 0) {
throw new Error(`awk exited ${res.status}\nstdout=${res.stdout}\nstderr=${res.stderr}`);
}
const out = res.stdout.trim() ? res.stdout.trim().split(/\r?\n/).map((line) => JSON.parse(line)) : [];
return { out, stderr: res.stderr };
}
function assert(cond, message) {
if (!cond) throw new Error(message);
}
const session = run([
request(1, "initialize", { protocolVersion: "2024-11-05" }),
notification("notifications/initialized"),
request(2, "tools/list"),
call(3, "awk_count", { input: "a\n\nb" }),
call(4, "awk_fields", { input: "name,age\nAlice,30\nBob,42", separator: ",", header: true }),
call(5, "awk_filter", { input: "alpha\nbeta\ngamma", pattern: "^a" }),
call(6, "awk_regex", { input: "alpha\nbeta\ngamma", pattern: "a" }),
call(7, "awk_transform", { input: " Hello\nWorld ", operation: "trim" }),
request(8, "unknown/method"),
call(9, "missing_tool", {}),
call(10, "awk_count", {}),
"{not-json",
]);
assert(session.out.length === 11, `expected 11 responses, got ${session.out.length}`);
assert(session.out[0].result.serverInfo.name === "ortho32-mcp-awk", "initialize server name mismatch");
assert(session.out[1].result.tools.length === 5, "tools/list should expose 5 tools");
assert(session.out[2].result.structuredContent.lines === 3, "awk_count line count mismatch");
assert(session.out[3].result.structuredContent.records[0].name === "Alice", "awk_fields header mapping mismatch");
assert(session.out[4].result.structuredContent.lines[0] === "alpha", "awk_filter mismatch");
assert(session.out[5].result.structuredContent.matchingLines === 3, "awk_regex mismatch");
assert(session.out[6].result.structuredContent.output === "Hello\nWorld", "awk_transform trim mismatch");
assert(session.out[7].error.code === -32601, "unknown method should be -32601");
assert(session.out[8].error.message.includes("Unknown tool"), "unknown tool error mismatch");
assert(session.out[9].error.message.includes("input required"), "invalid args error mismatch");
assert(session.out[10].error.code === -32700, "malformed JSON should be parse error");
const replayA = run([call(11, "awk_count", { input: "one\ntwo" })]).out[0];
const replayB = run([call(11, "awk_count", { input: "one\ntwo" })]).out[0];
assert(JSON.stringify(replayA) === JSON.stringify(replayB), "deterministic replay mismatch");
for (const msg of session.out) {
assert(msg.jsonrpc === "2.0", "stdout contained non JSON-RPC payload");
}
console.log(`AWK=${awk}`);
console.log("initialize: ok");
console.log("tools/list: ok");
console.log("tools/call: ok");
console.log("unknown method/tool/errors: ok");
console.log("notifications: ok");
console.log("malformed JSON: ok");
console.log("deterministic replay: ok");