Skip to main content
Get an MCP server that stamps the logged-in user on every task, asks the client for a form, and persists work in the engine you already run.

Isolate

WithTasksPlus records the owner. Another user sees not found.

Elicit

await server.ElicitAsync(form) parks the form on the task. The SDK interceptor resumes when the client answers.

Swap

Same identity factory. EF, Redis, Cosmos, or native Postgres.

Install

Core is enough for a single process. HTTP hosts also take the ASP.NET Core MCP package.
Analyzers ship in Core. A rebuild (and sometimes an IDE restart) is enough for squiggles.

Wire the server

Same identity factory on every host. HTTP reads HttpContext.User. Stdio uses a constant user id.
Register the store as a singleton. Stateless HTTP creates a fresh McpServer per POST; a scoped store looks empty on the next tasks/get.
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.

Write a task tool

Return Task<string> so a task-capable client can poll. Read the user from the principal the host already stamped.
Claim lookup is NameIdentifier, then sub, UPN, Name. No user on a client-facing call throws McpTaskUserRequiredException.

Ask the client for a form

await server.ElicitAsync(form) is enough inside a task. The SDK interceptor parks the form and resumes when the client answers.
Field-by-field Create, From<T>(), and metadata-driven builders are in Form elicitation. Broken From<T>() shapes are compiler diagnostics in Analyzers.

Run it

The in-memory sample is a complete Streamable HTTP host. Fake auth is the X-User-Id header.
MCP is at http://localhost:5280/. Send X-User-Id: alice.
  1. Call HelloWorld as alice with the Tasks extension. Keep the task id.
  2. tasks/get as alice returns the task.
  3. tasks/get as bob returns not found.
  4. Omit X-User-Id and client-facing task operations throw McpTaskUserRequiredException.
samples/CloudNimble.McpEssentials.Samples.Basic is in-memory.Entity Framework Core (localhost:5290), Redis (5300), Cosmos DB (5310), and PostgreSQL (5320) each have a host in the same folder. The store is the only thing that changes.

Swap the store

Identity stays on the platform accessor. Production registers a durable store with the same factory.
Map McpTasks with AddMcpTasksTable when you stay on Entity Framework Core. Redis and native Postgres are the buses for multi-node ElicitAsync. EF and Cosmos take an optional IMcpTaskInputSignalBus.

Next steps

Why MCP Essentials?

How this layer extends the official SDK for real-world servers.

Authenticated tasks

Isolation rules, overloads, and every sample.

Form elicitation

Field-by-field, From<T>(), and parking a form on a task.

Analyzers

MCPESS001–006 so a broken form fails the build.

Choose a store

EF Core, Redis, Cosmos, native Postgres, and the specified family.