import { randomBytes } from "node:crypto"; import { CLIENT_CAPABILITIES_META_KEY, createRequestStateCodec, McpServer, PROTOCOL_VERSION_META_KEY, } from "@modelcontextprotocol/server"; import type { AnalysisOperationPort } from "../application/AnalysisProvider.js"; import type { BinarySessionPort } from "../application/BinarySession.js"; import type { BrowserObservationPort } from "../application/BrowserObservationPort.js"; import type { BrowserScenarioCapturePort } from "../application/BrowserScenarioCapturePort.js"; import type { ElectronActiveObservationPort } from "../application/ElectronActiveObservationPort.js"; import type { ElectronObservationPort } from "../application/ElectronObservationPort.js"; import type { JavaScriptReplayHost, JavaScriptReplayPolicy, JavaScriptReplayRunner, } from "../application/JavaScriptReplayPlanning.js"; import type { JavaScriptRuntimeObservationPort } from "../application/JavaScriptRuntimeObservationPort.js"; import type { ManagedRuntimePolicy } from "../application/ManagedRuntimeCorrelationService.js"; import { MANAGED_RUNTIME_DISABLED } from "../application/ManagedRuntimeCorrelationService.js"; import type { PermissionAuthority } from "../application/PermissionAuthority.js"; import type { ProcessExecutionPolicy } from "../domain/processCapture.js"; import { PRODUCT_IDENTITY } from "../identity.js"; import { silentLogger, type Logger } from "../logger.js"; import { LinuxJavaScriptReplayRunner } from "../replay/LinuxJavaScriptReplayRunner.js"; import { SystemJavaScriptReplayHost } from "../replay/SystemJavaScriptReplayHost.js"; import { mcpEnvelopeValue } from "./mcpClientMetadata.js"; import { PROCESS_CAPTURE_ELICITATION_POLICY, type ProcessCaptureElicitation, type ProcessCaptureElicitationState, } from "./ProcessCaptureElicitation.js"; import { registerApplicationTools } from "./registerApplicationTools.js"; import { registerArtifactTools } from "./registerArtifactTools.js"; import { registerBrowserScenarioTool } from "./registerBrowserScenarioTool.js"; import { registerBrowserTools } from "./registerBrowserTools.js"; import { registerElectronTools } from "./registerElectronTools.js"; import { registerEnhancedTools } from "./registerEnhancedTools.js"; import { registerJavaScriptRuntimeObservationTools } from "./registerJavaScriptRuntimeObservationTools.js"; import { registerManagedTools } from "./registerManagedTools.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 { SessionAvailability } from "./sessionAvailabilityPolicy.js"; import { sessionAvailabilityPolicy } from "./sessionAvailabilityPolicy.js"; import { DENY_PROCESS_POLICY } from "./sessionToolPolicies.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 logger?: Logger; readonly processPolicy?: () => ProcessExecutionPolicy; readonly permissionAuthority?: PermissionAuthority; readonly browserObservation?: BrowserObservationPort; readonly browserScenarioCapture?: BrowserScenarioCapturePort; readonly electronObservation?: ElectronObservationPort; readonly electronActiveObservation?: ElectronActiveObservationPort; readonly javascriptRuntimeObservation?: JavaScriptRuntimeObservationPort; readonly artifactIntegrityContinueEnabled?: () => boolean; readonly javascriptReplayPolicy?: () => JavaScriptReplayPolicy; readonly javascriptReplayHost?: JavaScriptReplayHost; readonly javascriptReplayRunner?: JavaScriptReplayRunner; readonly managedRuntimePolicy?: () => ManagedRuntimePolicy; readonly availabilityPolicy?: () => SessionAvailability; } const installSessionToolAvailability = ( server: McpServer, session: BinarySessionPort | undefined, options: CreateServerOptions, ) => { if (session === undefined) return undefined; const policy = sessionAvailabilityPolicy(options.availabilityPolicy, { processPolicy: options.processPolicy?.() ?? DENY_PROCESS_POLICY, optionalFeatures: { browserObservationEnabled: options.browserObservation !== undefined, browserScenarioEnabled: options.browserScenarioCapture !== undefined, electronObservationEnabled: options.electronObservation !== undefined, electronAutomationEnabled: options.electronActiveObservation !== undefined && options.permissionAuthority !== undefined, v8InspectorObservationEnabled: options.javascriptRuntimeObservation !== undefined, javascriptReplayEnabled: options.javascriptReplayPolicy?.().status === "enabled", managedRuntimeEnabled: options.managedRuntimePolicy?.().status === "enabled", }, }); return { policy, }; }; const createProcessCaptureElicitation = ( stateCodec: ProcessCaptureElicitation["stateCodec"], ): ProcessCaptureElicitation => ({ stateCodec, supported: (context) => { const envelope = context.mcpReq.envelope; const version = mcpEnvelopeValue(envelope, PROTOCOL_VERSION_META_KEY); const capabilities = mcpEnvelopeValue( envelope, CLIENT_CAPABILITIES_META_KEY, ); return ( typeof version === "string" && PROCESS_CAPTURE_ELICITATION_POLICY.protocolVersions.some( (supported) => supported === version, ) && isRecord(capabilities) && isRecord(capabilities.elicitation) && capabilities.elicitation.form !== undefined ); }, now: Date.now, consumedNonces: new Map(), }); const createMcpServer = ( processCaptureStateCodec: ProcessCaptureElicitation["stateCodec"], session: BinarySessionPort | undefined, ): McpServer => new McpServer( { name: PRODUCT_IDENTITY.mcpServerKey, version: PRODUCT_IDENTITY.packageVersion, }, { capabilities: {}, inputRequired: { roundTimeoutMs: PROCESS_CAPTURE_ELICITATION_POLICY.roundTimeoutMs, }, requestState: { verify: processCaptureStateCodec.verify }, instructions: session === undefined ? ACTIVE_TARGET_INSTRUCTIONS : TARGET_FREE_INSTRUCTIONS, }, ); /** * 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 = ( analysis: AnalysisOperationPort, session?: BinarySessionPort, options: CreateServerOptions = {}, ): McpServer => { const startedAt = new Date().toISOString(); const logger = options.logger ?? silentLogger; const permissionAuthority = options.permissionAuthority?.createConnectionAuthority(); const processCaptureStateCodec = createRequestStateCodec({ key: randomBytes(32), ttlSeconds: PROCESS_CAPTURE_ELICITATION_POLICY.stateTtlSeconds, }); const server = createMcpServer(processCaptureStateCodec, session); const availability = installSessionToolAvailability(server, session, options); server.server.onclose = () => { permissionAuthority?.clearSessionGrants(); }; const toolLogger = logger.child({ layer: "server" }); const { activeTarget, recordEvidence, recordEvidenceWithUnknown } = createSessionRecorders(server, session); const processCaptureElicitation = createProcessCaptureElicitation( processCaptureStateCodec, ); const toolContext: ServerToolContext = { server, analysis, session, options, logger: toolLogger, permissionAuthority, activeTarget, recordEvidence, recordEvidenceWithUnknown, }; registerBinaryAnalysisTools(toolContext); registerObservationTools(toolContext); registerGuidedPrompts(server, analysis, session); if (session !== undefined) { registerSessionTools(server, session, toolLogger, { ...options, ...(availability === undefined ? {} : { availabilityPolicy: availability.policy }), ...(permissionAuthority === undefined ? {} : { permissionAuthority }), startedAt, processCaptureElicitation, }); } return server; }; const isRecord = (value: unknown): value is Record => typeof value === "object" && value !== null && !Array.isArray(value); const createSessionRecorders = ( server: McpServer, session: BinarySessionPort | undefined, ) => ({ activeTarget: session === undefined ? undefined : () => session.activeTarget(), recordEvidence: session === undefined ? undefined : (evidence: Parameters[0]) => { const recorded = session.recordEvidence(evidence); return recorded; }, recordEvidenceWithUnknown: session === undefined ? undefined : ( evidence: Parameters[0], input: Parameters[1], ) => { const recorded = session.recordEvidenceWithUnknown(evidence, input); return recorded; }, }); interface ServerToolContext extends ReturnType { readonly server: McpServer; readonly analysis: AnalysisOperationPort; readonly session: BinarySessionPort | undefined; readonly options: CreateServerOptions; readonly logger: Logger; readonly permissionAuthority: PermissionAuthority | undefined; } const registerBinaryAnalysisTools = ({ server, analysis, session, options, logger, permissionAuthority, activeTarget, recordEvidence, recordEvidenceWithUnknown, }: ServerToolContext): void => { const recordUnknown = session === undefined ? undefined : (input: Parameters[0]) => session.recordUnknown(input); const analysisOptions = { logger, activeTarget, recordEvidence, recordUnknown, }; registerOfficialTools(server, analysis, analysisOptions); registerEnhancedTools(server, analysis, { ...analysisOptions, analysisProfile: session === undefined ? undefined : () => session.analysisProfile(), }); const evidenceOptions = { logger, activeTarget, recordEvidence }; registerNativeTools(server, analysis, evidenceOptions); registerArtifactTools(server, analysis, { ...evidenceOptions, ...(permissionAuthority === undefined ? {} : { permissionAuthority }), }); registerManagedTools(server, analysis, { ...evidenceOptions, session, }); if (session !== undefined) registerManagedWorkflowTools(server, { logger, recordEvidence, recordEvidenceWithUnknown, session, runtime: { policy: options.managedRuntimePolicy ?? (() => MANAGED_RUNTIME_DISABLED), authority: permissionAuthority, }, }); }; const registerObservationTools = ({ server, options, logger, permissionAuthority, recordEvidence, recordEvidenceWithUnknown, }: ServerToolContext): void => { const common = { logger, permissionAuthority, recordEvidence }; registerBrowserTools(server, { ...common, browser: options.browserObservation, }); registerBrowserScenarioTool(server, { ...common, provider: options.browserScenarioCapture, }); registerElectronTools(server, { ...common, electron: options.electronObservation, electronActive: options.electronActiveObservation, }); registerJavaScriptRuntimeObservationTools(server, { ...common, runtime: options.javascriptRuntimeObservation, }); registerApplicationTools(server, { logger, recordEvidence, recordEvidenceWithUnknown, permissionAuthority, replay: { policy: options.javascriptReplayPolicy ?? (() => ({ status: "disabled" })), host: options.javascriptReplayHost ?? new SystemJavaScriptReplayHost(), runner: options.javascriptReplayRunner ?? new LinuxJavaScriptReplayRunner(), authority: permissionAuthority, }, }); };