feat(mcp)!: require Evidence for managed reconstruction

This commit is contained in:
morluto 2026-07-16 20:15:24 +08:00
parent fd08025dcd
commit d36af5d0f7
5 changed files with 52 additions and 15 deletions

View File

@ -25,9 +25,16 @@ export const importManagedReconstructionEvidence = (
const parsed = managedReconstructionImportInputSchema.safeParse(rawInput);
if (!parsed.success)
return err(new AnalysisInputError(OPERATION, { cause: parsed.error }));
return importManagedReconstructionEvidenceValidated(parsed.data);
};
/** Import reconstruction from input parsed by a trusted adapter. */
export const importManagedReconstructionEvidenceValidated = (
input: ManagedReconstructionImportInput,
): Result<Evidence, AnalysisError> => {
try {
const result = importManagedReconstruction(parsed.data);
return ok(createManagedReconstructionEvidence(parsed.data, result));
const result = importManagedReconstruction(input);
return ok(createManagedReconstructionEvidence(input, result));
} catch (cause: unknown) {
return err(
cause instanceof TypeError || cause instanceof z.ZodError

View File

@ -39,6 +39,12 @@ export const managedRuntimeCorrelationReferenceInputSchema =
.omit({ static_members: true })
.extend({ static_members_evidence_id: managedEvidenceIdSchema });
/** MCP reference for importing reconstruction against session Evidence. */
export const managedReconstructionReferenceInputSchema =
managedReconstructionImportInputSchema
.omit({ static_members: true })
.extend({ static_members_evidence_id: managedEvidenceIdSchema });
const comparisonOutputSchema =
managedWorkflowOutputSchemas.compare_managed_members;
if (comparisonOutputSchema === undefined)
@ -82,21 +88,22 @@ export const MANAGED_WORKFLOW_TOOL_CONTRACTS = [
},
{
name: "import_managed_reconstruction",
...toolContractMetadata("import_managed_reconstruction"),
description:
"Import decompiler-produced managed reconstruction against authenticated inspect_managed_members Evidence. The workflow locks each method to artifact SHA-256, MVID, metadata token, signature hash, and normalized IL hash, records the decompiler identity and options, and marks C# or pseudocode as analyst inference rather than canonical byte observation.",
kind: "application",
inputSchema: managedReconstructionImportInputSchema,
inputSchema: managedReconstructionReferenceInputSchema,
outputSchema: reconstructionOutputSchema,
annotations: {
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: false,
},
examples: [
{
title: "Import a decompiler reconstruction for one managed method",
input: MANAGED_RECONSTRUCTION_IMPORT_EXAMPLE,
input: {
static_members_evidence_id:
MANAGED_RECONSTRUCTION_IMPORT_EXAMPLE.static_members.evidence_id,
decompiler: MANAGED_RECONSTRUCTION_IMPORT_EXAMPLE.decompiler,
methods: MANAGED_RECONSTRUCTION_IMPORT_EXAMPLE.methods,
notes: MANAGED_RECONSTRUCTION_IMPORT_EXAMPLE.notes,
},
},
],
},

View File

@ -98,6 +98,7 @@ export const TOOL_EFFECTS: Readonly<Record<string, ToolEffects>> = {
inspect_managed_members: evidence,
inspect_managed_native_boundaries: evidence,
compare_managed_members: sessionEvidence,
import_managed_reconstruction: sessionEvidence,
plan_managed_runtime_correlation: sessionEvidence,
list_browser_targets: browserEvidence,
inspect_web_page: browserEvidence,

View File

@ -2,18 +2,18 @@ import type { McpServer } from "@modelcontextprotocol/server";
import type { BinarySessionPort } from "../application/BinarySession.js";
import { compareManagedMembersEvidenceValidated } from "../application/ManagedMemberComparisonService.js";
import { importManagedReconstructionEvidence } from "../application/ManagedReconstructionService.js";
import { importManagedReconstructionEvidenceValidated } from "../application/ManagedReconstructionService.js";
import {
planManagedRuntimeCorrelationEvidenceValidated,
type ManagedRuntimeCorrelationDependencies,
} from "../application/ManagedRuntimeCorrelationService.js";
import {
compareManagedMembersReferenceInputSchema,
managedReconstructionReferenceInputSchema,
managedRuntimeCorrelationReferenceInputSchema,
MANAGED_WORKFLOW_TOOL_CONTRACTS,
} from "../contracts/managedWorkflowToolContracts.js";
import { managedMemberComparisonResultSchema } from "../domain/managedMemberComparison.js";
import { managedReconstructionImportInputSchema } from "../domain/managedReconstruction.js";
import type { Evidence } from "../domain/evidence.js";
import type { EvidenceIntegrityError } from "../domain/errors.js";
import type { Result } from "../domain/result.js";
@ -120,11 +120,33 @@ export const registerManagedWorkflowTools = (
reconstructionContract.name,
toolRegistrationOptions(reconstructionContract),
async (input) => {
const parsed = managedReconstructionImportInputSchema.parse(input);
const parsedInput = safeParseToolInput(
managedReconstructionReferenceInputSchema,
input,
reconstructionContract.name,
);
if (!parsedInput.ok)
return toCallToolResult(parsedInput, reconstructionContract);
const resolved = resolveManagedEvidence(options.session, [
parsedInput.value.static_members_evidence_id,
]);
if (!resolved.ok)
return toCallToolResult(resolved, reconstructionContract);
const staticMembers = resolved.value[0];
if (staticMembers === undefined)
throw new TypeError(
"Managed reconstruction Evidence resolution failed",
);
const {
static_members_evidence_id: _staticMembersEvidenceId,
...referencedInput
} = parsedInput.value;
const parsed = { ...referencedInput, static_members: staticMembers };
const result = await logToolExecution(
options.logger,
reconstructionContract.name,
() => Promise.resolve(importManagedReconstructionEvidence(parsed)),
() =>
Promise.resolve(importManagedReconstructionEvidenceValidated(parsed)),
);
if (!result.ok) return toCallToolResult(result, reconstructionContract);
const recordedSource = options.recordEvidence?.(parsed.static_members);

View File

@ -209,7 +209,7 @@ describe("managed artifact MCP tools", () => {
await client.callTool({
name: "import_managed_reconstruction",
arguments: {
static_members: members,
static_members_evidence_id: members.evidence_id,
decompiler: {
name: "ilspycmd",
version: "9.1.0.7988",