Skip to content

chore: Follow-ups from the 2026-07-28 stateless adapter work #1166

Description

@jirispilka

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    debtCode quality improvement or decrease of technical debt.t-aiIssues owned by the AI team.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions