mirror of
https://github.com/morluto/rea.git
synced 2026-11-03 13:26:10 +00:00
Keep exact wildcard, whitespace and aligned-mask validation in the shared domain refinement. Advertise descriptive string inputs instead of repeating regexes, and remove discarded byte/mask array materialization. Preserve caller spelling and error precedence; cover large public stdio queries, provider-boundary parsing and connection recovery. Fixes #5257
64 lines
2.3 KiB
TypeScript
64 lines
2.3 KiB
TypeScript
import { expect, it } from "vitest";
|
|
import type { CallToolResult } from "@modelcontextprotocol/server";
|
|
|
|
import { connectLocalToolsMcp } from "../../fixtures/localToolsMcp.js";
|
|
import { parseMcpToolError } from "../../fixtures/mcpToolError.js";
|
|
|
|
const LARGE_PATTERN_BYTES = 4 * 1024 * 1024;
|
|
|
|
const inputValidationText = (result: CallToolResult): string => {
|
|
expect(result.isError).toBe(true);
|
|
const text = result.content.find((item) => item.type === "text");
|
|
if (text?.type !== "text") throw new Error("Input error omitted text");
|
|
expect(text.text).toMatch(
|
|
/^Input validation error: Invalid arguments for tool search_bytes:/u,
|
|
);
|
|
expect(text.text).not.toMatch(/RangeError|Maximum call stack/i);
|
|
return text.text;
|
|
};
|
|
|
|
it("validates large search_bytes inputs without breaking stdio MCP", async () => {
|
|
const { client, call } = await connectLocalToolsMcp();
|
|
|
|
const definition = (await client.listTools()).tools.find(
|
|
({ name }) => name === "search_bytes",
|
|
);
|
|
expect(definition?.inputSchema.properties?.pattern).toMatchObject({
|
|
type: "string",
|
|
});
|
|
expect(definition?.inputSchema.properties?.pattern).not.toHaveProperty(
|
|
"pattern",
|
|
);
|
|
expect(definition?.inputSchema.properties?.mask).toMatchObject({
|
|
type: "string",
|
|
});
|
|
expect(definition?.inputSchema.properties?.mask).not.toHaveProperty(
|
|
"pattern",
|
|
);
|
|
|
|
const initial = await call("search_bytes", { pattern: "aa" });
|
|
expect(parseMcpToolError(initial).error.code).toBe("target_unavailable");
|
|
|
|
const largePattern = "aa".repeat(LARGE_PATTERN_BYTES);
|
|
const largeSearch = await call("search_bytes", { pattern: largePattern });
|
|
expect(parseMcpToolError(largeSearch).error.code).toBe("target_unavailable");
|
|
|
|
const malformedPattern = `${largePattern.slice(0, -2)}a?`;
|
|
const malformed = inputValidationText(
|
|
await call("search_bytes", { pattern: malformedPattern }),
|
|
);
|
|
expect(malformed).toMatch(/pattern/i);
|
|
|
|
const oversizedMask = inputValidationText(
|
|
await call("search_bytes", {
|
|
pattern: "aa",
|
|
mask: "ff".repeat(LARGE_PATTERN_BYTES),
|
|
}),
|
|
);
|
|
expect(oversizedMask).toContain("Mask length must equal pattern length");
|
|
|
|
const final = await call("search_bytes", { pattern: "aa" });
|
|
expect(parseMcpToolError(final).error.code).toBe("target_unavailable");
|
|
await client.ping();
|
|
});
|