The MCP conformance lane is opt-in for local runs. Run it with:
pnpm test:mcp-conformanceCI runs the same command as the MCP conformance job of the Nightly
workflow (.github/workflows/nightly.yml), on the daily schedule and on
manual dispatch from any ref. The lane uses one Node version and one existing
fixture, and uploads the official runner's checks.json artifacts from
artifacts/mcp-conformance. It is not part of pull request, push, or default
Rstest execution.
Generated agent-bundle MCP artifacts are managed stdio executables; they do
not expose an HTTP transport seam. The harness therefore builds the existing
packages/agent-bundle/fixtures/route-harness generated-route fixture through
the public build pipeline, starts the generated stdio artifact, and forwards
raw JSON-RPC frames through the official SDK's
NodeStreamableHTTPServerTransport. The adapter owns only loopback transport,
health checking, bounded runner execution, and teardown. Route registration,
Flight rendering, schemas, and responses remain the generated artifact's
logic.
The copied fixture is narrowed to its MCP routes before building. Its state, event, and CLI sources are omitted because conformance needs one generated MCP surface and must not add the packed fixture's separate npm-install journey.
- Runner:
@modelcontextprotocol/conformance@0.1.16 - Suite:
server --suite active - Specification:
2025-11-25 - Scenarios: 30 total; 7 passed, 23 expected failures, 0 skipped
- Unexpected failures: 0
The dated expected-failure baseline is
packages/agent-bundle/tests/fixtures/mcp-conformance-expected-failures.yml.
It records three unsupported generated-server operation families
(completion/complete, logging/setLevel, and resource subscriptions) plus
the canonical content-specific tools, resources, and prompts that the reused
route harness does not provide. The official runner rejects both unexpected
failures and stale baseline entries, so newly fixed scenarios must be removed
from the baseline.
Issue #369 (the #96
acceptance remainder) is implemented for generated route servers: a tool route
that declares config.execution.taskSupport (optional or required,
validated as AB4836 and advertised in tools/list) may be called as a task
under the MCP 2025-11-25 Tasks utility. The 2026-09-02 deferral recorded
below was lifted after re-auditing the installed SDK.
What the audit found on @modelcontextprotocol/server@2.0.0 /
@modelcontextprotocol/client@2.0.0 (unchanged since the deferral):
- The task methods are outside the SDK's typed spec-method surface, but the
SDK's documented custom-method form,
setRequestHandler(method, { params }, handler)on the server andrequest(request, resultSchema)on the client, routes them, and the SDK exports their result schemas publicly asspecTypeSchemas.CreateTaskResult,GetTaskResult,CancelTaskResult,ListTasksResult(the deferral note's "not exported publicly" was wrong; they are keyed without theSchemasuffix). On a2025-11-25session the SDK's own method registry admitstasks/*; on2026-07-28it answers-32601before any handler and its codec stripsexecution.taskSupportandcapabilities.tasks, so a modern-revision client keeps the ordinary contract. - The SDK's
tools/callresult validation admitsCallToolResultonly and refuses ataskbody. The lifecycle therefore lives in aServersubclass (packages/agent-bundle/src/mcp-tasks.ts) whose_wrapHandler, the SDK's documented protected seam for role-specific request handling, answers a task-augmented request with aCreateTaskResultand runs the SDK-validated handler behind the task. Nothing reaches past the SDK's public or protected surface. - The
2026-07-28revision moves tasks to theio.modelcontextprotocol/tasksextension (SEP-2663) with a different shape (resultType: "task",tasks/update, notasks/result/tasks/list). This SDK release does not implement that extension; the generated server serves the core2025-11-25shape only and is gated on the negotiated protocol version. Serving the extension is a follow-up that inherits the same route contract.
Behaviour (proven at the mcp-in-memory level by
packages/agent-bundle/tests/projection/mcp-in-memory.test.ts, at the unit
level by packages/agent-bundle/tests/mcp-tasks.test.ts, and over real stdio
framing by packages/agent-bundle/tests/packed-stdio-projection.test.ts):
- A server with at least one opted-in tool declares
capabilities.tasks: { list, cancel, requests: { tools: { call } } }; a server with none declares nothing and processes a task-augmented request as an ordinary one (the fallback the utility requires of a receiver without the capability). tools/callwithparams.taskon an opted-in tool answers aCreateTaskResult(statusworking, honouredttl≤ 24 h,pollInterval≥ 100 ms,_meta["io.modelcontextprotocol/model-immediate-response"]); on aforbiddentool it is-32601; an ordinary call to arequiredtool is-32601.tasks/getreportsworkingwith the latest render progress (statusMessage,_meta["agent-bundle/progress"]),completed,failed(a result withisError: true, as the spec requires), orcancelled.tasks/resultblocks until the task settles and returns exactly what the ordinary call would have returned, stamped with_meta["io.modelcontextprotocol/related-task"]; a JSON-RPC error is returned as that error.tasks/canceltransitions tocancelledbefore answering and aborts the render through itsAbortSignal; cancelling a settled task is-32602, as is any unknowntaskId.tasks/listpages the session's tasks by cursor.- Progress notifications flow only under the client's own
progressToken, stamped with the related-task key; the task observes progress either way. Records are session-scoped, retained forttlafter settling, bounded at 256 per server, and cancelled when the session closes.
The conformance lane (server --suite active, specification 2025-11-25)
does not yet exercise the Tasks utility; when the official runner adds task
scenarios, the reused route harness's wait and catalog tools already
declare execution.taskSupport: "optional".
The section below is the dated deferral as recorded by #394, kept for the
audit trail. Its sentinel test (packages/rsc-runtime/tests/mcp-tasks-deferral.test.ts)
and the @ts-expect-error sentinel in mcp-in-memory.test.ts were removed
with the implementation; the SDK pin itself is unchanged.
Issue #369 tracks
the #96 acceptance remainder: a task-augmented tools/call that returns a
CreateTaskResult and resolves through tasks/get / tasks/result, with
tasks/cancel mapped into the renderer AbortSignal. It is deferred because
the installed SDK has no task runtime to build on; the repository does not
hand-roll the protocol outside the SDK's typed surface.
Audited state (2026-09-02):
- Generated servers use
@modelcontextprotocol/server@2.0.0with@modelcontextprotocol/client@2.0.0for in-memory proof; both share@modelcontextprotocol/core@2.0.0. Their latest core protocol revision is2025-11-25, the revision this conformance lane records. - The SDK ships the
2025-11-25task wire vocabulary as importable types only. Its own declaration states thattasks/get,tasks/result,tasks/list,tasks/cancel, andnotifications/tasks/statusare "wire vocabulary with no SDK runtime": the typedrequest(),setRequestHandler(), andctx.mcpReq.send()surfaces exclude them, the client refuses to issue them as spec methods, the only task-named runtime exports areRELATED_TASK_META_KEYand a deprecatedisTaskAugmentedRequestParamsguard, and noTaskStoreorexperimental.tasksnamespace exists (the 1.x experimental API was not carried into v2). - The
2026-07-28specification revision moved tasks out of the core protocol into theio.modelcontextprotocol/tasksextension (SEP-2663):tasks/resultandtasks/listare gone, polling goes throughtasks/get, andtasks/updateis new. Implementing the2025-11-25core shape now would target a surface the next revision removes. The SDK tracks the extension in typescript-sdk#2189 (open, "needs decision"); reported v2 gaps include customtasks/get/tasks/cancelhandlers being unreachable on a2026-07-28session (#2598) andClient.callTool()rejecting aCreateTaskResult(#2637).
Behaviour while deferred (fail-closed, both proven at the mcp-in-memory
level):
- Generated servers never advertise a
taskscapability, even to a client that negotiated one, so no client is invited into a lifecycle the server cannot serve. - A
tools/callcarrying task augmentation is processed as an ordinary request and returns one finalCallToolResultwith no task handle, the behaviour the2025-11-25Tasks utility requires of a receiver that declared no task capability for that request type.tasks/get,tasks/result,tasks/list, andtasks/cancelanswer with JSON-RPC-32601. - Ordinary progress-token gating and the single final
CallToolResultpinned by #175 are unchanged.
Unblock condition: a published @modelcontextprotocol/server /
@modelcontextprotocol/client release that routes task operations through its
typed surface, a server-side task store or registerTool task handler, a
typed client path for CreateTaskResult and task polling, and a tasks
capability the SDK itself gates, for whichever revision the conformance lane
then records. packages/rsc-runtime/tests/mcp-tasks-deferral.test.ts pins the
audited SDK version and asserts each fact above against the installed
packages; when one stops holding, that test (or pnpm typecheck, through its
@ts-expect-error sentinel on setRequestHandler('tasks/get', …)) fails and
this section must be re-audited before the pin is moved.