Skip to content

Commit 2835a17

Browse files
committed
internal/mcp: design.md: adjust resource design
Add handlers for reading resources. Change-Id: Ibe0553a02c6fa2b5159cc406c7c4481984b1a460 Reviewed-on: https://go-review.googlesource.com/c/tools/+/671357 Reviewed-by: Robert Findley <[email protected]> TryBot-Bypass: Jonathan Amsterdam <[email protected]>
1 parent 7b959ff commit 2835a17

File tree

1 file changed

+39
-5
lines changed

1 file changed

+39
-5
lines changed

internal/mcp/design/design.md

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -852,24 +852,58 @@ handler to a Go function using reflection to derive its arguments. We provide
852852
853853
### Resources and resource templates
854854
855-
Servers have Add and Remove methods for resources and resource templates:
855+
To add a resource or resource template to a server, users call the `AddResource` and
856+
`AddResourceTemplate` methods, passing the resource or template and a function for reading it:
857+
```go
858+
type ReadResourceHandler func(context.Context, *ServerSession, *Resource, *ReadResourceParams) (*ReadResourceResult, error)
859+
860+
func (*Server) AddResource(*Resource, ReadResourceHandler)
861+
func (*Server) AddResourceTemplate(*ResourceTemplate, ReadResourceHandler)
862+
```
863+
The `Resource` is passed to the reader function even though it is redundant (the function could have closed over it)
864+
so a single handler can support multiple resources.
865+
If the incoming resource matches a template, a `Resource` argument is constructed
866+
from the fields in the `ResourceTemplate`.
867+
The `ServerSession` argument is there so the reader can observe the client's roots.
856868
869+
To read files from the local filesystem, we recommend using `FileReadResourceHandler` to construct a handler:
857870
```go
858-
func (*Server) AddResources(resources ...*Resource)
859-
func (*Server) RemoveResources(names ...string)
860-
func (*Server) AddResourceTemplates(templates...*ResourceTemplate)
871+
// FileReadResourceHandler returns a ReadResourceHandler that reads paths using dir as a root directory.
872+
// It protects against path traversal attacks.
873+
// It will not read any file that is not in the root set of the client requesting the resource.
874+
func (*Server) FileReadResourceHandler(dir string) ReadResourceHandler
875+
```
876+
It guards against [path traversal attacks](https://go.dev/blog/osroot)
877+
and observes the client's roots.
878+
Here is an example:
879+
```go
880+
// Safely read "/public/puppies.txt".
881+
s.AddResource(
882+
&mcp.Resource{URI: "file:///puppies.txt"},
883+
s.FileReadResourceHandler("/public"))
884+
```
885+
886+
There are also server methods to remove resources and resource templates.
887+
```go
888+
func (*Server) RemoveResources(uris ...string)
861889
func (*Server) RemoveResourceTemplates(names ...string)
862890
```
891+
Resource templates don't have unique identifiers, so removing a name will remove all
892+
resource templates with that name.
863893
864894
Clients call `ListResources` to list the available resources, `ReadResource` to read
865895
one of them, and `ListResourceTemplates` to list the templates:
866896
867897
```go
868898
func (*ClientSession) ListResources(context.Context, *ListResourcesParams) (*ListResourcesResult, error)
869-
func (*ClientSession) ReadResource(context.Context, *ReadResourceParams) (*ReadResourceResult, error)
870899
func (*ClientSession) ListResourceTemplates(context.Context, *ListResourceTemplatesParams) (*ListResourceTemplatesResult, error)
900+
func (*ClientSession) ReadResource(context.Context, *ReadResourceParams) (*ReadResourceResult, error)
871901
```
872902
903+
`ReadResource` checks the incoming URI against the server's list of
904+
resources and resource templates to make sure it matches one of them,
905+
then returns the result of calling the associated reader function.
906+
873907
<!-- TODO: subscriptions -->
874908
875909
### ListChanged notifications

0 commit comments

Comments
 (0)