spike: MCP 2026-07-28 stateless server - #1143
Closed
jirispilka wants to merge 5 commits into
Closed
Conversation
Serve MCP revision 2026-07-28 (stateless) via a new createServer2 thin shell over the shared tool-call engine. Per-request client identity, capabilities, protocol version, mode, and report-problem gating come from the _meta envelope; the Apify token from authInfo. Outcomes map to the v2 SDK via ProtocolError + projectCallToolResult. No tasks/* handler and no logging side-channel. Adds the v2 SDK deps at exact 2.0.0-beta.5 and a public prepare() hook; threads per-request initializeRequestData so origin attribution (incl. APIFY_AI) works on the stateless path. Removes the ACTOR_MCP connect-failure logging notification from the shared dispatch path (server-side log and the soft-fail isError result retained) — a breaking v1 behavior change; closes the reachable sendLoggingMessage bug on the never-connected modern instance. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019nyfevcePd9aec2prAC6uh
Compute the modern surface's instructions report-problem flag from isReportProblemServableForClient (telemetry + client-known + not-blocklisted) instead of tool presence, so a blocklisted client is never told to call a tool that tools/list withholds per request. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019nyfevcePd9aec2prAC6uh
…ybso' into claude/migration-continuation-39ybso
This was referenced Jul 23, 2026
jirispilka
added a commit
that referenced
this pull request
Jul 24, 2026
## Why Closes #1146. Part of #1128. The prompt and resource services were welded to the v1 MCP SDK: `resource_service.ts` and `api_resources.ts` threw `McpError`, and prompt list/get lived private inside `ActorsMcpServer` and threw `McpError` too. The 2026-07-28 stateless-server spike (#1143) therefore had to duplicate the prompt handlers verbatim and hand-translate v1 errors into v2 `ProtocolError` — the wrong dependency direction. This is the second protocol-neutral prerequisite after #1145 (client context; this PR is stacked on `refactor/client-context`). ## What changed The services stop knowing about any protocol error class. They throw plain domain errors (`InvalidParamsError` / `InternalError`, new `src/mcp/errors.ts`, zero SDK imports) and return plain MCP-shaped data. A single seam — `toMcpError` in `server.ts` — projects each domain error 1:1 back to `McpError` in the `resources/read` and `prompts/get` handler bodies, copying `message` and `data` unchanged, so the wire output (code, message, presence of `data`) is byte-identical. Prompt list/get moved into `createPromptService(prompts)`, mirroring the existing `createResourceService` factory. `api_resources.ts`'s existing 3xx/4xx-except-429 vs 429/5xx/network-drop split now selects the domain-error class instead of an `ErrorCode`. No public/`./internals` surface change, no v2 SDK dependency. A future v2 adapter reuses the same services with its own `ProtocolError` projection. **Base:** stacked on `refactor/client-context` (#1145 work, not yet merged). Retarget to `master` once that lands. **Follow-up:** #1149 (live mcpc smoke test for the `prompts/get` success/invalid-args paths — test-infra, empty prompt registry blocks it today; unit-covered). ## Notes for reviewers (human-written) <!-- your words --> ## Proof it works - Unit: added `tests/unit/prompts.service.test.ts` (list / render / unknown-name / invalid-args, isolated from `ActorsMcpServer`); added an `InternalError`-class case at the resource-service layer and one through the real `resources/read` handler asserting `-32603` is not downgraded to `-32602`; existing resource assertions swapped 1:1 to the domain classes with no scenario loss. `pnpm run test:unit` — 1179 passed / 1 skipped. - Live mcpc probe (stdio + HTTP): `resources/read` of `file://missing.md`, `https://example.com/x`, and a real 404 dataset all return JSON-RPC `-32602` with `data:{uri}`; a successful dataset read still returns normal content; `resources/list` / `resources/templates/list` / `prompts/list` unchanged; `prompts/get` unknown-name returns `-32602` naming the available prompts; `tools/list`, `ping`, `search-actors`, `call-actor` all work post-refactor. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01SiMgojbYtbycioTzKyeV4R --- _Generated by [Claude Code](https://claude.ai/code/session_01SiMgojbYtbycioTzKyeV4R)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
jirispilka
pushed a commit
that referenced
this pull request
Jul 27, 2026
Extract all v1 MCP SDK wiring out of ActorsMcpServer into a new package-private LegacyMcpServer (src/mcp/legacy_server.ts). ActorsMcpServer becomes the shared-Apify facade that constructs and delegates to the adapter; shared tool execution now takes plain values instead of the server object or the v1 RequestHandlerExtra/McpError. This fixes the dependency direction PR #1143 exposed, so a later stateless adapter can reuse one Apify core. Behavior is unchanged: tool names, results, error codes/messages/data, notifications, Tasks, telemetry, and timing are byte-for-byte identical, verified by a live mcpc diff against the pre-change server plus the full unit suite (1177 passed + 1 skipped). close() teardown is the deliberate reverse-of-connect order (server close, then tool-map clear). BREAKING: the raw v1 .server and .taskStore are no longer public on ActorsMcpServer; they move to the private adapter. Runtime-safe for the hosted server, which reads neither, but external TypeScript consumers touching them break at compile time. Closes #1147 Part of #1128 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017zHfSwpr57aUaxYjxAgsof
jirispilka
added a commit
that referenced
this pull request
Jul 28, 2026
## Why Closes #1147. Part of #1128. `ActorsMcpServer` combined two concerns: shared Apify behavior (tools, `actorStore`, server-mode, prompts/resources, widgets, payments, telemetry) and sessionful v1 SDK wiring (the SDK `Server`, request handlers, initialize, Tasks, logging, notifications, errors, transport lifecycle). PR #1143 showed that adding a second (stateless) serving unit forced the dependency direction backwards — shared tool execution reached up into the class (`apifyMcpServer.server`, `.tools`, `extra`, `McpError`). This extraction fixes the direction so #1140 can expose both protocol eras through `dev_server.ts` while reusing one Apify core. **Breaking:** the raw v1 `.server` and `.taskStore` are no longer public on `ActorsMcpServer` — they move to the private adapter. Runtime-safe for `apify-mcp-server-internal` (it reads neither; verified against its `new ActorsMcpServer(...)` call sites, which use only `.options.initializeRequestData`, `.loadToolsByName()`, `.loadToolsFromUrl()`, `.listAllToolNames()`, `.connect()`). External TypeScript consumers touching those two members break at compile time — hence `refactor!`. ## What changed All v1 SDK wiring moved verbatim into a new package-private `LegacyMcpServer` (`src/mcp/legacy_server.ts`). `ActorsMcpServer` keeps the shared Apify behavior, constructs exactly one adapter, and delegates `connect()`/`close()` to it via a narrow `LegacyMcpServerHost` interface — the adapter never imports the concrete facade, and shared modules never import the adapter. Shared tool execution (`tool_call_engine`, `tool_dispatch`, `tool_call_telemetry`, `task_execution`) now takes plain values; `InternalToolArgs` drops `extra`/`apifyMcpServer`/`mcpServer`. No v2 SDK dependency and no stateless routing were added (that is #1140). Behavior is preserved byte-for-byte: tool names, results, error codes/messages/data, notifications, Tasks, telemetry, and timing are unchanged. `close()` teardown is the deliberate reverse-of-connect order (close the transport, then clear the tool map). ## Notes for reviewers (human-written) The huge diff is because of server.ts -> legacy_server.ts I checked it (also with Claude), all the changes seems to be ok. ## Proof it works A live `mcpc` probe built both this branch and the pre-change `master` server, then diffed their MCP output byte-for-byte: `initialize`, `tools/list` (all default-mode tools), `prompts/list`, `resources/list` + `read`, `tools/call` happy paths, a live Actor round-trip (`call-actor` → `get-dataset-items` → `get-key-value-store-record` → `resources/read`), every error path (invalid tool name, invalid arguments, resource 404, unknown prompt, tool-level `SOFT_FAIL`), `logging/setLevel`, and the task-mode pre-flight path were all identical (only per-run IDs/timestamps differed). The unit suite is unchanged at 1177 passed + 1 skipped, and `type-check`/`lint`/`check:agents`/`format:check` are clean. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_017zHfSwpr57aUaxYjxAgsof --- _Generated by [Claude Code](https://claude.ai/code/session_017zHfSwpr57aUaxYjxAgsof)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
Collaborator
Author
|
This is no longer needed, closing |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Status
Spike only. Do not merge.
This branch proved that the v2 SDK can register the 2026-07-28 surface, route it through
src/dev_server.ts, and reuse the shared tool-call engine. It also showed that adding stateless-only methods to the legacyActorsMcpServerand duplicating prompt/error adaptation produces the wrong dependency direction.The replacement work is tracked by:
Keep this PR open as implementation reference until the replacement for #1140 exists, then close it as superseded.
Part of #1128.