Hacktoberfest 2026: le issue che i maintainer hanno segnato per ottobre, aperte e adatte ai principianti. Sfoglia le issue Hacktoberfest

mcp: ReadResource caches the result of a multi round-trip retry and serves a retry request from the cache

Chiusa
#1,369 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

I maintainer di solito rispondono entro 1 giorno

@vbcherepanov ci sta già lavorando.

Dal 10/10/2026.

  • #1371 di @vbcherepanov — aperta

Valutazione

Difficoltà
3/5
Tempo stimato
1-2 giorni
Idoneità per principianti
55/100
Tipo di issue
Bug
Chiarezza
Specificata chiaramente
Stato di attività
Attiva
Stack tecnologico
go
Ambito
api

Direzione di ricerca

Start in ClientSession.ReadResource in mcp/client.go (around lines 1388-1407), where the cache lookup and store happen, and read how the multi round-trip middleware in mcp/mrtr.go (clientMultiRoundTripMiddleware, around line 64) retries inside handleSend. Skip the cache when the request carries InputResponses or RequestState, and do not store interim input_required results or retry results. Done when the two regression tests named in the report, TestMultiRoundTrip_ReadResource_RetryResultNotCached and TestMultiRoundTrip_ReadResource_ManualRetryBypassesCache in mrtr_test.go, pass and cacheable reads without input still hit the cache.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

Describe the bug

On a 2026-07-28 session, ClientSession.ReadResource keeps a per-URI cache of resources/read results. It does not take multi round-trip requests (SEP-2322) into account, in two ways:

  1. The result that ReadResource stores is whatever handleSend returns, and the multi round-trip sending middleware runs inside handleSend (mcp/mrtr.go:64, clientMultiRoundTripMiddleware). So the result of the retry that carried inputResponses and requestState is cached under the plain URI (mcp/client.go:1404 on main 3c08147). A resource gated behind an elicitation is served from the cache on the next read without asking the user again.
  2. The cache lookup happens before the request is sent and is keyed by URI only (mcp/client.go:1394). With the middleware disabled, the caller's manual retry (same URI, now with InputResponses and RequestState) is answered from the cache with the interim input_required result from the first read. The retry never reaches the server. The interim result itself should not have been cached either.

To Reproduce

package main

import (
	"context"
	"fmt"
	"log"
	"sync/atomic"

	"github.com/modelcontextprotocol/go-sdk/mcp"
)

func main() {
	ctx := context.Background()
	var handlerCalls, elicitations atomic.Int32
	server := mcp.NewServer(&mcp.Implementation{Name: "s", Version: "0"}, &mcp.ServerOptions{
		SetCacheable: func(_ context.Context, _ mcp.Request, c *mcp.Cacheable) {
			c.TTLMs = 60_000
			c.CacheScope = "private"
		},
	})
	server.AddResource(&mcp.Resource{URI: "secret://doc", Name: "doc"},
		func(_ context.Context, req *mcp.ReadResourceRequest) (*mcp.ReadResourceResult, error) {
			handlerCalls.Add(1)
			if req.Params.InputResponses == nil {
				return &mcp.ReadResourceResult{
					InputRequests: mcp.InputRequestMap{"confirm": &mcp.ElicitParams{Message: "Reveal the document?"}},
					RequestState:  "state-1",
				}, nil
			}
			return &mcp.ReadResourceResult{Contents: []*mcp.ResourceContents{{URI: req.Params.URI, Text: "the document"}}}, nil
		})
	ct, st := mcp.NewInMemoryTransports()
	if _, err := server.Connect(ctx, st, nil); err != nil {
		log.Fatal(err)
	}
	client := mcp.NewClient(&mcp.Implementation{Name: "c", Version: "0"}, &mcp.ClientOptions{
		ElicitationHandler: func(context.Context, *mcp.ElicitRequest) (*mcp.ElicitResult, error) {
			elicitations.Add(1)
			return &mcp.ElicitResult{Action: "accept"}, nil
		},
	})
	cs, err := client.Connect(ctx, ct, nil)
	if err != nil {
		log.Fatal(err)
	}
	defer cs.Close()

	for i := 1; i <= 2; i++ {
		res, err := cs.ReadResource(ctx, &mcp.ReadResourceParams{URI: "secret://doc"})
		if err != nil {
			log.Fatal(err)
		}
		fmt.Printf("read %d: contents=%d handlerCalls=%d elicitations=%d ttlMs=%d\n",
			i, len(res.Contents), handlerCalls.Load(), elicitations.Load(), res.TTLMs)
	}
}

Output on v1.8.0 and on main (3c08147):

read 1: contents=1 handlerCalls=2 elicitations=1 ttlMs=60000
read 2: contents=1 handlerCalls=2 elicitations=1 ttlMs=60000

The second read never reaches the server and never asks the user. It returns the same *ReadResourceResult pointer as the first read.

For the second symptom, create the client with MultiRoundTrip: &mcp.MultiRoundTripOptions{Disabled: true}, read once, then read again with InputResponses and the returned RequestState. The second call returns the first call's interim result (NeedsInput() is true, Contents is empty) and the handler is called once in total.

Expected behavior

  • The second automatic read goes to the server again and asks the user again: handlerCalls=4 elicitations=2.
  • A read that carries InputResponses or RequestState is sent to the server and is not answered from the cache.
  • Neither an interim input_required result nor the result of a retry is stored in the cache.
  • A resources/read result that needed no input is still cached as today.

Logs

Regression tests from the fix, run against main before the fix:

--- FAIL: TestMultiRoundTrip_ReadResource_RetryResultNotCached (0.00s)
    mrtr_test.go:1066: read 2: handler calls = 2, want 4
    mrtr_test.go:1069: read 2: elicitations = 1, want 2; the retry result was served from the cache
--- FAIL: TestMultiRoundTrip_ReadResource_ManualRetryBypassesCache (0.00s)
    mrtr_test.go:1116: retry: NeedsInput() = true, want false; the interim result was served from the cache

Additional context

Specification 2026-07-28, Caching, Cache Key: "Results produced by retrying a request through the multi round-trip requests mechanism, that is, requests carrying inputResponses or requestState, MUST NOT be cached, as they depend on inputs that are not part of the cache key." The same section: "Clients MUST NOT serve a cached response for a request whose method or parameters differ from the request that produced it." And under Cacheable Results: "Interim results with resultType: "input_required" are not cacheable and carry no caching hints."
https://modelcontextprotocol.io/specification/2026-07-28/server/utilities/caching#cache-key

Root cause: mcp/client.go:1388-1407 (ClientSession.ReadResource) looks the URI up and stores the result without regard to InputResponses, RequestState, NeedsInput() or whether the middleware retried.

Related observation, not part of this report: on the server side, Server.readResource calls resolveCacheable before it returns an input_required result (mcp/server.go:1136), so interim results go out with ttlMs and cacheScope set, while the specification says they carry no caching hints.

SDK versions: v1.8.0 (3f3b699) and main at 3c0814798be7e813740406029e8efef07828f34f (2026-10-09).
Go version: go1.27.1 darwin/arm64.

I have a fix with regression tests ready and will open a pull request.

Lingua principale
Go
Stelle
5.2k
Fork
583
Merge medio
1g 12h
PR unite (30g)
52

Preparare l'ambiente

Apri in Codespaces

Avvia il container di sviluppo del progetto nel browser, con il tuo account GitHub.

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di modelcontextprotocol/go-sdk

Tutte le issue di modelcontextprotocol/go-sdk

Issue simili

Altre issue su Go

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.