Repository navigation
.NET: Support MCP skills cacheScope with per-key cache partitions - #9986
SergeyMenshykh merged 12 commits into
Conversation
Validate cacheScope on skills/list, skills/get and resources/read responses. Treat missing or null scopes as public, and reject private responses unless CachePartitionKeySelector is configured. Keep a separate skills cache per partition key in a MemoryCache with optional count and expiration limits. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🟡 Changes recommended
Catalog cleanup is missing on source disposal and partition eviction, retaining related resources, while misplaced sample cache fields are ignored and cause repeated discovery requests.
2 open issues
What changed in this PR
This PR adds caller-specific cache partitions to .NET MCP skills, preventing private responses from being reused across callers with different partition keys.
Changes:
- Validate response cache scopes and reject private responses when no partition key is configured.
- Add per-key caches with optional capacity and expiration limits.
- Expand tests, API documentation, and the sample to explain cache isolation and authentication responsibilities.
| File | Description |
|---|---|
dotnet/tests/Microsoft.Agents.AI.Mcp.UnitTests/Skills/AgentMcpSkillTests.cs |
Default test resource responses to public scope. |
dotnet/tests/Microsoft.Agents.AI.Mcp.UnitTests/Skills/AgentMcpSkillsTests.Ttl.cs |
Add wall-clock support to the test clock. |
dotnet/tests/Microsoft.Agents.AI.Mcp.UnitTests/Skills/AgentMcpSkillsTests.Refresh.cs |
Use public scopes in refresh fixtures. |
dotnet/tests/Microsoft.Agents.AI.Mcp.UnitTests/Skills/AgentMcpSkillsTests.cs |
Default shared resource fixtures to public scope. |
dotnet/tests/Microsoft.Agents.AI.Mcp.UnitTests/Skills/AgentMcpSkillsTests.CacheScope.cs |
Test scope validation, partition isolation, retention, and concurrency. |
dotnet/tests/Microsoft.Agents.AI.Mcp.UnitTests/Skills/AgentMcpSkillsSerializationTests.cs |
Test scope serialization and invalid-value rejection. |
dotnet/src/Microsoft.Agents.AI.Mcp/Skills/McpSkillsListResult.cs |
Add listing response cache scope. |
dotnet/src/Microsoft.Agents.AI.Mcp/Skills/McpSkillsGetResult.cs |
Add metadata response cache scope. |
dotnet/src/Microsoft.Agents.AI.Mcp/Skills/McpSkillsCatalog.cs |
Validate scopes and enforce consistency across list pages. |
dotnet/src/Microsoft.Agents.AI.Mcp/Skills/McpSkillCacheScopeConverter.cs |
Parse known scopes and reject unsupported values. |
dotnet/src/Microsoft.Agents.AI.Mcp/Skills/AgentSkillsProviderBuilderMcpExtensions.cs |
Document partition configuration. |
dotnet/src/Microsoft.Agents.AI.Mcp/Skills/AgentMcpSkillsSourceOptions.cs |
Expose partition key and retention settings. |
dotnet/src/Microsoft.Agents.AI.Mcp/Skills/AgentMcpSkillsSource.cs |
Select and cache catalogs by caller key. |
dotnet/src/Microsoft.Agents.AI.Mcp/Skills/AgentMcpSkill.cs |
Validate resource response scope before caching. |
dotnet/src/Microsoft.Agents.AI.Mcp/Microsoft.Agents.AI.Mcp.csproj |
Add the memory-cache dependency. |
dotnet/samples/02-agents/AgentSkills/Agent_Step06_McpBasedSkills/README.md |
Explain isolation, retention, and credential responsibilities. |
dotnet/samples/02-agents/AgentSkills/Agent_Step06_McpBasedSkills/Program.cs |
Disable provider caching and add sample scope and TTL metadata. |
Review effort: Balanced
There was a problem hiding this comment.
MAF Automated Review — Iteration 1
Result: Findings reported
Scope: full PR (1 commit(s)): 8b69fe48f360
Model: gpt-5.6-sol
Overview
The change adds strict scope parsing, per-key catalogs, bounded-retention options, and extensive tests for pagination, concurrency, expiration, and private-response rejection. However, the provider builder's default outer cache can bypass the new selector and expose one caller's private skill objects to another, while the partition store remains unbounded by default and can retain attacker-created entries for the source lifetime.
Reviewed the supplied pull-request change set across correctness, security/reliability, architecture, and failure behavior.
2 verified findings remained after source verification (2 high) across 2 files. Details are attached to the affected lines below.
Affected areas: dotnet/src/Microsoft.Agents.AI.Mcp/Skills/AgentMcpSkillsSource.cs, dotnet/src/Microsoft.Agents.AI.Mcp/Skills/AgentMcpSkillsSourceOptions.cs
Code coverage overviewLanguages: C# C# / code-coverage/dotnet
View coverage of the 10 most impacted files.
ℹ️ Calculations represent line coverage. Results may change as calculations are updated. Updated |
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Motivation
The MCP skills extension spec requires
skills/list,skills/get, andresources/readresponses to declare acacheScope:publicresponses may be shared between callers, whileprivateones are specific to the caller's credentials. Add scope validation and separate caches per authorization context so a shared MCP skills source does not reuse one caller's private responses for another caller.Description
cacheScopeonskills/list(all pages must agree),skills/get, andresources/readresponses. Missing ornullscopes are treated aspublicbecause not all servers follow the spec yet. Unknown values inskills/listandskills/getare rejected.AgentMcpSkillsSourceOptions.CacheIsolationKeySelector, aligned with the naming inCachingAgentSkillsSourceOptions. Each key gets its own skills cache. Without a selector, callers share one cache and private responses throwInvalidOperationException. A configured selector must return a non-empty key.MemoryCache.MaxCachedPartitionsdefaults to 100; explicitly setting it tonullallows unlimited partitions. OptionalPartitionSlidingExpirationandPartitionAbsoluteExpirationcontrol partition retention independently of response TTLs.IDisposable; in-flight discovery and retained resource reads remain supported..DisableCaching()builder configuration so discovery reaches the MCP source's isolation selector and TTL checks on every call.ttlMsandcacheScopeplacement so both apply to the outerskills/listresponse.Related Issue