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.AddMcpTasksTable on ModelBuilder and on DbContextOptionsBuilder, and a singleton store that opens a scope per operation so a standard scoped AddDbContext works.
Install
Map the table
Keep MCP task rows on their own table; they expire in minutes. The default name isMcpTasks. 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.
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 SDKWithTasks API captures the store before Build(); WithTasksPlus stamps the provider on first use.
IMcpServerBuilder:
IServiceScope, resolves TContext, and disposes the scope.
Options
PassAction<McpTaskStoreOptions> as the next argument after identity.
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 raisesInputResponseReceived 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 wantLISTEN / NOTIFY without EF. Reach for SQL Server when you want ADO.NET or Dapper only.
See Stores for the rest of the family.