Tasks¶
pymcp-kit supports task-aware tool execution, result polling, cancellation, and progress reporting. Tasks are enabled by default at the capability level, but each tool still decides whether task augmentation is allowed.
Task Execution Flow¶
Task-aware tools can return immediately with task metadata, continue in the background, emit status/progress notifications, and expose the final structured result through tasks/result:
sequenceDiagram
autonumber
participant Client
participant Runtime
participant Engine as Task Engine
participant Tool
Client->>Runtime: tools/call<br/>_meta.task requested
Runtime->>Runtime: verify task capability and tool execution policy
Runtime->>Engine: create task
Engine-->>Client: task accepted / pending response
Engine->>Tool: run tool with task_context and cancel_token
Tool->>Engine: task_context.send_progress(...)
Engine-->>Client: notifications/progress
Engine-->>Client: notifications/tasks/status
alt user input required
Tool->>Engine: task_context.require_input(...)
Engine-->>Client: notifications/tasks/status waiting
Client->>Runtime: elicitation response
Runtime->>Engine: resume task
end
alt client cancels
Client->>Runtime: tasks/cancel
Runtime->>Engine: cancel task
Engine->>Tool: cancel_token trips
Engine-->>Client: notifications/tasks/status cancelled
else tool completes
Tool-->>Engine: content and structuredContent
Engine-->>Client: notifications/tasks/status complete
Client->>Runtime: tasks/result
Runtime->>Engine: load stored result
Engine-->>Client: final MCP result shape
end Opt A Tool Into Tasks¶
Use execution.taskSupport on the tool definition:
"optional": the tool can run directly or as a task"required": the caller must use task augmentation"forbidden": the tool only runs as a normal request
from pymcp.registry import tool_registry
@tool_registry.register(
execution={
"taskSupport": "optional",
"runner": "async",
}
)
async def build_index(repo: str) -> str:
return f"Indexed {repo}"
Injected Task Helpers¶
When the tool signature asks for them, the runtime can inject:
cancel_tokentask_contextrequest_context
task_context exposes helpers like:
await task_context.send_progress(...)await task_context.require_input(...)await task_context.set_working(...)
cancel_token lets the tool stop cooperatively.
Example: Progress, Cancellation, And Elicitation¶
import asyncio
from pymcp.registry import tool_registry
from pymcp.runtime.context import RequestContext
from pymcp.session import request_elicitation
from pymcp.tasks import CancellationToken
@tool_registry.register(
execution={
"taskSupport": "optional",
"runner": "async",
}
)
async def review_repository(
repo: str,
cancel_token: CancellationToken | None = None,
task_context=None,
request_context: RequestContext | None = None,
) -> dict[str, object]:
stages = ["clone", "index", "summarize"]
for index, stage in enumerate(stages, start=1):
if cancel_token is not None:
cancel_token.check_cancelled()
await asyncio.sleep(0)
if task_context is not None:
await task_context.send_progress(
index,
total=len(stages),
message=f"Finished {stage} for {repo}",
)
if (
task_context is not None
and request_context is not None
and request_context.session_id is not None
):
await task_context.require_input("Choose the publish target before continuing.")
_, response = await request_elicitation(
request_context.app,
request_context.session_id,
{"message": "Which environment should receive the report?"},
task_id=task_context.task_id,
)
await task_context.set_working("Continuing after user input.")
return {
"content": [{"type": "text", "text": f"Review complete for {repo}"}],
"structuredContent": {"repo": repo, "status": "complete"},
}
Task RPC Surface¶
The built-in handlers expose:
tasks/listtasks/gettasks/canceltasks/result
Task-related flows also emit:
notifications/tasks/statusnotifications/progress
If a tool returns a structured MCP result directly, tasks/result preserves that result shape instead of flattening it into plain text.
Ownership And Access¶
Task visibility follows the caller context:
- without auth, tasks are scoped to the session
- with auth, tasks are bound to the authenticated principal
That means the same authenticated principal can access its tasks across sessions, while a different principal cannot.
Notes¶
- If a client did not negotiate task support for
tools/call, the runtime ignores task metadata and processes the tool call normally. - Stdio and Streamable HTTP both support task side-channel traffic such as status and progress updates.