JEISA-WEBAPP/scripts/test-pb-mcp.mjs
Dave 0b8f003f87 feat(po): add MCP server, agent-friendly PO API, and PDF rendering
- server/pb-mcp.mjs: 15-tool MCP server for purchase-order workflow
  (CRUD, suppliers, stock, customers, customer POs, validate_draft,
  generate_order_number)
- src/lib/api/purchaseOrders.ts: typed helpers shared by UI + MCP + n8n
- src/components/purchase-orders/pdf.ts: jsPDF PO generator
- src/components/purchase-orders/ViewOrderDialog.tsx: read-only dialog
- src/components/purchase-orders/AttachmentsDialog.tsx: attachment mgmt
- src/components/purchase-orders/NumberInput.tsx: numeric input
- scripts/mcp-create-po.mjs: end-to-end driver (MCP stdio smoke test)
- pocketbase/pb_migrations/1786200007_*: schema migration
- docs/MCP_AND_N8N.md: MCP + n8n integration guide

Verified end-to-end via MCP: generate_order_number → create_purchase_order
with snapshot_items for an actual quote (DRILL-19042 / 161053).
2026-09-07 11:12:13 +02:00

98 lines
2.7 KiB
JavaScript

#!/usr/bin/env node
/**
* End-to-end smoke test for server/pb-mcp.mjs.
*
* Spawns the MCP server over stdio, sends an `initialize` + `tools/list`
* handshake, then calls `list_customers` to confirm the live PB connection
* is working. Prints whatever the server returns.
*
* node scripts/test-pb-mcp.mjs
*/
import { spawn } from "node:child_process";
import { fileURLToPath } from "node:url";
import path from "node:path";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const ROOT = path.resolve(__dirname, "..");
const child = spawn("node", ["--import", "tsx/esm", "server/pb-mcp.mjs"], {
cwd: ROOT,
stdio: ["pipe", "pipe", "inherit"],
});
let buffer = "";
const pending = new Map();
let nextId = 1;
function send(method, params = {}) {
return new Promise((resolve, reject) => {
const id = nextId++;
pending.set(id, { resolve, reject });
const msg = JSON.stringify({ jsonrpc: "2.0", id, method, params });
child.stdin.write(msg + "\n");
});
}
child.stdout.on("data", (chunk) => {
buffer += chunk.toString("utf8");
let nl;
while ((nl = buffer.indexOf("\n")) !== -1) {
const line = buffer.slice(0, nl).trim();
buffer = buffer.slice(nl + 1);
if (!line) continue;
let parsed;
try {
parsed = JSON.parse(line);
} catch (err) {
console.error("[test] non-JSON line:", line);
continue;
}
if (parsed.id != null && pending.has(parsed.id)) {
const { resolve, reject } = pending.get(parsed.id);
pending.delete(parsed.id);
if (parsed.error) reject(new Error(JSON.stringify(parsed.error)));
else resolve(parsed.result);
}
}
});
const shutdown = () => {
child.kill();
process.exit(0);
};
process.on("SIGINT", shutdown);
process.on("SIGTERM", shutdown);
try {
console.log("[test] initialize");
const init = await send("initialize", {
protocolVersion: "2024-11-05",
capabilities: {},
clientInfo: { name: "test-pb-mcp", version: "0.0.0" },
});
console.log("[test] server:", init.serverInfo);
const tools = await send("tools/list");
console.log(`[test] tools advertised: ${tools.tools.length}`);
for (const t of tools.tools) console.log(` - ${t.name}`);
console.log("\n[test] list_customers q=JER");
const customers = await send("tools/call", {
name: "list_customers",
arguments: { q: "JER" },
});
console.log(JSON.stringify(customers, null, 2));
console.log("\n[test] list_customer_pos perPage=3");
const cpos = await send("tools/call", {
name: "list_customer_pos",
arguments: { perPage: 3 },
});
console.log(JSON.stringify(cpos, null, 2));
} catch (err) {
console.error("[test] FAILED:", err?.message || err);
process.exitCode = 1;
} finally {
child.kill();
}