From e8056a1475a91ed57e15182e7ab4b62591774d80 Mon Sep 17 00:00:00 2001 From: Ed Zynda Date: Wed, 5 Mar 2025 22:41:55 +0300 Subject: [PATCH 1/2] set all capabilities to off and auto enable if items are added --- server/server.go | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/server/server.go b/server/server.go index 64ed9529..2ce9b80a 100644 --- a/server/server.go +++ b/server/server.go @@ -211,9 +211,9 @@ func NewMCPServer( notificationHandlers: make(map[string]NotificationHandlerFunc), notifications: make(chan ServerNotification, 100), capabilities: serverCapabilities{ - tools: &toolCapabilities{}, - resources: &resourceCapabilities{}, - prompts: &promptCapabilities{}, + tools: nil, + resources: nil, + prompts: nil, logging: false, }, } @@ -325,7 +325,7 @@ func (s *MCPServer) HandleMessage( } return s.handleListResourceTemplates(ctx, baseMessage.ID, request) case "resources/read": - if !s.capabilities.resources.listChanged { + if s.capabilities.resources == nil { return createErrorResponse( baseMessage.ID, mcp.METHOD_NOT_FOUND, @@ -359,7 +359,7 @@ func (s *MCPServer) HandleMessage( } return s.handleListPrompts(ctx, baseMessage.ID, request) case "prompts/get": - if !s.capabilities.prompts.listChanged { + if s.capabilities.prompts == nil { return createErrorResponse( baseMessage.ID, mcp.METHOD_NOT_FOUND, @@ -393,7 +393,7 @@ func (s *MCPServer) HandleMessage( } return s.handleListTools(ctx, baseMessage.ID, request) case "tools/call": - if !s.capabilities.tools.listChanged || len(s.tools) == 0 { + if s.capabilities.tools == nil { return createErrorResponse( baseMessage.ID, mcp.METHOD_NOT_FOUND, @@ -424,7 +424,7 @@ func (s *MCPServer) AddResource( handler ResourceHandlerFunc, ) { if s.capabilities.resources == nil { - panic("Resource capabilities not enabled") + s.capabilities.resources = &resourceCapabilities{} } s.mu.Lock() defer s.mu.Unlock() @@ -440,7 +440,7 @@ func (s *MCPServer) AddResourceTemplate( handler ResourceTemplateHandlerFunc, ) { if s.capabilities.resources == nil { - panic("Resource capabilities not enabled") + s.capabilities.resources = &resourceCapabilities{} } s.mu.Lock() defer s.mu.Unlock() @@ -453,7 +453,7 @@ func (s *MCPServer) AddResourceTemplate( // AddPrompt registers a new prompt handler with the given name func (s *MCPServer) AddPrompt(prompt mcp.Prompt, handler PromptHandlerFunc) { if s.capabilities.prompts == nil { - panic("Prompt capabilities not enabled") + s.capabilities.prompts = &promptCapabilities{} } s.mu.Lock() defer s.mu.Unlock() @@ -468,6 +468,9 @@ func (s *MCPServer) AddTool(tool mcp.Tool, handler ToolHandlerFunc) { // AddTools registers multiple tools at once func (s *MCPServer) AddTools(tools ...ServerTool) { + if s.capabilities.tools == nil { + s.capabilities.tools = &toolCapabilities{} + } s.mu.Lock() for _, entry := range tools { s.tools[entry.Tool.Name] = entry -- Gitee From eb8d5f0cf6387953dcf8d771ab4aa8ccccc5fb03 Mon Sep 17 00:00:00 2001 From: Ed Zynda Date: Wed, 5 Mar 2025 22:55:13 +0300 Subject: [PATCH 2/2] update checks and tests --- server/server.go | 65 ++++++++++++++++++++++++++++--------------- server/server_test.go | 24 +++++++++------- 2 files changed, 57 insertions(+), 32 deletions(-) diff --git a/server/server.go b/server/server.go index 2ce9b80a..78868668 100644 --- a/server/server.go +++ b/server/server.go @@ -163,9 +163,13 @@ type toolCapabilities struct { // WithResourceCapabilities configures resource-related server capabilities func WithResourceCapabilities(subscribe, listChanged bool) ServerOption { return func(s *MCPServer) { - s.capabilities.resources = &resourceCapabilities{ - subscribe: subscribe, - listChanged: listChanged, + if !subscribe && !listChanged { + s.capabilities.resources = nil + } else { + s.capabilities.resources = &resourceCapabilities{ + subscribe: subscribe, + listChanged: listChanged, + } } } } @@ -173,8 +177,12 @@ func WithResourceCapabilities(subscribe, listChanged bool) ServerOption { // WithPromptCapabilities configures prompt-related server capabilities func WithPromptCapabilities(listChanged bool) ServerOption { return func(s *MCPServer) { - s.capabilities.prompts = &promptCapabilities{ - listChanged: listChanged, + if !listChanged { + s.capabilities.prompts = nil + } else { + s.capabilities.prompts = &promptCapabilities{ + listChanged: listChanged, + } } } } @@ -182,8 +190,12 @@ 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, + if !listChanged { + s.capabilities.tools = nil + } else { + s.capabilities.tools = &toolCapabilities{ + listChanged: listChanged, + } } } } @@ -376,7 +388,7 @@ func (s *MCPServer) HandleMessage( } return s.handleGetPrompt(ctx, baseMessage.ID, request) case "tools/list": - if len(s.tools) == 0 { + if s.capabilities.tools == nil { return createErrorResponse( baseMessage.ID, mcp.METHOD_NOT_FOUND, @@ -528,24 +540,33 @@ func (s *MCPServer) handleInitialize( ) mcp.JSONRPCMessage { capabilities := mcp.ServerCapabilities{} - capabilities.Resources = &struct { - Subscribe bool `json:"subscribe,omitempty"` - ListChanged bool `json:"listChanged,omitempty"` - }{ - Subscribe: s.capabilities.resources.subscribe, - ListChanged: s.capabilities.resources.listChanged, + // Only add resource capabilities if they're configured + if s.capabilities.resources != nil { + capabilities.Resources = &struct { + Subscribe bool `json:"subscribe,omitempty"` + ListChanged bool `json:"listChanged,omitempty"` + }{ + Subscribe: s.capabilities.resources.subscribe, + ListChanged: s.capabilities.resources.listChanged, + } } - capabilities.Prompts = &struct { - ListChanged bool `json:"listChanged,omitempty"` - }{ - ListChanged: s.capabilities.prompts.listChanged, + // Only add prompt capabilities if they're configured + if s.capabilities.prompts != nil { + capabilities.Prompts = &struct { + ListChanged bool `json:"listChanged,omitempty"` + }{ + ListChanged: s.capabilities.prompts.listChanged, + } } - capabilities.Tools = &struct { - ListChanged bool `json:"listChanged,omitempty"` - }{ - ListChanged: s.capabilities.tools.listChanged, + // Only add tool capabilities if they're configured + if s.capabilities.tools != nil { + capabilities.Tools = &struct { + ListChanged bool `json:"listChanged,omitempty"` + }{ + ListChanged: s.capabilities.tools.listChanged, + } } if s.capabilities.logging { diff --git a/server/server_test.go b/server/server_test.go index 4574456a..57943432 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -40,13 +40,9 @@ func TestMCPServer_Capabilities(t *testing.T) { ) assert.Equal(t, "test-server", initResult.ServerInfo.Name) assert.Equal(t, "1.0.0", initResult.ServerInfo.Version) - assert.NotNil(t, initResult.Capabilities.Resources) - assert.False(t, initResult.Capabilities.Resources.Subscribe) - assert.False(t, initResult.Capabilities.Resources.ListChanged) - assert.NotNil(t, initResult.Capabilities.Prompts) - assert.False(t, initResult.Capabilities.Prompts.ListChanged) - assert.NotNil(t, initResult.Capabilities.Tools) - assert.False(t, initResult.Capabilities.Tools.ListChanged) + assert.Nil(t, initResult.Capabilities.Resources) + assert.Nil(t, initResult.Capabilities.Prompts) + assert.Nil(t, initResult.Capabilities.Tools) assert.Nil(t, initResult.Capabilities.Logging) }, }, @@ -118,8 +114,8 @@ func TestMCPServer_Capabilities(t *testing.T) { 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) + // Tools capability should be nil when WithToolCapabilities(false) is used + assert.Nil(t, initResult.Capabilities.Tools) assert.NotNil(t, initResult.Capabilities.Logging) }, @@ -210,7 +206,15 @@ func TestMCPServer_Tools(t *testing.T) { assert.Equal(t, "notifications/tools/list_changed", notifications[0].Notification.Method) // One for DeleteTools assert.Equal(t, "notifications/tools/list_changed", notifications[1].Notification.Method) - assert.Equal(t, "Tools not supported", toolsList.(mcp.JSONRPCError).Error.Message) + + // Expect a successful response with an empty list of tools + resp, ok := toolsList.(mcp.JSONRPCResponse) + assert.True(t, ok, "Expected JSONRPCResponse, got %T", toolsList) + + result, ok := resp.Result.(mcp.ListToolsResult) + assert.True(t, ok, "Expected ListToolsResult, got %T", resp.Result) + + assert.Empty(t, result.Tools, "Expected empty tools list") }, }, } -- Gitee