Files
rea/tests/boundary/mcp/toolInputAdmission.test.ts
T
b25d3517b6 fix(browser): report cancellation after cleanup and source-map abort (#2893)
* fix(browser): report cancellation after cleanup and source-map abort

An interrupt during CDP cleanup could still publish the captured page, and a source-map download abort was wrapped as a provider failure. Both now return AnalysisCancelledError for the requested operation.

* fix(setup): keep Devin configuration JSON-formatted

The empty DSH_HOME fix also changed Devin's declared format to "devin", which is not a configuration format and stops the typecheck. Devin still writes mcp_config.json.

* test(browser): keep cleanup cancellation coverage in its own suite

The added interrupt cases made the existing lifecycle suite exceed the function-length lint limit.

* test(ghidra): drop the duplicated path join import

The inventory MCP test imported join twice, which fails lint on current main.

* test(cli): type the retained skill directory pairs

noUncheckedIndexedAccess treats the unlabeled pair list as an open array, so the directory passed to join was string or undefined.

* fix(browser): preserve source-map cancellation results

* style(test): format the partial Frida load cancellation fixture

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: REA Maintenance <rea-maintenance@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 03:09:57 -07:00

95 lines
3.6 KiB
TypeScript

import { Client, InMemoryTransport } from "@modelcontextprotocol/client";
import type { CallToolResult } from "@modelcontextprotocol/server";
import { STDIO_DEFAULT_MAX_BUFFER_SIZE } from "@modelcontextprotocol/server";
import { describe, expect, it } from "vitest";
import { TOOL_CONTRACTS } from "../../../src/contracts/toolContracts.js";
import { COMPACT_INPUT_SCHEMA_BUDGET_BYTES } from "../../../src/config/mcpInputSchemaProfile.js";
import { EvidenceMcpServer } from "../../../src/server/EvidenceMcpServer.js";
import { ToolResultDelivery } from "../../../src/server/toolResult.js";
import { toolRegistrationOptions } from "../../../src/server/toolRegistrationOptions.js";
const createServer = (profile: "full" | "compact"): EvidenceMcpServer =>
new EvidenceMcpServer(
{ name: "tool-input-admission-test", version: "0" },
{ capabilities: {} },
undefined,
new ToolResultDelivery(STDIO_DEFAULT_MAX_BUFFER_SIZE),
profile === "compact"
? { budgetBytes: COMPACT_INPUT_SCHEMA_BUDGET_BYTES }
: undefined,
);
// These tests cover the registered SDK boundary with deterministic sentinel
// handlers. They establish canonical rejection before dispatch; they do not
// exercise providers, targets, or the shape of SDK-generated error text.
describe("catalog-wide MCP input admission", () => {
it.each(["full", "compact"] as const)(
"rejects unknown root keys before dispatch with the %s advertised profile",
async (profile) => {
const server = createServer(profile);
const dispatches = new Map<string, number>();
for (const contract of TOOL_CONTRACTS) {
server.registerTool(
contract.name,
toolRegistrationOptions(contract),
async (): Promise<CallToolResult> => {
dispatches.set(
contract.name,
(dispatches.get(contract.name) ?? 0) + 1,
);
return {
content: [{ type: "text", text: "sentinel handler reached" }],
isError: true,
};
},
);
}
const client = new Client({
name: "tool-input-admission-test",
version: "0",
});
const [clientTransport, serverTransport] =
InMemoryTransport.createLinkedPair();
try {
await server.connect(serverTransport);
await client.connect(clientTransport);
expect(TOOL_CONTRACTS.length).toBeGreaterThan(0);
for (const contract of TOOL_CONTRACTS) {
const input = {
...contract.examples[0]?.input,
__rea_unknown_admission_probe__: true,
};
expect(contract.inputSchema.safeParse(input).success).toBe(false);
const result = await client.callTool({
name: contract.name,
arguments: input,
});
expect(result.isError, contract.name).toBe(true);
expect(dispatches.has(contract.name), contract.name).toBe(false);
}
const firstWithExample = TOOL_CONTRACTS.find(
(contract) => contract.examples[0] !== undefined,
);
const liveExample = firstWithExample?.examples[0];
if (firstWithExample === undefined || liveExample === undefined)
throw new Error(
"No canonical example is available for the liveness check",
);
const live = await client.callTool({
name: firstWithExample.name,
arguments: liveExample.input,
});
expect(live.isError).toBe(true);
expect(dispatches.get(firstWithExample.name)).toBe(1);
} finally {
await Promise.allSettled([client.close(), server.close()]);
}
},
);
});