Skip to content

Commit 52ee285

Browse files
committed
mcp: add syntax and scheme validation to AddResourceTemplate
1 parent b392875 commit 52ee285

File tree

2 files changed

+50
-1
lines changed

2 files changed

+50
-1
lines changed

mcp/server.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"github.com/modelcontextprotocol/go-sdk/internal/jsonrpc2"
2323
"github.com/modelcontextprotocol/go-sdk/internal/util"
2424
"github.com/modelcontextprotocol/go-sdk/jsonrpc"
25+
"github.com/yosida95/uritemplate/v3"
2526
)
2627

2728
const DefaultPageSize = 1000
@@ -220,7 +221,19 @@ func (s *Server) RemoveResources(uris ...string) {
220221
func (s *Server) AddResourceTemplate(t *ResourceTemplate, h ResourceHandler) {
221222
s.changeAndNotify(notificationResourceListChanged, &ResourceListChangedParams{},
222223
func() bool {
223-
// TODO: check template validity.
224+
// Validate the URI template syntax
225+
_, err := uritemplate.New(t.URITemplate)
226+
if err != nil {
227+
panic(fmt.Errorf("URI template %s is invalid: %v", t.URITemplate, err))
228+
}
229+
// Ensure the URI template has a valid scheme
230+
u, err := url.Parse(t.URITemplate)
231+
if err != nil {
232+
panic(err) // url.Parse includes the URI in the error
233+
}
234+
if !u.IsAbs() {
235+
panic(fmt.Errorf("URI template %s needs a scheme", t.URITemplate))
236+
}
224237
s.resourceTemplates.add(&serverResourceTemplate{t, h})
225238
return true
226239
})

mcp/server_test.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,3 +371,39 @@ func TestServerCapabilities(t *testing.T) {
371371
})
372372
}
373373
}
374+
375+
func TestServerAddResourceTemplate(t *testing.T) {
376+
tests := []struct {
377+
name string
378+
template string
379+
expectPanic bool
380+
}{
381+
{"ValidFileTemplate", "file:///{a}/{b}", false},
382+
{"ValidCustomScheme", "myproto:///{a}", false},
383+
{"MissingScheme1", "://example.com/{path}", true},
384+
{"MissingScheme2", "/api/v1/users/{id}", true},
385+
{"EmptyVariable", "file:///{}/{b}", true},
386+
{"UnclosedVariable", "file:///{a", true},
387+
}
388+
389+
for _, tt := range tests {
390+
t.Run(tt.name, func(t *testing.T) {
391+
rt := ResourceTemplate{URITemplate: tt.template}
392+
393+
defer func() {
394+
if r := recover(); r != nil {
395+
if !tt.expectPanic {
396+
t.Errorf("%s: unexpected panic: %v", tt.name, r)
397+
}
398+
} else {
399+
if tt.expectPanic {
400+
t.Errorf("%s: expected panic but did not panic", tt.name)
401+
}
402+
}
403+
}()
404+
405+
s := NewServer(testImpl, nil)
406+
s.AddResourceTemplate(&rt, nil)
407+
})
408+
}
409+
}

0 commit comments

Comments
 (0)