fix(mcp): release optional providers when transport startup fails

Close the browser scenario owner alongside session, Android and servers when transport startup throws. Preserve cleanup failures in the existing diagnostics.

Reviewed-PR: #3230
Co-authored-by: qorvr <330186599+qorvr@users.noreply.github.com>
This commit is contained in:
morlutoandqorvr committed 2026-10-14 16:10:46 +08:00
1 parent 69b8509602
commit c482ddd6ad
2 files changed
+58 -7

No files matched your search

+17 -7
View File
@@ -115,12 +115,17 @@ export const startMcpTransport = async (
},
);
} catch (cause: unknown) {
const [binaryCleanup, androidCleanup, serverCleanup] =
await Promise.allSettled([
session.close(),
closeAndroid(),
closeServers(),
]);
const [
binaryCleanup,
androidCleanup,
serverCleanup,
optionalProviderCleanup,
] = await Promise.allSettled([
session.close(),
closeAndroid(),
closeServers(),
closeOptionalProviders(),
]);
serverLogger.error(
{
error: cause instanceof Error ? cause.message : String(cause),
@@ -136,7 +141,12 @@ export const startMcpTransport = async (
);
dependencies.writeStderr(`${cleanupError.message}\n`);
}
for (const cleanup of [binaryCleanup, androidCleanup, serverCleanup]) {
for (const cleanup of [
binaryCleanup,
androidCleanup,
serverCleanup,
optionalProviderCleanup,
]) {
if (cleanup.status === "rejected") {
const reason =
cleanup.reason instanceof Error
@@ -59,3 +59,44 @@ it("keeps the shared browser scenario provider open when the SDK discards its di
await session.close();
}
});
it("closes optional providers when MCP server startup throws", async () => {
const session = createTestBinarySession(() => {
throw new Error("Unexpected deep provider");
});
const close = vi.fn(async () => undefined);
const browserScenarioCapture: BrowserScenarioCapturePort = {
identity: () => ({
id: "fake-browser",
name: "Fake browser",
version: "1",
}),
captureScenario: async () => {
throw new Error("Unexpected scenario capture");
},
close,
};
const runtime: RuntimeDependencies = {
env: {},
serve: () => {
throw new Error("Server startup failed");
},
writeStderr: () => undefined,
setExitCode: () => undefined,
registerShutdown: () => () => undefined,
};
try {
const transport = await startMcpTransport(runtime, session, {
logger: silentLogger,
serverLogger: silentLogger,
environment: runtime.env,
delivery: new ToolResultDelivery(STDIO_DEFAULT_MAX_BUFFER_SIZE),
loadOptionalProviders: async () => ({ browserScenarioCapture }),
});
expect(transport).toEqual({ ok: false });
expect(close).toHaveBeenCalledTimes(1);
} finally {
await session.close();
}
});