McpEssentials.EntityFrameworkCore is the first durable authenticated task store. It subclasses McpTaskStoreBase and persists McpTaskEntity rows through your DbContext.
Any EF provider
SQL Server, SQLite, PostgreSQL, MySQL, MariaDB, Cosmos via EF, Oracle — whatever you already pass to
AddDbContext.Host owns the model
Your context, your connection, your migrations. The store opens a scope per operation.
Optional bus
Multi-node
ElicitAsync takes IMcpTaskInputSignalBus.This package works with any EF Core database provider. 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
- AddDbContext
Use this in a single-project app, or whenever the project that owns
AppDbContext already references this package. Mapping lives with the rest of the model. Migrations from that project see the table.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.
Register the store as a singleton. The context stays scoped. Each operation opens an IServiceScope, resolves TContext, and disposes the scope.
- HTTP
- Stdio
- Store only
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.
Events
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.
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.PostgreSQL
Npgsql or Dapper, and
LISTEN / NOTIFY, with no DbContext.SQL Server
ADO.NET or Dapper only. Specified; use this package with
UseSqlServer until it ships.Related
Sample
Runnable SQLite host.
Stores
Pick a backing store by the engine you already run.
Authenticated tasks
Identity factory, isolation rules, and the in-memory store.
Cosmos DB
Native Cosmos when you want in-partition point reads instead of
UseCosmos.