Skip to content

Commit caa195b

Browse files
authored
Add tool capabilities to serverCapabilities (#36)
* Add tool capabilities to MCPServer structure Signed-off-by: edmondfrank <[email protected]> * Update capabilities handling and test cases for tools, prompts, and resources Signed-off-by: edmondfrank <[email protected]> * Alter default capabilities for server and related tests. Signed-off-by: edmondfrank <[email protected]> --------- Signed-off-by: edmondfrank <[email protected]>
1 parent 78f35a5 commit caa195b

File tree

3 files changed

+84
-17
lines changed

3 files changed

+84
-17
lines changed

client/sse_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ func TestSSEMCPClient(t *testing.T) {
1616
"1.0.0",
1717
server.WithResourceCapabilities(true, true),
1818
server.WithPromptCapabilities(true),
19+
server.WithToolCapabilities(true),
1920
)
2021

2122
// Add a test tool

server/server.go

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ func (s *MCPServer) SendNotificationToClient(
138138

139139
// serverCapabilities defines the supported features of the MCP server
140140
type serverCapabilities struct {
141+
tools *toolCapabilities
141142
resources *resourceCapabilities
142143
prompts *promptCapabilities
143144
logging bool
@@ -154,6 +155,11 @@ type promptCapabilities struct {
154155
listChanged bool
155156
}
156157

158+
// toolCapabilities defines the supported tool-related features
159+
type toolCapabilities struct {
160+
listChanged bool
161+
}
162+
157163
// WithResourceCapabilities configures resource-related server capabilities
158164
func WithResourceCapabilities(subscribe, listChanged bool) ServerOption {
159165
return func(s *MCPServer) {
@@ -173,6 +179,15 @@ func WithPromptCapabilities(listChanged bool) ServerOption {
173179
}
174180
}
175181

182+
// WithToolCapabilities configures tool-related server capabilities
183+
func WithToolCapabilities(listChanged bool) ServerOption {
184+
return func(s *MCPServer) {
185+
s.capabilities.tools = &toolCapabilities{
186+
listChanged: listChanged,
187+
}
188+
}
189+
}
190+
176191
// WithLogging enables logging capabilities for the server
177192
func WithLogging() ServerOption {
178193
return func(s *MCPServer) {
@@ -195,6 +210,12 @@ func NewMCPServer(
195210
version: version,
196211
notificationHandlers: make(map[string]NotificationHandlerFunc),
197212
notifications: make(chan ServerNotification, 100),
213+
capabilities: serverCapabilities{
214+
tools: &toolCapabilities{},
215+
resources: &resourceCapabilities{},
216+
prompts: &promptCapabilities{},
217+
logging: false,
218+
},
198219
}
199220

200221
for _, opt := range opts {
@@ -304,7 +325,7 @@ func (s *MCPServer) HandleMessage(
304325
}
305326
return s.handleListResourceTemplates(ctx, baseMessage.ID, request)
306327
case "resources/read":
307-
if s.capabilities.resources == nil {
328+
if !s.capabilities.resources.listChanged {
308329
return createErrorResponse(
309330
baseMessage.ID,
310331
mcp.METHOD_NOT_FOUND,
@@ -338,7 +359,7 @@ func (s *MCPServer) HandleMessage(
338359
}
339360
return s.handleListPrompts(ctx, baseMessage.ID, request)
340361
case "prompts/get":
341-
if s.capabilities.prompts == nil {
362+
if !s.capabilities.prompts.listChanged {
342363
return createErrorResponse(
343364
baseMessage.ID,
344365
mcp.METHOD_NOT_FOUND,
@@ -372,7 +393,7 @@ func (s *MCPServer) HandleMessage(
372393
}
373394
return s.handleListTools(ctx, baseMessage.ID, request)
374395
case "tools/call":
375-
if len(s.tools) == 0 {
396+
if !s.capabilities.tools.listChanged || len(s.tools) == 0 {
376397
return createErrorResponse(
377398
baseMessage.ID,
378399
mcp.METHOD_NOT_FOUND,
@@ -508,20 +529,20 @@ func (s *MCPServer) handleInitialize(
508529
Subscribe bool `json:"subscribe,omitempty"`
509530
ListChanged bool `json:"listChanged,omitempty"`
510531
}{
511-
Subscribe: false,
512-
ListChanged: true,
532+
Subscribe: s.capabilities.resources.subscribe,
533+
ListChanged: s.capabilities.resources.listChanged,
513534
}
514535

515536
capabilities.Prompts = &struct {
516537
ListChanged bool `json:"listChanged,omitempty"`
517538
}{
518-
ListChanged: true,
539+
ListChanged: s.capabilities.prompts.listChanged,
519540
}
520541

521542
capabilities.Tools = &struct {
522543
ListChanged bool `json:"listChanged,omitempty"`
523544
}{
524-
ListChanged: true,
545+
ListChanged: s.capabilities.tools.listChanged,
525546
}
526547

527548
if s.capabilities.logging {

server/server_test.go

Lines changed: 55 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,11 @@ func TestMCPServer_Capabilities(t *testing.T) {
4242
assert.Equal(t, "1.0.0", initResult.ServerInfo.Version)
4343
assert.NotNil(t, initResult.Capabilities.Resources)
4444
assert.False(t, initResult.Capabilities.Resources.Subscribe)
45-
assert.True(t, initResult.Capabilities.Resources.ListChanged)
45+
assert.False(t, initResult.Capabilities.Resources.ListChanged)
4646
assert.NotNil(t, initResult.Capabilities.Prompts)
47-
assert.True(t, initResult.Capabilities.Prompts.ListChanged)
47+
assert.False(t, initResult.Capabilities.Prompts.ListChanged)
4848
assert.NotNil(t, initResult.Capabilities.Tools)
49-
assert.True(t, initResult.Capabilities.Tools.ListChanged)
49+
assert.False(t, initResult.Capabilities.Tools.ListChanged)
5050
assert.Nil(t, initResult.Capabilities.Logging)
5151
},
5252
},
@@ -55,6 +55,7 @@ func TestMCPServer_Capabilities(t *testing.T) {
5555
options: []ServerOption{
5656
WithResourceCapabilities(true, true),
5757
WithPromptCapabilities(true),
58+
WithToolCapabilities(true),
5859
WithLogging(),
5960
},
6061
validate: func(t *testing.T, response mcp.JSONRPCMessage) {
@@ -73,8 +74,8 @@ func TestMCPServer_Capabilities(t *testing.T) {
7374
assert.Equal(t, "1.0.0", initResult.ServerInfo.Version)
7475

7576
assert.NotNil(t, initResult.Capabilities.Resources)
76-
// Resources capabilities are now always false for subscribe and true for listChanged
77-
assert.False(t, initResult.Capabilities.Resources.Subscribe)
77+
78+
assert.True(t, initResult.Capabilities.Resources.Subscribe)
7879
assert.True(t, initResult.Capabilities.Resources.ListChanged)
7980

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

87+
assert.NotNil(t, initResult.Capabilities.Logging)
88+
},
89+
},
90+
{
91+
name: "Specific capabilities",
92+
options: []ServerOption{
93+
WithResourceCapabilities(true, false),
94+
WithPromptCapabilities(true),
95+
WithToolCapabilities(false),
96+
WithLogging(),
97+
},
98+
validate: func(t *testing.T, response mcp.JSONRPCMessage) {
99+
resp, ok := response.(mcp.JSONRPCResponse)
100+
assert.True(t, ok)
101+
102+
initResult, ok := resp.Result.(mcp.InitializeResult)
103+
assert.True(t, ok)
104+
105+
assert.Equal(
106+
t,
107+
mcp.LATEST_PROTOCOL_VERSION,
108+
initResult.ProtocolVersion,
109+
)
110+
assert.Equal(t, "test-server", initResult.ServerInfo.Name)
111+
assert.Equal(t, "1.0.0", initResult.ServerInfo.Version)
112+
113+
assert.NotNil(t, initResult.Capabilities.Resources)
114+
115+
assert.True(t, initResult.Capabilities.Resources.Subscribe)
116+
assert.False(t, initResult.Capabilities.Resources.ListChanged)
117+
118+
assert.NotNil(t, initResult.Capabilities.Prompts)
119+
assert.True(t, initResult.Capabilities.Prompts.ListChanged)
120+
121+
assert.NotNil(t, initResult.Capabilities.Tools)
122+
assert.False(t, initResult.Capabilities.Tools.ListChanged)
123+
86124
assert.NotNil(t, initResult.Capabilities.Logging)
87125
},
88126
},
@@ -525,6 +563,7 @@ func TestMCPServer_HandleUndefinedHandlers(t *testing.T) {
525563
server := NewMCPServer("test-server", "1.0.0",
526564
WithResourceCapabilities(true, true),
527565
WithPromptCapabilities(true),
566+
WithToolCapabilities(true),
528567
)
529568

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

602641
func TestMCPServer_HandleMethodsWithoutCapabilities(t *testing.T) {
603-
server := NewMCPServer(
604-
"test-server",
605-
"1.0.0",
606-
)
607-
608642
tests := []struct {
609643
name string
610644
message string
645+
options []ServerOption
611646
expectedErr int
612647
}{
613648
{
@@ -620,6 +655,9 @@ func TestMCPServer_HandleMethodsWithoutCapabilities(t *testing.T) {
620655
"name": "test-tool"
621656
}
622657
}`,
658+
options: []ServerOption{
659+
WithToolCapabilities(false),
660+
},
623661
expectedErr: mcp.METHOD_NOT_FOUND,
624662
},
625663
{
@@ -632,6 +670,9 @@ func TestMCPServer_HandleMethodsWithoutCapabilities(t *testing.T) {
632670
"name": "test-prompt"
633671
}
634672
}`,
673+
options: []ServerOption{
674+
WithPromptCapabilities(false),
675+
},
635676
expectedErr: mcp.METHOD_NOT_FOUND,
636677
},
637678
{
@@ -644,12 +685,16 @@ func TestMCPServer_HandleMethodsWithoutCapabilities(t *testing.T) {
644685
"uri": "test-resource"
645686
}
646687
}`,
688+
options: []ServerOption{
689+
WithResourceCapabilities(false, false),
690+
},
647691
expectedErr: mcp.METHOD_NOT_FOUND,
648692
},
649693
}
650694

651695
for _, tt := range tests {
652696
t.Run(tt.name, func(t *testing.T) {
697+
server := NewMCPServer("test-server", "1.0.0", tt.options...)
653698
response := server.HandleMessage(
654699
context.Background(),
655700
[]byte(tt.message),

0 commit comments

Comments
 (0)