mirror of
https://github.com/morluto/rea
synced 2026-10-28 06:46:47 -04:00
refactor(mcp): share entropy summary delivery
This commit is contained in:
@@ -1,12 +1,11 @@
|
||||
import type { ByteEntropyService } from "../application/binaryDiagnostics/ByteEntropyService.js";
|
||||
import type { EvidenceMcpServer } from "./EvidenceMcpServer.js";
|
||||
|
||||
import { retainedSummaryDelivery } from "./retainedSummaryDelivery.js";
|
||||
import type { EvidenceLookup } from "../application/EvidenceInputResolver.js";
|
||||
import type { EvidenceWriter } from "../application/investigation/InvestigationRecordPort.js";
|
||||
import { toolContract } from "../contracts/toolContracts.js";
|
||||
import type { Logger } from "pino";
|
||||
import { runEvidenceToolOperation } from "./contractToolHandler.js";
|
||||
import { createRetainedSummaryToolHandler } from "./contractToolHandler.js";
|
||||
import { toolRegistrationOptions } from "./toolRegistrationOptions.js";
|
||||
import type { WithAdmittedAnalysis } from "./analysisAdmission.js";
|
||||
|
||||
@@ -20,28 +19,16 @@ export const registerByteEntropyTool = (
|
||||
withAdmittedAnalysis: WithAdmittedAnalysis | undefined,
|
||||
): void => {
|
||||
const contract = toolContract("inspect_byte_entropy");
|
||||
const delivery = retainedSummaryDelivery(contract.name, {
|
||||
recordEvidence,
|
||||
evidenceById,
|
||||
});
|
||||
server.registerTool(
|
||||
contract.name,
|
||||
toolRegistrationOptions(contract),
|
||||
async ({ detail, ...request }, context) =>
|
||||
runEvidenceToolOperation(
|
||||
{ server, logger, recordEvidence, withAdmittedAnalysis },
|
||||
contract,
|
||||
undefined,
|
||||
{ input: request, context },
|
||||
async (input, { signal }) => {
|
||||
const parameters = { detail };
|
||||
const ready = delivery.preflight(parameters);
|
||||
if (!ready.ok) return ready;
|
||||
const inspected = await entropyService.inspect(input, { signal });
|
||||
return inspected.ok
|
||||
? delivery.projectEvidence(parameters, inspected.value)
|
||||
: inspected;
|
||||
},
|
||||
),
|
||||
createRetainedSummaryToolHandler(
|
||||
server,
|
||||
contract,
|
||||
(input, options) => entropyService.inspect(input, options),
|
||||
logger,
|
||||
{ recordEvidence, evidenceById },
|
||||
withAdmittedAnalysis,
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
@@ -33,48 +33,63 @@ import { parseNativeExports } from "../../../src/native/exports/NativeExportPars
|
||||
import { createNativeExportsService } from "../../../src/composition/binaryDiagnostics.js";
|
||||
import { registerNativeExportsTool } from "../../../src/server/registerNativeExportsTool.js";
|
||||
|
||||
it("refuses a native exports summary before file admission when retention is unavailable", async () => {
|
||||
const service = createNativeExportsService();
|
||||
const server = new EvidenceMcpServer(
|
||||
{ name: "exports-preflight", version: "1" },
|
||||
{},
|
||||
undefined,
|
||||
createToolResultDelivery(undefined),
|
||||
);
|
||||
registerNativeExportsTool(server, service, silentLogger);
|
||||
const client = new Client({ name: "exports-preflight-client", version: "1" });
|
||||
onTestFinished(async () => {
|
||||
await client.close();
|
||||
await server.close();
|
||||
await service.close();
|
||||
});
|
||||
const [clientTransport, serverTransport] =
|
||||
InMemoryTransport.createLinkedPair();
|
||||
await server.connect(serverTransport);
|
||||
await client.connect(clientTransport);
|
||||
const schemas = await advertisedMcpToolSchemas(
|
||||
client,
|
||||
"inspect_native_exports",
|
||||
);
|
||||
expect(
|
||||
schemas.ajv.validate(schemas.inputSchema, {
|
||||
path: "/absent/native-library.so",
|
||||
detail: "summary",
|
||||
}),
|
||||
).toBe(true);
|
||||
const refused = await client.callTool({
|
||||
name: "inspect_native_exports",
|
||||
arguments: { path: "/absent/native-library.so", detail: "summary" },
|
||||
});
|
||||
expect(parseMcpToolError(refused).error).toMatchObject({
|
||||
code: "capability_unavailable",
|
||||
});
|
||||
const complete = await client.callTool({
|
||||
name: "inspect_native_exports",
|
||||
arguments: { path: "/absent/native-library.so" },
|
||||
});
|
||||
expect(parseMcpToolError(complete).error.code).toBe("invalid_request");
|
||||
});
|
||||
it.each(["inspect_native_exports", "inspect_byte_entropy"] as const)(
|
||||
"refuses %s summary before file admission when retention is unavailable",
|
||||
async (name) => {
|
||||
const service =
|
||||
name === "inspect_native_exports"
|
||||
? createNativeExportsService()
|
||||
: new ByteEntropyService();
|
||||
const server = new EvidenceMcpServer(
|
||||
{ name: "exports-preflight", version: "1" },
|
||||
{},
|
||||
undefined,
|
||||
createToolResultDelivery(undefined),
|
||||
);
|
||||
if (service instanceof ByteEntropyService)
|
||||
registerByteEntropyTool(
|
||||
server,
|
||||
service,
|
||||
silentLogger,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
);
|
||||
else registerNativeExportsTool(server, service, silentLogger);
|
||||
const client = new Client({
|
||||
name: "exports-preflight-client",
|
||||
version: "1",
|
||||
});
|
||||
onTestFinished(async () => {
|
||||
await client.close();
|
||||
await server.close();
|
||||
await service.close();
|
||||
});
|
||||
const [clientTransport, serverTransport] =
|
||||
InMemoryTransport.createLinkedPair();
|
||||
await server.connect(serverTransport);
|
||||
await client.connect(clientTransport);
|
||||
const schemas = await advertisedMcpToolSchemas(client, name);
|
||||
expect(
|
||||
schemas.ajv.validate(schemas.inputSchema, {
|
||||
path: "/absent/native-library.so",
|
||||
detail: "summary",
|
||||
}),
|
||||
).toBe(true);
|
||||
const refused = await client.callTool({
|
||||
name,
|
||||
arguments: { path: "/absent/native-library.so", detail: "summary" },
|
||||
});
|
||||
expect(parseMcpToolError(refused).error).toMatchObject({
|
||||
code: "capability_unavailable",
|
||||
});
|
||||
const complete = await client.callTool({
|
||||
name,
|
||||
arguments: { path: "/absent/native-library.so" },
|
||||
});
|
||||
expect(parseMcpToolError(complete).error.code).toBe("invalid_request");
|
||||
},
|
||||
);
|
||||
|
||||
it.each([false, true])(
|
||||
"preserves a completed byte report through MCP cleanup failure and retry (retained=%s)",
|
||||
|
||||
Reference in New Issue
Block a user