Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 51 additions & 11 deletions mcp/mrtr.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,10 @@ func clientMultiRoundTripMiddleware() Middleware {
if err != nil {
return nil, err
}
setMultiRoundTripRetryParams(req, responses, mrtrResult.requestState())
req, err = setMultiRoundTripRetryParams(method, req, responses, mrtrResult.requestState())
if err != nil {
return nil, err
}
}
}
}
Expand Down Expand Up @@ -152,7 +155,10 @@ func serverMultiRoundTripMiddleware() Middleware {
if err != nil {
return nil, err
}
setMultiRoundTripRetryParams(req, responses, mrtrResult.requestState())
req, err = setMultiRoundTripRetryParams(method, req, responses, mrtrResult.requestState())
if err != nil {
return nil, err
}
return next(ctx, method, req)
}
}
Expand Down Expand Up @@ -213,20 +219,54 @@ func createMessageParamsToWithTools(p *CreateMessageParams) *CreateMessageWithTo
}
}

func setMultiRoundTripRetryParams(req Request, responses InputResponseMap, state string) {
// setMultiRoundTripRetryParams returns the request for the next round of a
// multi-round-trip retry: a request whose params are a shallow copy of the
// original, carrying the fulfilled responses and the request state to echo.
// The original params are never mutated: on the client side they are
// caller-owned, and a params struct reused across calls must not carry one
// call's inputResponses and requestState into the next, where a server
// gating on them (for example an elicitation confirmation) would treat the
// new call as already answered.
//
// An unrecognized params type is an error. It cannot occur on the server
// side, but client sending middleware runs before the retry loop and may
// substitute its own params.
func setMultiRoundTripRetryParams(method string, req Request, responses InputResponseMap, state string) (Request, error) {
var retryParams Params
switch p := req.GetParams().(type) {
case *CallToolParams:
p.InputResponses = responses
p.RequestState = state
cp := *p
cp.InputResponses = responses
cp.RequestState = state
retryParams = &cp
case *CallToolParamsRaw:
p.InputResponses = responses
p.RequestState = state
cp := *p
cp.InputResponses = responses
cp.RequestState = state
retryParams = &cp
case *GetPromptParams:
p.InputResponses = responses
p.RequestState = state
cp := *p
cp.InputResponses = responses
cp.RequestState = state
retryParams = &cp
case *ReadResourceParams:
p.InputResponses = responses
p.RequestState = state
cp := *p
cp.InputResponses = responses
cp.RequestState = state
retryParams = &cp
default:
return nil, fmt.Errorf("multi-round-trip: unsupported params type %T", p)
}
switch s := req.GetSession().(type) {
case *ClientSession:
return newClientRequest(s, retryParams), nil
case *ServerSession:
// Rebuild with the same constructor handleReceive uses: the
// receiving dispatch type-asserts on the request's concrete
// *ServerRequest[P] type, and Extra must carry over.
return s.receivingMethodInfos()[method].newRequest(s, retryParams, req.GetExtra()), nil
default:
return nil, fmt.Errorf("multi-round-trip: unsupported session type %T", s)
}
}

Expand Down
Loading