Capabilities define which features a client or server supports. They are negotiated during the initialization handshake to establish what operations are available in a session.
InitializeParams with ClientCapabilitiesInitializeResult containing ServerCapabilitiesInitializedParams notification to complete handshaketype ServerCapabilities struct {
Completions *CompletionCapabilities `json:"completions,omitempty"`
Experimental map[string]any `json:"experimental,omitempty"`
Logging *LoggingCapabilities `json:"logging,omitempty"`
Prompts *PromptCapabilities `json:"prompts,omitempty"`
Resources *ResourceCapabilities `json:"resources,omitempty"`
Tools *ToolCapabilities `json:"tools,omitempty"`
}Describes capabilities a server supports.
Fields:
Completions: Present if server supports argument completionExperimental: Experimental/custom capabilitiesLogging: Present if server supports loggingPrompts: Present if server offers promptsResources: Present if server offers resourcesTools: Present if server offers toolsExample:
server := mcp.NewServer(
impl,
&mcp.ServerOptions{
HasTools: true,
HasPrompts: true,
HasResources: true,
},
)
// Server automatically declares capabilities based on optionstype ToolCapabilities struct {
ListChanged bool `json:"listChanged,omitempty"`
}Present if server offers tools to clients.
Fields:
ListChanged: Whether server will send notifications when tool list changesExample:
// Server with tools that notify on changes
server := mcp.NewServer(impl, &mcp.ServerOptions{
HasTools: true,
})
// Later, after adding/removing tools
server.AddTool(tool, handler)
// Clients are automatically notified if they support ittype PromptCapabilities struct {
ListChanged bool `json:"listChanged,omitempty"`
}Present if server offers prompt templates.
Fields:
ListChanged: Whether server will send notifications when prompt list changesExample:
// Server with prompts that notify on changes
server := mcp.NewServer(impl, &mcp.ServerOptions{
HasPrompts: true,
})
server.AddPrompt(prompt, handler)
// Clients are automatically notified if they support ittype ResourceCapabilities struct {
ListChanged bool `json:"listChanged,omitempty"`
Subscribe bool `json:"subscribe,omitempty"`
}Present if server offers resources.
Fields:
ListChanged: Whether server will send notifications when resource list changesSubscribe: Whether server supports resource subscriptionsExample:
// Server with resources and subscription support
server := mcp.NewServer(impl, &mcp.ServerOptions{
HasResources: true,
SubscribeHandler: func(ctx context.Context, req *mcp.SubscribeRequest) error {
// Handle subscription
return subscriptionManager.Subscribe(req.Params.URI)
},
UnsubscribeHandler: func(ctx context.Context, req *mcp.UnsubscribeRequest) error {
// Handle unsubscription
return subscriptionManager.Unsubscribe(req.Params.URI)
},
})
// Notify subscribers when resource changes
server.ResourceUpdated(ctx, &mcp.ResourceUpdatedNotificationParams{
URI: "file:///config.json",
})type CompletionCapabilities struct{}Present if server supports argument autocompletion for prompts and resources.
Example:
// Server with completion support
server := mcp.NewServer(impl, &mcp.ServerOptions{
CompletionHandler: func(ctx context.Context, req *mcp.CompleteRequest) (*mcp.CompleteResult, error) {
// Provide completion suggestions
return &mcp.CompleteResult{
Completion: mcp.CompletionResultDetails{
Values: []string{"option1", "option2", "option3"},
Total: 3,
},
}, nil
},
})type LoggingCapabilities struct{}Present if server supports sending log messages to client.
Example:
// Server that sends logs
server := mcp.NewServer(impl, nil)
// In a session, send log messages
session.Log(ctx, &mcp.LoggingMessageParams{
Level: mcp.LevelInfo,
Data: "Operation completed",
})type ClientCapabilities struct {
Experimental map[string]any `json:"experimental,omitempty"`
Roots struct {
ListChanged bool `json:"listChanged,omitempty"`
} `json:"roots,omitempty"`
Sampling *SamplingCapabilities `json:"sampling,omitempty"`
Elicitation *ElicitationCapabilities `json:"elicitation,omitempty"`
}Describes capabilities a client supports.
Fields:
Experimental: Experimental/custom capabilitiesRoots.ListChanged: Whether client will notify when root list changesSampling: Present if client supports LLM sampling requestsElicitation: Present if client supports user input elicitationExample:
client := mcp.NewClient(
impl,
&mcp.ClientOptions{
CreateMessageHandler: handleSampling,
ElicitationHandler: handleElicitation,
},
)
// Client automatically declares capabilities based on handlerstype SamplingCapabilities struct{}Describes client's LLM sampling capabilities.
Present if client can handle sampling/createMessage requests from servers.
Example:
client := mcp.NewClient(impl, &mcp.ClientOptions{
CreateMessageHandler: func(ctx context.Context, req *mcp.CreateMessageRequest) (*mcp.CreateMessageResult, error) {
// Use an LLM to generate response
response := callLLM(req.Params.Messages, req.Params.MaxTokens)
return &mcp.CreateMessageResult{
Role: "assistant",
Model: "gpt-4",
Content: &mcp.TextContent{Text: response},
StopReason: "end_turn",
}, nil
},
})type ElicitationCapabilities struct{}Describes client's user input elicitation capabilities.
Present if client can handle elicitation/elicit requests from servers.
Example:
client := mcp.NewClient(impl, &mcp.ClientOptions{
ElicitationHandler: func(ctx context.Context, req *mcp.ElicitRequest) (*mcp.ElicitResult, error) {
// Show form to user based on requestedSchema
userInput := promptUser(req.Params.Message, req.Params.RequestedSchema)
return &mcp.ElicitResult{
Action: "accept",
Content: userInput,
}, nil
},
})Clients can declare that they will notify servers when their root list changes.
Example:
client := mcp.NewClient(impl, nil)
// Add roots
client.AddRoots(&mcp.Root{
URI: "file:///project",
Name: "Project Directory",
})
// Remove roots
client.RemoveRoots("file:///project")
// Connected servers are automatically notifiedfunc myToolHandler(ctx context.Context, req *mcp.CallToolRequest, input Input) (
*mcp.CallToolResult,
Output,
error,
) {
// Check if client supports sampling
initParams := req.Session.InitializeParams()
if initParams.Capabilities.Sampling != nil {
// Client can handle LLM requests
result, err := req.Session.CreateMessage(ctx, &mcp.CreateMessageParams{
MaxTokens: 1000,
Messages: []*mcp.SamplingMessage{
{Role: "user", Content: &mcp.TextContent{Text: "Generate a summary"}},
},
})
if err == nil {
// Use LLM response
}
}
return nil, output, nil
}session, err := client.Connect(ctx, transport, nil)
if err != nil {
log.Fatal(err)
}
// Check server capabilities
initResult := session.InitializeResult()
if initResult.Capabilities.Tools != nil {
// Server offers tools
tools, err := session.ListTools(ctx, &mcp.ListToolsParams{})
if err == nil {
fmt.Printf("Server has %d tools\n", len(tools.Tools))
}
if initResult.Capabilities.Tools.ListChanged {
// Server will notify when tools change
// Handler in ClientOptions will be called
}
}
if initResult.Capabilities.Resources != nil {
// Server offers resources
if initResult.Capabilities.Resources.Subscribe {
// Can subscribe to resource updates
err := session.Subscribe(ctx, &mcp.SubscribeParams{
URI: "file:///config.json",
})
}
}
if initResult.Capabilities.Logging != nil {
// Server can send logs
// LoggingMessageHandler in ClientOptions will be called
}
if initResult.Capabilities.Completions != nil {
// Server supports argument completion
result, err := session.Complete(ctx, &mcp.CompleteParams{
Ref: &mcp.CompleteReference{Type: "ref/prompt", Name: "greeting"},
Argument: mcp.CompleteParamsArgument{Name: "name", Value: "Al"},
Context: &mcp.CompleteContext{Arguments: map[string]string{}},
})
// result.Completion.Values contains suggestions like ["Alice", "Alex", "Allan"]
}Both clients and servers can declare experimental capabilities for custom features.
Example:
// Server with experimental capability
serverCaps := &mcp.ServerCapabilities{
Tools: &mcp.ToolCapabilities{},
Experimental: map[string]any{
"customFeature": map[string]any{
"version": "1.0",
"enabled": true,
},
},
}Capabilities can be extended in future protocol versions:
// Forward compatibility pattern
func checkCapability(caps *mcp.ServerCapabilities) bool {
// Check for capability
if caps.Tools != nil {
return true
}
// Check experimental namespace for pre-release features
if exp, ok := caps.Experimental["futureTools"].(map[string]any); ok {
if enabled, ok := exp["enabled"].(bool); ok && enabled {
return true
}
}
return false
}// Declare all capabilities upfront
server := mcp.NewServer(impl, &mcp.ServerOptions{
Instructions: "A full-featured MCP server",
HasTools: true,
HasPrompts: true,
HasResources: true,
CompletionHandler: completionHandler,
SubscribeHandler: subscribeHandler,
// Capabilities are automatically declared based on options
})// Provide handlers for all supported capabilities
client := mcp.NewClient(impl, &mcp.ClientOptions{
CreateMessageHandler: samplingHandler,
ElicitationHandler: elicitationHandler,
ToolListChangedHandler: toolsChangedHandler,
ResourceListChangedHandler: resourcesChangedHandler,
LoggingMessageHandler: loggingHandler,
// Capabilities are automatically declared based on handlers
})// Gracefully handle missing capabilities
func useServerFeature(session *mcp.ClientSession) error {
caps := session.InitializeResult().Capabilities
// Check before using feature
if caps.Resources != nil && caps.Resources.Subscribe {
return session.Subscribe(ctx, &mcp.SubscribeParams{URI: uri})
}
// Fall back to polling
go pollResource(session, uri)
return nil
}// Use experimental namespace for version-specific features
serverCaps := &mcp.ServerCapabilities{
Tools: &mcp.ToolCapabilities{ListChanged: true},
Experimental: map[string]any{
"org.example.advancedTools": map[string]any{
"version": "2.0",
"features": []string{"streaming", "cancellation"},
},
},
}package main
import (
"context"
"log"
"github.com/modelcontextprotocol/go-sdk/mcp"
)
func main() {
// Create client with full capabilities
client := mcp.NewClient(
&mcp.Implementation{Name: "full-client", Version: "1.0.0"},
&mcp.ClientOptions{
// Sampling capability
CreateMessageHandler: func(ctx context.Context, req *mcp.CreateMessageRequest) (*mcp.CreateMessageResult, error) {
return &mcp.CreateMessageResult{
Role: "assistant",
Model: "gpt-4",
Content: &mcp.TextContent{Text: "Generated response"},
}, nil
},
// Elicitation capability
ElicitationHandler: func(ctx context.Context, req *mcp.ElicitRequest) (*mcp.ElicitResult, error) {
return &mcp.ElicitResult{
Action: "accept",
Content: map[string]any{"input": "user data"},
}, nil
},
// Notification handlers
ToolListChangedHandler: func(ctx context.Context, req *mcp.ToolListChangedRequest) {
log.Println("Server tools changed")
},
ResourceListChangedHandler: func(ctx context.Context, req *mcp.ResourceListChangedRequest) {
log.Println("Server resources changed")
},
LoggingMessageHandler: func(ctx context.Context, req *mcp.LoggingMessageRequest) {
log.Printf("[%s] %v", req.Params.Level, req.Params.Data)
},
},
)
// Add roots (client capability)
client.AddRoots(&mcp.Root{
URI: "file:///workspace",
Name: "Workspace",
})
// Connect to server
session, err := client.Connect(ctx, transport, nil)
if err != nil {
log.Fatal(err)
}
defer session.Close()
// Inspect server capabilities
serverCaps := session.InitializeResult().Capabilities
log.Printf("Server capabilities:")
log.Printf(" Tools: %v (ListChanged: %v)",
serverCaps.Tools != nil,
serverCaps.Tools != nil && serverCaps.Tools.ListChanged)
log.Printf(" Resources: %v (Subscribe: %v)",
serverCaps.Resources != nil,
serverCaps.Resources != nil && serverCaps.Resources.Subscribe)
log.Printf(" Prompts: %v", serverCaps.Prompts != nil)
log.Printf(" Logging: %v", serverCaps.Logging != nil)
log.Printf(" Completions: %v", serverCaps.Completions != nil)
// Use features based on capabilities
if serverCaps.Tools != nil {
tools, _ := session.ListTools(ctx, &mcp.ListToolsParams{})
log.Printf(" Found %d tools", len(tools.Tools))
}
if serverCaps.Resources != nil && serverCaps.Resources.Subscribe {
session.Subscribe(ctx, &mcp.SubscribeParams{URI: "file:///data"})
}
}