Files
reverse-engineering-anything/src/main.ts
T
Kaoru0822andClaude Opus 5.5 7c9c1ebdb7 fix(mcp): keep shared optional providers open across discarded probes (#2205)
startMcpTransport loads optional observation providers once per process
and passes them to every server instance the SDK constructs. Each
instance's closeProviders also closed the shared browser scenario
provider, so when the SDK discarded a server/discover probe instance for a
legacy initialize, every later capture_browser_scenario call failed with
"Browser scenario provider is closing." Server instances no longer close
the caller-supplied provider; the transport returns
closeOptionalProviders and process shutdown closes it once.

Fixes #2195

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 06:56:45 -07:00

119 lines
3.9 KiB
JavaScript

#!/usr/bin/env node
import { snapshotEnvironment } from "./process/snapshotEnvironment.js";
import { pathToFileURL } from "node:url";
import { realpathSync } from "node:fs";
import { serveStdio } from "@modelcontextprotocol/server/stdio";
import { parseConfig } from "./config/parseConfig.js";
import { createBinarySession } from "./composition/binary.js";
import { createLogger } from "./logger.js";
import { projectAnalysisError } from "./domain/analysisErrorProjection.js";
import type { RuntimeDependencies } from "./main/types.js";
import { SERVER_START_FAILED } from "./main/messages.js";
import { openInitialTarget } from "./main/startup.js";
import { startMcpTransport } from "./main/transport.js";
import {
createOptionalObservationFactories,
loadOptionalObservationProviders,
} from "./composition/optionalObservationProviders.js";
import { createToolResultDelivery } from "./server/toolResult.js";
import { createShutdown } from "./main/shutdown.js";
const runtimeDependencies = (): RuntimeDependencies => ({
env: process.env,
serve: serveStdio,
writeStderr: (text) => process.stderr.write(text),
setExitCode: (code) => {
process.exitCode = code;
},
registerShutdown: (handler) => {
process.once("SIGINT", handler);
process.once("SIGTERM", handler);
process.stdin.once("end", handler);
process.stdin.once("close", handler);
return () => {
process.off("SIGINT", handler);
process.off("SIGTERM", handler);
process.stdin.off("end", handler);
process.stdin.off("close", handler);
};
},
});
/**
* Start the long-lived MCP adapter and install idempotent shutdown handlers.
* The adapter owns its BinarySession for the process lifetime; EOF and process
* signals close bridge resources without assuming ownership of the Hopper app.
*/
export const run = async (
dependencies: RuntimeDependencies = runtimeDependencies(),
): Promise<number> => {
const environment = snapshotEnvironment(dependencies.env);
const config = parseConfig(environment);
if (!config.ok) {
dependencies.writeStderr(`${projectAnalysisError(config.error).message}\n`);
return 1;
}
const logger = createLogger("mcp", config.value.logLevel);
const serverLogger = logger.child({ layer: "server" });
const session = createBinarySession(config.value, logger, environment);
const opened = await openInitialTarget(
session,
config.value,
serverLogger,
dependencies.writeStderr,
);
if (!opened.ok) return opened.exitCode;
const transport = await startMcpTransport(dependencies, session, {
logger,
serverLogger,
environment,
delivery: createToolResultDelivery(config.value.mcpMaxResponseBytes),
loadOptionalProviders:
dependencies.loadOptionalProviders ??
(() =>
loadOptionalObservationProviders(
createOptionalObservationFactories(environment),
)),
});
if (!transport.ok) return 1;
createShutdown({
handle: transport.handle,
closeAndroid: transport.closeAndroid,
closeOptionalProviders: transport.closeOptionalProviders,
session,
dependencies,
serverLogger,
});
return 0;
};
/** Run the executable boundary without exposing an unexpected startup cause. */
export const runEntrypoint = async (
start: () => Promise<number> = () => run(),
writeStderr: (text: string) => void = (text) => process.stderr.write(text),
setExitCode: (code: number) => void = (code) => {
process.exitCode = code;
},
): Promise<void> => {
try {
setExitCode(await start());
} catch (cause: unknown) {
// Intentionally redact the startup cause from caller-visible output;
// unexpected failures must not leak internal details.
void cause;
writeStderr(`${SERVER_START_FAILED}\n`);
setExitCode(1);
}
};
const entryPath = process.argv[1];
if (
entryPath !== undefined &&
pathToFileURL(realpathSync(entryPath)).href === import.meta.url
) {
void runEntrypoint();
}