Defines the behaviour for resource repositories and handles their execution.
Repositories expose data to the MCP server either via fixed URIs or URI templates. This module validates repository definitions and orchestrates URI parsing and content retrieval.
Example
defmodule MyResourceRepo do
@behaviour GenMCP.Suite.ResourceRepo
@impl true
def prefix(_arg), do: "file:///"
@impl true
def list(_cursor, _channel, _arg) do
resources = [
%{uri: "file:///readme.txt", name: "README"}
]
{resources, nil}
end
@impl true
def read("file:///readme.txt", _channel, _arg) do
result = GenMCP.MCP.read_resource_result(
uri: "file:///readme.txt",
text: "Hello world"
)
{:ok, result}
end
def read(_uri, _channel, _arg), do: {:error, :not_found}
end
Summary
Callbacks
Returns a page of available resources.
Customizes URI parsing for template resources.
Returns the URI prefix for routing requests.
Returns the content of a resource.
Defines the URI template for dynamic resources.
Functions
Returns a descriptor for the given module or {module, arg} tuple.
Lists resources from the repository.
Retrieves content for a specific resource URI.
Types
@type arg() :: term()
@type contents() :: [ GenMCP.MCP.TextResourceContents.t() | GenMCP.MCP.BlobResourceContents.t() ]
@type resource_repo() :: module() | {module(), arg()} | resource_repo_descriptor()
Callbacks
@callback list(pagination_token :: String.t() | nil, GenMCP.Mux.Channel.t(), arg()) :: {[resource_item()], next_cursor :: term() | nil}
Returns a page of available resources.
Supports pagination via a cursor. Returns a tuple {prompts, next_cursor}.
If next_cursor is nil, there are no more pages.
Examples
def list(nil, _channel, _arg) do
resources = [%{uri: "file:///readme.txt", name: "README"}]
{resources, nil}
end
@callback parse_uri(uri :: String.t(), arg()) :: {:ok, %{required(String.t()) => term()}} | {:ok, String.t()} | {:error, String.t()}
Customizes URI parsing for template resources.
Called before read/3 to extract arguments from the URI.
If not implemented, Texture.UriTemplate.match/2 is used.
Examples
def parse_uri("file:///" <> path, _arg) do
{:ok, %{"path" => path}}
end
Returns the URI prefix for routing requests.
Examples
def prefix(_arg), do: "file:///"
@callback read(uri_or_template_args, GenMCP.Mux.Channel.t(), arg()) :: {:ok, GenMCP.MCP.ReadResourceResult.t()} | {:error, :not_found | String.t()} when uri_or_template_args: String.t() | %{required(String.t()) => term()}
Returns the content of a resource.
Receives the URI string (for direct resources) or a map of template arguments (for templates).
Must return {:ok, result} or an error tuple.
Examples
Direct resource:
def read("file:///readme.txt", _channel, _arg) do
result =
GenMCP.MCP.read_resource_result(
uri: "file:///readme.txt",
text: "Hello world"
)
{:ok, result}
endTemplate resource:
def read(%{"path" => ["config", "app.json"]}, _channel, _arg) do
result =
GenMCP.MCP.read_resource_result(
uri: "file:///config/app.json",
text: "{}"
)
{:ok, result}
end
@callback template(arg()) :: template_descriptor()
Defines the URI template for dynamic resources.
Must return a map with :uriTemplate and :name.
Examples
def template(_arg) do
%{uriTemplate: "file:///{path}", name: "File"}
end
Functions
@spec expand(resource_repo()) :: resource_repo_descriptor()
Returns a descriptor for the given module or {module, arg} tuple.
@spec list_resources( resource_repo_descriptor(), String.t() | nil, GenMCP.Mux.Channel.t() ) :: {[resource_item()], next_cursor :: term() | nil}
Lists resources from the repository.
Delegates to the list/3 callback and returns the resources and next pagination cursor.
Example
{resources, next_cursor} = GenMCP.Suite.ResourceRepo.list_resources(repo, nil, channel)
@spec read_resource(resource_repo_descriptor(), String.t(), GenMCP.Mux.Channel.t()) :: {:ok, GenMCP.MCP.ReadResourceResult.t()} | {:error, {:resource_not_found, String.t()} | String.t()}
Retrieves content for a specific resource URI.
For template-based repositories, parses the URI (via parse_uri/2 or
default matching) before calling read/3.
Normalizes {:error, :not_found} into {:error, {:resource_not_found, uri}}
automatically.
Example
{:ok, result} = GenMCP.Suite.ResourceRepo.read_resource(repo, "file:///readme.txt", channel)