diff --git a/mcptest/mcptest.go b/mcptest/mcptest.go index bc7ccc0fa..df85753f2 100644 --- a/mcptest/mcptest.go +++ b/mcptest/mcptest.go @@ -23,7 +23,7 @@ type Server struct { tools []server.ServerTool prompts []server.ServerPrompt resources []server.ServerResource - resourceTemplates []ServerResourceTemplate + resourceTemplates []server.ServerResourceTemplate cancel func() @@ -107,22 +107,16 @@ func (s *Server) AddResources(resources ...server.ServerResource) { s.resources = append(s.resources, resources...) } -// ServerResourceTemplate combines a ResourceTemplate with its handler function. -type ServerResourceTemplate struct { - Template mcp.ResourceTemplate - Handler server.ResourceTemplateHandlerFunc -} - // AddResourceTemplate adds a resource template to an unstarted server. func (s *Server) AddResourceTemplate(template mcp.ResourceTemplate, handler server.ResourceTemplateHandlerFunc) { - s.resourceTemplates = append(s.resourceTemplates, ServerResourceTemplate{ + s.resourceTemplates = append(s.resourceTemplates, server.ServerResourceTemplate{ Template: template, Handler: handler, }) } // AddResourceTemplates adds multiple resource templates to an unstarted server. -func (s *Server) AddResourceTemplates(templates ...ServerResourceTemplate) { +func (s *Server) AddResourceTemplates(templates ...server.ServerResourceTemplate) { s.resourceTemplates = append(s.resourceTemplates, templates...) } @@ -142,10 +136,7 @@ func (s *Server) Start(ctx context.Context) error { mcpServer.AddTools(s.tools...) mcpServer.AddPrompts(s.prompts...) mcpServer.AddResources(s.resources...) - - for _, template := range s.resourceTemplates { - mcpServer.AddResourceTemplate(template.Template, template.Handler) - } + mcpServer.AddResourceTemplates(s.resourceTemplates...) logger := log.New(&s.logBuffer, "", 0) diff --git a/server/server.go b/server/server.go index 46e6d9c57..d0bf18453 100644 --- a/server/server.go +++ b/server/server.go @@ -64,6 +64,12 @@ type ServerResource struct { Handler ResourceHandlerFunc } +// ServerResourceTemplate combines a ResourceTemplate with its handler function. +type ServerResourceTemplate struct { + Template mcp.ResourceTemplate + Handler ResourceTemplateHandlerFunc +} + // serverKey is the context key for storing the server instance type serverKey struct{} @@ -360,17 +366,16 @@ func (s *MCPServer) RemoveResource(uri string) { } } -// AddResourceTemplate registers a new resource template and its handler -func (s *MCPServer) AddResourceTemplate( - template mcp.ResourceTemplate, - handler ResourceTemplateHandlerFunc, -) { +// AddResourceTemplates registers multiple resource templates at once +func (s *MCPServer) AddResourceTemplates(resourceTemplates ...ServerResourceTemplate) { s.implicitlyRegisterResourceCapabilities() s.resourcesMu.Lock() - s.resourceTemplates[template.URITemplate.Raw()] = resourceTemplateEntry{ - template: template, - handler: handler, + for _, entry := range resourceTemplates { + s.resourceTemplates[entry.Template.URITemplate.Raw()] = resourceTemplateEntry{ + template: entry.Template, + handler: entry.Handler, + } } s.resourcesMu.Unlock() @@ -381,6 +386,14 @@ func (s *MCPServer) AddResourceTemplate( } } +// AddResourceTemplate registers a new resource template and its handler +func (s *MCPServer) AddResourceTemplate( + template mcp.ResourceTemplate, + handler ResourceTemplateHandlerFunc, +) { + s.AddResourceTemplates(ServerResourceTemplate{Template: template, Handler: handler}) +} + // AddPrompts registers multiple prompts at once func (s *MCPServer) AddPrompts(prompts ...ServerPrompt) { s.implicitlyRegisterPromptCapabilities() diff --git a/server/server_test.go b/server/server_test.go index 1c81d18dd..bd7dd4719 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -7,6 +7,7 @@ import ( "errors" "fmt" "reflect" + "slices" "sort" "testing" "time" @@ -1456,6 +1457,54 @@ func TestMCPServer_ResourceTemplates(t *testing.T) { assert.Equal(t, "text/plain", resultContent.MIMEType) assert.Equal(t, "test content: something", resultContent.Text) }) + + server.AddResourceTemplates( + ServerResourceTemplate{ + Template: mcp.NewResourceTemplate( + "test://test-another-resource-1", + "Another Resource 1", + ), + Handler: func(ctx context.Context, request mcp.ReadResourceRequest) ([]mcp.ResourceContents, error) { + return []mcp.ResourceContents{}, nil + }, + }, + ServerResourceTemplate{ + Template: mcp.NewResourceTemplate( + "test://test-another-resource-2", + "Another Resource 2", + ), + Handler: func(ctx context.Context, request mcp.ReadResourceRequest) ([]mcp.ResourceContents, error) { + return []mcp.ResourceContents{}, nil + }, + }, + ) + + t.Run("Check bulk add resource templates", func(t *testing.T) { + assert.Equal(t, 3, len(server.resourceTemplates)) + }) + + t.Run("Get resource template again", func(t *testing.T) { + response := server.HandleMessage( + context.Background(), + []byte(listMessage), + ) + assert.NotNil(t, response) + + resp, ok := response.(mcp.JSONRPCResponse) + assert.True(t, ok) + listResult, ok := resp.Result.(mcp.ListResourceTemplatesResult) + assert.True(t, ok) + assert.Len(t, listResult.ResourceTemplates, 3) + + // resource templates are stored in a map, so the order is not guaranteed + for _, rt := range listResult.ResourceTemplates { + assert.True(t, slices.Contains([]string{ + "My Resource", + "Another Resource 1", + "Another Resource 2", + }, rt.Name)) + } + }) } func createTestServer() *MCPServer {