Skip to content

Resources

Resources are a core primitive in the Model Context Protocol (MCP) that allow servers to expose data and content that can be read by clients and used as context for LLM interactions.

MCP resources is a concept of MCP servers. Server should list resources when requested with method resources/list and retrieve when requested with method resources/read.

Servers also can provide dynamic resources using templates by listing them via resources/templates/list, however this feature is not yet supported in Foxy Contexts.

In Foxy Contexts there are two ways to include resources in your server:

  • using fxctx.NewResource and fxctx.AsResource to define and provide resources
  • using fxctx.NewResourceProvider and fxctx.AsResourceProvider to define and provide resource providers

Approach with resource provider is more flexible and allows to provide resources dynamically, however all such resources would be still included in response for resources/list, in contrasts to concept of templates (which are not yet supported).

NewResource

In order to create new static resource you shall use fxctx.NewResource function.

func NewHelloWorldResource() fxctx.Resource {
    return fxctx.NewResource(
        mcp.Resource{
            Name:        "hello-world",
            Uri:         "hello-world://hello-world",
            MimeType:    Ptr("application/json"),
            Description: Ptr("Hello World Resource"),
            Annotations: &mcp.ResourceAnnotations{
                Audience: []mcp.Role{
                    mcp.RoleAssistant, mcp.RoleUser,
                },
            },
        },
        func(uri string) (*mcp.ReadResourceResult, error) {
            return &mcp.ReadResourceResult{
                Contents: []interface{}{
                    mcp.TextResourceContents{
                        MimeType: Ptr("application/json"),
                        Text:     `{"hello": "world"}`,
                        Uri:      uri,
                    },
                },
            }, nil
        },
    )
},

Provide resource to fx

Now in order to let MCP server know about this resource you would need to provide it to fx when creating fx app like this:

fx.New(
    // other registered stuff ...
    fx.Provide(fxctx.AsResource(NewHelloWorldResource)),
    // and more ...
)

fxctx.AsResource is a helper function that wraps your resource into fx DI container and adds a certain tags, that would be used later to find all resources and register them in MCP server.

Head further to ResourceMux to see how to register such resource in MCP server, or see next section to learn how to provide dynamic resources.

NewResourceProvider

In order to create new resource provider that would be returning resources dynamically, you shall use fxctx.NewResourceProvider function. It would then take two functions - one in order to list resources and another to read them.

func NewGreatResourceProvider() fxctx.ResourceProvider {
    return fxctx.NewResourceProvider(
        // This is the callback that would be executed when the resources/list is requested:
        func() ([]mcp.Resource, error) {
            return []mcp.Resource{
                {
                    Name: "my-great-resource-one",
                    Description: Ptr("Does something great"),
                    Uri: "/resources/great/one",
                },
            }, nil
        },
        //  This function reads the resource for a given uri to run when resources/read is requested:
        func(uri string) (*mcp.ReadResourceResult, error) {

            // you would probably be doing something more complicated here
            // like reading from a database or calling an external service
            // based on what you have parsed from the uri
            if uri == "/resources/great/one" {
                return &mcp.ReadResourceResult{
                    Contents: []interface{}{
                        mcp.TextResourceContents{
                            MimeType: Ptr("application/json"),
                            Text:     string(`{"great": "resource"}`),
                            Uri:      uri,
                        }
                    },
                }, nil
            }

            // this error would be wrapped in JSON-RPC error response
            return nil, fmt.Errorf("resource not found")
        },
    )
}

Provide resource provider to fx

Now in order to let MCP server know about this resource provider you would need to provide it to fx when creating fx app like this:

fx.New(
    // other registered stuff ...
    fx.Provide(fxctx.AsResourceProvider(NewGreatResourceProvider)),
    // and more ...
)

fxctx.AsResourceProvider is a helper function that wraps your resource provider into fx DI container and adds a certain tags, that would be used later to find all resource providers and register them in MCP server.

ResourceMux

Discovery and registration of resources would be done by ResourceMux, which you can simply provide to fx all like this:

fx.New(
    // other registered stuff ...
    fxctx.ProvideResourceMux(),
    // and more ...
)

To sum up you would need to provide all your resource providers and ResourceMux to fx:

fx.New(
    // other registered stuff ...
    fx.Provide(fxctx.AsResourceProvider(NewGreatResourceProvider)), // dynamic resources are provided by resource providers
    fx.Provide(fxctx.AsResourceProvider(NewAnotherResourceProvider)), // you can provide multiple resource providers
    fx.Provide(fxctx.AsResource(NewHelloWorldResource)), // static resources are provided directly
    fxctx.ProvideResourceMux(),
    // and more ...
)

Wrapping it all together

Finally, when starting the server, you would need to register handlers for resources in your server using server.ServerStartCallbackOption:

fx.New(
    // other registered stuff ...
    fx.Provide(fxctx.AsResourceProvider(NewGreatResourceProvider)),
    fx.Provide(fxctx.AsResourceProvider(NewAnotherResourceProvider)),
    fx.Provide(fxctx.AsResource(NewHelloWorldResource)),
    fxctx.ProvideResourceMux(), // this makes sure that ResourceMux can get all our resource providers and is provided itself

    // Start the server using stdio transport
    fx.Invoke((func(
        lc fx.Lifecycle,
        resourceMux fxctx.ResourceMux,
    ) {
        transport := stdio.NewTransport()
        lc.Append(fx.Hook{
            OnStart: func(ctx context.Context) error {
                go func() {
                    transport.Run(
                        &mcp.ServerCapabilities{
                            Resources: &mcp.ServerCapabilitiesResources{
                                ListChanged: Ptr(false),
                                Subscribe:   Ptr(false),
                            },
                        },
                        &mcp.Implementation{
                            Name:    "my-mcp-great-server",
                            Version: "0.0.1",
                        },
                        server.ServerStartCallbackOption{
                            Callback: func(s server.Server) {
                                // This makes sure that server is aware of the tools
                                // we have registered and both can list and call them
                                resourceMux.RegisterHandlers(s)
                            },
                        },
                    )
                }()
                return nil
            },
            OnStop: func(ctx context.Context) error {
                return transport.Shutdown(ctx)
            },
        })
    })),
)

Examples

Check out complete examples of MCP Servers with resources:

  • k8s_contexts_resources - provides k8s contexts as resources
  • hello_world_resource - provides one static very simple resource
  • [git_repository_resource]https://github.com/strowk/foxy-contexts/tree/main/examples/hello_world_resource) - provides one static resource with information about git repository