mcp: add syntax and scheme validation to AddResourceTemplate (#253)
Add template validation and scheme check in `AddResourceTemplate`.
This commit is contained in:
parent
46ba813860
commit
732b97fdaf
2 changed files with 50 additions and 1 deletions
|
|
@ -22,6 +22,7 @@ import (
|
||||||
"github.com/modelcontextprotocol/go-sdk/internal/jsonrpc2"
|
"github.com/modelcontextprotocol/go-sdk/internal/jsonrpc2"
|
||||||
"github.com/modelcontextprotocol/go-sdk/internal/util"
|
"github.com/modelcontextprotocol/go-sdk/internal/util"
|
||||||
"github.com/modelcontextprotocol/go-sdk/jsonrpc"
|
"github.com/modelcontextprotocol/go-sdk/jsonrpc"
|
||||||
|
"github.com/yosida95/uritemplate/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
const DefaultPageSize = 1000
|
const DefaultPageSize = 1000
|
||||||
|
|
@ -229,7 +230,19 @@ func (s *Server) RemoveResources(uris ...string) {
|
||||||
func (s *Server) AddResourceTemplate(t *ResourceTemplate, h ResourceHandler) {
|
func (s *Server) AddResourceTemplate(t *ResourceTemplate, h ResourceHandler) {
|
||||||
s.changeAndNotify(notificationResourceListChanged, &ResourceListChangedParams{},
|
s.changeAndNotify(notificationResourceListChanged, &ResourceListChangedParams{},
|
||||||
func() bool {
|
func() bool {
|
||||||
// TODO: check template validity.
|
// Validate the URI template syntax
|
||||||
|
_, err := uritemplate.New(t.URITemplate)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("URI template %q is invalid: %w", t.URITemplate, err))
|
||||||
|
}
|
||||||
|
// Ensure the URI template has a valid scheme
|
||||||
|
u, err := url.Parse(t.URITemplate)
|
||||||
|
if err != nil {
|
||||||
|
panic(err) // url.Parse includes the URI in the error
|
||||||
|
}
|
||||||
|
if !u.IsAbs() {
|
||||||
|
panic(fmt.Errorf("URI template %q needs a scheme", t.URITemplate))
|
||||||
|
}
|
||||||
s.resourceTemplates.add(&serverResourceTemplate{t, h})
|
s.resourceTemplates.add(&serverResourceTemplate{t, h})
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -373,6 +373,42 @@ func TestServerCapabilities(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestServerAddResourceTemplate(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
template string
|
||||||
|
expectPanic bool
|
||||||
|
}{
|
||||||
|
{"ValidFileTemplate", "file:///{a}/{b}", false},
|
||||||
|
{"ValidCustomScheme", "myproto:///{a}", false},
|
||||||
|
{"MissingScheme1", "://example.com/{path}", true},
|
||||||
|
{"MissingScheme2", "/api/v1/users/{id}", true},
|
||||||
|
{"EmptyVariable", "file:///{}/{b}", true},
|
||||||
|
{"UnclosedVariable", "file:///{a", true},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
rt := ResourceTemplate{URITemplate: tt.template}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
if !tt.expectPanic {
|
||||||
|
t.Errorf("%s: unexpected panic: %v", tt.name, r)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if tt.expectPanic {
|
||||||
|
t.Errorf("%s: expected panic but did not panic", tt.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
s := NewServer(testImpl, nil)
|
||||||
|
s.AddResourceTemplate(&rt, nil)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TestServerSessionkeepaliveCancelOverwritten is to verify that `ServerSession.keepaliveCancel` is assigned exactly once,
|
// TestServerSessionkeepaliveCancelOverwritten is to verify that `ServerSession.keepaliveCancel` is assigned exactly once,
|
||||||
// ensuring that only a single goroutine is responsible for the session's keepalive ping mechanism.
|
// ensuring that only a single goroutine is responsible for the session's keepalive ping mechanism.
|
||||||
func TestServerSessionkeepaliveCancelOverwritten(t *testing.T) {
|
func TestServerSessionkeepaliveCancelOverwritten(t *testing.T) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue