Skip to main content

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:

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​

CallSends
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.

The legacy resources/subscribe

client.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

Additional examples​