Resources
In the Basics chapter, we learned how to read a resource. In this section, we’ll explore in more detail how deal with resources provided by the MCP server.
Reading a Resource
To read a resource, use client.resources().read().
It requires the resource URI.
use neva::prelude::*;
#[tokio::main]
async fn main() -> Result<(), Error> {
let mut client = Client::new()
.with_options(|opt| opt
.with_stdio(
"cargo",
["run", "--manifest-path", "./neva-mcp-server/Cargo.toml"]));
client.connect().await?;
let resource = client.resources().read("res://resource-1").await?;
println!("{:?}", resource.contents);
client.disconnect().await
}
Contents
In the example above, read returns a ReadResourceResult,
which contains a Vec of ResourceContents.
You can access individual resource fields using the following methods:
uri()title()mime()annotations()text()— returns the text contentblob()— returns the binary (blob) contentjson()— returns the JSON content
The builders — with_mime, with_title, … — are server-side; a client reads.
A resource served as text/html;profile=mcp-app is an
MCP App document; ui() reads its security block.
Listing Resources
| Call | Sends |
|---|---|
client.resources().list(cursor) | One page of resources/list |
client.resources().list_all() | Every page of resources/list, together |
client.resources().templates(cursor) | One page of resources/templates/list |
Subscribing to resource updates
Two notifications carry resource changes: notifications/resources/list_changed
when the list itself changes (the server must have declared listChanged),
and notifications/resources/updated when a specific resource changes (the
server must have declared subscribe).
Register the handlers after connect() — they assert on what the server
advertises, which is not known until discovery has run:
client.on_resources_changed(|_: Notification| async {
println!("Resource list has been updated");
});
client.on_resource_changed(|n: Notification| async move {
let params = n.params::<SubscribeRequestParams>()
.expect("Expected SubscribeRequestParams");
println!("Resource '{}' has been updated", params.uri);
});
Handlers alone do not make notifications arrive: under MCP 2026-07-28 the
client asks for them with a subscriptions/listen stream, which is where the
per-resource subscription lives:
let mut subscription = client
.listen(SubscriptionFilter::new()
.with_resources_changed()
.with_resource("res://some-resource"))
.await?;
// ...
subscription.cancel().await?;
See Subscriptions for the filter, the acknowledgment, and the subscription's lifecycle.
resources/subscribeclient.resources().subscribe(uri) / unsubscribe(uri) send the legacy RPC
pair. They stay compiled — the dual-mode fallback still reaches legacy peers —
but reject a 2026-07-28 peer with MethodNotFound. Use
SubscriptionFilter::with_resource(uri) instead, as above.
Learn By Example
Here you may find the full example