ManagedCode.MCPGateway exposes IMcpGatewayToolEmbeddingStore so hosts can reuse tool embeddings between index builds.
The package still needs built-in process-local caching for two different concerns:
- tool-embedding reuse between index builds
- optional runtime reuse for normalized queries, query embeddings, and exact repeated search results
Horizontal scale, durability, and cross-replica cache coherence remain separate concerns.
The built-in McpGatewayInMemoryToolEmbeddingStore will use IMemoryCache for process-local embedding reuse, and the package will expose a dedicated AddMcpGatewayInMemoryToolEmbeddingStore() registration path that wires the store to host-owned cache services. Direct store construction must pass an explicit IMemoryCache; the store must not create or dispose a private cache. Cache entries set sizes so host-owned MemoryCacheOptions.SizeLimit policies can bound memory.
The gateway runtime will expose IMcpGatewaySearchCache as the runtime cache boundary. AddMcpGateway(...) registers a no-op default so the core package path does not force IMemoryCache into every host. Hosts that want process-local search-path reuse can opt into AddMcpGatewayInMemorySearchCache(), which wires McpGatewayInMemorySearchCache to host-owned IMemoryCache. Direct search cache construction must pass an explicit IMemoryCache; entries carry both TTL and size metadata.
Durable or distributed embedding reuse will remain the responsibility of host-provided IMcpGatewayToolEmbeddingStore implementations.
flowchart LR
Host["Host application"] --> DI["AddMcpGateway(...)"]
DI --> SearchCache["IMcpGatewaySearchCache"]
SearchCache --> NoOpCache["McpGatewayNoOpSearchCache"]
Host --> SearchCacheRegistration["AddMcpGatewayInMemorySearchCache()"]
SearchCacheRegistration --> SearchCache
SearchCacheRegistration --> MemoryCache["IMemoryCache"]
Host --> CacheRegistration["AddMcpGatewayInMemoryToolEmbeddingStore()"]
CacheRegistration --> Store["IMcpGatewayToolEmbeddingStore"]
CacheRegistration --> MemoryCache
Store --> Runtime["McpGatewayRuntime"]
Runtime --> Search["Index build / vector reuse"]
Runtime --> SearchCache
Host --> CustomStore["Custom durable store (optional)"]
CustomStore --> Runtime
Pros:
- smallest core package surface
- no opinionated local cache behavior
Cons:
- worse onboarding for hosts that only need local embedding reuse
- forces boilerplate for a very common optional scenario
Pros:
- stronger scaling story out of the box
- state can survive process restarts
Cons:
- introduces infrastructure and configuration assumptions into the core package
- forces dependency choices that belong to the host
- conflicts with the package goal of keeping embedding persistence optional
Pros:
- shared cache behavior is explicit in the package internals
- less host code for cache-backed multi-instance deployments
Cons:
- couples gateway runtime code to a specific cache family
- weakens the current abstraction boundary around
IMcpGatewayToolEmbeddingStore - makes single-instance local reuse heavier than necessary
Positive:
- the built-in store relies on a standard .NET caching primitive
- hosts can register the process-local store with one DI call and reuse the shared
IMemoryCache - direct cache/store construction requires an explicit caller-owned cache, so ownership and disposal remain visible
- the runtime can reuse expensive search-path artifacts without wrapping
IChatClientorIEmbeddingGenerator - hosts can choose no-op,
IMemoryCache, or custom runtime search-cache behavior explicitly - process-local cache behavior stays explicitly separate from durable/distributed storage concerns
- fingerprint-agnostic lookups become deterministic by reusing the latest cached embedding for the same tool document
Trade-offs:
- the package takes a new runtime dependency on
Microsoft.Extensions.Caching.Memory - the built-in store is still process-local only and does not solve multi-instance cache sharing
- hosts that need hard process-local size limits must configure the supplied
IMemoryCachepolicy or provide a dedicated cache instance through DI
Mitigations:
- keep
IMcpGatewayToolEmbeddingStoreas the only abstraction consumed by runtime code - document clearly that
AddMcpGatewayInMemoryToolEmbeddingStore()is for process-local reuse only - keep durable/distributed examples based on host-provided store implementations
McpGatewayRuntimeMUST depend onIMcpGatewaySearchCachefor runtime search caching and MUST NOT depend onIMemoryCachedirectly.AddMcpGateway(...)MUST register a no-opIMcpGatewaySearchCacheby default.AddMcpGatewayInMemorySearchCache()MUST remain opt-in and MUST provisionIMemoryCache.McpGatewayInMemoryToolEmbeddingStoreMUST remain optional and MUST NOT become a mandatory dependency for gateway usage.AddMcpGatewayInMemoryToolEmbeddingStore()MUST register the built-in store through the hostIServiceCollectionand MUST provisionIMemoryCache.McpGatewayInMemorySearchCacheandMcpGatewayInMemoryToolEmbeddingStoreMUST require caller-providedIMemoryCacheand MUST NOT create hidden privateMemoryCacheinstances.- Built-in
IMemoryCacheentries MUST set entry sizes so host-owned cache size policies work when configured. - Hosts that need cross-instance persistence or replication MUST continue to provide their own
IMcpGatewayToolEmbeddingStore. - The built-in store MUST clone vectors on read/write boundaries so callers cannot mutate cached embedding buffers in place.
Rollout:
- Add the
Microsoft.Extensions.Caching.Memorydependency to the package. - Implement
McpGatewayInMemoryToolEmbeddingStorewithIMemoryCache. - Expose
AddMcpGatewayInMemoryToolEmbeddingStore()for host DI registration. - Expose
IMcpGatewaySearchCachewith a no-op default and an opt-inAddMcpGatewayInMemorySearchCache()registration path. - Update README and architecture docs to distinguish process-local cache reuse from durable storage.
Rollback:
- Remove
AddMcpGatewayInMemoryToolEmbeddingStore()only if the package intentionally stops shipping a built-in process-local embedding store. - Keep
IMcpGatewayToolEmbeddingStoreas the only runtime dependency boundary unless the package intentionally adopts a different cache abstraction.
dotnet restore ManagedCode.MCPGateway.slnxdotnet build ManagedCode.MCPGateway.slnx -c Release --no-restoredotnet build ManagedCode.MCPGateway.slnx -c Release --no-restore -p:RunAnalyzers=truedotnet test --solution ManagedCode.MCPGateway.slnx -c Release --no-buildroslynator analyze src/ManagedCode.MCPGateway/ManagedCode.MCPGateway.csproj -p Configuration=Release --severity-level warningroslynator analyze tests/ManagedCode.MCPGateway.Tests/ManagedCode.MCPGateway.Tests.csproj -p Configuration=Release --severity-level warningcloc --include-lang=C# src tests
- Register
Microsoft.Extensions.Caching.Memoryas a package dependency. - Implement the built-in store with
IMemoryCachelookups keyed by tool id, document hash, and embedding fingerprint. - Keep durable vector reuse behind
IMcpGatewayToolEmbeddingStore. - Add
IMcpGatewaySearchCachefor normalized-query reuse, query-embedding reuse, and repeated search-result reuse. - Add tests for the cache-backed store, runtime cache reuse, and cache invalidation after rebuilds.
- Update README and architecture docs so process-local cache reuse is not described as durable persistence.
- Product: the package still offers a zero-infrastructure local cache option, but durable storage remains opt-in.
- Dev: use
AddMcpGatewayInMemoryToolEmbeddingStore()when the host only needs process-local embedding reuse, and opt intoAddMcpGatewayInMemorySearchCache()only when process-local runtime search caching is desired. - QA: verify vector reuse, clone safety, normalization/query cache reuse, and cache invalidation after rebuilds.
- DevOps: multi-instance deployments still need a host-provided durable/distributed
IMcpGatewayToolEmbeddingStore.