Skip to content

Add tool capabilities to serverCapabilities #36

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions client/sse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ func TestSSEMCPClient(t *testing.T) {
"1.0.0",
server.WithResourceCapabilities(true, true),
server.WithPromptCapabilities(true),
server.WithToolCapabilities(true),
)

// Add a test tool
Expand Down
35 changes: 28 additions & 7 deletions server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ func (s *MCPServer) SendNotificationToClient(

// serverCapabilities defines the supported features of the MCP server
type serverCapabilities struct {
tools *toolCapabilities
resources *resourceCapabilities
prompts *promptCapabilities
logging bool
Expand All @@ -154,6 +155,11 @@ type promptCapabilities struct {
listChanged bool
}

// toolCapabilities defines the supported tool-related features
type toolCapabilities struct {
listChanged bool
}

// WithResourceCapabilities configures resource-related server capabilities
func WithResourceCapabilities(subscribe, listChanged bool) ServerOption {
return func(s *MCPServer) {
Expand All @@ -173,6 +179,15 @@ func WithPromptCapabilities(listChanged bool) ServerOption {
}
}

// WithToolCapabilities configures tool-related server capabilities
func WithToolCapabilities(listChanged bool) ServerOption {
return func(s *MCPServer) {
s.capabilities.tools = &toolCapabilities{
listChanged: listChanged,
}
}
}

// WithLogging enables logging capabilities for the server
func WithLogging() ServerOption {
return func(s *MCPServer) {
Expand All @@ -195,6 +210,12 @@ func NewMCPServer(
version: version,
notificationHandlers: make(map[string]NotificationHandlerFunc),
notifications: make(chan ServerNotification, 100),
capabilities: serverCapabilities{
tools: &toolCapabilities{},
resources: &resourceCapabilities{},
prompts: &promptCapabilities{},
logging: false,
},
}

for _, opt := range opts {
Expand Down Expand Up @@ -304,7 +325,7 @@ func (s *MCPServer) HandleMessage(
}
return s.handleListResourceTemplates(ctx, baseMessage.ID, request)
case "resources/read":
if s.capabilities.resources == nil {
if !s.capabilities.resources.listChanged {
return createErrorResponse(
baseMessage.ID,
mcp.METHOD_NOT_FOUND,
Expand Down Expand Up @@ -338,7 +359,7 @@ func (s *MCPServer) HandleMessage(
}
return s.handleListPrompts(ctx, baseMessage.ID, request)
case "prompts/get":
if s.capabilities.prompts == nil {
if !s.capabilities.prompts.listChanged {
return createErrorResponse(
baseMessage.ID,
mcp.METHOD_NOT_FOUND,
Expand Down Expand Up @@ -372,7 +393,7 @@ func (s *MCPServer) HandleMessage(
}
return s.handleListTools(ctx, baseMessage.ID, request)
case "tools/call":
if len(s.tools) == 0 {
if !s.capabilities.tools.listChanged || len(s.tools) == 0 {
return createErrorResponse(
baseMessage.ID,
mcp.METHOD_NOT_FOUND,
Expand Down Expand Up @@ -508,20 +529,20 @@ func (s *MCPServer) handleInitialize(
Subscribe bool `json:"subscribe,omitempty"`
ListChanged bool `json:"listChanged,omitempty"`
}{
Subscribe: false,
ListChanged: true,
Subscribe: s.capabilities.resources.subscribe,
ListChanged: s.capabilities.resources.listChanged,
}

capabilities.Prompts = &struct {
ListChanged bool `json:"listChanged,omitempty"`
}{
ListChanged: true,
ListChanged: s.capabilities.prompts.listChanged,
}

capabilities.Tools = &struct {
ListChanged bool `json:"listChanged,omitempty"`
}{
ListChanged: true,
ListChanged: s.capabilities.tools.listChanged,
}

if s.capabilities.logging {
Expand Down
65 changes: 55 additions & 10 deletions server/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,11 @@ func TestMCPServer_Capabilities(t *testing.T) {
assert.Equal(t, "1.0.0", initResult.ServerInfo.Version)
assert.NotNil(t, initResult.Capabilities.Resources)
assert.False(t, initResult.Capabilities.Resources.Subscribe)
assert.True(t, initResult.Capabilities.Resources.ListChanged)
assert.False(t, initResult.Capabilities.Resources.ListChanged)
assert.NotNil(t, initResult.Capabilities.Prompts)
assert.True(t, initResult.Capabilities.Prompts.ListChanged)
assert.False(t, initResult.Capabilities.Prompts.ListChanged)
assert.NotNil(t, initResult.Capabilities.Tools)
assert.True(t, initResult.Capabilities.Tools.ListChanged)
assert.False(t, initResult.Capabilities.Tools.ListChanged)
assert.Nil(t, initResult.Capabilities.Logging)
},
},
Expand All @@ -55,6 +55,7 @@ func TestMCPServer_Capabilities(t *testing.T) {
options: []ServerOption{
WithResourceCapabilities(true, true),
WithPromptCapabilities(true),
WithToolCapabilities(true),
WithLogging(),
},
validate: func(t *testing.T, response mcp.JSONRPCMessage) {
Expand All @@ -73,8 +74,8 @@ func TestMCPServer_Capabilities(t *testing.T) {
assert.Equal(t, "1.0.0", initResult.ServerInfo.Version)

assert.NotNil(t, initResult.Capabilities.Resources)
// Resources capabilities are now always false for subscribe and true for listChanged
assert.False(t, initResult.Capabilities.Resources.Subscribe)

assert.True(t, initResult.Capabilities.Resources.Subscribe)
assert.True(t, initResult.Capabilities.Resources.ListChanged)

assert.NotNil(t, initResult.Capabilities.Prompts)
Expand All @@ -83,6 +84,43 @@ func TestMCPServer_Capabilities(t *testing.T) {
assert.NotNil(t, initResult.Capabilities.Tools)
assert.True(t, initResult.Capabilities.Tools.ListChanged)

assert.NotNil(t, initResult.Capabilities.Logging)
},
},
{
name: "Specific capabilities",
options: []ServerOption{
WithResourceCapabilities(true, false),
WithPromptCapabilities(true),
WithToolCapabilities(false),
WithLogging(),
},
validate: func(t *testing.T, response mcp.JSONRPCMessage) {
resp, ok := response.(mcp.JSONRPCResponse)
assert.True(t, ok)

initResult, ok := resp.Result.(mcp.InitializeResult)
assert.True(t, ok)

assert.Equal(
t,
mcp.LATEST_PROTOCOL_VERSION,
initResult.ProtocolVersion,
)
assert.Equal(t, "test-server", initResult.ServerInfo.Name)
assert.Equal(t, "1.0.0", initResult.ServerInfo.Version)

assert.NotNil(t, initResult.Capabilities.Resources)

assert.True(t, initResult.Capabilities.Resources.Subscribe)
assert.False(t, initResult.Capabilities.Resources.ListChanged)

assert.NotNil(t, initResult.Capabilities.Prompts)
assert.True(t, initResult.Capabilities.Prompts.ListChanged)

assert.NotNil(t, initResult.Capabilities.Tools)
assert.False(t, initResult.Capabilities.Tools.ListChanged)

assert.NotNil(t, initResult.Capabilities.Logging)
},
},
Expand Down Expand Up @@ -525,6 +563,7 @@ func TestMCPServer_HandleUndefinedHandlers(t *testing.T) {
server := NewMCPServer("test-server", "1.0.0",
WithResourceCapabilities(true, true),
WithPromptCapabilities(true),
WithToolCapabilities(true),
)

// Add a test tool to enable tool capabilities
Expand Down Expand Up @@ -600,14 +639,10 @@ func TestMCPServer_HandleUndefinedHandlers(t *testing.T) {
}

func TestMCPServer_HandleMethodsWithoutCapabilities(t *testing.T) {
server := NewMCPServer(
"test-server",
"1.0.0",
)

tests := []struct {
name string
message string
options []ServerOption
expectedErr int
}{
{
Expand All @@ -620,6 +655,9 @@ func TestMCPServer_HandleMethodsWithoutCapabilities(t *testing.T) {
"name": "test-tool"
}
}`,
options: []ServerOption{
WithToolCapabilities(false),
},
expectedErr: mcp.METHOD_NOT_FOUND,
},
{
Expand All @@ -632,6 +670,9 @@ func TestMCPServer_HandleMethodsWithoutCapabilities(t *testing.T) {
"name": "test-prompt"
}
}`,
options: []ServerOption{
WithPromptCapabilities(false),
},
expectedErr: mcp.METHOD_NOT_FOUND,
},
{
Expand All @@ -644,12 +685,16 @@ func TestMCPServer_HandleMethodsWithoutCapabilities(t *testing.T) {
"uri": "test-resource"
}
}`,
options: []ServerOption{
WithResourceCapabilities(false, false),
},
expectedErr: mcp.METHOD_NOT_FOUND,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
server := NewMCPServer("test-server", "1.0.0", tt.options...)
response := server.HandleMessage(
context.Background(),
[]byte(tt.message),
Expand Down