Mechanism revised 2026-07-10 (implemented in #1080). The original proposal — return paymentRequiredResult directly and skip task creation entirely — cannot ship: SDK 1.29.0's Server.setRequestHandler validates every task-augmented tools/call response against CreateTaskResultSchema (requires a task field), so a bare CallToolResult surfaces as McpError(InvalidParams) instead of the x402 payload. The shipped fix resolves the task synchronously at creation instead: same goals (no zombie working phase, faster x402 delivery, dead code removed), SDK-compliant wire shape.
Problem
When paymentRequiredResult is set (missing payment signature), we still create a task and pass the error into executeToolAndUpdateTask(), which immediately stores it as a completed-with-error result via an async setImmediate hop. The outcome is known before execution: the client sees a fake working phase, and the task path carries dead re-check code. Same applies to standbyRejection and any future synchronously-resolvable pre-flight check.
Fix (as shipped in #1080)
In the handler's task branch, right after createTask: if a pre-flight value is set (standby priority first, matching the sync path), store it as the terminal completed result, emit a single completed status notification (no working transition), and return { task: { ...task, status: 'completed' } }. Telemetry rides the handler's finally, firing once with the same properties as the non-task path. The dead standby/402 re-checks and their two carrier params are deleted from executeToolAndUpdateTask.
Sequencing
Sub-issue of umbrella #658. Landed after #1061 (behavior-pinning tests, PR #1073) and before #1063 (dispatchToolCall extraction) — removing the pre-flight checks from the task path shrinks that extraction.
Wire-visible change (flagged for the internal contract suite in #1080): a task-augmented call that fails pre-flight now returns a CreateTaskResult that is already completed; previously it transitioned through working and completed asynchronously. Notification timing changes accordingly (single completed, no working).
Context
Discovered during #680 (task status workaround).
Problem
When
paymentRequiredResultis set (missing payment signature), we still create a task and pass the error intoexecuteToolAndUpdateTask(), which immediately stores it as a completed-with-error result via an asyncsetImmediatehop. The outcome is known before execution: the client sees a fakeworkingphase, and the task path carries dead re-check code. Same applies tostandbyRejectionand any future synchronously-resolvable pre-flight check.Fix (as shipped in #1080)
In the handler's task branch, right after
createTask: if a pre-flight value is set (standby priority first, matching the sync path), store it as the terminalcompletedresult, emit a singlecompletedstatus notification (noworkingtransition), and return{ task: { ...task, status: 'completed' } }. Telemetry rides the handler'sfinally, firing once with the same properties as the non-task path. The dead standby/402 re-checks and their two carrier params are deleted fromexecuteToolAndUpdateTask.Sequencing
Sub-issue of umbrella #658. Landed after #1061 (behavior-pinning tests, PR #1073) and before #1063 (
dispatchToolCallextraction) — removing the pre-flight checks from the task path shrinks that extraction.Wire-visible change (flagged for the internal contract suite in #1080): a task-augmented call that fails pre-flight now returns a
CreateTaskResultthat is alreadycompleted; previously it transitioned throughworkingand completed asynchronously. Notification timing changes accordingly (singlecompleted, noworking).Context
Discovered during #680 (task status workaround).