fix(mcp): integrate managed contracts after rebase

This commit is contained in:
morluto 2026-07-16 19:34:57 +08:00
parent 6e6827cd76
commit 9c4a93d9b1
11 changed files with 350 additions and 173 deletions

View File

@ -1,7 +1,7 @@
{
"generated_from": "@modelcontextprotocol/server 2.0.0-beta.4 wire registration",
"tool_count": 84,
"tools_list_bytes": 518457,
"tool_count": 88,
"tools_list_bytes": 517143,
"budgets": {
"tools_list_bytes": 524288,
"per_tool_bytes": 65536
@ -15,15 +15,15 @@
},
{
"name": "analyze_function",
"bytes": 10206,
"bytes": 8935,
"input_schema_bytes": 2037,
"output_schema_bytes": 7692
"output_schema_bytes": 6421
},
{
"name": "analyze_javascript_application",
"bytes": 19871,
"bytes": 17446,
"input_schema_bytes": 2633,
"output_schema_bytes": 16597
"output_schema_bytes": 14172
},
{
"name": "analyze_swift_types",
@ -33,45 +33,45 @@
},
{
"name": "analyze_web_bundle",
"bytes": 14995,
"bytes": 14276,
"input_schema_bytes": 6550,
"output_schema_bytes": 7940
"output_schema_bytes": 7221
},
{
"name": "batch_decompile",
"bytes": 12102,
"bytes": 10638,
"input_schema_bytes": 226,
"output_schema_bytes": 11475
"output_schema_bytes": 10011
},
{
"name": "binary_overview",
"bytes": 1643,
"bytes": 1620,
"input_schema_bytes": 250,
"output_schema_bytes": 977
"output_schema_bytes": 954
},
{
"name": "binary_session",
"bytes": 13235,
"bytes": 11822,
"input_schema_bytes": 367,
"output_schema_bytes": 12368
"output_schema_bytes": 10955
},
{
"name": "build_call_path",
"bytes": 11300,
"input_schema_bytes": 7493,
"output_schema_bytes": 3335
"bytes": 10235,
"input_schema_bytes": 6850,
"output_schema_bytes": 2913
},
{
"name": "capture_process_scenario",
"bytes": 20991,
"input_schema_bytes": 10463,
"output_schema_bytes": 9774
"bytes": 19830,
"input_schema_bytes": 10303,
"output_schema_bytes": 8773
},
{
"name": "capture_web_screenshot",
"bytes": 5661,
"bytes": 5389,
"input_schema_bytes": 771,
"output_schema_bytes": 4454
"output_schema_bytes": 4182
},
{
"name": "close_binary",
@ -87,51 +87,57 @@
},
{
"name": "compare_application_versions",
"bytes": 27986,
"input_schema_bytes": 10250,
"output_schema_bytes": 17168
"bytes": 24888,
"input_schema_bytes": 9296,
"output_schema_bytes": 15024
},
{
"name": "compare_artifacts",
"bytes": 4095,
"bytes": 3869,
"input_schema_bytes": 759,
"output_schema_bytes": 2851
"output_schema_bytes": 2625
},
{
"name": "compare_bundles",
"bytes": 4559,
"bytes": 4169,
"input_schema_bytes": 928,
"output_schema_bytes": 3132
"output_schema_bytes": 2742
},
{
"name": "compare_functions",
"bytes": 4828,
"bytes": 4356,
"input_schema_bytes": 725,
"output_schema_bytes": 3611
"output_schema_bytes": 3139
},
{
"name": "compare_managed_members",
"bytes": 7583,
"input_schema_bytes": 960,
"output_schema_bytes": 6099
},
{
"name": "compare_process_captures",
"bytes": 3866,
"bytes": 3311,
"input_schema_bytes": 565,
"output_schema_bytes": 2716
"output_schema_bytes": 2161
},
{
"name": "compare_web_captures",
"bytes": 33869,
"input_schema_bytes": 31489,
"output_schema_bytes": 1940
"bytes": 29996,
"input_schema_bytes": 27683,
"output_schema_bytes": 1873
},
{
"name": "compare_web_screenshots",
"bytes": 3428,
"input_schema_bytes": 1489,
"bytes": 3351,
"input_schema_bytes": 1412,
"output_schema_bytes": 1546
},
{
"name": "correlate_static_and_runtime",
"bytes": 13332,
"input_schema_bytes": 8963,
"output_schema_bytes": 3876
"bytes": 12426,
"input_schema_bytes": 8307,
"output_schema_bytes": 3626
},
{
"name": "current_address",
@ -159,27 +165,27 @@
},
{
"name": "discover_webmcp_tools",
"bytes": 7256,
"bytes": 6797,
"input_schema_bytes": 1087,
"output_schema_bytes": 5765
"output_schema_bytes": 5306
},
{
"name": "export_evidence_bundle",
"bytes": 12103,
"bytes": 11008,
"input_schema_bytes": 196,
"output_schema_bytes": 11479
"output_schema_bytes": 10384
},
{
"name": "extract_artifact",
"bytes": 6414,
"bytes": 6212,
"input_schema_bytes": 1321,
"output_schema_bytes": 4701
"output_schema_bytes": 4499
},
{
"name": "find_changed_behavior",
"bytes": 12296,
"input_schema_bytes": 8586,
"output_schema_bytes": 3182
"bytes": 12036,
"input_schema_bytes": 8540,
"output_schema_bytes": 2968
},
{
"name": "find_xrefs_to_name",
@ -189,9 +195,9 @@
},
{
"name": "get_call_graph",
"bytes": 12061,
"bytes": 10619,
"input_schema_bytes": 361,
"output_schema_bytes": 11228
"output_schema_bytes": 9786
},
{
"name": "get_objc_classes",
@ -225,51 +231,63 @@
},
{
"name": "inspect_electron_page",
"bytes": 10081,
"bytes": 9514,
"input_schema_bytes": 1936,
"output_schema_bytes": 7685
"output_schema_bytes": 7118
},
{
"name": "inspect_macho",
"bytes": 9368,
"bytes": 7672,
"input_schema_bytes": 62,
"output_schema_bytes": 8933
"output_schema_bytes": 7237
},
{
"name": "inspect_managed_artifact",
"bytes": 12894,
"bytes": 11489,
"input_schema_bytes": 1251,
"output_schema_bytes": 11159
"output_schema_bytes": 9754
},
{
"name": "inspect_managed_members",
"bytes": 13925,
"input_schema_bytes": 2048,
"output_schema_bytes": 11387
},
{
"name": "inspect_managed_native_boundaries",
"bytes": 9704,
"input_schema_bytes": 1267,
"output_schema_bytes": 7912
},
{
"name": "inspect_plist",
"bytes": 2883,
"bytes": 2769,
"input_schema_bytes": 189,
"output_schema_bytes": 2320
"output_schema_bytes": 2206
},
{
"name": "inspect_signature",
"bytes": 2946,
"bytes": 2793,
"input_schema_bytes": 62,
"output_schema_bytes": 2519
"output_schema_bytes": 2366
},
{
"name": "inspect_web_page",
"bytes": 23933,
"bytes": 21829,
"input_schema_bytes": 5042,
"output_schema_bytes": 18381
"output_schema_bytes": 16277
},
{
"name": "inventory_artifact",
"bytes": 10608,
"bytes": 9791,
"input_schema_bytes": 1885,
"output_schema_bytes": 8336
"output_schema_bytes": 7519
},
{
"name": "list_architectures",
"bytes": 2782,
"bytes": 2555,
"input_schema_bytes": 62,
"output_schema_bytes": 2387
"output_schema_bytes": 2160
},
{
"name": "list_bookmarks",
@ -279,9 +297,9 @@
},
{
"name": "list_browser_targets",
"bytes": 3072,
"bytes": 2950,
"input_schema_bytes": 658,
"output_schema_bytes": 1966
"output_schema_bytes": 1844
},
{
"name": "list_documents",
@ -291,39 +309,39 @@
},
{
"name": "list_electron_targets",
"bytes": 2991,
"bytes": 2869,
"input_schema_bytes": 636,
"output_schema_bytes": 1932
"output_schema_bytes": 1810
},
{
"name": "list_names",
"bytes": 2842,
"bytes": 2786,
"input_schema_bytes": 594,
"output_schema_bytes": 1818
"output_schema_bytes": 1762
},
{
"name": "list_procedures",
"bytes": 2739,
"bytes": 2683,
"input_schema_bytes": 475,
"output_schema_bytes": 1818
"output_schema_bytes": 1762
},
{
"name": "list_segments",
"bytes": 2817,
"bytes": 2656,
"input_schema_bytes": 282,
"output_schema_bytes": 2128
"output_schema_bytes": 1967
},
{
"name": "list_strings",
"bytes": 2837,
"bytes": 2781,
"input_schema_bytes": 594,
"output_schema_bytes": 1818
"output_schema_bytes": 1762
},
{
"name": "list_unknowns",
"bytes": 4477,
"bytes": 4279,
"input_schema_bytes": 358,
"output_schema_bytes": 3710
"output_schema_bytes": 3512
},
{
"name": "next_address",
@ -333,9 +351,9 @@
},
{
"name": "observe_web_session",
"bytes": 6320,
"bytes": 5976,
"input_schema_bytes": 765,
"output_schema_bytes": 5090
"output_schema_bytes": 4746
},
{
"name": "open_binary",
@ -343,6 +361,12 @@
"input_schema_bytes": 386,
"output_schema_bytes": 696
},
{
"name": "plan_managed_runtime_correlation",
"bytes": 7556,
"input_schema_bytes": 2128,
"output_schema_bytes": 4768
},
{
"name": "prev_address",
"bytes": 1045,
@ -387,21 +411,21 @@
},
{
"name": "procedure_references",
"bytes": 4319,
"bytes": 4192,
"input_schema_bytes": 798,
"output_schema_bytes": 3054
"output_schema_bytes": 2927
},
{
"name": "reconcile_javascript_runtime",
"bytes": 30201,
"input_schema_bytes": 9566,
"output_schema_bytes": 20110
"bytes": 27214,
"input_schema_bytes": 8993,
"output_schema_bytes": 17696
},
{
"name": "record_unknown",
"bytes": 6725,
"bytes": 6527,
"input_schema_bytes": 2646,
"output_schema_bytes": 3685
"output_schema_bytes": 3487
},
{
"name": "resolve_containing_procedure",
@ -411,21 +435,21 @@
},
{
"name": "run_controlled_replay",
"bytes": 21072,
"input_schema_bytes": 6098,
"output_schema_bytes": 14387
"bytes": 19234,
"input_schema_bytes": 5922,
"output_schema_bytes": 12725
},
{
"name": "search_procedures",
"bytes": 2238,
"bytes": 2215,
"input_schema_bytes": 812,
"output_schema_bytes": 964
"output_schema_bytes": 941
},
{
"name": "search_strings",
"bytes": 2247,
"bytes": 2224,
"input_schema_bytes": 812,
"output_schema_bytes": 964
"output_schema_bytes": 941
},
{
"name": "set_address_name",
@ -471,9 +495,9 @@
},
{
"name": "trace_application_feature",
"bytes": 27741,
"input_schema_bytes": 9241,
"output_schema_bytes": 17969
"bytes": 24958,
"input_schema_bytes": 8688,
"output_schema_bytes": 15739
},
{
"name": "trace_feature",
@ -489,21 +513,21 @@
},
{
"name": "update_unknown",
"bytes": 7884,
"input_schema_bytes": 3810,
"output_schema_bytes": 3685
"bytes": 7572,
"input_schema_bytes": 3696,
"output_schema_bytes": 3487
},
{
"name": "verify_reconstruction",
"bytes": 6692,
"input_schema_bytes": 2688,
"output_schema_bytes": 3510
"bytes": 6161,
"input_schema_bytes": 2455,
"output_schema_bytes": 3212
},
{
"name": "verify_unknown_resolution",
"bytes": 4481,
"bytes": 4283,
"input_schema_bytes": 177,
"output_schema_bytes": 3866
"output_schema_bytes": 3668
},
{
"name": "xrefs",

View File

@ -29,15 +29,23 @@ export const compareManagedMembersEvidence = (
const operation = "compare_managed_members";
const parsed = compareManagedMembersInputSchema.safeParse(rawInput);
if (!parsed.success) return err(new AnalysisInputError(operation));
return compareManagedMembersEvidenceValidated(parsed.data);
};
/** Compare managed members from input parsed by a trusted adapter. */
export const compareManagedMembersEvidenceValidated = (
input: CompareManagedMembersInput,
): Result<Evidence, AnalysisError> => {
const operation = "compare_managed_members";
try {
const left = parseManagedMemberEvidence(parsed.data.left);
const right = parseManagedMemberEvidence(parsed.data.right);
const left = parseManagedMemberEvidence(input.left);
const right = parseManagedMemberEvidence(input.right);
const result = compareManagedMembers(
{ evidenceId: left.evidenceId, result: left.result },
{ evidenceId: right.evidenceId, result: right.result },
parsed.data.limits,
input.limits,
);
return ok(createManagedMemberComparisonEvidence(parsed.data, result));
return ok(createManagedMemberComparisonEvidence(input, result));
} catch (cause: unknown) {
return workflowFailure(operation, cause);
}

View File

@ -42,6 +42,17 @@ export const planManagedRuntimeCorrelationEvidence = async (
const parsed = managedRuntimeCorrelationInputSchema.safeParse(rawInput);
if (!parsed.success)
return err(new AnalysisInputError(OPERATION, { cause: parsed.error }));
return planManagedRuntimeCorrelationEvidenceValidated(
dependencies,
parsed.data,
);
};
/** Plan runtime correlation from input parsed by a trusted adapter. */
export const planManagedRuntimeCorrelationEvidenceValidated = async (
dependencies: ManagedRuntimeCorrelationDependencies,
input: ManagedRuntimeCorrelationInput,
): Promise<Result<Evidence, AnalysisError>> => {
if (!dependencies.policy.enabled)
return err(
new AnalysisCapabilityUnavailableError(
@ -61,7 +72,7 @@ export const planManagedRuntimeCorrelationEvidence = async (
let staticEvidence: Evidence;
let artifactPath: string;
try {
staticEvidence = parseEvidence(parsed.data.static_members);
staticEvidence = parseEvidence(input.static_members);
artifactPath = artifactPathFor(staticEvidence);
} catch (cause: unknown) {
return err(new AnalysisInputError(OPERATION, { cause }));
@ -74,7 +85,7 @@ export const planManagedRuntimeCorrelationEvidence = async (
environment_names: [],
network: "none",
mount: false,
operation_identity: operationIdentity(parsed.data, staticEvidence),
operation_identity: operationIdentity(input, staticEvidence),
},
"read",
{ restartRequired: true },
@ -89,7 +100,7 @@ export const planManagedRuntimeCorrelationEvidence = async (
);
try {
const result = planManagedRuntimeCorrelation(
parsed.data,
input,
dependencies.policy.executablePath,
authorized.value.grant_id,
);
@ -106,10 +117,10 @@ export const planManagedRuntimeCorrelationEvidence = async (
operation: OPERATION,
parameters: {
static_members_evidence_id: staticEvidence.evidence_id,
method: jsonValueSchema.parse(parsed.data.method),
requested_effect: parsed.data.requested_effect,
host: jsonValueSchema.parse(parsed.data.host),
bounds: jsonValueSchema.parse(parsed.data.bounds),
method: jsonValueSchema.parse(input.method),
requested_effect: input.requested_effect,
host: jsonValueSchema.parse(input.host),
bounds: jsonValueSchema.parse(input.bounds),
},
result: jsonValueSchema.parse(result),
rawResult: null,

View File

@ -135,17 +135,12 @@ export const MANAGED_TOOL_CONTRACTS = [
},
{
name: "inspect_managed_members",
...toolContractMetadata("inspect_managed_members"),
description:
"Inspect bounded PE/CLI metadata members, signatures, method body IL hashes, exception regions, call edges, and field-access anchors without loading or executing target code. Metadata tokens are reported as build-local coordinates bound to the artifact SHA-256 and MVID.",
kind: "managed-provider",
inputSchema: managedMemberInputSchema,
outputSchema: memberOutputSchema,
annotations: {
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: false,
},
examples: [
{
title: "Example inspect managed members request",
@ -155,17 +150,12 @@ export const MANAGED_TOOL_CONTRACTS = [
},
{
name: "inspect_managed_native_boundaries",
...toolContractMetadata("inspect_managed_native_boundaries"),
description:
"Inspect PE/CLI ModuleRef, ImplMap/PInvoke declarations, CLI native-header indicators, and non-IL method implementation flags without loading or executing target code. Results are managed declarations and degraded native-boundary observations, not verified native exports or addresses.",
kind: "managed-provider",
inputSchema: managedNativeBoundaryInputSchema,
outputSchema: nativeBoundaryOutputSchema,
annotations: {
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: false,
},
examples: [
{
title: "Example inspect managed/native boundary request",

View File

@ -8,6 +8,36 @@ import {
MANAGED_RECONSTRUCTION_IMPORT_EXAMPLE,
MANAGED_RUNTIME_CORRELATION_EXAMPLE,
} from "./managedWorkflowExamples.js";
import { toolContractMetadata } from "./toolEffects.js";
const managedEvidenceIdSchema = z
.string()
.regex(/^ev_[a-f0-9]{64}$/u)
.describe("Session-owned inspect_managed_members Evidence ID");
/** MCP references for comparing two session-owned managed observations. */
export const compareManagedMembersReferenceInputSchema = z
.strictObject({
left_evidence_id: managedEvidenceIdSchema,
right_evidence_id: managedEvidenceIdSchema,
limits: compareManagedMembersInputSchema.shape.limits,
unknown_registry_approved:
compareManagedMembersInputSchema.shape.unknown_registry_approved,
})
.superRefine((input, context) => {
if (input.left_evidence_id === input.right_evidence_id)
context.addIssue({
code: "custom",
path: ["right_evidence_id"],
message: "Managed member Evidence must be distinct",
});
});
/** MCP reference for planning from one session-owned managed observation. */
export const managedRuntimeCorrelationReferenceInputSchema =
managedRuntimeCorrelationInputSchema
.omit({ static_members: true })
.extend({ static_members_evidence_id: managedEvidenceIdSchema });
const comparisonOutputSchema =
managedWorkflowOutputSchemas.compare_managed_members;
@ -32,21 +62,21 @@ if (reconstructionOutputSchema === undefined)
export const MANAGED_WORKFLOW_TOOL_CONTRACTS = [
{
name: "compare_managed_members",
...toolContractMetadata("compare_managed_members"),
description:
"Compare two authenticated inspect_managed_members Evidence records using unique-only CIL/signature and structural method-shape tiers. Names are reported as observations but are not used as a matching basis; metadata tokens are remapped as build-local coordinates bound to each artifact SHA-256 and MVID.",
kind: "application",
inputSchema: compareManagedMembersInputSchema,
inputSchema: compareManagedMembersReferenceInputSchema,
outputSchema: comparisonOutputSchema,
annotations: {
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: false,
},
examples: [
{
title: "Compare two managed member observations",
input: MANAGED_MEMBER_COMPARISON_EXAMPLE,
input: {
left_evidence_id: MANAGED_MEMBER_COMPARISON_EXAMPLE.left.evidence_id,
right_evidence_id:
MANAGED_MEMBER_COMPARISON_EXAMPLE.right.evidence_id,
limits: MANAGED_MEMBER_COMPARISON_EXAMPLE.limits,
},
},
],
},
@ -72,22 +102,26 @@ export const MANAGED_WORKFLOW_TOOL_CONTRACTS = [
},
{
name: "plan_managed_runtime_correlation",
...toolContractMetadata("plan_managed_runtime_correlation"),
description:
"Prepare a separately authorized managed runtime-correlation admission plan from authenticated inspect_managed_members Evidence. The operation is default-disabled, requires the managed_runtime permission ceiling and grant, locks the exact artifact SHA-256, MVID, method signature, and normalized IL shape, distinguishes attach/load/debugger/reflection/instrumentation effects, and records that no target code was executed.",
kind: "application",
inputSchema: managedRuntimeCorrelationInputSchema,
inputSchema: managedRuntimeCorrelationReferenceInputSchema,
outputSchema: runtimeOutputSchema,
annotations: {
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: false,
},
examples: [
{
title: "Plan an exact-build managed runtime correlation",
input: MANAGED_RUNTIME_CORRELATION_EXAMPLE,
input: {
static_members_evidence_id:
MANAGED_RUNTIME_CORRELATION_EXAMPLE.static_members.evidence_id,
method: MANAGED_RUNTIME_CORRELATION_EXAMPLE.method,
requested_effect:
MANAGED_RUNTIME_CORRELATION_EXAMPLE.requested_effect,
host: MANAGED_RUNTIME_CORRELATION_EXAMPLE.host,
bounds: MANAGED_RUNTIME_CORRELATION_EXAMPLE.bounds,
},
},
],
},
] as const satisfies readonly ToolContract[];
import { z } from "zod";

View File

@ -95,6 +95,10 @@ export const TOOL_EFFECTS: Readonly<Record<string, ToolEffects>> = {
inventory_artifact: evidence,
extract_artifact: effects({ mutatesSession: true, writesFilesystem: true }),
inspect_managed_artifact: evidence,
inspect_managed_members: evidence,
inspect_managed_native_boundaries: evidence,
compare_managed_members: sessionEvidence,
plan_managed_runtime_correlation: sessionEvidence,
list_browser_targets: browserEvidence,
inspect_web_page: browserEvidence,
analyze_web_bundle: browserEvidence,

View File

@ -183,6 +183,7 @@ export const createServer = (
logger: toolLogger,
recordEvidence,
recordEvidenceWithUnknown,
session,
runtime: {
policy: options.managedRuntimePolicy ?? {
enabled: false,

View File

@ -1,24 +1,31 @@
import type { McpServer } from "@modelcontextprotocol/server";
import type { BinarySessionPort } from "../application/BinarySession.js";
import { compareManagedMembersEvidence } from "../application/ManagedMemberComparisonService.js";
import { compareManagedMembersEvidenceValidated } from "../application/ManagedMemberComparisonService.js";
import { importManagedReconstructionEvidence } from "../application/ManagedReconstructionService.js";
import {
planManagedRuntimeCorrelationEvidence,
planManagedRuntimeCorrelationEvidenceValidated,
type ManagedRuntimeCorrelationDependencies,
} from "../application/ManagedRuntimeCorrelationService.js";
import { MANAGED_WORKFLOW_TOOL_CONTRACTS } from "../contracts/managedWorkflowToolContracts.js";
import {
compareManagedMembersInputSchema,
managedMemberComparisonResultSchema,
} from "../domain/managedMemberComparison.js";
compareManagedMembersReferenceInputSchema,
managedRuntimeCorrelationReferenceInputSchema,
MANAGED_WORKFLOW_TOOL_CONTRACTS,
} from "../contracts/managedWorkflowToolContracts.js";
import { managedMemberComparisonResultSchema } from "../domain/managedMemberComparison.js";
import { managedReconstructionImportInputSchema } from "../domain/managedReconstruction.js";
import { managedRuntimeCorrelationInputSchema } from "../domain/managedRuntimeCorrelation.js";
import type { Evidence } from "../domain/evidence.js";
import {
EvidenceIntegrityError,
EvidenceReferenceError,
} from "../domain/errors.js";
import { err, type Result } from "../domain/result.js";
import type { Logger } from "../logger.js";
import { logToolExecution } from "./toolLogging.js";
import { toCallToolResult } from "./toolResult.js";
import { toolRegistrationOptions } from "./toolRegistrationOptions.js";
import { safeParseToolInput } from "./toolInputValidation.js";
import { resolveSessionEvidenceIds } from "./sessionEvidence.js";
interface ManagedWorkflowToolRegistration {
readonly logger: Logger;
@ -27,6 +34,7 @@ interface ManagedWorkflowToolRegistration {
| BinarySessionPort["recordEvidenceWithUnknown"]
| undefined;
readonly runtime: ManagedRuntimeCorrelationDependencies;
readonly session: BinarySessionPort | undefined;
}
/** Register provider-neutral managed-code workflows. */
@ -41,11 +49,31 @@ export const registerManagedWorkflowTools = (
compareContract.name,
toolRegistrationOptions(compareContract),
async (input) => {
const parsed = compareManagedMembersInputSchema.parse(input);
const parsedInput = safeParseToolInput(
compareManagedMembersReferenceInputSchema,
input,
compareContract.name,
);
if (!parsedInput.ok)
return toCallToolResult(parsedInput, compareContract);
const resolved = resolveManagedEvidence(options.session, [
parsedInput.value.left_evidence_id,
parsedInput.value.right_evidence_id,
]);
if (!resolved.ok) return toCallToolResult(resolved, compareContract);
const [left, right] = resolved.value;
if (left === undefined || right === undefined)
throw new TypeError("Managed comparison Evidence resolution failed");
const {
left_evidence_id: _leftEvidenceId,
right_evidence_id: _rightEvidenceId,
...referencedInput
} = parsedInput.value;
const parsed = { ...referencedInput, left, right };
const result = await logToolExecution(
options.logger,
compareContract.name,
() => Promise.resolve(compareManagedMembersEvidence(parsed)),
() => Promise.resolve(compareManagedMembersEvidenceValidated(parsed)),
);
if (!result.ok) return toCallToolResult(result, compareContract);
const recorded = recordSources(options.recordEvidence, [
@ -117,11 +145,33 @@ export const registerManagedWorkflowTools = (
runtimeContract.name,
toolRegistrationOptions(runtimeContract),
async (input) => {
const parsed = managedRuntimeCorrelationInputSchema.parse(input);
const parsedInput = safeParseToolInput(
managedRuntimeCorrelationReferenceInputSchema,
input,
runtimeContract.name,
);
if (!parsedInput.ok)
return toCallToolResult(parsedInput, runtimeContract);
const resolved = resolveManagedEvidence(options.session, [
parsedInput.value.static_members_evidence_id,
]);
if (!resolved.ok) return toCallToolResult(resolved, runtimeContract);
const staticMembers = resolved.value[0];
if (staticMembers === undefined)
throw new TypeError("Managed runtime Evidence resolution failed");
const {
static_members_evidence_id: _staticMembersEvidenceId,
...referencedInput
} = parsedInput.value;
const parsed = { ...referencedInput, static_members: staticMembers };
const result = await logToolExecution(
options.logger,
runtimeContract.name,
() => planManagedRuntimeCorrelationEvidence(options.runtime, parsed),
() =>
planManagedRuntimeCorrelationEvidenceValidated(
options.runtime,
parsed,
),
);
if (!result.ok) return toCallToolResult(result, runtimeContract);
const recordedSource = options.recordEvidence?.(parsed.static_members);
@ -147,6 +197,26 @@ const contract = (
return found;
};
const resolveManagedEvidence = (
session: BinarySessionPort | undefined,
evidenceIds: readonly string[],
): Result<Evidence[], EvidenceIntegrityError> => {
const firstId = evidenceIds[0] ?? "ev_missing";
return session === undefined
? err(
new EvidenceReferenceError(
firstId,
"missing",
"inspect_managed_members",
null,
),
)
: resolveSessionEvidenceIds(session, evidenceIds, {
operation: "inspect_managed_members",
predicate: "rea.analysis/v2",
});
};
const recordSources = (
recordEvidence: ManagedWorkflowToolRegistration["recordEvidence"],
sources: readonly Evidence[],

View File

@ -127,13 +127,35 @@ const compactJsonSchema = (
const counts = new Map<string, number>();
collectSchemas(schemaBody, counts, true);
const candidates = [...counts.entries()]
.filter(([encoded, count]) => count > 1 && encoded.length >= 128)
.filter(([, count]) => count > 1)
.sort(
([left], [right]) =>
right.length - left.length || left.localeCompare(right),
)
.map(([encoded], index) => [encoded, `rea_${String(index)}`] as const);
.map(([encoded]) => encoded);
if (candidates.length === 0) return schemaBody;
let compacted: Record<string, unknown> = schemaBody;
let compactedBytes = JSON.stringify(compacted).length;
const selected: Array<readonly [string, string]> = [];
for (const encoded of candidates) {
const proposed = [
...selected,
[encoded, `rea_${String(selected.length)}`] as const,
];
const candidate = compactWithDefinitions(schemaBody, proposed);
const candidateBytes = JSON.stringify(candidate).length;
if (candidateBytes >= compactedBytes) continue;
selected.push([encoded, `rea_${String(selected.length)}`]);
compacted = candidate;
compactedBytes = candidateBytes;
}
return compacted;
};
const compactWithDefinitions = (
schemaBody: Readonly<Record<string, unknown>>,
candidates: readonly (readonly [string, string])[],
): Record<string, unknown> => {
const names = new Map(candidates);
const definitions = Object.fromEntries(
candidates.map(([encoded, name]) => [

View File

@ -101,6 +101,7 @@ describe("tool contract surface", () => {
...NATIVE_TOOL_CONTRACTS,
...ARTIFACT_TOOL_CONTRACTS,
...MANAGED_TOOL_CONTRACTS,
...MANAGED_WORKFLOW_TOOL_CONTRACTS,
...BROWSER_TOOL_CONTRACTS,
...ELECTRON_TOOL_CONTRACTS,
...APPLICATION_TOOL_CONTRACTS,

View File

@ -117,12 +117,13 @@ describe("managed artifact MCP tools", () => {
},
});
const members = structured(
const membersResult = structured(
await client.callTool({
name: "inspect_managed_members",
arguments: { method_limit: 1 },
}),
);
const members = sessionEvidence(session, membersResult);
expect(members).toMatchObject({
operation: "inspect_managed_members",
@ -144,10 +145,7 @@ describe("managed artifact MCP tools", () => {
);
expect(boundaries).toMatchObject({
operation: "inspect_managed_native_boundaries",
provider: { id: "rea-dotnet-static" },
subject: { local_path: path, format: "pe" },
normalized_result: {
result: {
identity_scope: { token_identity: "build-local" },
pinvoke_imports: { total: 0, limit: 1 },
native_implementations: { total: 0 },
@ -158,18 +156,19 @@ describe("managed artifact MCP tools", () => {
name: "open_binary",
arguments: { path: rightPath },
});
const rightMembers = structured(
const rightMembersResult = structured(
await client.callTool({
name: "inspect_managed_members",
arguments: { method_limit: 1 },
}),
);
const compared = structured(
const rightMembers = sessionEvidence(session, rightMembersResult);
const comparedResult = structured(
await client.callTool({
name: "compare_managed_members",
arguments: {
left: members,
right: rightMembers,
left_evidence_id: members.evidence_id,
right_evidence_id: rightMembers.evidence_id,
limits: {
max_method_matches: 100,
max_field_matches: 100,
@ -178,6 +177,7 @@ describe("managed artifact MCP tools", () => {
},
}),
);
const compared = sessionEvidence(session, comparedResult);
expect(compared).toMatchObject({
operation: "compare_managed_members",
@ -205,7 +205,7 @@ describe("managed artifact MCP tools", () => {
.parse(method).items[0];
expect(methodItem).toBeDefined();
if (methodItem === undefined) return;
const imported = structured(
const importedResult = structured(
await client.callTool({
name: "import_managed_reconstruction",
arguments: {
@ -233,6 +233,7 @@ describe("managed artifact MCP tools", () => {
},
}),
);
const imported = sessionEvidence(session, importedResult);
expect(imported).toMatchObject({
operation: "import_managed_reconstruction",
@ -249,11 +250,11 @@ describe("managed artifact MCP tools", () => {
],
},
});
const planned = structured(
const plannedResult = structured(
await client.callTool({
name: "plan_managed_runtime_correlation",
arguments: {
static_members: members,
static_members_evidence_id: members.evidence_id,
method: {
token: methodItem.token,
signature_sha256: methodItem.signature.raw_sha256,
@ -275,6 +276,7 @@ describe("managed artifact MCP tools", () => {
},
}),
);
const planned = sessionEvidence(session, plannedResult);
expect(planned).toMatchObject({
operation: "plan_managed_runtime_correlation",
@ -302,3 +304,13 @@ const structured = (result: CallToolResult): Record<string, unknown> => {
throw new Error("missing structured result");
return z.record(z.string(), z.unknown()).parse(result.structuredContent);
};
const sessionEvidence = (
session: BinarySession,
result: Readonly<Record<string, unknown>>,
): Record<string, unknown> => {
const evidenceId = z.string().parse(result.evidence_id);
const evidence = session.evidenceById(evidenceId);
if (evidence === undefined) throw new Error("missing session Evidence");
return z.record(z.string(), z.unknown()).parse(evidence);
};