Skip to content

Commit d5d23eb

Browse files
committed
Handle static and dynamic resources correctly
1 parent 1a6ee6a commit d5d23eb

File tree

3 files changed

+207
-151
lines changed

3 files changed

+207
-151
lines changed

examples/server/everything/stdio/main.go

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,15 @@ func NewMCPServer() *MCPServer {
4747
allResources: generateResources(),
4848
}
4949

50-
s.server.AddResource("test://static/resource/", s.handleReadResource)
50+
s.server.AddResource(mcp.Resource{
51+
URI: "test://static/resource",
52+
Name: "Static Resource",
53+
}, s.handleReadResource)
5154
s.server.AddResourceTemplate(
52-
"test://static/resource/{id}",
55+
mcp.ResourceTemplate{
56+
URITemplate: "test://dynamoc/resource/{id}",
57+
Name: "Dynamic Resource",
58+
},
5359
s.handleResourceTemplate,
5460
)
5561
s.server.AddPrompt(mcp.NewPrompt(string(SIMPLE),
@@ -84,8 +90,11 @@ func NewMCPServer() *MCPServer {
8490
mcp.Required(),
8591
),
8692
), s.handleAddTool)
87-
s.server.AddTool(mcp.NewTool(string(LONG_RUNNING_OPERATION),
88-
mcp.WithDescription("Demonstrates a long running operation with progress updates"),
93+
s.server.AddTool(mcp.NewTool(
94+
string(LONG_RUNNING_OPERATION),
95+
mcp.WithDescription(
96+
"Demonstrates a long running operation with progress updates",
97+
),
8998
mcp.WithNumber("duration",
9099
mcp.Description("Duration of the operation in seconds"),
91100
mcp.DefaultNumber(10),
@@ -95,6 +104,7 @@ func NewMCPServer() *MCPServer {
95104
mcp.DefaultNumber(5),
96105
),
97106
), s.handleLongRunningOperationTool)
107+
98108
// s.server.AddTool(mcp.Tool{
99109
// Name: string(SAMPLE_LLM),
100110
// Description: "Samples from an LLM using MCP's sampling feature",
@@ -167,12 +177,12 @@ func (s *MCPServer) runUpdateInterval() {
167177
}
168178

169179
func (s *MCPServer) handleReadResource(
170-
arguments map[string]interface{},
180+
request mcp.ReadResourceRequest,
171181
) ([]interface{}, error) {
172182
return []interface{}{
173183
mcp.TextResourceContents{
174184
ResourceContents: mcp.ResourceContents{
175-
URI: "test://static/resource/1",
185+
URI: "test://static/resource",
176186
MIMEType: "text/plain",
177187
},
178188
Text: "This is a sample resource",
@@ -181,12 +191,16 @@ func (s *MCPServer) handleReadResource(
181191
}
182192

183193
func (s *MCPServer) handleResourceTemplate(
184-
arguments map[string]interface{},
185-
) (mcp.ResourceTemplate, error) {
186-
return mcp.ResourceTemplate{
187-
URITemplate: "test://static/resource/{id}",
188-
Name: "Static Resource",
189-
Description: "A static resource with a numeric ID",
194+
request mcp.ReadResourceRequest,
195+
) ([]interface{}, error) {
196+
return []interface{}{
197+
mcp.TextResourceContents{
198+
ResourceContents: mcp.ResourceContents{
199+
URI: request.Params.URI,
200+
MIMEType: "text/plain",
201+
},
202+
Text: "This is a sample resource",
203+
},
190204
}, nil
191205
}
192206

server/server.go

Lines changed: 79 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,31 @@ import (
55
"context"
66
"encoding/json"
77
"fmt"
8+
"regexp"
89

910
"github.com/mark3labs/mcp-go/mcp"
1011
)
1112

13+
// resourceEntry holds both a resource and its handler
14+
type resourceEntry struct {
15+
resource mcp.Resource
16+
handler ResourceHandlerFunc
17+
}
18+
19+
// resourceTemplateEntry holds both a template and its handler
20+
type resourceTemplateEntry struct {
21+
template mcp.ResourceTemplate
22+
handler ResourceTemplateHandlerFunc
23+
}
24+
1225
// ServerOption is a function that configures an MCPServer.
1326
type ServerOption func(*MCPServer)
1427

1528
// ResourceHandlerFunc is a function that returns resource contents.
16-
type ResourceHandlerFunc func(arguments map[string]interface{}) ([]interface{}, error)
29+
type ResourceHandlerFunc func(request mcp.ReadResourceRequest) ([]interface{}, error)
1730

1831
// ResourceTemplateHandlerFunc is a function that returns a resource template.
19-
type ResourceTemplateHandlerFunc func(arguments map[string]interface{}) (mcp.ResourceTemplate, error)
32+
type ResourceTemplateHandlerFunc func(request mcp.ReadResourceRequest) ([]interface{}, error)
2033

2134
// PromptHandlerFunc handles prompt requests with given arguments.
2235
type PromptHandlerFunc func(arguments map[string]string) (*mcp.GetPromptResult, error)
@@ -32,8 +45,8 @@ type NotificationHandlerFunc func(notification mcp.JSONRPCNotification)
3245
type MCPServer struct {
3346
name string
3447
version string
35-
resources map[string]ResourceHandlerFunc
36-
resourceTemplates map[string]ResourceTemplateHandlerFunc
48+
resources map[string]resourceEntry
49+
resourceTemplates map[string]resourceTemplateEntry
3750
prompts map[string]mcp.Prompt
3851
promptHandlers map[string]PromptHandlerFunc
3952
tools map[string]mcp.Tool
@@ -92,8 +105,8 @@ func NewMCPServer(
92105
opts ...ServerOption,
93106
) *MCPServer {
94107
s := &MCPServer{
95-
resources: make(map[string]ResourceHandlerFunc),
96-
resourceTemplates: make(map[string]ResourceTemplateHandlerFunc),
108+
resources: make(map[string]resourceEntry),
109+
resourceTemplates: make(map[string]resourceTemplateEntry),
97110
prompts: make(map[string]mcp.Prompt),
98111
promptHandlers: make(map[string]PromptHandlerFunc),
99112
tools: make(map[string]mcp.Tool),
@@ -298,26 +311,34 @@ func (s *MCPServer) HandleMessage(
298311
}
299312
}
300313

301-
// AddResource registers a new resource handler for the given URI
302-
func (s *MCPServer) AddResource(uri string, handler ResourceHandlerFunc) {
314+
// AddResource registers a new resource and its handler
315+
func (s *MCPServer) AddResource(
316+
resource mcp.Resource,
317+
handler ResourceHandlerFunc,
318+
) {
303319
if s.capabilities.resources == nil {
304320
panic("Resource capabilities not enabled")
305321
}
306-
s.resources[uri] = handler
322+
s.resources[resource.URI] = resourceEntry{
323+
resource: resource,
324+
handler: handler,
325+
}
307326
}
308327

309-
// AddResourceTemplate registers a new resource template handler for the given URI template
328+
// AddResourceTemplate registers a new resource template and its handler
310329
func (s *MCPServer) AddResourceTemplate(
311-
uriTemplate string,
330+
template mcp.ResourceTemplate,
312331
handler ResourceTemplateHandlerFunc,
313332
) {
314333
if s.capabilities.resources == nil {
315334
panic("Resource capabilities not enabled")
316335
}
317-
s.resourceTemplates[uriTemplate] = handler
336+
s.resourceTemplates[template.URITemplate] = resourceTemplateEntry{
337+
template: template,
338+
handler: handler,
339+
}
318340
}
319341

320-
321342
// AddPrompt registers a new prompt handler with the given name
322343
func (s *MCPServer) AddPrompt(prompt mcp.Prompt, handler PromptHandlerFunc) {
323344
if s.capabilities.prompts == nil {
@@ -401,10 +422,8 @@ func (s *MCPServer) handleListResources(
401422
request mcp.ListResourcesRequest,
402423
) mcp.JSONRPCMessage {
403424
resources := make([]mcp.Resource, 0, len(s.resources))
404-
for uri := range s.resources {
405-
resources = append(resources, mcp.Resource{
406-
URI: uri,
407-
})
425+
for _, entry := range s.resources {
426+
resources = append(resources, entry.resource)
408427
}
409428

410429
result := mcp.ListResourcesResult{
@@ -419,25 +438,10 @@ func (s *MCPServer) handleListResources(
419438
func (s *MCPServer) handleListResourceTemplates(
420439
id interface{},
421440
request mcp.ListResourceTemplatesRequest,
422-
) mcp.
423-
JSONRPCMessage {
441+
) mcp.JSONRPCMessage {
424442
templates := make([]mcp.ResourceTemplate, 0, len(s.resourceTemplates))
425-
for uriTemplate, handler := range s.resourceTemplates {
426-
template, err := handler(nil)
427-
if err != nil {
428-
return createErrorResponse(
429-
id,
430-
mcp.INTERNAL_ERROR,
431-
fmt.Sprintf(
432-
"Error getting template for %s: %v",
433-
uriTemplate,
434-
err,
435-
),
436-
)
437-
}
438-
439-
template.URITemplate = uriTemplate
440-
templates = append(templates, template)
443+
for _, entry := range s.resourceTemplates {
444+
templates = append(templates, entry.template)
441445
}
442446

443447
result := mcp.ListResourceTemplatesResult{
@@ -453,26 +457,51 @@ func (s *MCPServer) handleReadResource(
453457
id interface{},
454458
request mcp.ReadResourceRequest,
455459
) mcp.JSONRPCMessage {
456-
handler, ok := s.resources[request.Params.URI]
457-
if !ok {
458-
return createErrorResponse(
459-
id,
460-
mcp.INVALID_PARAMS,
461-
fmt.Sprintf("No handler found for resource URI: %s", request.Params.
462-
URI),
463-
)
460+
// First try direct resource handlers
461+
if entry, ok := s.resources[request.Params.URI]; ok {
462+
contents, err := entry.handler(request)
463+
if err != nil {
464+
return createErrorResponse(id, mcp.INTERNAL_ERROR, err.Error())
465+
}
466+
return createResponse(id, mcp.ReadResourceResult{Contents: contents})
464467
}
465468

466-
contents, err := handler(request.Params.Arguments)
467-
if err != nil {
468-
return createErrorResponse(id, mcp.INTERNAL_ERROR, err.Error())
469+
// If no direct handler found, try matching against templates
470+
for uriTemplate, entry := range s.resourceTemplates {
471+
if matchesTemplate(request.Params.URI, uriTemplate) {
472+
contents, err := entry.handler(request)
473+
if err != nil {
474+
return createErrorResponse(id, mcp.INTERNAL_ERROR, err.Error())
475+
}
476+
return createResponse(
477+
id,
478+
mcp.ReadResourceResult{Contents: contents},
479+
)
480+
}
469481
}
470482

471-
result := mcp.ReadResourceResult{
472-
Contents: contents,
473-
}
483+
return createErrorResponse(
484+
id,
485+
mcp.INVALID_PARAMS,
486+
fmt.Sprintf(
487+
"No handler found for resource URI: %s",
488+
request.Params.URI,
489+
),
490+
)
491+
}
474492

475-
return createResponse(id, result)
493+
// matchesTemplate checks if a URI matches a URI template pattern
494+
func matchesTemplate(uri string, template string) bool {
495+
// Convert template into a regex pattern
496+
pattern := template
497+
// Replace {name} with ([^/]+)
498+
pattern = regexp.QuoteMeta(pattern)
499+
pattern = regexp.MustCompile(`\\\{[^}]+\\\}`).
500+
ReplaceAllString(pattern, `([^/]+)`)
501+
pattern = "^" + pattern + "$"
502+
503+
matched, _ := regexp.MatchString(pattern, uri)
504+
return matched
476505
}
477506

478507
func (s *MCPServer) handleListPrompts(

0 commit comments

Comments
 (0)