Skip to main content
McpEssentials.EntityFrameworkCore is the first durable authenticated task store. It subclasses McpTaskStoreBase and persists McpTaskEntity rows through your DbContext.
This package works with any EF Core database provider. SQL Server, SQLite, PostgreSQL (Npgsql), MySQL, MariaDB, Cosmos via EF, Oracle, and whatever else you already pass to AddDbContext are all valid. The store talks to TContext.Set<McpTaskEntity>(); it never binds a specific engine.
The host owns the context, the connection, and migrations. This package ships the entity, AddMcpTasksTable on ModelBuilder and on DbContextOptionsBuilder, and a singleton store that opens a scope per operation so a standard scoped AddDbContext works.

Install

Register an EF Core provider on the context the way you already do:

Map the table

Keep MCP task rows on their own table; they expire in minutes. The default name is McpTasks. Pass a name and schema on either call to change it: AddMcpTasksTable("AppTasks", "mcp"). The store talks to TContext.Set<McpTaskEntity>(). The host does not need a DbSet. Both options use the same host DbContext. Pick one.

OnModelCreating

Use this in a single-project app, or whenever the context project is already allowed to depend on MCP.

AddDbContext

Use this when the context type lives in a library (data, persistence, domain) that other projects consume without pulling MCP. Call AddMcpTasksTable on the options next to UseSqlServer (or whatever provider you already use). EF still uses the same host DbContext; the mapping runs after OnModelCreating.
Run migrations with the application as the startup project so the options hook is in the model:
A migration from the context project alone will not see McpTasks and can emit a drop. Mapped columns include TaskId, OwnerUserId, Status (enum name as string), timestamps, TimeToLive, ExpiresAt, DiscardAfter (UTC ticks so every provider, including SQLite, can ExecuteDelete), JSON payloads, and an application-managed RowVersion. Indexes cover owner, (OwnerUserId, TaskId), ExpiresAt, and DiscardAfter. SQL Server hosts may remap RowVersion as rowversion in their own configuration.

Wire the store

Same identity factory as Authenticated tasks. The SDK WithTasks API captures the store before Build(); WithTasksPlus stamps the provider on first use.
Stdio / single user
Store only, without IMcpServerBuilder:
Register the store as a singleton. The context stays scoped. Each operation opens an IServiceScope, resolves TContext, and disposes the scope.

Options

Pass Action<McpTaskStoreOptions> as the next argument after identity.
Call SweepExpiredAsync from a hosted service or SQL Agent wrapper when AutoSweep is off. The public handle always runs a full pass. The EF drain is a set-delete: DELETE WHERE DiscardAfter IS NOT NULL AND DiscardAfter <= now. It does not load the table. SQLite stores DiscardAfter as UTC ticks so that comparison translates; SQL Server and PostgreSQL use the same path. Per-row get and mutate still use ShouldDiscard (default HasExpired). Override SweepExpiredCoreAsync when bulk criteria must differ from the stamp.

Multi-node elicitation

A single instance raises InputResponseReceived in-process. Several nodes that wait on ElicitAsync need a bus: register IMcpTaskInputSignalBus in DI (Redis, Service Bus, …). The store publishes after a successful resolve and subscribes so the originating process wakes up. Payload is task id plus request id. Keep tokens and elicitation secrets off the bus; the row already has the response body. Missing bus plus multiple nodes means elicitation waits until timeout. That is a host misconfiguration.

Isolation

CreateTaskAsync stamps OwnerUserId. Client-facing get, cancel, and resolve require that user. Another user’s task is not found. Server-side complete, fail, and set-input skip the live-user check because the SDK runs those from a background DI scope after the originating HTTP request is gone.

When to use a native package instead

Stay on this package when the host already uses EF Core. Reach for PostgreSQL when you want LISTEN / NOTIFY without EF. Reach for SQL Server when you want ADO.NET or Dapper only. See Stores for the rest of the family.