McpEssentials.Redis is the authenticated task store for hosts that already run Redis. It subclasses NotifyingMcpTaskStoreBase and persists StoredMcpTask snapshots as JSON strings.
Prefer this over IDistributedCache when the cache is Redis and several nodes wait on ElicitAsync. Redis expires keys; the request-path sweep is off by default.
JSON strings
One key per task.
OwnerUserId lives on the snapshot.Native expiry
PEXPIREAT from DiscardAfter. AutoSweep stays off.Pub/sub fan-out
Resolved input publishes on
{prefix}:task:input. Echoes of this process are dropped.IConnectionMultiplexer. This package ships the store and registration. Register the multiplexer as a singleton.
Install
1
Add the package
2
Register the multiplexer
Wire the store
Same identity factory as Authenticated tasks. Redis has no generic context type, so the MCP builder method isWithRedisTasks rather than WithTasksPlus<TContext>.
- HTTP
- Stdio
- Store only
Keys and TTL
DiscardAfter is stamped at write as CreatedAt + 2×TimeToLive. Redis PEXPIREAT uses that instant. Unlimited TTL (null) leaves the key until a terminal owner get (ServeAndDrop) or an explicit delete.
AutoSweep defaults to false. SweepExpiredAsync is a no-op; Redis owns expiry. Per-row get still honors ShouldDiscard so a custom clock can drop a key before Redis does. A custom ShouldDiscard that disagrees with DiscardAfter should also override SweepExpiredCoreAsync and skip native expiry.
Replica sets: set
WaitReplicas so CreateTaskAsync does not return until a later get on another node can see the key. Standalone Redis leaves this at 0.Isolation
CreateTaskAsync stamps OwnerUserId. Client-facing get/cancel/resolve require that user. Server-side complete/fail/input-request skip the live-user check. Another user’s task is not found. A missing key is not found; get does not distinguish expiry from isolation.
Creates use a Lua EXISTS then SET. Updates compare the expected JSON then SET + PEXPIREAT. A terminal owner get with ServeAndDrop (default) deletes the key after serving it.
Events
After a successful resolve, the publishing node raisesInputResponseReceived locally, then publishes taskId + newline + requestId on {prefix}:task:input. Other nodes subscribe and raise a stub response so ElicitAsync waiters wake. The publisher drops its own pub/sub echo.
Related
Sample
Runnable host. Expects Redis at
localhost:6379.Stores
Pick a backing store by the engine you already run.
PostgreSQL
The other native channel:
LISTEN / NOTIFY on Npgsql.IDistributedCache
Specified abstraction over whatever cache you already registered.