Skip to content

SEP-2663: Tasks Extension #1013

Open

Description

@localden

Tracking implementation of SEP-2663 for the 2026-07-28 MCP spec release.

Activity

  1. added
    enhancementNew feature or request
    extensionsWork relating to MCP extensions - see https://modelcontextprotocol.io/extensions/overview
    P2Moderate issues affecting some users, edge cases, potentially valuable feature
    on Jun 23, 2026
  2. Kehrlann commented on Jun 24, 2026

    @Kehrlann
    Contributor

    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.

  3. added this to the 3.x Planning milestone on Jun 25, 2026
  4. anushree1808 commented on Sep 1, 2026

    @anushree1808

    Hi. What is the estimated timeline for the new protocol version and task extensions to be available?

  5. liotur commented on Oct 12, 2026

    @liotur

    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?

  6. chemicL commented on Oct 15, 2026

    @chemicL
    Member

    @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.

  7. modified the milestones: 2.2.0-M1, 2.2.0-M2 on Oct 16, 2026
  8. liotur commented on Oct 16, 2026

    @liotur

    @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:

    1. 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.)

    2. 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?

    3. 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.

  9. modified the milestones: 2.2.0-M2, 2.2.0-M3 on Nov 2, 2026
  10. Kehrlann commented on Nov 6, 2026

    @Kehrlann
    Contributor

    @liotur

    1. 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:
    public class CustomTasksFeature implements McpFeature {
    
    	private final ToolsFeature delegate = null; // todo: inject the real thing
    
    	@Override
    	public Mono<? extends McpAsyncResponse<? extends McpSchema.Result>> handle(McpRequestContext ctx, Object params) {
    		return preProcess(ctx, params)
    			// then optionally call the tool
    			.flatMap(preProcessed -> {
    				// you can do if/else here, or in a deferred mono
    				if (preProcessed.someDecision()) {
    					return Mono.just(McpAsyncResponse.result(McpSchema.CallToolResult.builder().isError(true).build()));
    				}
    				else {
    					return delegate.handle(ctx, params);
    				}
    			})
    			// post-processing
    			.map(response -> {
    				if (response instanceof McpAsyncResponse.Result<? extends McpSchema.Result> result
    						&& result.result() instanceof McpSchema.CallToolResult toolResult) {
                        // here you could validate the output schema
    					var updatedResult = postProcess(toolResult);
    					return McpAsyncResponse.result(updatedResult);
    				}
    				return response;
    			});
    	}
    
    	private McpSchema.CallToolResult postProcess(McpSchema.CallToolResult toolResult) {
    		return null;
    	}
    
    	private Mono<PreProcessed> preProcess(McpRequestContext ctx, Object params) {
    		return null;
    	}
    
    	record PreProcessed(McpRequestContext ctx, Object params, Object otherStuff) {
    		boolean someDecision() {
    			return true;
    		}
    	}
    
    	// ...
    
    }
    1. 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.
    2. 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Moderate issues affecting some users, edge cases, potentially valuable featureenhancementNew feature or requestextensionsWork relating to MCP extensions - see https://modelcontextprotocol.io/extensions/overview

    Projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions