44 "context"
55 "encoding/json"
66 "fmt"
7- "net/http"
87 "slices"
98 "sync"
109 "sync/atomic"
@@ -25,6 +24,7 @@ type Client struct {
2524 serverCapabilities mcp.ServerCapabilities
2625 protocolVersion string
2726 samplingHandler SamplingHandler
27+ rootsHandler RootsHandler
2828 elicitationHandler ElicitationHandler
2929}
3030
@@ -38,15 +38,26 @@ func WithClientCapabilities(capabilities mcp.ClientCapabilities) ClientOption {
3838}
3939
4040// WithSamplingHandler sets the sampling handler for the client.
41- // When set, the client will declare sampling capability during initialization.
41+ // WithSamplingHandler sets the SamplingHandler on the client and causes the client to declare sampling
42+ // capability during Initialize. The provided `handler` will be invoked for incoming sampling requests.
43+ // The returned ClientOption applies this handler to a Client.
4244func WithSamplingHandler (handler SamplingHandler ) ClientOption {
4345 return func (c * Client ) {
4446 c .samplingHandler = handler
4547 }
4648}
4749
50+ // WithRootsHandler sets the roots handler for the client.
51+ // WithRootsHandler returns a ClientOption that sets the client's RootsHandler.
52+ // When provided, the client will declare the roots capability (ListChanged) during initialization.
53+ func WithRootsHandler (handler RootsHandler ) ClientOption {
54+ return func (c * Client ) {
55+ c .rootsHandler = handler
56+ }
57+ }
58+
4859// WithElicitationHandler sets the elicitation handler for the client.
49- // When set, the client will declare elicitation capability during initialization.
60+ // to declare elicitation capability during initialization.
5061func WithElicitationHandler (handler ElicitationHandler ) ClientOption {
5162 return func (c * Client ) {
5263 c .elicitationHandler = handler
@@ -141,7 +152,6 @@ func (c *Client) sendRequest(
141152 ctx context.Context ,
142153 method string ,
143154 params any ,
144- header http.Header ,
145155) (* json.RawMessage , error ) {
146156 if ! c .initialized && method != "initialize" {
147157 return nil , fmt .Errorf ("client not initialized" )
@@ -154,7 +164,6 @@ func (c *Client) sendRequest(
154164 ID : mcp .NewRequestId (id ),
155165 Method : method ,
156166 Params : params ,
157- Header : header ,
158167 }
159168
160169 response , err := c .transport .SendRequest (ctx , request )
@@ -180,6 +189,13 @@ func (c *Client) Initialize(
180189 if c .samplingHandler != nil {
181190 capabilities .Sampling = & struct {}{}
182191 }
192+ if c .rootsHandler != nil {
193+ capabilities .Roots = & struct {
194+ ListChanged bool `json:"listChanged,omitempty"`
195+ }{
196+ ListChanged : true ,
197+ }
198+ }
183199 // Add elicitation capability if handler is configured
184200 if c .elicitationHandler != nil {
185201 capabilities .Elicitation = & struct {}{}
@@ -196,7 +212,7 @@ func (c *Client) Initialize(
196212 Capabilities : capabilities ,
197213 }
198214
199- response , err := c .sendRequest (ctx , "initialize" , params , request . Header )
215+ response , err := c .sendRequest (ctx , "initialize" , params )
200216 if err != nil {
201217 return nil , err
202218 }
@@ -241,7 +257,7 @@ func (c *Client) Initialize(
241257}
242258
243259func (c * Client ) Ping (ctx context.Context ) error {
244- _ , err := c .sendRequest (ctx , "ping" , nil , nil )
260+ _ , err := c .sendRequest (ctx , "ping" , nil )
245261 return err
246262}
247263
@@ -322,7 +338,7 @@ func (c *Client) ReadResource(
322338 ctx context.Context ,
323339 request mcp.ReadResourceRequest ,
324340) (* mcp.ReadResourceResult , error ) {
325- response , err := c .sendRequest (ctx , "resources/read" , request .Params , request . Header )
341+ response , err := c .sendRequest (ctx , "resources/read" , request .Params )
326342 if err != nil {
327343 return nil , err
328344 }
@@ -334,15 +350,15 @@ func (c *Client) Subscribe(
334350 ctx context.Context ,
335351 request mcp.SubscribeRequest ,
336352) error {
337- _ , err := c .sendRequest (ctx , "resources/subscribe" , request .Params , request . Header )
353+ _ , err := c .sendRequest (ctx , "resources/subscribe" , request .Params )
338354 return err
339355}
340356
341357func (c * Client ) Unsubscribe (
342358 ctx context.Context ,
343359 request mcp.UnsubscribeRequest ,
344360) error {
345- _ , err := c .sendRequest (ctx , "resources/unsubscribe" , request .Params , request . Header )
361+ _ , err := c .sendRequest (ctx , "resources/unsubscribe" , request .Params )
346362 return err
347363}
348364
@@ -386,7 +402,7 @@ func (c *Client) GetPrompt(
386402 ctx context.Context ,
387403 request mcp.GetPromptRequest ,
388404) (* mcp.GetPromptResult , error ) {
389- response , err := c .sendRequest (ctx , "prompts/get" , request .Params , request . Header )
405+ response , err := c .sendRequest (ctx , "prompts/get" , request .Params )
390406 if err != nil {
391407 return nil , err
392408 }
@@ -434,7 +450,7 @@ func (c *Client) CallTool(
434450 ctx context.Context ,
435451 request mcp.CallToolRequest ,
436452) (* mcp.CallToolResult , error ) {
437- response , err := c .sendRequest (ctx , "tools/call" , request .Params , request . Header )
453+ response , err := c .sendRequest (ctx , "tools/call" , request .Params )
438454 if err != nil {
439455 return nil , err
440456 }
@@ -446,15 +462,15 @@ func (c *Client) SetLevel(
446462 ctx context.Context ,
447463 request mcp.SetLevelRequest ,
448464) error {
449- _ , err := c .sendRequest (ctx , "logging/setLevel" , request .Params , request . Header )
465+ _ , err := c .sendRequest (ctx , "logging/setLevel" , request .Params )
450466 return err
451467}
452468
453469func (c * Client ) Complete (
454470 ctx context.Context ,
455471 request mcp.CompleteRequest ,
456472) (* mcp.CompleteResult , error ) {
457- response , err := c .sendRequest (ctx , "completion/complete" , request .Params , request . Header )
473+ response , err := c .sendRequest (ctx , "completion/complete" , request .Params )
458474 if err != nil {
459475 return nil , err
460476 }
@@ -467,6 +483,27 @@ func (c *Client) Complete(
467483 return & result , nil
468484}
469485
486+ func (c * Client ) RootListChanges (
487+ ctx context.Context ,
488+ ) error {
489+ // Send root list changes notification
490+ notification := mcp.JSONRPCNotification {
491+ JSONRPC : mcp .JSONRPC_VERSION ,
492+ Notification : mcp.Notification {
493+ Method : mcp .MethodNotificationToolsListChanged ,
494+ },
495+ }
496+
497+ err := c .transport .SendNotification (ctx , notification )
498+ if err != nil {
499+ return fmt .Errorf (
500+ "failed to send root list change notification: %w" ,
501+ err ,
502+ )
503+ }
504+ return nil
505+ }
506+
470507// handleIncomingRequest processes incoming requests from the server.
471508// This is the main entry point for server-to-client requests like sampling and elicitation.
472509func (c * Client ) handleIncomingRequest (ctx context.Context , request transport.JSONRPCRequest ) (* transport.JSONRPCResponse , error ) {
@@ -477,6 +514,8 @@ func (c *Client) handleIncomingRequest(ctx context.Context, request transport.JS
477514 return c .handleElicitationRequestTransport (ctx , request )
478515 case string (mcp .MethodPing ):
479516 return c .handlePingRequestTransport (ctx , request )
517+ case string (mcp .MethodListRoots ):
518+ return c .handleListRootsRequestTransport (ctx , request )
480519 default :
481520 return nil , fmt .Errorf ("unsupported request method: %s" , request .Method )
482521 }
@@ -539,6 +578,37 @@ func (c *Client) handleSamplingRequestTransport(ctx context.Context, request tra
539578 return response , nil
540579}
541580
581+ // handleListRootsRequestTransport handles list roots requests at the transport level.
582+ func (c * Client ) handleListRootsRequestTransport (ctx context.Context , request transport.JSONRPCRequest ) (* transport.JSONRPCResponse , error ) {
583+ if c .rootsHandler == nil {
584+ return nil , fmt .Errorf ("no roots handler configured" )
585+ }
586+
587+ // Create the MCP request
588+ mcpRequest := mcp.ListRootsRequest {
589+ Request : mcp.Request {
590+ Method : string (mcp .MethodListRoots ),
591+ },
592+ }
593+
594+ // Call the list roots handler
595+ result , err := c .rootsHandler .ListRoots (ctx , mcpRequest )
596+ if err != nil {
597+ return nil , err
598+ }
599+
600+ // Marshal the result
601+ resultBytes , err := json .Marshal (result )
602+ if err != nil {
603+ return nil , fmt .Errorf ("failed to marshal result: %w" , err )
604+ }
605+
606+ // Create the transport response
607+ response := transport .NewJSONRPCResultResponse (request .ID , resultBytes )
608+
609+ return response , nil
610+ }
611+
542612// handleElicitationRequestTransport handles elicitation requests at the transport level.
543613func (c * Client ) handleElicitationRequestTransport (ctx context.Context , request transport.JSONRPCRequest ) (* transport.JSONRPCResponse , error ) {
544614 if c .elicitationHandler == nil {
@@ -594,7 +664,7 @@ func listByPage[T any](
594664 request mcp.PaginatedRequest ,
595665 method string ,
596666) (* T , error ) {
597- response , err := client .sendRequest (ctx , method , request .Params , nil )
667+ response , err := client .sendRequest (ctx , method , request .Params )
598668 if err != nil {
599669 return nil , err
600670 }
@@ -635,4 +705,4 @@ func (c *Client) GetSessionId() string {
635705// IsInitialized returns true if the client has been initialized.
636706func (c * Client ) IsInitialized () bool {
637707 return c .initialized
638- }
708+ }
0 commit comments