Repository navigation
Conversation
The legacy resources/read path decodes the backend result into mcp.ReadResourceResult to rewrite URIs and re-encodes it. Cacheable has no omitempty, so backends that omit ttlMs/cacheScope were proxied as "cacheScope":"", which is not a valid value. Normalize the hints with mergeCachingHintsFromBackends, matching the list merge behavior from theagentrouter#2545, and add wire-level tests. Related: theagentrouter#2665 Signed-off-by: Abdulaziz Alharbi <246710009+abdulaziz-dev-lab@users.noreply.github.com>
✅ Deploy Preview for theagentrouter canceled.
|
Read results can depend on the authenticated user, and the gateway cannot tell when they don't, so a missing cacheScope now becomes "private" instead of "public". The caching spec recommends "private" for user-dependent resources/read results. A missing ttlMs still decodes to 0 (immediately stale), and hints sent by the backend are passed through unchanged. The tests also cover a backend that sends an empty cacheScope, and check that an explicit "public" from the backend is kept. Signed-off-by: Abdulaziz Alharbi <246710009+abdulaziz-dev-lab@users.noreply.github.com>
nacx
approved these changes
Oct 5, 2026
nacx
enabled auto-merge (squash)
October 5, 2026 14:06
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Contributor
Author
|
/retest |
Contributor
Author
Contributor
Author
|
/retest |
1 similar comment
Contributor
Author
|
/retest |
Contributor
Author
|
/retest |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
The legacy
resources/readpath decodes the backend result intomcp.ReadResourceResultto rewrite resource URIs and then re-encodes it.mcp.Cacheablehas noomitempty(still true in go-sdk v1.8.0), so when a backend omitsttlMs/cacheScope, as pre-SEP-2549 servers do, the client receives"cacheScope":"". That is not a valid value, and strict clients reject the result (#2665). The list merges have set these fields since #2545, so this is the last place ininternal/mcpproxythat re-encodes a cacheable result without them.This sets a missing
cacheScopeto"private"before re-encoding. Read results can depend on the authenticated user and the gateway cannot tell when they don't, so it does not mark them shareable; the spec recommends"private"for such results [1]. A missingttlMsdecodes to0, which the spec treats as immediately stale. Hints sent by the backend are passed through unchanged.Tests: the existing
resources/readcase inTest_maybeResponseModifynow uses a raw backend payload (the old fixture was built withjson.Marshal(&mcp.ReadResourceResult{})and already contained the empty scope). New cases check the encoded result when the backend omits the hints, sends an emptycacheScope, or sends its own (ttlMs: 5000,"public").I also checked it end to end with
aigw runin front of a minimal MCP server:{"contents":[...]}{"ttlMs":0,"cacheScope":"",...}{"ttlMs":0,"cacheScope":"",...}{"ttlMs":0,"cacheScope":"",...}{"contents":[...]}{"ttlMs":0,"cacheScope":"private",...}{"ttlMs":0,"cacheScope":"",...}{"ttlMs":0,"cacheScope":"private",...}{"ttlMs":5000,"cacheScope":"public",...}{"ttlMs":5000,"cacheScope":"public",...}Related Issues/PRs (if applicable)
Fixes #2665 (the list responses were fixed in #2545; this covers
resources/read)Special notes for reviewers (if applicable)
resources/readhandler fills them in asttlMs: 0/"private", the same default as here. This is a small fix for main until feat(mcp): stateless spec modern target apis and integration (28-07-2026) #2692 lands: if feat(mcp): stateless spec modern target apis and integration (28-07-2026) #2692 merges first I'll close this; if this merges first, feat(mcp): stateless spec modern target apis and integration (28-07-2026) #2692 can drop these lines when it rebases. One difference: on feat(mcp): stateless spec modern target apis and integration (28-07-2026) #2692's legacy path, an upstream that itself sends"cacheScope":""(e.g. another proxy built on go-sdk types) is still forwarded as is.maybeResponseModify; the conflict with this PR is only inlegacy_test.go.1: https://modelcontextprotocol.io/specification/2026-07-28/server/utilities/caching#cache-scope-field