Skip to content

Commit ad296c3

Browse files
committed
refactor: align request parameter handling to support SEP-2575 _meta injection across all protocols
1 parent 96cef62 commit ad296c3

3 files changed

Lines changed: 57 additions & 44 deletions

File tree

mcp/client.go

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,14 @@ type clientSessionState struct {
422422

423423
func (cs *ClientSession) InitializeResult() *InitializeResult { return cs.state.InitializeResult }
424424

425+
// usesNewProtocol reports whether this session has negotiated a protocol
426+
// version >= 2026-06-30, which requires the SEP-2575 per-request `_meta`
427+
// triple on every outgoing request.
428+
func (cs *ClientSession) usesNewProtocol() bool {
429+
res := cs.state.InitializeResult
430+
return res != nil && res.ProtocolVersion >= protocolVersion20260630
431+
}
432+
425433
func (cs *ClientSession) ID() string {
426434
if c, ok := cs.mcpConn.(hasSessionID); ok {
427435
return c.SessionID()
@@ -1076,23 +1084,32 @@ func newClientRequest[P Params](cs *ClientSession, params P) *ClientRequest[P] {
10761084

10771085
// Ping makes an MCP "ping" request to the server.
10781086
func (cs *ClientSession) Ping(ctx context.Context, params *PingParams) error {
1079-
_, err := handleSend[*emptyResult](ctx, methodPing, newClientRequest(cs, orZero[*PingParams](params)))
1087+
_, err := handleSend[*emptyResult](ctx, methodPing, newClientRequest(cs, orZero[Params](params)))
10801088
return err
10811089
}
10821090

10831091
// ListPrompts lists prompts that are currently available on the server.
10841092
func (cs *ClientSession) ListPrompts(ctx context.Context, params *ListPromptsParams) (*ListPromptsResult, error) {
1085-
return handleSend[*ListPromptsResult](ctx, methodListPrompts, newClientRequest(cs, orZero[*ListPromptsParams](params)))
1093+
if params == nil && cs.usesNewProtocol() {
1094+
params = &ListPromptsParams{}
1095+
}
1096+
return handleSend[*ListPromptsResult](ctx, methodListPrompts, newClientRequest(cs, orZero[Params](params)))
10861097
}
10871098

10881099
// GetPrompt gets a prompt from the server.
10891100
func (cs *ClientSession) GetPrompt(ctx context.Context, params *GetPromptParams) (*GetPromptResult, error) {
1090-
return handleSend[*GetPromptResult](ctx, methodGetPrompt, newClientRequest(cs, orZero[*GetPromptParams](params)))
1101+
if params == nil && cs.usesNewProtocol() {
1102+
params = &GetPromptParams{}
1103+
}
1104+
return handleSend[*GetPromptResult](ctx, methodGetPrompt, newClientRequest(cs, orZero[Params](params)))
10911105
}
10921106

10931107
// ListTools lists tools that are currently available on the server.
10941108
func (cs *ClientSession) ListTools(ctx context.Context, params *ListToolsParams) (*ListToolsResult, error) {
1095-
result, err := handleSend[*ListToolsResult](ctx, methodListTools, newClientRequest(cs, orZero[*ListToolsParams](params)))
1109+
if params == nil && cs.usesNewProtocol() {
1110+
params = &ListToolsParams{}
1111+
}
1112+
result, err := handleSend[*ListToolsResult](ctx, methodListTools, newClientRequest(cs, orZero[Params](params)))
10961113
if err != nil {
10971114
return nil, err
10981115
}
@@ -1115,44 +1132,56 @@ func (cs *ClientSession) CallTool(ctx context.Context, params *CallToolParams) (
11151132
if tool := cs.getCachedTool(params.Name); tool != nil {
11161133
ctx = context.WithValue(ctx, toolContextKey, tool)
11171134
}
1118-
return handleSend[*CallToolResult](ctx, methodCallTool, newClientRequest(cs, orZero[*CallToolParams](params)))
1135+
return handleSend[*CallToolResult](ctx, methodCallTool, newClientRequest(cs, orZero[Params](params)))
11191136
}
11201137

11211138
func (cs *ClientSession) SetLoggingLevel(ctx context.Context, params *SetLoggingLevelParams) error {
1122-
_, err := handleSend[*emptyResult](ctx, methodSetLevel, newClientRequest(cs, orZero[*SetLoggingLevelParams](params)))
1139+
_, err := handleSend[*emptyResult](ctx, methodSetLevel, newClientRequest(cs, orZero[Params](params)))
11231140
return err
11241141
}
11251142

11261143
// ListResources lists the resources that are currently available on the server.
11271144
func (cs *ClientSession) ListResources(ctx context.Context, params *ListResourcesParams) (*ListResourcesResult, error) {
1128-
return handleSend[*ListResourcesResult](ctx, methodListResources, newClientRequest(cs, orZero[*ListResourcesParams](params)))
1145+
if params == nil && cs.usesNewProtocol() {
1146+
params = &ListResourcesParams{}
1147+
}
1148+
return handleSend[*ListResourcesResult](ctx, methodListResources, newClientRequest(cs, orZero[Params](params)))
11291149
}
11301150

11311151
// ListResourceTemplates lists the resource templates that are currently available on the server.
11321152
func (cs *ClientSession) ListResourceTemplates(ctx context.Context, params *ListResourceTemplatesParams) (*ListResourceTemplatesResult, error) {
1133-
return handleSend[*ListResourceTemplatesResult](ctx, methodListResourceTemplates, newClientRequest(cs, orZero[*ListResourceTemplatesParams](params)))
1153+
if params == nil && cs.usesNewProtocol() {
1154+
params = &ListResourceTemplatesParams{}
1155+
}
1156+
return handleSend[*ListResourceTemplatesResult](ctx, methodListResourceTemplates, newClientRequest(cs, orZero[Params](params)))
11341157
}
11351158

11361159
// ReadResource asks the server to read a resource and return its contents.
11371160
func (cs *ClientSession) ReadResource(ctx context.Context, params *ReadResourceParams) (*ReadResourceResult, error) {
1138-
return handleSend[*ReadResourceResult](ctx, methodReadResource, newClientRequest(cs, orZero[*ReadResourceParams](params)))
1161+
if params == nil && cs.usesNewProtocol() {
1162+
params = &ReadResourceParams{}
1163+
}
1164+
return handleSend[*ReadResourceResult](ctx, methodReadResource, newClientRequest(cs, orZero[Params](params)))
11391165
}
11401166

11411167
func (cs *ClientSession) Complete(ctx context.Context, params *CompleteParams) (*CompleteResult, error) {
1142-
return handleSend[*CompleteResult](ctx, methodComplete, newClientRequest(cs, orZero[*CompleteParams](params)))
1168+
if params == nil && cs.usesNewProtocol() {
1169+
params = &CompleteParams{}
1170+
}
1171+
return handleSend[*CompleteResult](ctx, methodComplete, newClientRequest(cs, orZero[Params](params)))
11431172
}
11441173

11451174
// Subscribe sends a "resources/subscribe" request to the server, asking for
11461175
// notifications when the specified resource changes.
11471176
func (cs *ClientSession) Subscribe(ctx context.Context, params *SubscribeParams) error {
1148-
_, err := handleSend[*emptyResult](ctx, methodSubscribe, newClientRequest(cs, orZero[*SubscribeParams](params)))
1177+
_, err := handleSend[*emptyResult](ctx, methodSubscribe, newClientRequest(cs, orZero[Params](params)))
11491178
return err
11501179
}
11511180

11521181
// Unsubscribe sends a "resources/unsubscribe" request to the server, cancelling
11531182
// a previous subscription.
11541183
func (cs *ClientSession) Unsubscribe(ctx context.Context, params *UnsubscribeParams) error {
1155-
_, err := handleSend[*emptyResult](ctx, methodUnsubscribe, newClientRequest(cs, orZero[*UnsubscribeParams](params)))
1184+
_, err := handleSend[*emptyResult](ctx, methodUnsubscribe, newClientRequest(cs, orZero[Params](params)))
11561185
return err
11571186
}
11581187

@@ -1224,7 +1253,10 @@ func (c *Client) callElicitationCompleteHandler(ctx context.Context, req *Elicit
12241253
// This can be used if the client is performing a long-running task that was
12251254
// initiated by the server.
12261255
func (cs *ClientSession) NotifyProgress(ctx context.Context, params *ProgressNotificationParams) error {
1227-
return handleNotify(ctx, notificationProgress, newClientRequest(cs, orZero[*ProgressNotificationParams](params)))
1256+
if params == nil && cs.usesNewProtocol() {
1257+
params = &ProgressNotificationParams{}
1258+
}
1259+
return handleNotify(ctx, notificationProgress, newClientRequest(cs, orZero[Params](params)))
12281260
}
12291261

12301262
// Tools provides an iterator for all tools available on the server,

mcp/server.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1122,7 +1122,7 @@ func (ss *ServerSession) callProgressNotificationHandler(ctx context.Context, p
11221122
// This is typically used to report on the status of a long-running request
11231123
// that was initiated by the client.
11241124
func (ss *ServerSession) NotifyProgress(ctx context.Context, params *ProgressNotificationParams) error {
1125-
return handleNotify(ctx, notificationProgress, newServerRequest(ss, orZero[*ProgressNotificationParams](params)))
1125+
return handleNotify(ctx, notificationProgress, newServerRequest(ss, orZero[Params](params)))
11261126
}
11271127

11281128
func newServerRequest[P Params](ss *ServerSession, params P) *ServerRequest[P] {
@@ -1197,7 +1197,7 @@ func (ss *ServerSession) ID() string {
11971197

11981198
// Ping pings the client.
11991199
func (ss *ServerSession) Ping(ctx context.Context, params *PingParams) error {
1200-
_, err := handleSend[*emptyResult](ctx, methodPing, newServerRequest(ss, orZero[*PingParams](params)))
1200+
_, err := handleSend[*emptyResult](ctx, methodPing, newServerRequest(ss, orZero[Params](params)))
12011201
return err
12021202
}
12031203

@@ -1206,7 +1206,7 @@ func (ss *ServerSession) ListRoots(ctx context.Context, params *ListRootsParams)
12061206
if err := ss.checkInitialized(methodListRoots); err != nil {
12071207
return nil, err
12081208
}
1209-
return handleSend[*ListRootsResult](ctx, methodListRoots, newServerRequest(ss, orZero[*ListRootsParams](params)))
1209+
return handleSend[*ListRootsResult](ctx, methodListRoots, newServerRequest(ss, orZero[Params](params)))
12101210
}
12111211

12121212
// CreateMessage sends a sampling request to the client.
@@ -1226,7 +1226,7 @@ func (ss *ServerSession) CreateMessage(ctx context.Context, params *CreateMessag
12261226
p2.Messages = []*SamplingMessage{} // avoid JSON "null"
12271227
params = &p2
12281228
}
1229-
res, err := handleSend[*CreateMessageWithToolsResult](ctx, methodCreateMessage, newServerRequest(ss, orZero[*CreateMessageParams](params)))
1229+
res, err := handleSend[*CreateMessageWithToolsResult](ctx, methodCreateMessage, newServerRequest(ss, orZero[Params](params)))
12301230
if err != nil {
12311231
return nil, err
12321232
}
@@ -1263,7 +1263,7 @@ func (ss *ServerSession) CreateMessageWithTools(ctx context.Context, params *Cre
12631263
p2.Messages = []*SamplingMessageV2{} // avoid JSON "null"
12641264
params = &p2
12651265
}
1266-
return handleSend[*CreateMessageWithToolsResult](ctx, methodCreateMessage, newServerRequest(ss, orZero[*CreateMessageWithToolsParams](params)))
1266+
return handleSend[*CreateMessageWithToolsResult](ctx, methodCreateMessage, newServerRequest(ss, orZero[Params](params)))
12671267
}
12681268

12691269
// Elicit sends an elicitation request to the client asking for user input.
@@ -1302,7 +1302,7 @@ func (ss *ServerSession) Elicit(ctx context.Context, params *ElicitParams) (*Eli
13021302
}
13031303
}
13041304

1305-
res, err := handleSend[*ElicitResult](ctx, methodElicit, newServerRequest(ss, orZero[*ElicitParams](params)))
1305+
res, err := handleSend[*ElicitResult](ctx, methodElicit, newServerRequest(ss, orZero[Params](params)))
13061306
if err != nil {
13071307
return nil, err
13081308
}
@@ -1353,7 +1353,7 @@ func (ss *ServerSession) Log(ctx context.Context, params *LoggingMessageParams)
13531353
if compareLevels(params.Level, logLevel) < 0 {
13541354
return nil
13551355
}
1356-
return handleNotify(ctx, notificationLoggingMessage, newServerRequest(ss, orZero[*LoggingMessageParams](params)))
1356+
return handleNotify(ctx, notificationLoggingMessage, newServerRequest(ss, orZero[Params](params)))
13571357
}
13581358

13591359
// AddSendingMiddleware wraps the current sending method handler using the provided

mcp/shared.go

Lines changed: 5 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -97,10 +97,6 @@ func defaultSendingMethodHandler(ctx context.Context, method string, req Request
9797
// This can be called from user code, with an arbitrary value for method.
9898
return nil, jsonrpc2.ErrNotHandled
9999
}
100-
// Populate the SEP-2575 per-request _meta triple. This may allocate a
101-
// fresh Params struct on the request if the caller passed nil and the
102-
// session has negotiated the new protocol.
103-
injectRequestMeta(req)
104100
params := req.GetParams()
105101
if initParams, ok := params.(*InitializeParams); ok {
106102
// Fix the marshaling of initialize params, to work around #607.
@@ -109,12 +105,10 @@ func defaultSendingMethodHandler(ctx context.Context, method string, req Request
109105
// capabilities, so any panic here is a bug.
110106
params = initParams.toV2()
111107
}
112-
// Collapse a typed-nil-wrapping interface (e.g. (*ListToolsParams)(nil))
113-
// to a true nil interface so that the JSON-RPC layer omits the "params"
114-
// field on the wire.
115-
if params != nil && params.isNil() {
116-
params = nil
117-
}
108+
// Populate the SEP-2575 per-request _meta triple on the outgoing request.
109+
// This is a no-op for old protocol versions and for requests where the
110+
// caller did not provide params.
111+
injectRequestMeta(req)
118112

119113
// Notifications don't have results.
120114
if strings.HasPrefix(method, "notifications/") {
@@ -231,8 +225,7 @@ func injectRequestMeta(req Request) {
231225
}
232226
params := req.GetParams()
233227
if params == nil || params.isNil() {
234-
req.setEmptyParams()
235-
params = req.GetParams()
228+
return
236229
}
237230
m := params.GetMeta()
238231
if m == nil {
@@ -585,7 +578,6 @@ type Request interface {
585578
GetParams() Params
586579
// GetExtra returns the Extra field for ServerRequests, and nil for ClientRequests.
587580
GetExtra() *RequestExtra
588-
setEmptyParams()
589581
}
590582

591583
// A ClientRequest is a request to a client.
@@ -638,17 +630,6 @@ func (r *ServerRequest[P]) GetParams() Params { return r.Params }
638630
func (r *ClientRequest[P]) GetExtra() *RequestExtra { return nil }
639631
func (r *ServerRequest[P]) GetExtra() *RequestExtra { return r.Extra }
640632

641-
func (r *ClientRequest[P]) setEmptyParams() {
642-
baseType := reflect.TypeFor[P]().Elem()
643-
ptrVal := reflect.New(baseType)
644-
r.Params = ptrVal.Interface().(P)
645-
}
646-
func (r *ServerRequest[P]) setEmptyParams() {
647-
baseType := reflect.TypeFor[P]().Elem()
648-
ptrVal := reflect.New(baseType)
649-
r.Params = ptrVal.Interface().(P)
650-
}
651-
652633
// ProtocolVersion returns the protocol version negotiated for this request.
653634
//
654635
// For requests following the >= 2026-06-30 protocol, the value is read from

0 commit comments

Comments
 (0)