import { Client, InMemoryTransport } from "@modelcontextprotocol/client"; import { describe, expect, it } from "vitest"; import { BinarySession } from "../src/application/BinarySession.js"; import { FUNCTION_COMPARISON_EXAMPLE } from "../src/contracts/functionComparisonExample.js"; import { createEvidence } from "../src/domain/evidence.js"; import { createServer } from "../src/server/createServer.js"; import { observed } from "./fixtures/analysisExecution.js"; describe("function comparison MCP integration", () => { it("records linked comparison Evidence and an approved residual unknown", async () => { const session = new BinarySession(() => ({ health: () => Promise.resolve(), execute: () => Promise.resolve(observed(null)), close: () => Promise.resolve(), })); expect(session.recordEvidence(FUNCTION_COMPARISON_EXAMPLE.left).ok).toBe( true, ); expect(session.recordEvidence(FUNCTION_COMPARISON_EXAMPLE.right).ok).toBe( true, ); const server = createServer(session, session); const client = new Client({ name: "function-comparison-test", version: "1", }); const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair(); try { await server.connect(serverTransport); await client.connect(clientTransport); const result = await client.callTool({ name: "compare_functions", arguments: { left_evidence_ids: [FUNCTION_COMPARISON_EXAMPLE.left.evidence_id], right_evidence_ids: [FUNCTION_COMPARISON_EXAMPLE.right.evidence_id], unknown_registry_approved: true, }, }); expect(result.isError).not.toBe(true); expect(result.structuredContent).toMatchObject({ result: { status: expect.any(String) }, evidence_id: expect.stringMatching(/^ev_[a-f0-9]{64}$/u), evidence_uri: expect.stringMatching(/^rea:\/\/evidence\/ev_/u), }); const unknowns = await client.callTool({ name: "list_unknowns", arguments: { domain: "function-comparison" }, }); expect(unknowns.structuredContent).toMatchObject({ result: [ expect.objectContaining({ domain: "function-comparison", recommended_probes: [ expect.objectContaining({ rationale: "Capture complete dossiers for both functions under the same target context and analysis limits.", }), ], }), ], }); } finally { await Promise.allSettled([ client.close(), server.close(), session.close(), ]); } }); it("rejects dangling Evidence links before recording output", async () => { const session = new BinarySession(() => ({ health: () => Promise.resolve(), execute: () => Promise.resolve(observed(null)), close: () => Promise.resolve(), })); const server = createServer(session, session); const client = new Client({ name: "function-link-test", version: "1" }); const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair(); try { await server.connect(serverTransport); await client.connect(clientTransport); const result = await client.callTool({ name: "compare_functions", arguments: { left_evidence_ids: [FUNCTION_COMPARISON_EXAMPLE.left.evidence_id], right_evidence_ids: [FUNCTION_COMPARISON_EXAMPLE.right.evidence_id], }, }); expect(result.isError).toBe(true); expect(session.exportEvidenceBundle().records).toEqual([]); } finally { await Promise.allSettled([ client.close(), server.close(), session.close(), ]); } }); it("rejects Evidence IDs with the wrong operation", async () => { const session = new BinarySession(() => ({ health: () => Promise.resolve(), execute: () => Promise.resolve(observed(null)), close: () => Promise.resolve(), })); const wrong = createEvidence( undefined, { id: "fixture", name: "Fixture", version: "1" }, { operation: "inventory_artifact", parameters: {}, result: {}, }, ); session.recordEvidence(wrong); session.recordEvidence(FUNCTION_COMPARISON_EXAMPLE.right); const server = createServer(session, session); const client = new Client({ name: "function-authority-test", version: "1", }); const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair(); try { await server.connect(serverTransport); await client.connect(clientTransport); const result = await client.callTool({ name: "compare_functions", arguments: { left_evidence_ids: [wrong.evidence_id], right_evidence_ids: [FUNCTION_COMPARISON_EXAMPLE.right.evidence_id], }, }); expect(result.isError).toBe(true); expect(session.exportEvidenceBundle().records).toHaveLength(2); } finally { await Promise.allSettled([ client.close(), server.close(), session.close(), ]); } }); });