Skip to main content
MCP Tasks (SEP-2663) let a client start long-running work, get a task id, and poll tasks/get until the work finishes. The C# SDK stores that state in IMcpTaskStore. The built-in InMemoryMcpTaskStore is shared across every request to the process. InMemoryMcpTaskStorePlus records the owner on create and never leaks another user’s task, payload, or existence. Durable providers persist the same contract.

Stamp the owner

CreateTaskAsync reads the current user and records it on the snapshot.

Isolate later calls

Get, cancel, and resolve require that user. Anyone else sees not found.

Complete in the background

The SDK finishes work after the HTTP request is gone. Complete, fail, and set-input skip the live-user check.

Wire it up

HTTP hosts register IHttpContextAccessor the platform way and pass a factory that resolves it from the built IServiceProvider.
The SDK WithTasks API needs the store object at registration time, before Build(). The factory overload creates the store immediately and captures the provider when the container is built. Each client-facing task call then reads HttpContext.User.
Async [McpServerTool] methods that return Task / Task<T> automatically advertise task support. A client that opts into io.modelcontextprotocol/tasks gets a task id owned by the user on that request.
No user on a client-facing call throws McpTaskUserRequiredException.

Overloads

Each overload has a matching AddInMemoryMcpTaskStorePlus on IServiceCollection. Pass McpTaskStoreOptions as the next argument to set DefaultTimeToLive and DefaultPollIntervalMs. null TTL keeps tasks until the store discards them. Claim lookup matches the SDK session identity order: NameIdentifier, sub, UPN, Name. Types are unsealed; subclass McpTaskStoreBase to change isolation, TTL, or storage.

Isolation rules

Owner comparison is ordinal. Register the store as a singleton. Stateless HTTP creates a fresh McpServer per POST; a scoped store looks empty on the next tasks/get.
Do not store MCP task rows on domain tables. Tasks expire in minutes; orders and users do not.

Samples

Each sample is a standalone Streamable HTTP host. Fake authentication is the X-User-Id header. One tool, HelloWorld, injects ClaimsPrincipal and returns Task<string> so a task-capable client gets a trackable task. The store is the only thing that changes.
In-memory. No extra infrastructure.
Point a harness at http://localhost:5280/ with X-User-Id: alice. Call HelloWorld with the Tasks extension, then tasks/get as alice and as bob. Bob sees not found.samples/…Samples.Basic

Swap the backing store

IMcpTaskStorePlus and McpTaskStoreBase are the contract durable providers implement. Pick a backing store in Stores. HTTP, Stdio, and Store only tabs on each store page match the wiring tabs above.

Entity Framework Core

Any EF provider. Host owns the DbContext and migrations.

Redis

Native expiry and pub/sub for multi-node elicitation.

Cosmos DB

Point reads by owner, native document TTL, index for background complete.

PostgreSQL

Npgsql without EF. LISTEN/NOTIFY wakes elicitation on other nodes.

Form elicitation

Ask the client for a form while a task is running.

Analyzers

Catch broken forms at compile time.