-
Notifications
You must be signed in to change notification settings - Fork 787
feat: implement support for SEP-1686 Tasks #755
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
4cf98e9 to
82ccfa1
Compare
|
Nice to have this in, the api seems quite the same as typescript sdk |
|
@LucaButBoring Thanks. I'll try to review the PR. In the meantime, please rebase against |
82ccfa1 to
8baba5e
Compare
|
Rebased - this is (unfortunately) a very large PR, but I recommend starting with the code related to the key design decisions, and there's also a new integration test ( |
| * @see McpSchema.CallToolResult | ||
| * @see McpSchema.CreateMessageResult | ||
| */ | ||
| public <T extends McpSchema.ServerTaskPayloadResult> T getTaskResult( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this be ClientTaskPayloadResult? Regardless, there is no current way to get the input_required request from the client for a task.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the client, so when it calls tasks/result, it will only receive the appropriate server message for the task (currently only CallToolResult).
The input_required-bound request happens asynchronously relative to this but (with SSE) requires the client to invoke tasks/result first.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a lengthy (and admittedly hard to parse) note on the input_required status in the spec explaining the side-channeling here: https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/tasks#input-required-status
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why have the typeRef? There's only 1 possible result type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mostly for consistency with the server version of this interface, and so that we don't need to make a breaking change if (I expect "when") we add more supported client->server methods for Tasks.
| * @param resultTypeRef Type reference for deserializing the result. | ||
| * @return The task result. | ||
| */ | ||
| public <T extends McpSchema.ServerTaskPayloadResult> T getTaskResult(String taskId, TypeRef<T> resultTypeRef) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same as above
|
I'm testing with our server implementation. The protocol version in the initialize request is |
Implements SEP-1686 (Tasks), from the latest specification release.
Motivation and Context
Tasks address several protocol gaps:
start_tool,get_status,get_resulttools; a single task-aware tool handles the full lifecycleUsage
Server: Defining a Task-Aware Tool
Server: Using TaskContext for Lifecycle Management
TaskSupportMode options:
REQUIRED(default): Must have task metadata; returns error otherwiseOPTIONAL: Works with or without task metadata; auto-polling shim provides backward compatibilityFORBIDDEN: No task support (regular tools)Client: Streaming API (Recommended)
Drop-in replacement for
callToolthat handles polling automatically:Client: Task-Based API (For Explicit Control)
For consumers who need custom polling behavior, cancellation logic, or batched task management:
Similar patterns exist for sampling (
createMessageStream/createMessageTask) and elicitation (createElicitationStream/createElicitationTask).Server: Bidirectional Task Flows
Servers can send task-augmented requests to clients, assuming the client has configured its own
TaskStore:Key Design Decisions
Experimental namespace - All task APIs are in
io.modelcontextprotocol.experimental.tasks, signaling that the API may change (matches TypeScript/Python SDKs)TaskStore abstraction - Interface for pluggable storage;
InMemoryTaskStoreprovided for development and testing. The originating request (e.g.,CallToolRequest) is stored alongside the task, so tool routing can be derived from stored context rather than maintained as separate mapping state.Auto-polling shim -
OPTIONALmode tools work transparently for non-task-aware clientsDefense-in-depth session isolation - Session ID required on all TaskStore operations; enforced at both server and storage layers to prevent cross-session task access
nullforsessionIdbypasses validation (single-tenant mode). This is used byMcpAsyncClientsince clients are inherently single-tenant - there's only one session, so cross-session isolation doesn't apply.How Has This Been Tested?
Breaking Changes
None
Types of changes
Checklist
Additional context
Closes #668
This PR also includes a tweak to how
202 Acceptedis handled by the client implementation, which was done to handle how the TypeScript server SDK configures its response headers when accepting JSON-RPC responses and notifications from the client - in particular, sendingInitializeNotificationproduced an exception in the Java SDK client before this, which made testing compatibility with the existing TS SDK's Tasks implementation rather difficult.