You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I'd like to pick up the Tasks extension for the Java SDK. Some of the tools on the MCP servers I run in production need a human approval before they execute, so I'm also looking at SEP-2848, which builds on Tasks. @chemicL should this wait for #1170 to land, or is it fine to start against the current server API?
@liotur thanks. #1170 is just the first step, we need to follow up with the client implementation. From then on we can think of implementing tasks. Please do contribute here in the form of ideas around the design for the extension. In the modern server primitives I tried to come up with more flexible interfaces for pluggability in mind so feedback on that will be very valuable.
@chemicL thanks. I read through #1170 with Tasks in mind. Most of it fits as a plain feature: a TasksFeature can serve tasks/get, tasks/update and tasks/cancel, advertise the extension and check ctx.clientCapabilities().extensions(), and since CallToolOutcome is open, tools/call can already return a CreateTaskResult. A few questions:
Where should the decision to create a task live? Today it's either a feature that wraps ToolsFeature, or the repository returning CreateTaskResult. With the repository route, the result that tasks/get serves later skips the outputSchema check, since that's private to ToolsFeature. Either way, it's up to each implementation not to return a task to a client that didn't declare the extension on that request. McpServer already rejects InputRequired from methods that don't support it. Would a similar check for task results fit? (Add ability to intercept tool call executions. #720 looks related.)
Whatever runs the tool after CreateTaskResult can only reuse the original McpRequestContext (final, package-private constructor, no attributes). So there's no way to hand the tool anything task-specific, like a cancel flag for blocking code. Is reusing the original context the intended pattern?
Every tasks/* call needs an auth check, but the only principal hook is the extractor on HmacRequestStateCodec, and it defaults to null. Would a server-level principal resolver that features can read make sense? SEP-2848 would need the same principal in requestState and in the task.
Smaller: SEP-2663 has clients send Mcp-Name set to the taskId on tasks/*, but the transport checks Mcp-Name only for three hard-coded methods, so an extension can't opt in.
Happy to open separate issues for any of these if you'd rather track them outside #1013.
I think your intuition is good: you'd wrap the ToolsFeature. Returning a CreateTaskResultcan be in a normal handler in the tools repo. However, everything else, including task/get, needs to be in a feature. In there, you can then validate the output schema of task/get's result. We haven't planned interceptors, as this can be modeled with features directly. The API is low level and needs a bit of Reactor, but your feature will probably do something like:
publicclassCustomTasksFeatureimplementsMcpFeature {
privatefinalToolsFeaturedelegate = null; // todo: inject the real thing@OverridepublicMono<? extendsMcpAsyncResponse<? extendsMcpSchema.Result>> handle(McpRequestContextctx, Objectparams) {
returnpreProcess(ctx, params)
// then optionally call the tool
.flatMap(preProcessed -> {
// you can do if/else here, or in a deferred monoif (preProcessed.someDecision()) {
returnMono.just(McpAsyncResponse.result(McpSchema.CallToolResult.builder().isError(true).build()));
}
else {
returndelegate.handle(ctx, params);
}
})
// post-processing
.map(response -> {
if (responseinstanceofMcpAsyncResponse.Result<? extendsMcpSchema.Result> result
&& result.result() instanceofMcpSchema.CallToolResulttoolResult) {
// here you could validate the output schemavarupdatedResult = postProcess(toolResult);
returnMcpAsyncResponse.result(updatedResult);
}
returnresponse;
});
}
privateMcpSchema.CallToolResultpostProcess(McpSchema.CallToolResulttoolResult) {
returnnull;
}
privateMono<PreProcessed> preProcess(McpRequestContextctx, Objectparams) {
returnnull;
}
recordPreProcessed(McpRequestContextctx, Objectparams, ObjectotherStuff) {
booleansomeDecision() {
returntrue;
}
}
// ...
}
Correct, context is known at task creation time and that's about it - if the client doesn't poll, we don't know anything else. Note that context is mostly information about the request, e.g. client capabilities, name and version. The context, in turn, context an McpTransportContext which can hold arbitrary data injected through an McpContextExtractor. This allows you to pass, say, the principal executing the task, or a tenant header, etc, but that's request level. I'm not sure what you mean by cancel flag; but I imagine you want to pass "extra arguments" to the tool. In that case, in the "preprocessing" step above, you change the CallToolRequest to have more arguments, maybe gated in _meta or something.
Weeeeelllll tasks are vague about auth. SEP says "Servers MUST perform authentication and authorization checks" but in STDIO there is no concept of auth. The hook for getting a principal is the contextExtractor, as stated above. It's server-level (transport-level, technically), and ends up in McpRequestContext.transportContext() under whatever key you choose for your principal.
Mcp-Name: that's a gap indeed. That's an annoying abstraction leak between the transport layer and server layer - what about tasks on STDIO ?
You could solve it again with a transport extractor + feature-level validation, but the whole idea of a feature is to self-contain extensions.
I think for Mcp-Name we'll probably take a shortcut and carry it forward. Opened #1317 for this purpose.
Activity
Kehrlann commentedon Jun 24, 2026
We will prioritize extensions in the SDK after we have the core spec covered.
If you need hooks to provide your own implementation, please open a new issue and link to this one.
anushree1808 commentedon Sep 1, 2026
Hi. What is the estimated timeline for the new protocol version and task extensions to be available?
liotur commentedon Oct 12, 2026
I'd like to pick up the Tasks extension for the Java SDK. Some of the tools on the MCP servers I run in production need a human approval before they execute, so I'm also looking at SEP-2848, which builds on Tasks. @chemicL should this wait for #1170 to land, or is it fine to start against the current server API?
chemicL commentedon Oct 15, 2026
@liotur thanks. #1170 is just the first step, we need to follow up with the client implementation. From then on we can think of implementing tasks. Please do contribute here in the form of ideas around the design for the extension. In the modern server primitives I tried to come up with more flexible interfaces for pluggability in mind so feedback on that will be very valuable.
liotur commentedon Oct 16, 2026
@chemicL thanks. I read through #1170 with Tasks in mind. Most of it fits as a plain feature: a
TasksFeaturecan servetasks/get,tasks/updateandtasks/cancel, advertise the extension and checkctx.clientCapabilities().extensions(), and sinceCallToolOutcomeis open,tools/callcan already return aCreateTaskResult. A few questions:Where should the decision to create a task live? Today it's either a feature that wraps
ToolsFeature, or the repository returningCreateTaskResult. With the repository route, the result thattasks/getserves later skips the outputSchema check, since that's private toToolsFeature. Either way, it's up to each implementation not to return a task to a client that didn't declare the extension on that request.McpServeralready rejectsInputRequiredfrom methods that don't support it. Would a similar check for task results fit? (Add ability to intercept tool call executions. #720 looks related.)Whatever runs the tool after
CreateTaskResultcan only reuse the originalMcpRequestContext(final, package-private constructor, no attributes). So there's no way to hand the tool anything task-specific, like a cancel flag for blocking code. Is reusing the original context the intended pattern?Every
tasks/*call needs an auth check, but the only principal hook is the extractor onHmacRequestStateCodec, and it defaults tonull. Would a server-level principal resolver that features can read make sense? SEP-2848 would need the same principal inrequestStateand in the task.Smaller: SEP-2663 has clients send
Mcp-Nameset to thetaskIdontasks/*, but the transport checksMcp-Nameonly for three hard-coded methods, so an extension can't opt in.Happy to open separate issues for any of these if you'd rather track them outside #1013.
Kehrlann commentedon Nov 6, 2026
@liotur
ToolsFeature. Returning aCreateTaskResultcan be in a normal handler in the tools repo. However, everything else, includingtask/get, needs to be in a feature. In there, you can then validate the output schema oftask/get's result. We haven't planned interceptors, as this can be modeled with features directly. The API is low level and needs a bit of Reactor, but your feature will probably do something like:McpTransportContextwhich can hold arbitrary data injected through anMcpContextExtractor. This allows you to pass, say, the principal executing the task, or a tenant header, etc, but that's request level. I'm not sure what you mean by cancel flag; but I imagine you want to pass "extra arguments" to the tool. In that case, in the "preprocessing" step above, you change theCallToolRequestto have more arguments, maybe gated in_metaor something.contextExtractor, as stated above. It's server-level (transport-level, technically), and ends up inMcpRequestContext.transportContext()under whatever key you choose for your principal.Mcp-Name: that's a gap indeed. That's an annoying abstraction leak between the transport layer and server layer - what about tasks on STDIO ?You could solve it again with a transport extractor + feature-level validation, but the whole idea of a feature is to self-contain extensions.
I think for Mcp-Name we'll probably take a shortcut and carry it forward. Opened #1317 for this purpose.