Files
rea/src/server/registerApplicationTools/projectMobileApplicationGraph.ts
T
ysopwnable ef1a0cc65a refactor(apple): group artifact producers and verification (#952)
* refactor(apple): group artifact producers and verification

* test(apple): run affected native plist CLI regressions
2026-10-07 21:09:33 +08:00

74 lines
2.7 KiB
TypeScript

import type { McpServer } from "@modelcontextprotocol/server";
import { projectAndroidApplicationEvidence } from "../../application/android/AndroidApplicationService.js";
import { projectAppleApplicationEvidence } from "../../application/apple/AppleApplicationService.js";
import {
APPLICATION_TOOL_CONTRACTS,
applicationToolContract,
} from "../../contracts/applicationToolContracts.js";
import type { AnalysisError } from "../../domain/analysisErrorBase.js";
import type { Evidence } from "../../domain/evidence.js";
import type { Result } from "../../domain/result.js";
import { recordSessionEvidenceSources } from "../sessionEvidence.js";
import { logToolExecution } from "../toolLogging.js";
import { toolRegistrationOptions } from "../toolRegistrationOptions.js";
import { toCallToolResult } from "../toolResult.js";
import { recordResult } from "./helpers.js";
import type { ApplicationToolRegistration } from "./types.js";
/** Register execution-free Android and Apple inventory projection tools. */
export const registerProjectMobileApplicationGraphTools = (
server: McpServer,
options: ApplicationToolRegistration,
): void => {
registerProjection({
server,
options,
contract: applicationToolContract("project_android_application_graph"),
project: projectAndroidApplicationEvidence,
});
registerProjection({
server,
options,
contract: applicationToolContract("project_apple_application_graph"),
project: projectAppleApplicationEvidence,
});
};
const registerProjection = (context: {
readonly server: McpServer;
readonly options: ApplicationToolRegistration;
readonly contract: (typeof APPLICATION_TOOL_CONTRACTS)[number];
readonly project: (input: unknown) => Result<Evidence, AnalysisError>;
}): void => {
const { server, options, contract, project } = context;
server.registerTool(
contract.name,
toolRegistrationOptions(contract),
async (input: unknown) => {
const result = await logToolExecution(options.logger, contract.name, () =>
Promise.resolve(project(input)),
);
if (!result.ok) return toCallToolResult(result, contract);
const recordedSources = recordSessionEvidenceSources(
options.recordEvidence,
inventoryEvidenceSources(input),
);
if (!recordedSources.ok)
return toCallToolResult(recordedSources, contract);
return recordResult(options, contract, result.value);
},
);
};
const inventoryEvidenceSources = (input: unknown): readonly Evidence[] => {
if (
typeof input !== "object" ||
input === null ||
!("inventory_evidence" in input) ||
!Array.isArray(input.inventory_evidence)
)
return [];
return input.inventory_evidence as readonly Evidence[];
};