Skip to content

Commit 2bf58b2

Browse files
authored
Fix tool calls (#38)
* set all capabilities to off and auto enable if items are added * update checks and tests
1 parent caa195b commit 2bf58b2

File tree

2 files changed

+69
-41
lines changed

2 files changed

+69
-41
lines changed

server/server.go

+55-31
Original file line numberDiff line numberDiff line change
@@ -163,27 +163,39 @@ type toolCapabilities struct {
163163
// WithResourceCapabilities configures resource-related server capabilities
164164
func WithResourceCapabilities(subscribe, listChanged bool) ServerOption {
165165
return func(s *MCPServer) {
166-
s.capabilities.resources = &resourceCapabilities{
167-
subscribe: subscribe,
168-
listChanged: listChanged,
166+
if !subscribe && !listChanged {
167+
s.capabilities.resources = nil
168+
} else {
169+
s.capabilities.resources = &resourceCapabilities{
170+
subscribe: subscribe,
171+
listChanged: listChanged,
172+
}
169173
}
170174
}
171175
}
172176

173177
// WithPromptCapabilities configures prompt-related server capabilities
174178
func WithPromptCapabilities(listChanged bool) ServerOption {
175179
return func(s *MCPServer) {
176-
s.capabilities.prompts = &promptCapabilities{
177-
listChanged: listChanged,
180+
if !listChanged {
181+
s.capabilities.prompts = nil
182+
} else {
183+
s.capabilities.prompts = &promptCapabilities{
184+
listChanged: listChanged,
185+
}
178186
}
179187
}
180188
}
181189

182190
// WithToolCapabilities configures tool-related server capabilities
183191
func WithToolCapabilities(listChanged bool) ServerOption {
184192
return func(s *MCPServer) {
185-
s.capabilities.tools = &toolCapabilities{
186-
listChanged: listChanged,
193+
if !listChanged {
194+
s.capabilities.tools = nil
195+
} else {
196+
s.capabilities.tools = &toolCapabilities{
197+
listChanged: listChanged,
198+
}
187199
}
188200
}
189201
}
@@ -211,9 +223,9 @@ func NewMCPServer(
211223
notificationHandlers: make(map[string]NotificationHandlerFunc),
212224
notifications: make(chan ServerNotification, 100),
213225
capabilities: serverCapabilities{
214-
tools: &toolCapabilities{},
215-
resources: &resourceCapabilities{},
216-
prompts: &promptCapabilities{},
226+
tools: nil,
227+
resources: nil,
228+
prompts: nil,
217229
logging: false,
218230
},
219231
}
@@ -325,7 +337,7 @@ func (s *MCPServer) HandleMessage(
325337
}
326338
return s.handleListResourceTemplates(ctx, baseMessage.ID, request)
327339
case "resources/read":
328-
if !s.capabilities.resources.listChanged {
340+
if s.capabilities.resources == nil {
329341
return createErrorResponse(
330342
baseMessage.ID,
331343
mcp.METHOD_NOT_FOUND,
@@ -359,7 +371,7 @@ func (s *MCPServer) HandleMessage(
359371
}
360372
return s.handleListPrompts(ctx, baseMessage.ID, request)
361373
case "prompts/get":
362-
if !s.capabilities.prompts.listChanged {
374+
if s.capabilities.prompts == nil {
363375
return createErrorResponse(
364376
baseMessage.ID,
365377
mcp.METHOD_NOT_FOUND,
@@ -376,7 +388,7 @@ func (s *MCPServer) HandleMessage(
376388
}
377389
return s.handleGetPrompt(ctx, baseMessage.ID, request)
378390
case "tools/list":
379-
if len(s.tools) == 0 {
391+
if s.capabilities.tools == nil {
380392
return createErrorResponse(
381393
baseMessage.ID,
382394
mcp.METHOD_NOT_FOUND,
@@ -393,7 +405,7 @@ func (s *MCPServer) HandleMessage(
393405
}
394406
return s.handleListTools(ctx, baseMessage.ID, request)
395407
case "tools/call":
396-
if !s.capabilities.tools.listChanged || len(s.tools) == 0 {
408+
if s.capabilities.tools == nil {
397409
return createErrorResponse(
398410
baseMessage.ID,
399411
mcp.METHOD_NOT_FOUND,
@@ -424,7 +436,7 @@ func (s *MCPServer) AddResource(
424436
handler ResourceHandlerFunc,
425437
) {
426438
if s.capabilities.resources == nil {
427-
panic("Resource capabilities not enabled")
439+
s.capabilities.resources = &resourceCapabilities{}
428440
}
429441
s.mu.Lock()
430442
defer s.mu.Unlock()
@@ -440,7 +452,7 @@ func (s *MCPServer) AddResourceTemplate(
440452
handler ResourceTemplateHandlerFunc,
441453
) {
442454
if s.capabilities.resources == nil {
443-
panic("Resource capabilities not enabled")
455+
s.capabilities.resources = &resourceCapabilities{}
444456
}
445457
s.mu.Lock()
446458
defer s.mu.Unlock()
@@ -453,7 +465,7 @@ func (s *MCPServer) AddResourceTemplate(
453465
// AddPrompt registers a new prompt handler with the given name
454466
func (s *MCPServer) AddPrompt(prompt mcp.Prompt, handler PromptHandlerFunc) {
455467
if s.capabilities.prompts == nil {
456-
panic("Prompt capabilities not enabled")
468+
s.capabilities.prompts = &promptCapabilities{}
457469
}
458470
s.mu.Lock()
459471
defer s.mu.Unlock()
@@ -468,6 +480,9 @@ func (s *MCPServer) AddTool(tool mcp.Tool, handler ToolHandlerFunc) {
468480

469481
// AddTools registers multiple tools at once
470482
func (s *MCPServer) AddTools(tools ...ServerTool) {
483+
if s.capabilities.tools == nil {
484+
s.capabilities.tools = &toolCapabilities{}
485+
}
471486
s.mu.Lock()
472487
for _, entry := range tools {
473488
s.tools[entry.Tool.Name] = entry
@@ -525,24 +540,33 @@ func (s *MCPServer) handleInitialize(
525540
) mcp.JSONRPCMessage {
526541
capabilities := mcp.ServerCapabilities{}
527542

528-
capabilities.Resources = &struct {
529-
Subscribe bool `json:"subscribe,omitempty"`
530-
ListChanged bool `json:"listChanged,omitempty"`
531-
}{
532-
Subscribe: s.capabilities.resources.subscribe,
533-
ListChanged: s.capabilities.resources.listChanged,
543+
// Only add resource capabilities if they're configured
544+
if s.capabilities.resources != nil {
545+
capabilities.Resources = &struct {
546+
Subscribe bool `json:"subscribe,omitempty"`
547+
ListChanged bool `json:"listChanged,omitempty"`
548+
}{
549+
Subscribe: s.capabilities.resources.subscribe,
550+
ListChanged: s.capabilities.resources.listChanged,
551+
}
534552
}
535553

536-
capabilities.Prompts = &struct {
537-
ListChanged bool `json:"listChanged,omitempty"`
538-
}{
539-
ListChanged: s.capabilities.prompts.listChanged,
554+
// Only add prompt capabilities if they're configured
555+
if s.capabilities.prompts != nil {
556+
capabilities.Prompts = &struct {
557+
ListChanged bool `json:"listChanged,omitempty"`
558+
}{
559+
ListChanged: s.capabilities.prompts.listChanged,
560+
}
540561
}
541562

542-
capabilities.Tools = &struct {
543-
ListChanged bool `json:"listChanged,omitempty"`
544-
}{
545-
ListChanged: s.capabilities.tools.listChanged,
563+
// Only add tool capabilities if they're configured
564+
if s.capabilities.tools != nil {
565+
capabilities.Tools = &struct {
566+
ListChanged bool `json:"listChanged,omitempty"`
567+
}{
568+
ListChanged: s.capabilities.tools.listChanged,
569+
}
546570
}
547571

548572
if s.capabilities.logging {

server/server_test.go

+14-10
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,9 @@ func TestMCPServer_Capabilities(t *testing.T) {
4040
)
4141
assert.Equal(t, "test-server", initResult.ServerInfo.Name)
4242
assert.Equal(t, "1.0.0", initResult.ServerInfo.Version)
43-
assert.NotNil(t, initResult.Capabilities.Resources)
44-
assert.False(t, initResult.Capabilities.Resources.Subscribe)
45-
assert.False(t, initResult.Capabilities.Resources.ListChanged)
46-
assert.NotNil(t, initResult.Capabilities.Prompts)
47-
assert.False(t, initResult.Capabilities.Prompts.ListChanged)
48-
assert.NotNil(t, initResult.Capabilities.Tools)
49-
assert.False(t, initResult.Capabilities.Tools.ListChanged)
43+
assert.Nil(t, initResult.Capabilities.Resources)
44+
assert.Nil(t, initResult.Capabilities.Prompts)
45+
assert.Nil(t, initResult.Capabilities.Tools)
5046
assert.Nil(t, initResult.Capabilities.Logging)
5147
},
5248
},
@@ -118,8 +114,8 @@ func TestMCPServer_Capabilities(t *testing.T) {
118114
assert.NotNil(t, initResult.Capabilities.Prompts)
119115
assert.True(t, initResult.Capabilities.Prompts.ListChanged)
120116

121-
assert.NotNil(t, initResult.Capabilities.Tools)
122-
assert.False(t, initResult.Capabilities.Tools.ListChanged)
117+
// Tools capability should be nil when WithToolCapabilities(false) is used
118+
assert.Nil(t, initResult.Capabilities.Tools)
123119

124120
assert.NotNil(t, initResult.Capabilities.Logging)
125121
},
@@ -210,7 +206,15 @@ func TestMCPServer_Tools(t *testing.T) {
210206
assert.Equal(t, "notifications/tools/list_changed", notifications[0].Notification.Method)
211207
// One for DeleteTools
212208
assert.Equal(t, "notifications/tools/list_changed", notifications[1].Notification.Method)
213-
assert.Equal(t, "Tools not supported", toolsList.(mcp.JSONRPCError).Error.Message)
209+
210+
// Expect a successful response with an empty list of tools
211+
resp, ok := toolsList.(mcp.JSONRPCResponse)
212+
assert.True(t, ok, "Expected JSONRPCResponse, got %T", toolsList)
213+
214+
result, ok := resp.Result.(mcp.ListToolsResult)
215+
assert.True(t, ok, "Expected ListToolsResult, got %T", resp.Result)
216+
217+
assert.Empty(t, result.Tools, "Expected empty tools list")
214218
},
215219
},
216220
}

0 commit comments

Comments
 (0)