diff --git a/src/application/ManagedReconstructionService.ts b/src/application/ManagedReconstructionService.ts index 872f73c04..b867450f2 100644 --- a/src/application/ManagedReconstructionService.ts +++ b/src/application/ManagedReconstructionService.ts @@ -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 => { 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 diff --git a/src/contracts/managedWorkflowToolContracts.ts b/src/contracts/managedWorkflowToolContracts.ts index b689e7b1c..58ef1beea 100644 --- a/src/contracts/managedWorkflowToolContracts.ts +++ b/src/contracts/managedWorkflowToolContracts.ts @@ -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, + }, }, ], }, diff --git a/src/contracts/toolEffects.ts b/src/contracts/toolEffects.ts index 9c266a87d..a24cfe50b 100644 --- a/src/contracts/toolEffects.ts +++ b/src/contracts/toolEffects.ts @@ -98,6 +98,7 @@ export const TOOL_EFFECTS: Readonly> = { 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, diff --git a/src/server/registerManagedWorkflowTools.ts b/src/server/registerManagedWorkflowTools.ts index 98036fcd7..c811f0550 100644 --- a/src/server/registerManagedWorkflowTools.ts +++ b/src/server/registerManagedWorkflowTools.ts @@ -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); diff --git a/tests/managedMcp.test.ts b/tests/managedMcp.test.ts index 666b53cc6..ecef778ee 100644 --- a/tests/managedMcp.test.ts +++ b/tests/managedMcp.test.ts @@ -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",