tasks commands give the CLI the manual-testing loop the Inspector’s Tasks tab has: create a task from a tool call, read it, answer its input, cancel it, and watch it run to a terminal status — on whichever tasks wire the server resolves to.
For judging a server’s conformance to the tasks spec, see MCP Tasks Conformance. These commands do the opposite job: they assume the server works and let you drive it.
Every task command connects directly to the server you name, exactly like
tools call. Tasks are not available in hosted mode.The two wires
A connection resolves to exactly one tasks wire, and the verbs differ between them. Ask before you assume:tasks capabilities always exits 0 — "wire": "none" is a valid answer, not a failure. Every other task command exits 1 on a server with no tasks wire.
Pass
--wire legacy or --wire extension to any command to assert the wire you expect; a mismatch exits 1 rather than quietly running against the other one.
On the legacy wire the sub-capabilities are independent: a server that declares only
tasks.cancel still resolves to wire: "legacy". Commands check the specific capability they need and exit 1 with TASKS_UNSUPPORTED if the server never declared it, rather than issuing a request it did not advertise. tasks list is the one that would otherwise mislead — the SDK turns a method-unavailable response into an empty list, which reads as “no tasks running” when the truth is “this server cannot tell you”.Creating a task
Add--task to a normal tools call. The CLI picks the right opt-in for the resolved wire:
tools call exit contract: a result with isError: true exits 1, exactly as it would without --task. The 0-on-isError rule below applies only to a task that completed.
--task-ttl <ms> requests a TTL. It applies only to the legacy wire; on the extension wire it is a usage error (exit 2), because SEP-2663 gives the client no say in the TTL.
--task cannot be combined with --ui, --reporter, --validate-response, or --expect-success: a task-creation envelope is not a CallToolResult, and those flags all assume one.
Inspecting a task
TASK_UNKNOWN_OR_EXPIRED with exit 1.
tasks result exists only on the legacy wire. On the extension wire a completed task carries its result inline, so tasks result exits 1 and points you at tasks get.
tasks cancel on the extension wire is cooperative: the ack is empty, the task may still be running, and it may still reach completed. Re-poll with tasks get.
Answering input_required
On the extension wire a task that needs input exposes inputRequests on tasks get. Answer them with tasks update:
--input-responses accepts inline JSON, @path, or - for stdin, like --tool-args. The ack is empty by design — the task’s status does not move until a later tasks get.
Only elicitation/create requests are answerable. A task asking for roots/list or sampling/createMessage is reported as an unanswered rejection; the Inspector UI cannot answer those either.
Watching a task
tasks watch polls a task until it reaches a terminal status, printing transitions to stderr and a single envelope to stdout:
{ "created": …, "watch": … } and exits with the watch’s code.
Interrupting before the task exists — Ctrl-C while the tools/call is still in flight — emits { "status": "aborted", "phase": "create", "wire": … } and exits 130, the same code an interrupted watch uses. There is no task id to report yet, so phase says where the interrupt landed instead.
Pacing
--poll-interval-ms sets your minimum interval. The server’s advertised pollIntervalMs, any Retry-After the transport surfaces on a failed read, and error backoff still apply as floors on top of it, so the CLI cannot poll a server faster than that server asked for.
--duration-ms bounds the whole watch (default 5 minutes). --max-input-rounds and --max-consecutive-errors bound the input loop and transient read failures.
Interactivity
--interactive answers the task’s elicitations from the terminal. Like every other interactive verb, it needs a real TTY on stdin: piped or redirected stdin, --yes, CI, or MCPJAM_NON_INTERACTIVE all make it decline cleanly rather than prompt into the void. A declined or unanswered input ends the watch with exit 6, and you can answer out of band with tasks update and watch again.
Ctrl-C (or SIGTERM) aborts the watch cleanly: you still get an envelope, with "outcome": "aborted" and exit 130.
tasks watch does not consult the host tasks policy. Like the Inspector’s Tools tab, it is a debugging affordance — the policy is a modelling concern for hosts, not a boundary on manual testing.Watch exit codes
A completed task whose result carries
isError: true still exits 0: the task finished, and the tool’s error is in the envelope. Branch on task.result.isError if you need to fail on it.
Outcomes that collapse onto 1 are still distinguishable — outcome is always in the envelope.
