Files
next-tjwater-agent/tests/mcp/nativeCallIdentity.test.ts
T

43 lines
2.9 KiB
TypeScript

import { fromJsonSchema } from "@modelcontextprotocol/server";
import { normalizeRuntimeMessage } from "../../src/runtime/opencode2-events.js";
import { expect, it } from "bun:test";
import { resolveMatchingNativeCall } from "../../src/mcp/native-call-identity.js";
import { allowedQueryTools } from "../../src/runtime/query-mcp-policy.js";
import { QUERY_MCP_AUTOALLOW_TOOL_IDS } from "../../src/runtime/product-mcp-tools.js";
const part = (callID: string, status = "running", input: unknown = { duration: 60 }) => ({ type: "tool", tool: "mcp_tjwater_run_simulation", callID, state: { status, input } });
it("resolves only an exact active persisted native call and fails closed on ambiguity", () => {
expect(resolveMatchingNativeCall([{ parts: [part("old", "completed"), part("current")] }], "run_simulation", { duration: 60 })).toBe("current");
expect(resolveMatchingNativeCall([{ parts: [part("current")] }], "run_simulation", { duration: 61 })).toBeNull();
expect(resolveMatchingNativeCall([{ parts: [part("a"), part("b")] }], "run_simulation", { duration: 60 })).toBeNull();
});
it("allows readers only job reads and never autoallows cancellation", () => {
const context = { sessionId: "s", clientSessionId: "c", actorKey: "a", projectKey: "p", projectId: "p", traceId: "t", purpose: "task_execution" as const };
const tools = allowedQueryTools(context, true);
for (const name of ["get_calculation_job", "get_calculation_result", "list_calculation_jobs"]) expect(tools).toContain(`mcp_tjwater_${name}`);
expect(tools).not.toContain("mcp_tjwater_cancel_calculation_job");
expect(tools).not.toContain("mcp_tjwater_run_simulation");
expect(QUERY_MCP_AUTOALLOW_TOOL_IDS).not.toContain("mcp_tjwater_cancel_calculation_job");
});
it("matches omitted MCP defaults against the actual native message-list normalizer", async () => {
const schema = fromJsonSchema<Record<string, unknown>>({
type: "object", additionalProperties: false,
properties: { timeout_seconds: { type: "integer", default: 120 }, dry_run: { type: "boolean", default: true } },
});
for (const input of [{}, { timeout_seconds: 900 }, { dry_run: false }]) {
const validated = await schema["~standard"].validate(input);
expect(validated.issues).toBeUndefined();
if (!("value" in validated)) throw new Error("MCP input validation failed");
expect(validated.value).toEqual(input);
const native = {
id: "assistant-message", type: "assistant", time: { created: 1 },
content: [{ type: "tool", id: "native-call", name: "mcp_tjwater_run_simulation", time: { created: 1 }, state: { status: "running", input } }],
} as unknown as Parameters<typeof normalizeRuntimeMessage>[0];
const messages = [normalizeRuntimeMessage(native, "native-session")];
expect(resolveMatchingNativeCall(messages, "run_simulation", validated.value!)).toBe("native-call");
expect(resolveMatchingNativeCall(messages, "run_simulation", { ...validated.value, arbitrary_parameter: true })).toBeNull();
}
});