diff --git a/docs/server.md b/docs/server.md index 3de5c12..e16f2fc 100644 --- a/docs/server.md +++ b/docs/server.md @@ -109,7 +109,129 @@ func Example_prompts() { ## Resources - +In MCP terms, a _resource_ is some data referenced by a URI. +MCP servers can serve resources to clients. +They can register resources individually, or register a _resource template_ +that uses a URI pattern to describe a collection of resources. + + +**Client-side**: +Call [`ClientSession.ReadResource`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession.ReadResource) +to read a resource. +The SDK ensures that a read succeeds only if the URI matches a registered resource exactly, +or matches the URI pattern of a resource template. + +To list a server's resources and resource templates, use the +[`ClientSession.Resources`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession.Resources) +and +[`ClientSession.ResourceTemplates`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession.ResourceTemplates) +iterators, or the lower-level `ListXXX` calls (see [pagination](#pagination)). +Set +[`ClientOptions.ResourceListChangedHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientOptions.ResourceListChangedHandler) +to be notified of changes in the lists of resources or resource templates. + +Clients can be notified when the contents of a resource changes by subscribing to the resource's URI. +Call +[`ClientSession.Subscribe`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession.Subscribe) +to subscribe to a resource +and +[`ClientSession.Unsubscribe`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession.Unsubscribe) +to unsubscribe. +Set +[`ClientOptions.ResourceUpdatedHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientOptions.ResourceUpdatedHandler) +to be notified of changes to subscribed resources. + +**Server-side**: +Use +[`Server.AddResource`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#Server.AddResource) +or +[`Server.AddResourceTemplate`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#Server.AddResourceTemplate) +to add a resource or resource template to the server along with its handler. +A +[`ResourceHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ResourceHandler) +maps a URI to the contents of a resource, which can include text, binary data, +or both. +If `AddResource` or `AddResourceTemplate` is called before a server is connected, the server will have the +`resources` capability. +The server will have the `resources` capability if any resource or resource template is added before the +server is connected to a client, or if +[`ServerOptions.HasResources`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerOptions.HasResources) +is explicitly set. When a prompt is added, any clients already connected to the +server will be notified via a `notifications/resources/list_changed` +notification. + + +```go +func Example_resources() { + ctx := context.Background() + + resources := map[string]string{ + "file:///a": "a", + "file:///dir/x": "x", + "file:///dir/y": "y", + } + + handler := func(_ context.Context, req *mcp.ReadResourceRequest) (*mcp.ReadResourceResult, error) { + uri := req.Params.URI + c, ok := resources[uri] + if !ok { + return nil, mcp.ResourceNotFoundError(uri) + } + return &mcp.ReadResourceResult{ + Contents: []*mcp.ResourceContents{{URI: uri, Text: c}}, + }, nil + } + + // Create a server with a single resource. + s := mcp.NewServer(&mcp.Implementation{Name: "server", Version: "v0.0.1"}, nil) + s.AddResource(&mcp.Resource{URI: "file:///a"}, handler) + s.AddResourceTemplate(&mcp.ResourceTemplate{URITemplate: "file:///dir/{f}"}, handler) + + // Create a client. + c := mcp.NewClient(&mcp.Implementation{Name: "client", Version: "v0.0.1"}, nil) + + // Connect the server and client. + t1, t2 := mcp.NewInMemoryTransports() + if _, err := s.Connect(ctx, t1, nil); err != nil { + log.Fatal(err) + } + cs, err := c.Connect(ctx, t2, nil) + if err != nil { + log.Fatal(err) + } + defer cs.Close() + + // List resources and resource templates. + for r, err := range cs.Resources(ctx, nil) { + if err != nil { + log.Fatal(err) + } + fmt.Println(r.URI) + } + for r, err := range cs.ResourceTemplates(ctx, nil) { + if err != nil { + log.Fatal(err) + } + fmt.Println(r.URITemplate) + } + + // Read resources. + for _, path := range []string{"a", "dir/x", "b"} { + res, err := cs.ReadResource(ctx, &mcp.ReadResourceParams{URI: "file:///" + path}) + if err != nil { + fmt.Println(err) + } else { + fmt.Println(res.Contents[0].Text) + } + } + // Output: + // file:///a + // file:///dir/{f} + // a + // x + // calling "resources/read": Resource not found +} +``` ## Tools diff --git a/internal/docs/server.src.md b/internal/docs/server.src.md index 0ef476c..345036b 100644 --- a/internal/docs/server.src.md +++ b/internal/docs/server.src.md @@ -35,7 +35,59 @@ notification. ## Resources - +In MCP terms, a _resource_ is some data referenced by a URI. +MCP servers can serve resources to clients. +They can register resources individually, or register a _resource template_ +that uses a URI pattern to describe a collection of resources. + + +**Client-side**: +Call [`ClientSession.ReadResource`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession.ReadResource) +to read a resource. +The SDK ensures that a read succeeds only if the URI matches a registered resource exactly, +or matches the URI pattern of a resource template. + +To list a server's resources and resource templates, use the +[`ClientSession.Resources`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession.Resources) +and +[`ClientSession.ResourceTemplates`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession.ResourceTemplates) +iterators, or the lower-level `ListXXX` calls (see [pagination](#pagination)). +Set +[`ClientOptions.ResourceListChangedHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientOptions.ResourceListChangedHandler) +to be notified of changes in the lists of resources or resource templates. + +Clients can be notified when the contents of a resource changes by subscribing to the resource's URI. +Call +[`ClientSession.Subscribe`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession.Subscribe) +to subscribe to a resource +and +[`ClientSession.Unsubscribe`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession.Unsubscribe) +to unsubscribe. +Set +[`ClientOptions.ResourceUpdatedHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientOptions.ResourceUpdatedHandler) +to be notified of changes to subscribed resources. + +**Server-side**: +Use +[`Server.AddResource`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#Server.AddResource) +or +[`Server.AddResourceTemplate`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#Server.AddResourceTemplate) +to add a resource or resource template to the server along with its handler. +A +[`ResourceHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ResourceHandler) +maps a URI to the contents of a resource, which can include text, binary data, +or both. +If `AddResource` or `AddResourceTemplate` is called before a server is connected, the server will have the +`resources` capability. +The server will have the `resources` capability if any resource or resource template is added before the +server is connected to a client, or if +[`ServerOptions.HasResources`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerOptions.HasResources) +is explicitly set. When a prompt is added, any clients already connected to the +server will be notified via a `notifications/resources/list_changed` +notification. + + +%include ../../mcp/server_example_test.go resources - ## Tools diff --git a/mcp/server_example_test.go b/mcp/server_example_test.go index 16f19e2..d9dd168 100644 --- a/mcp/server_example_test.go +++ b/mcp/server_example_test.go @@ -82,3 +82,76 @@ func Example_prompts() { } // !-prompts + +// !+resources +func Example_resources() { + ctx := context.Background() + + resources := map[string]string{ + "file:///a": "a", + "file:///dir/x": "x", + "file:///dir/y": "y", + } + + handler := func(_ context.Context, req *mcp.ReadResourceRequest) (*mcp.ReadResourceResult, error) { + uri := req.Params.URI + c, ok := resources[uri] + if !ok { + return nil, mcp.ResourceNotFoundError(uri) + } + return &mcp.ReadResourceResult{ + Contents: []*mcp.ResourceContents{{URI: uri, Text: c}}, + }, nil + } + + // Create a server with a single resource. + s := mcp.NewServer(&mcp.Implementation{Name: "server", Version: "v0.0.1"}, nil) + s.AddResource(&mcp.Resource{URI: "file:///a"}, handler) + s.AddResourceTemplate(&mcp.ResourceTemplate{URITemplate: "file:///dir/{f}"}, handler) + + // Create a client. + c := mcp.NewClient(&mcp.Implementation{Name: "client", Version: "v0.0.1"}, nil) + + // Connect the server and client. + t1, t2 := mcp.NewInMemoryTransports() + if _, err := s.Connect(ctx, t1, nil); err != nil { + log.Fatal(err) + } + cs, err := c.Connect(ctx, t2, nil) + if err != nil { + log.Fatal(err) + } + defer cs.Close() + + // List resources and resource templates. + for r, err := range cs.Resources(ctx, nil) { + if err != nil { + log.Fatal(err) + } + fmt.Println(r.URI) + } + for r, err := range cs.ResourceTemplates(ctx, nil) { + if err != nil { + log.Fatal(err) + } + fmt.Println(r.URITemplate) + } + + // Read resources. + for _, path := range []string{"a", "dir/x", "b"} { + res, err := cs.ReadResource(ctx, &mcp.ReadResourceParams{URI: "file:///" + path}) + if err != nil { + fmt.Println(err) + } else { + fmt.Println(res.Contents[0].Text) + } + } + // Output: + // file:///a + // file:///dir/{f} + // a + // x + // calling "resources/read": Resource not found +} + +// !-resources