go-sdk icon indicating copy to clipboard operation
go-sdk copied to clipboard

How to check client capabilities and handle unsupported capability errors?

Open robbyt opened this issue 2 months ago • 2 comments

Is your feature request related to a problem? Please describe. I have two semi-related questions, caused by uncertainty of how to check client capabilities from an MCP server.

  1. How can I checking the client capabilities before using them?
  2. Can you expos the errors from internal/jsonrpc so I can check for them? (is this a possible solution?)

re 1: How to check Capabilities I'd like to figure out if a MCP client supports a specific MCP server capability (e.g., ListRoots). I'm assuming client capabilities by reading the fields in the mcp.ClientCapabilities struct- Is this advisable?

Also, the Roots field is a value struct, so I can't check for nil on that like I can with the other capabilities fields Sampling or Elicitation: https://github.com/modelcontextprotocol/go-sdk/blob/d256a9c1094ee4044dc3fa6e55d3559df0047107/mcp/protocol.go#L183-L195

re 2: Error types from internal/jsonrpc When using the elicitation/sampleing/logging capabilities, the examples just return an error to the client. The don't differentiate between the different types, or use any errors.Is(). So when a client doesn't support a capability (e.g., roots), the SDK returns a JSON-RPC error code -32601 (Method Not Found). However, ErrMethodNotFound is in the internal jsonrpc2 package and cannot be imported by my project, so I can't use errors.Is() to check if an error from ListRoots(), CreateMessage(), or Elicit() is due to an unsupported client capability.

Describe the solution you'd like

  1. Consistent capability structure: Make Roots a pointer type like the other capabilities:
type ClientCapabilities struct {
	Roots       *RootsCapabilities       `json:"roots,omitempty"`  // Changed from inline struct
	Sampling    *SamplingCapabilities    `json:"sampling,omitempty"`
	Elicitation *ElicitationCapabilities `json:"elicitation,omitempty"`
}

type RootsCapabilities struct {
	ListChanged bool `json:"listChanged,omitempty"`
}

This would allow checking Capabilities.Roots != nil to determine if roots are supported at all.

  1. Export JSON-RPC errors: Export common JSON-RPC errors from the public jsonrpc package:
// jsonrpc/jsonrpc.go
var (
	ErrMethodNotFound = internal.ErrMethodNotFound
	ErrInvalidParams  = internal.ErrInvalidParams
	// etc.
)

This would enable library authors to use errors.Is(err, jsonrpc.ErrMethodNotFound) to detect specific error conditions.

Describe alternatives you've considered

Right now I need to do string matching on the error, or infer client capabilities from reading the ClientCapabilities.

Additional context Possibly related to #166 Thank you for reading, and designing such a useful library!

robbyt avatar Oct 27 '25 21:10 robbyt

CC @jba

The way this is supposed to work is that you'd check for the presence of the Roots capability. The fact that the Roots client capability is not a distinguished type is almost certainly an oversight. It looks like it dates back to an earlier version of the SDK, and was somehow un-pointerized in https://go-review.git.corp.google.com/c/tools/+/672575.

Given that we have a backward compatibility promise at this point, it may be too late to fix this :(. I'm not sure if we can make an exception for what appears to clearly be a bug.

findleyr avatar Oct 27 '25 21:10 findleyr

Well, this is unfortunate. I've drafted a workaround in #608, for discussion.

Regarding errors: yes I agree we should expose distinguished errors. I think this is largely covered by #452.

findleyr avatar Oct 27 '25 22:10 findleyr