Two remote-worker fixes. A separate worker process cannot reach the submitting
process's server map, so a mounted task's ctx.fastmcp/CurrentFastMCP() fell back
to the root; the worker now re-resolves the owning child from the root using the
snapshotted tool name. And restoring headers no longer fabricates a live Request
— get_http_headers() reads a dedicated task-headers context var while
get_http_request()/CurrentRequest() correctly keep raising inside a task.
Three review fixes: transparent call_tool(timeout=N) now enforces one deadline
across the whole poll loop (not per-request), matching the sync timeout; the
tools/call interceptor resolves the client-requested component version instead
of the highest; tasks/cancel runs under the per-task update lock and re-resolves
the live leg, so it can't cancel a stale leg while an update enqueues the next.
A guard task parked on input has an already-COMPLETED Docket execution, so
docket.cancel on it was a no-op: tasks/get reported input_required forever and
tasks/update could still resume it. Record a durable logical-cancellation
marker that tasks/get reports as cancelled and tasks/update refuses to resume,
and clear the parked leg's outstanding requests on cancel.
A queued task can outlive its submitter's token expiry: install the snapshot
token only if still valid, matching the SDK bearer check, so a delayed task
never runs under credentials a live request would reject. ToolTask.wait now
bounds each tasks/get by the remaining deadline so a stalled poll cannot block
past the caller's timeout.
Resolve the error-masking policy via the worker-server resolver instead of
the active Context: a task tool that raises without requesting a ctx param
has no active context, so the old lookup leaked unmasked error text past
mask_error_details=True. Also route custom Tool subclasses through the same
error-conversion wrapper as FunctionTool.
- Client task support is opt-in via importing fastmcp_tasks (drop the core
auto-load of companion packages); a plain Client never advertises tasks.
- A worker restores the submitting caller's auth token and headers from the
task snapshot into the standard ambient context, so get_access_token() /
get_http_headers() work in a distributed worker with no new core hooks.
- worker_cli validates the loaded extension's resolved backend, not env defaults,
so a constructor-configured Redis worker starts.
- Thread the per-call read timeout through task polling; bound ToolTask.wait by
its deadline; set_elicitation_callback rebuilds internal extensions so a
later-set handler answers in-task input.
- README imports TaskConfig from fastmcp.utilities.tasks.
Co-Authored-By: Claude <noreply@anthropic.com>
- tasks/get|update|cancel now return -32003 when the client did not declare the
tasks extension for the request (SEP-2663 MUST).
- A task tool that raises is a completed task with an is_error result, not a
failed task; failed is reserved for protocol faults, matching a live tools/call.
- A per-task lock serializes concurrent tasks/update so two racing answers cannot
each enqueue a next leg (double execution).
Co-Authored-By: Claude <noreply@anthropic.com>
A task tool that returns InputRequiredResult now ends its leg (freeing the
worker) and stores the ask as durable state; tasks/update enqueues a fresh
Docket execution (the next leg) with accumulated request_state/input_responses
injected via ctx. No worker ever blocks on input, so a parked task no longer
holds up shutdown. Imperative ctx.elicit() inside a task is removed and raises
with guard-pattern guidance.
Co-Authored-By: Claude <noreply@anthropic.com>
Widen the tools/call result serialization (via a refcounted, modern-gated wrap
installed by TasksExtension) so a CreateTaskResult reaches the client instead of
being stripped by the CallToolResult|InputRequiredResult surface — the SDK ships
claim consumption but no production. Emit the resultType discriminator the
protocol requires (task on CreateTaskResult, complete on the tasks/* results);
the draft schema forbids it (additionalProperties:false), a contradiction
reported upstream. Closes compliance gaps G1/G4/G5.
Co-Authored-By: Claude <noreply@anthropic.com>
TasksExtension serves io.modelcontextprotocol/tasks on the extension API:
a decide-and-task tools/call interceptor (era-gated to modern connections),
tasks/get with inlined results and inputRequests, tasks/update delivering
poll-based in-task elicitation, tasks/cancel, durable creation, and
auth-scoped task isolation. Wire models validate against the vendored
ext-tasks schema. Worker-side Context hooks are refcounted so sibling
servers cannot strand each other's workers.
Co-Authored-By: Claude <noreply@anthropic.com>
Engine modules (keys, context snapshot, docket lifespan, worker CLI,
client handles) move intact; SEP-1686 wire modules park in _legacy_wire
for adaptation to SEP-2663. Core keeps task=True declaration on tools
only and raises at serve time until the tasks extension is registered.
Co-Authored-By: Claude <noreply@anthropic.com>