import { snapshotEnvironment } from "../process/snapshotEnvironment.js"; import type { EvmInterfaceService } from "../application/evm/EvmInterfaceService.js"; import { createEvmInterfaceService } from "../composition/evm.js"; import { registerEvmTools } from "./registerEvmTools.js"; import { registerRecordedCrashTools } from "./registerRecordedCrashTools.js"; import { createRecordedCrashService } from "../composition/binaryDiagnostics.js"; import type { RecordedCrashService } from "../application/binaryDiagnostics/RecordedCrashService.js"; import { registerAnalysisViewTool } from "./registerAnalysisViewTool.js"; import { registerBinaryDiagnosticsTools } from "./registerBinaryDiagnosticsTools.js"; import { createBinaryLayoutService } from "../composition/binaryDiagnostics.js"; import type { BinaryLayoutService } from "../application/binaryDiagnostics/BinaryLayoutService.js"; import { EvidenceMcpServer } from "./EvidenceMcpServer.js"; import { createToolResultDelivery, type ToolResultDelivery, } from "./toolResult.js"; import { parseMcpResponseBudget } from "../config/mcpResponseBudget.js"; import { isAbsolute } from "node:path"; import type { BinarySessionPort } from "../application/binary/BinarySessionPort.js"; import type { BrowserObservationPort } from "../application/BrowserObservationPort.js"; import type { BrowserScenarioCapturePort } from "../application/BrowserScenarioCapturePort.js"; import type { ElectronActiveObservationPort } from "../application/javascript/ElectronActiveObservationPort.js"; import type { ElectronObservationPort } from "../application/javascript/ElectronObservationPort.js"; import type { JavaScriptRuntimeObservationPort } from "../application/javascript/JavaScriptRuntimeObservationPort.js"; import type { OptionalProviderLoadFailures } from "../application/OptionalObservationProviders.js"; import { PRODUCT_IDENTITY } from "../identity.js"; import { silentLogger } from "../logger.js"; import type { Logger } from "pino"; import { registerApplicationTools } from "./registerApplicationTools.js"; import { registerArtifactTools } from "./registerArtifactTools.js"; import { registerBrowserScenarioTool } from "./registerBrowserScenarioTool.js"; import { registerBrowserTools } from "./registerBrowserTools.js"; import { registerWebScriptTool } from "./registerWebScriptTool.js"; import { registerWebModuleTool } from "./registerWebModuleTool.js"; import type { WebModuleTraceService } from "../application/WebModuleTraceService.js"; import { createWebModuleTraceService } from "../composition/webModules.js"; import { createWebSourceLocationService } from "../composition/webSourceLocations.js"; import { registerWebSourceLocationTool } from "./registerWebSourceLocationTool.js"; import type { WebSourceLocationService } from "../application/WebSourceLocationService.js"; import type { WebRuntimeService } from "../application/WebRuntimeService.js"; import { createWebRuntimeService } from "../composition/webRuntime.js"; import { registerWebRuntimeTools } from "./registerWebRuntimeTools.js"; import type { WebNetworkCaptureService } from "../application/WebNetworkCaptureService.js"; import { createWebNetworkCaptureService } from "../composition/webNetworkCaptures.js"; import { registerWebNetworkCaptureTool } from "./registerWebNetworkCaptureTool.js"; import { registerJavaScriptRecoveryTool } from "./registerJavaScriptRecoveryTool.js"; import { JavaScriptRecoveryService } from "../application/javascript/JavaScriptRecoveryService.js"; import type { JavaScriptRecoveryPort } from "../application/javascript/JavaScriptRecoveryPort.js"; import { createJavaScriptRecoveryProvider } from "../composition/javascriptRecovery.js"; import { registerElectronTools } from "./registerElectronTools.js"; import { registerEnhancedTools } from "./registerEnhancedTools.js"; import { registerJavaScriptRuntimeObservationTools } from "./registerJavaScriptRuntimeObservationTools.js"; import { registerManagedTools } from "./registerManagedTools.js"; import { registerFirmwareTools } from "./registerFirmwareTools.js"; import { FirmwareAnalysisService } from "../application/firmware/FirmwareAnalysisService.js"; import type { FirmwareAnalysisPort } from "../application/firmware/FirmwareAnalysisPort.js"; import { createFirmwareAnalysisProvider } from "../composition/firmware.js"; import { registerAndroidTools } from "./registerAndroidTools.js"; import { AndroidAnalysisService } from "../application/android/AndroidAnalysisService.js"; import type { AndroidAnalysisPort } from "../application/android/AndroidAnalysisPort.js"; import { createAndroidAnalysisProvider } from "../composition/android.js"; import { registerManagedWorkflowTools } from "./registerManagedWorkflowTools.js"; import { registerNativeTools } from "./registerNativeTools.js"; import { registerOfficialTools } from "./registerOfficialTools.js"; import { registerGuidedPrompts } from "./registerPrompts.js"; import { registerSessionTools } from "./registerSessionTools.js"; import type { AvailabilityPolicy } from "../application/CapabilityInventory.js"; import { sessionAvailabilityPolicy } from "./sessionAvailabilityPolicy.js"; import { withAdmittedAnalysis, type ServerAnalysisSource, } from "./analysisAdmission.js"; import type { WithAdmittedAnalysis } from "./analysisAdmission.js"; const TARGET_FREE_INSTRUCTIONS = "REA provides reverse-engineering tools for local artifacts, native binaries, managed code, browser pages, and runtimes. Use the tool that directly answers the question; discover targets or inspect inventory only when needed. Tool results include inline Evidence and report their coverage and limitations."; const ACTIVE_TARGET_INSTRUCTIONS = "REA analyzes the active reverse-engineering target. Use the analysis tool that answers the question directly. Search or list symbols when discovery is needed; analyze_function provides a function dossier, and focused procedure tools return individual facets."; export interface CreateServerOptions { readonly environment?: Readonly; readonly delivery?: ToolResultDelivery; readonly evmInterface?: EvmInterfaceService; readonly logger?: Logger; readonly binaryLayout?: BinaryLayoutService; readonly recordedCrash?: RecordedCrashService; readonly firmwareAnalysis?: FirmwareAnalysisPort; readonly javascriptRecovery?: JavaScriptRecoveryPort; readonly webModuleTrace?: WebModuleTraceService; readonly webSourceLocation?: WebSourceLocationService; readonly webRuntime?: WebRuntimeService; readonly webNetworkCapture?: WebNetworkCaptureService; readonly androidAnalysis?: AndroidAnalysisPort; readonly browserObservation?: BrowserObservationPort; readonly browserScenarioCapture?: BrowserScenarioCapturePort; readonly electronObservation?: ElectronObservationPort; readonly electronActiveObservation?: ElectronActiveObservationPort; readonly javascriptRuntimeObservation?: JavaScriptRuntimeObservationPort; readonly availabilityPolicy?: () => AvailabilityPolicy; readonly optionalProviderLoadFailures?: OptionalProviderLoadFailures; } const installSessionToolAvailability = ( session: BinarySessionPort | undefined, options: CreateServerOptions, environment: Readonly, ) => { if (session === undefined) return undefined; const linux = process.platform === "linux"; const linuxX64 = linux && process.arch === "x64"; const pwntools = linuxX64 && isAbsolute(environment.REA_PWNTOOLS_PYTHON ?? ""); const policy = sessionAvailabilityPolicy(options.availabilityPolicy, { optionalProviderLoadFailures: options.optionalProviderLoadFailures, optionalFeatures: { evmInterfaceEnabled: options.evmInterface !== undefined || linuxX64, webModuleResolutionEnabled: options.webModuleTrace !== undefined || isAbsolute(environment.REA_BROWSER_EXECUTABLE ?? ""), recordedCrashEnabled: options.recordedCrash !== undefined || pwntools, binaryLayoutEnabled: options.binaryLayout !== undefined || pwntools, firmwareInspectionEnabled: options.firmwareAnalysis !== undefined || (linux && isAbsolute(environment.REA_BINWALK_COMMAND ?? "")), firmwareExtractionEnabled: options.firmwareAnalysis !== undefined || (linux && isAbsolute(environment.REA_UNBLOB_COMMAND ?? "")), browserObservationEnabled: options.browserObservation !== undefined, javascriptRecoveryEnabled: options.javascriptRecovery !== undefined || (linuxX64 && isAbsolute(environment.REA_WAKARU_COMMAND ?? "")), browserScenarioEnabled: options.browserScenarioCapture !== undefined, electronObservationEnabled: options.electronObservation !== undefined, electronAutomationEnabled: options.electronActiveObservation !== undefined, v8InspectorObservationEnabled: options.javascriptRuntimeObservation !== undefined, }, }); return { policy, }; }; const createMcpServer = ( session: BinarySessionPort | undefined, delivery: ToolResultDelivery, ): EvidenceMcpServer => new EvidenceMcpServer( { name: PRODUCT_IDENTITY.mcpServerKey, version: PRODUCT_IDENTITY.packageVersion, }, { capabilities: {}, instructions: session === undefined ? ACTIVE_TARGET_INSTRUCTIONS : TARGET_FREE_INSTRUCTIONS, }, session === undefined ? undefined : (evidence) => session.recordEvidence(evidence), delivery, ); /** * Construct one MCP server without acquiring subprocess resources. * Supplying a session adds target lifecycle tools; omitting it retains the * fixed-target seam used by focused tests and embedders. */ export const createServer = ( source: ServerAnalysisSource, options: CreateServerOptions = {}, ): EvidenceMcpServer => { const session = source.kind === "session" ? source.session : undefined; const analysis = source.kind === "session" ? source.session : source.analysis; const environment = snapshotEnvironment(options.environment ?? process.env); const delivery = selectToolResultDelivery(environment, options.delivery); const selectedOptions = { ...options, environment, delivery }; const startedAt = new Date().toISOString(); const logger = options.logger ?? silentLogger; const server = createMcpServer(session, delivery); const android = options.androidAnalysis ?? createAndroidAnalysisProvider(environment); const availability = installSessionToolAvailability( session, selectedOptions, environment, ); const toolLogger = logger.child({ layer: "server" }); const { evidenceById, activeTarget, recordEvidence, recordEvidenceWithUnknown, } = createSessionRecorders(server, session); const toolContext: ServerToolContext = { server, session, options: selectedOptions, environment, logger: toolLogger, evidenceById, activeTarget, recordEvidence, recordEvidenceWithUnknown, withAdmittedAnalysis: withAdmittedAnalysis(source), }; registerBinaryAnalysisTools(toolContext); const previousOnclose = server.server.onclose; server.server.onclose = () => { previousOnclose?.(); void android.close().catch((cause: unknown) => { logger.error( { error: cause instanceof Error ? cause.message : String(cause) }, "Android provider cleanup failed", ); }); }; const closeServer = server.close.bind(server); server.close = async () => { const results = await Promise.allSettled([closeServer(), android.close()]); for (const result of results) if (result.status === "rejected") throw result.reason; }; registerConfiguredAnalysisTools(toolContext, android); registerObservationTools(toolContext); registerGuidedPrompts(server, analysis, session); if (session !== undefined) { registerSessionTools(server, session, toolLogger, { ...selectedOptions, ...(availability === undefined ? {} : { availabilityPolicy: availability.policy }), androidAnalysisAvailability: (signal) => android.inspectAvailability(signal), startedAt, }); } return server; }; const selectToolResultDelivery = ( environment: Readonly, selected: ToolResultDelivery | undefined, ): ToolResultDelivery => { if (selected !== undefined) return selected; const configured = parseMcpResponseBudget( environment.REA_MCP_MAX_RESPONSE_BYTES, ); if (!configured.ok) throw configured.error; return createToolResultDelivery(configured.value); }; const registerConfiguredAnalysisTools = ( { server, options, environment, logger: toolLogger, evidenceById, recordEvidence, }: ServerToolContext, android: AndroidAnalysisPort, ): void => { registerAndroidTools( server, new AndroidAnalysisService(android), toolLogger, recordEvidence, ); registerBinaryDiagnosticsTools( server, options.binaryLayout ?? createBinaryLayoutService(environment), toolLogger, recordEvidence, ); registerAnalysisViewTool(server, toolLogger, evidenceById, recordEvidence); registerEvmTools( server, options.evmInterface ?? createEvmInterfaceService(environment), toolLogger, recordEvidence, ); registerRecordedCrashTools( server, options.recordedCrash ?? createRecordedCrashService(environment), toolLogger, recordEvidence, ); registerFirmwareTools( server, new FirmwareAnalysisService( options.firmwareAnalysis ?? createFirmwareAnalysisProvider(environment), ), toolLogger, recordEvidence, ); registerWebModuleTool( server, options.webModuleTrace ?? createWebModuleTraceService(environment), toolLogger, recordEvidence, ); registerWebSourceLocationTool( server, options.webSourceLocation ?? createWebSourceLocationService(environment), toolLogger, recordEvidence, ); registerWebRuntimeTools( server, options.webRuntime ?? createWebRuntimeService(), toolLogger, recordEvidence, ); registerWebNetworkCaptureTool( server, options.webNetworkCapture ?? createWebNetworkCaptureService(environment), toolLogger, recordEvidence, ); registerJavaScriptRecoveryTool( server, new JavaScriptRecoveryService( options.javascriptRecovery ?? createJavaScriptRecoveryProvider(environment), ), toolLogger, recordEvidence, ); }; const createSessionRecorders = ( server: EvidenceMcpServer, session: BinarySessionPort | undefined, ) => ({ evidenceById: session === undefined ? undefined : (evidenceId: string) => session.evidenceForAnalysis?.(evidenceId) ?? session.evidenceById(evidenceId), activeTarget: session === undefined ? undefined : () => session.activeTarget(), recordEvidence: session === undefined ? undefined : (evidence: Parameters[0]) => session.recordEvidence(evidence), recordEvidenceWithUnknown: session === undefined ? undefined : ( evidence: Parameters[0], input: Parameters[1], ) => session.recordEvidenceWithUnknown(evidence, input), }); interface ServerToolContext extends ReturnType { readonly server: EvidenceMcpServer; readonly session: BinarySessionPort | undefined; readonly options: CreateServerOptions; readonly environment: Readonly; readonly logger: Logger; readonly withAdmittedAnalysis: WithAdmittedAnalysis; } const registerBinaryAnalysisTools = ({ server, session, logger, activeTarget, recordEvidence, recordEvidenceWithUnknown, withAdmittedAnalysis, }: ServerToolContext): void => { const recordUnknown = session === undefined ? undefined : (input: Parameters[0]) => session.recordUnknown(input); const analysisOptions = { logger, activeTarget, recordEvidence, recordUnknown, withAdmittedAnalysis, }; registerOfficialTools(server, analysisOptions); registerEnhancedTools(server, { ...analysisOptions, analysisProfile: session === undefined ? undefined : () => session.analysisProfile(), allowsSnapshotReplay: session === undefined ? undefined : (operation) => session.allowsSnapshotReplay(operation), recordWorkflowSnapshot: session === undefined ? undefined : (input) => session.recordWorkflowSnapshot(input), }); const evidenceOptions = { logger, activeTarget, recordEvidence, withAdmittedAnalysis, }; registerNativeTools(server, evidenceOptions); registerArtifactTools(server, evidenceOptions); registerManagedTools(server, evidenceOptions); if (session !== undefined) registerManagedWorkflowTools(server, { logger, recordEvidence, recordEvidenceWithUnknown, session, }); }; const registerObservationTools = ({ evidenceById, server, options, logger, recordEvidence, recordEvidenceWithUnknown, }: ServerToolContext): void => { const common = { logger, recordEvidence }; registerWebScriptTool(server, common); registerBrowserTools(server, { ...common, browser: options.browserObservation, loadFailure: options.optionalProviderLoadFailures?.browserObservation, }); registerBrowserScenarioTool(server, { ...common, provider: options.browserScenarioCapture, loadFailure: options.optionalProviderLoadFailures?.browserScenarioCapture, }); registerElectronTools(server, { ...common, electron: options.electronObservation, electronActive: options.electronActiveObservation, observationLoadFailure: options.optionalProviderLoadFailures?.electronObservation, activeLoadFailure: options.optionalProviderLoadFailures?.electronActiveObservation, }); registerJavaScriptRuntimeObservationTools(server, { ...common, runtime: options.javascriptRuntimeObservation, loadFailure: options.optionalProviderLoadFailures?.javascriptRuntimeObservation, }); registerApplicationTools(server, { evidenceById, logger, recordEvidence, recordEvidenceWithUnknown, }); };