Repository: modelcontextprotocol/go-sdk

Issue:
Proposal: reachable client capabilities in Server Session
**Is your feature request related to a problem? Please describe.**
MCP Servers may want to check the capabilities of the client and decide actions based on it. For instance, MCP Server checks whether client supports elicitation or not. If client supports this, MCP Server initiates an elicitation request.

**Describe the solution you'd like**
Client capabilities are already embedded and stored in https://github.com/modelcontextprotocol/go-sdk/blob/78a66a438a4ef06fa50c8e5d6c559669f26ccd3b/mcp/server.go#L523 ServerSession. However, MCP Servers can't access to this field. I recommend to introduce a new function such as `GetInitializeParams`, or whatever that is used in tool calls to check the client capabilities.

**Describe alternatives you've considered**
Nothing comes to my mind as an alternative. But I appreciate if there is already a way and we can close this issue.

**Additional context**
I've opened this https://github.com/mark3labs/mcp-go/pull/491 to embed capabilities in session. However, those fields are already stored in here. We just need a way to expose it.


Hints from issue comments:
@samthanawalla: Same problem as #166, exposing InitializeParams seems like the solution for ServerSession and exposing InitializeResult is the solution for ClientSession.@findleyr: @samthanawalla want to pick this up, since you reviewed the opposite direction?

Solution patch:
diff --git a/mcp/client.go b/mcp/client.go
index fdf0541..2511c05 100644
--- a/mcp/client.go
+++ b/mcp/client.go
@@ -105,6 +105,15 @@ func (e unsupportedProtocolVersionError) Error() string {
 // ClientSessionOptions is reserved for future use.
 type ClientSessionOptions struct{}
 
+func (c *Client) capabilities() *ClientCapabilities {
+	caps := &ClientCapabilities{}
+	caps.Roots.ListChanged = true
+	if c.opts.CreateMessageHandler != nil {
+		caps.Sampling = &SamplingCapabilities{}
+	}
+	return caps
+}
+
 // Connect begins an MCP session by connecting to a server over the given
 // transport, and initializing the session.
 //
@@ -118,16 +127,10 @@ func (c *Client) Connect(ctx context.Context, t Transport, _ *ClientSessionOptio
 		return nil, err
 	}
 
-	caps := &ClientCapabilities{}
-	caps.Roots.ListChanged = true
-	if c.opts.CreateMessageHandler != nil {
-		caps.Sampling = &SamplingCapabilities{}
-	}
-
 	params := &InitializeParams{
 		ProtocolVersion: latestProtocolVersion,
 		ClientInfo:      c.impl,
-		Capabilities:    caps,
+		Capabilities:    c.capabilities(),
 	}
 	req := &ClientRequest[*InitializeParams]{Session: cs, Params: params}
 	res, err := handleSend[*InitializeResult](ctx, methodInitialize, req)
diff --git a/mcp/server.go b/mcp/server.go
index 2a8ab93..d98ff8a 100644
--- a/mcp/server.go
+++ b/mcp/server.go
@@ -849,6 +849,8 @@ func (ss *ServerSession) handle(ctx context.Context, req *jsonrpc.Request) (any,
 	return handleReceive(ctx, ss, req)
 }
 
+func (ss *ServerSession) InitializeParams() *InitializeParams { return ss.state.InitializeParams }
+
 func (ss *ServerSession) initialize(ctx context.Context, params *InitializeParams) (*InitializeResult, error) {
 	if params == nil {
 		return nil, fmt.Errorf("%w: \"params\" must be be provided", jsonrpc2.ErrInvalidParams)



Test patch:
diff --git a/mcp/client_test.go b/mcp/client_test.go
index 7920c55..469fa3f 100644
--- a/mcp/client_test.go
+++ b/mcp/client_test.go
@@ -190,3 +190,48 @@ func TestClientPaginateVariousPageSizes(t *testing.T) {
 		})
 	}
 }
+
+func TestClientCapabilities(t *testing.T) {
+	testCases := []struct {
+		name             string
+		configureClient  func(s *Client)
+		clientOpts       ClientOptions
+		wantCapabilities *ClientCapabilities
+	}{
+		{
+			name:            "With initial capabilities",
+			configureClient: func(s *Client) {},
+			wantCapabilities: &ClientCapabilities{
+				Roots: struct {
+					ListChanged bool "json:\"listChanged,omitempty\""
+				}{ListChanged: true},
+			},
+		},
+		{
+			name:            "With sampling",
+			configureClient: func(s *Client) {},
+			clientOpts: ClientOptions{
+				CreateMessageHandler: func(context.Context, *ClientRequest[*CreateMessageParams]) (*CreateMessageResult, error) {
+					return nil, nil
+				},
+			},
+			wantCapabilities: &ClientCapabilities{
+				Roots: struct {
+					ListChanged bool "json:\"listChanged,omitempty\""
+				}{ListChanged: true},
+				Sampling: &SamplingCapabilities{},
+			},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			client := NewClient(testImpl, &tc.clientOpts)
+			tc.configureClient(client)
+			gotCapabilities := client.capabilities()
+			if diff := cmp.Diff(tc.wantCapabilities, gotCapabilities); diff != "" {
+				t.Errorf("capabilities() mismatch (-want +got):\n%s", diff)
+			}
+		})
+	}
+}