Skip to main content
McpEssentials.Cosmos is the authenticated task store for hosts that already run Azure Cosmos DB for NoSQL. It subclasses McpTaskStoreBase and persists StoredMcpTask snapshots as documents. Prefer this over Entity Framework Core with UseCosmos when you want in-partition point reads and native document TTL. Cosmos expires documents; the request-path sweep is off by default.

Partition by owner

Task documents live under /ownerUserId. Client-facing get is a point read.

Native document TTL

ttl is DiscardAfter as whole seconds. AutoSweep stays off.

Index for complete

McpTaskIndex maps taskId → ownerUserId so background complete still finds the row.
The host owns CosmosClient. This package ships the store and registration. Register the client as a singleton.

Install

1

Add the package

2

Register the client

Pass CosmosMcpTaskStore.DefaultClientOptions(). The Cosmos SDK default serializer is Newtonsoft; without these options, id / ttl / camelCase enum names on task documents do not map.

Wire the store

Same identity factory as Authenticated tasks. Cosmos has no generic context type, so the MCP builder method is WithCosmosTasks rather than WithTasksPlus<TContext>.

Containers and TTL

CreateTaskAsync writes the task, then the index. If the index write fails, the task document is deleted. Client-facing get/cancel/resolve are in-partition. Background complete/fail/input-request read the index first, then point-read the task. DiscardAfter is stamped at write as CreatedAt + 2×TimeToLive. The document ttl property is that instant as whole seconds from now (at least 1). The index row gets the same ttl so it does not outlive the task. Unlimited TTL (null) leaves both documents until a terminal owner get (ServeAndDrop) or an explicit delete. Both containers enable per-document TTL (DefaultTimeToLive = -1). AutoSweep defaults to false. SweepExpiredAsync is a no-op; Cosmos owns expiry. Per-row get still honors ShouldDiscard so a custom clock can drop a document before Cosmos does. On first use the store creates the database and containers when they are missing. Default throughput is 400 RU/s (CosmosMcpTaskStoreOptions.Throughput).
Serverless accounts should pre-create the containers and set Throughput to null.

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 document is not found; get does not distinguish expiry from isolation. Updates replace the task document with an ETag IfMatch. A terminal owner get with ServeAndDrop (default) deletes the task and the index row after serving it. Do not encode the user id in the task id.

Events

v1 does not subscribe to the change feed. After a successful resolve, the node raises InputResponseReceived locally.
Multi-node ElicitAsync needs an IMcpTaskInputSignalBus. Azure Service Bus is the usual pair. Missing bus plus multiple nodes means ElicitAsync waits until timeout.

Sample

Runnable host. Expects the emulator at https://localhost:8081/.

Stores

Pick a backing store by the engine you already run.

Entity Framework Core

Stay on EF when the host already maps Cosmos with UseCosmos.

Authenticated tasks

Identity factory, isolation rules, and the in-memory store.