Grouped follow-ups from #1163, #1164 and #1165 (stateless adapter, its preparatory refactor, and the clearTools() fix). Part of #1128.
Each item was raised and evidenced during review, and deliberately left out of those PRs under one-thing-per-change. They are independent of one another — split any of them out if it grows.
1. subscriptions/listen is not actually refused
The method is out of scope, but registering no handler does not refuse it: the SDK's serving entry answers it upstream of our handlers. With a valid notifications filter it returns HTTP 200 and opens a keepalive SSE stream that honors none of our declared capabilities (we declare no listChanged and no resources.subscribe) and can never emit, since nothing here publishes to the SDK's event bus. -32601 is unreachable on that method.
The dev server's per-request finally { await handler.close() } closes the stream, but a long-lived host has nothing that does — so this is a resource-leak risk for the hosted route in apify/apify-mcp-server-internal#676.
Refuse the method before the SDK entry sees it, and drop the code comment marking it deferred.
2. Adapter-level failures are silent in the dev server
createMcpHandler is given an onerror callback, but the following toNodeHandler(handler)(req, res, req.body) call is bare. Per the SDK those cover different failures, so a request-conversion or handler.fetch throw before a response is written is currently logged nowhere.
3. Dead server.tools.clear() workarounds
These existed only to dodge the close() throw fixed in #1163, and are now dead:
tests/unit/mcp.server.capability_gating.test.ts:66
tests/unit/mcp.server.report_problem_gating.test.ts:57
tests/integration/internals.test.ts:51,76
The first two could not be removed in #1165 because an approved success criterion required those files to stay byte-identical as the refactor's behavior-preservation proof.
4. Four headers still describe server.ts as the sole protocol boundary
None mention the new stateless adapter or its error projection:
A one-clause fix in each.
5. .mcp.json has no apps-mode or payment-only target
Apps/widget-mode mcpc probing needed an ad-hoc out-of-repo config on both attempts, because the available targets are only storybook/figma/dev/stdio/stdio-full. There is likewise no payment-provider target, so the unauthenticated and x402/Skyfire resources/read shapes cannot be probed through mcpc at all.
6. Consider deriving tools instead of mutating it
Raised during review as a judgment call, not a defect. The parameterization in #1164 brings it within reach: making the facade's tools a derived view of (toolSources, servingContext) would delete pendingToolsUntilClientKnown, composePendingToolsForClient and serverModeResolved, and would make the stateless snapshot the same derivation with a different view rather than a second composition site.
Deliberately not attempted so far — it is larger than the refactor already folded in, tools is a public readonly Map that the legacy adapter reads live, and upsertTools callers write to it.
Already filed separately
#1159 (report-problem has no outputSchema) and #1160 (get-actor-run-log has no outputSchema) — no action needed here.
Grouped follow-ups from #1163, #1164 and #1165 (stateless adapter, its preparatory refactor, and the
clearTools()fix). Part of #1128.Each item was raised and evidenced during review, and deliberately left out of those PRs under one-thing-per-change. They are independent of one another — split any of them out if it grows.
1.
subscriptions/listenis not actually refusedThe method is out of scope, but registering no handler does not refuse it: the SDK's serving entry answers it upstream of our handlers. With a valid
notificationsfilter it returns HTTP 200 and opens a keepalive SSE stream that honors none of our declared capabilities (we declare nolistChangedand noresources.subscribe) and can never emit, since nothing here publishes to the SDK's event bus.-32601is unreachable on that method.The dev server's per-request
finally { await handler.close() }closes the stream, but a long-lived host has nothing that does — so this is a resource-leak risk for the hosted route in apify/apify-mcp-server-internal#676.Refuse the method before the SDK entry sees it, and drop the code comment marking it deferred.
2. Adapter-level failures are silent in the dev server
createMcpHandleris given anonerrorcallback, but the followingtoNodeHandler(handler)(req, res, req.body)call is bare. Per the SDK those cover different failures, so a request-conversion orhandler.fetchthrow before a response is written is currently logged nowhere.3. Dead
server.tools.clear()workaroundsThese existed only to dodge the
close()throw fixed in #1163, and are now dead:tests/unit/mcp.server.capability_gating.test.ts:66tests/unit/mcp.server.report_problem_gating.test.ts:57tests/integration/internals.test.ts:51,76The first two could not be removed in #1165 because an approved success criterion required those files to stay byte-identical as the refactor's behavior-preservation proof.
4. Four headers still describe
server.tsas the sole protocol boundaryNone mention the new stateless adapter or its error projection:
src/mcp/errors.ts:1-6— still says "a future v2 adapter maps them", which feat: Serve 2026-07-28 MCP requests from a stateless adapter #1165 makes concretely falsesrc/prompts/prompt_service.ts:25src/resources/api_resources.ts:180src/resources/AGENTS.md:47A one-clause fix in each.
5.
.mcp.jsonhas no apps-mode or payment-only targetApps/widget-mode
mcpcprobing needed an ad-hoc out-of-repo config on both attempts, because the available targets are onlystorybook/figma/dev/stdio/stdio-full. There is likewise no payment-provider target, so the unauthenticated and x402/Skyfireresources/readshapes cannot be probed throughmcpcat all.6. Consider deriving
toolsinstead of mutating itRaised during review as a judgment call, not a defect. The parameterization in #1164 brings it within reach: making the facade's
toolsa derived view of(toolSources, servingContext)would deletependingToolsUntilClientKnown,composePendingToolsForClientandserverModeResolved, and would make the stateless snapshot the same derivation with a different view rather than a second composition site.Deliberately not attempted so far — it is larger than the refactor already folded in,
toolsis a public readonlyMapthat the legacy adapter reads live, andupsertToolscallers write to it.Already filed separately
#1159 (
report-problemhas nooutputSchema) and #1160 (get-actor-run-loghas nooutputSchema) — no action needed here.