or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

auth.mdindex.mdjsonrpc.mdmcp-capabilities.mdmcp-client.mdmcp-content.mdmcp-protocol.mdmcp-server.mdmcp-transports.mdoauthex.md
tile.json

mcp-capabilities.mddocs/

MCP Capabilities

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.

Capability Negotiation Flow

  1. Client sends InitializeParams with ClientCapabilities
  2. Server responds with InitializeResult containing ServerCapabilities
  3. Both sides can only use features declared in the other's capabilities
  4. Client sends InitializedParams notification to complete handshake

Server Capabilities

type 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 completion
  • Experimental: Experimental/custom capabilities
  • Logging: Present if server supports logging
  • Prompts: Present if server offers prompts
  • Resources: Present if server offers resources
  • Tools: Present if server offers tools

Example:

server := mcp.NewServer(
	impl,
	&mcp.ServerOptions{
		HasTools:     true,
		HasPrompts:   true,
		HasResources: true,
	},
)
// Server automatically declares capabilities based on options

Tool Capabilities

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

Present if server offers tools to clients.

Fields:

  • ListChanged: Whether server will send notifications when tool list changes

Example:

// 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 it

Prompt Capabilities

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

Present if server offers prompt templates.

Fields:

  • ListChanged: Whether server will send notifications when prompt list changes

Example:

// 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 it

Resource Capabilities

type 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 changes
  • Subscribe: Whether server supports resource subscriptions

Example:

// 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",
})

Completion Capabilities

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
	},
})

Logging Capabilities

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",
})

Client Capabilities

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 capabilities
  • Roots.ListChanged: Whether client will notify when root list changes
  • Sampling: Present if client supports LLM sampling requests
  • Elicitation: Present if client supports user input elicitation

Example:

client := mcp.NewClient(
	impl,
	&mcp.ClientOptions{
		CreateMessageHandler: handleSampling,
		ElicitationHandler:   handleElicitation,
	},
)
// Client automatically declares capabilities based on handlers

Sampling Capabilities

type 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
	},
})

Elicitation Capabilities

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
	},
})

Roots Capability

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 notified

Checking Capabilities

Server Checking Client Capabilities

func 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
}

Client Checking Server Capabilities

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"]
}

Experimental Capabilities

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,
		},
	},
}

Capability Evolution

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
}

Best Practices

Server Capability Declaration

// 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
})

Client Capability Declaration

// 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
})

Dynamic Feature Detection

// 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
}

Capability Versioning

// 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"},
		},
	},
}

Complete Capability Example

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"})
	}
}