MCP Resources
Resources provide data that language models can access. Each resource has a URI, name, MIME type, and a data provider function.
Resource Structure
Every resource in ModelContextProtocol.jl is represented by the MCPResource struct:
uri: Unique URI identifier for the resourcename: Human-readable resource namedescription: Explanation of the resource's purposemime_type: Content type (e.g., "application/json", "text/plain")data_provider: Function that returns the resource's dataannotations: Optional metadata about the resource
Creating Resources
Here's how to create a basic resource:
using URIs
weather_resource = MCPResource(
uri = "weather://current",
name = "Current Weather",
description = "Current weather conditions",
mime_type = "application/json",
data_provider = () -> Dict(
"temperature" => 22.5,
"conditions" => "Partly Cloudy",
"updated" => Dates.format(now(), "yyyy-mm-dd HH:MM:SS")
)
)Note: The uri field accepts both strings and URI objects. Strings are automatically converted to URIs.
Data Providers
The data_provider is a zero-argument function called on every resources/read. It can return different types of data:
For simple data (automatically serialized to JSON):
- Return Julia objects (Dict, Array, etc.) that can be JSON-serialized
- These become a text contents entry with the resource's
mime_type
For verbatim text: return a
String— used as the text contents as-isFor explicit control over content (including binary):
- Return
TextResourceContentsfor text data - Return
BlobResourceContentsfor binary data (base64blobon the wire) - Return a
Vectorof them for multiple contents entries
- Return
Registering Resources
Resources can be registered with a server in two ways:
- During server creation:
server = mcp_server(
name = "my-server",
resources = my_resource # Single resource or vector of resources
)- After server creation:
register!(server, my_resource)Directory-Based Organization
Resources can be organized in directory structures and auto-registered:
my_server/
└── resources/
├── weather.jl
└── stock_data.jlEach file should export one or more MCPResource instances:
# weather.jl
using ModelContextProtocol
using Dates
weather_resource = MCPResource(
uri = "weather://current",
name = "Current Weather",
description = "Current weather conditions",
mime_type = "application/json",
data_provider = () -> Dict(
"temperature" => 22.5,
"conditions" => "Partly Cloudy",
"updated" => Dates.format(now(), "yyyy-mm-dd HH:MM:SS")
)
)Then auto-register from the directory:
server = mcp_server(
name = "my-server",
auto_register_dir = "my_server"
)Advanced Examples
Resource with Dynamic Data
The data_provider is called with no arguments on every resources/read. What it returns determines the wire contents:
- A
TextResourceContentsorBlobResourceContents(or a vector of them) is serialized directly — this is how binary resources are served (base64blobon the wire) and how you control the contentsuri/mimeTypeper entry. - A
Stringbecomes the text contents verbatim, with the resource'smime_type. - Anything else is JSON-encoded into a text contents entry.
# JSON data (encoded automatically)
log_resource = MCPResource(
uri = "app://logs/recent",
name = "Recent Log Entries",
description = "The most recent application log entries",
mime_type = "application/json",
data_provider = function ()
entries = isfile("app.log") ?
collect(Iterators.take(eachline("app.log"), 50)) : String[]
return Dict("count" => length(entries), "entries" => entries)
end
)Resource with Binary Data
Return a BlobResourceContents to serve binary data (base64-encoded on the wire):
image_resource = MCPResource(
uri = "images://logo",
name = "Logo Image",
description = "Company logo",
mime_type = "image/png",
data_provider = () -> BlobResourceContents(
uri = "images://logo",
mime_type = "image/png",
blob = read("logo.png") # Vector{UInt8}
)
)This pairs naturally with ResourceLink tool results: a tool can return a link to a large artifact, and the client then reads the binary via resources/read.
URI Templates
For a parameterized family of resources — content-addressed artifacts, per-id results, file trees — register a ResourceTemplate instead of one resource per URI. Templates use RFC 6570 level-1 placeholders ({var}, matching one path segment) and are advertised to clients via resources/templates/list:
artifact_template = ResourceTemplate(
name = "artifact",
uri_template = "app://artifact/{id}",
description = "Content-addressed result artifacts",
mime_type = "image/png",
data_provider = (uri, vars) -> BlobResourceContents(
uri = uri,
mime_type = "image/png",
blob = read(artifact_path(vars["id"]))
)
)
server = mcp_server(
name = "my-server",
resource_templates = [artifact_template] # or register!(server, artifact_template)
)A resources/read whose URI matches no exact resource is routed through the templates: the first match with a provider serves it. The provider receives the requested URI — provider(uri) — or opts into provider(uri, vars) to also get the extracted placeholder values. Return values follow the same contract as resource providers (ResourceContents/vector, String verbatim, JSON fallback). Exact-URI resources always take precedence over templates.