Skip to content

Subscriptions

Jeffrie Budde edited this page Nov 25, 2025 · 1 revision

Subscriptions & Notifications

Real-time resource updates are built in. Works with STDIO, SSE, and WebSocket (see Transports for connectionId handling).

Flow

  1. Client calls resources/subscribe with a URI.
  2. Server validates the resource and returns { id, uri, created_at, client_id }.
  3. Server sends notifications/resources/updated when content changes.
  4. Client can unsubscribe via resources/unsubscribe or list via resources/list_subscriptions.

Minimal Server Pattern

use fastrmcp::prelude::*;
use fastrmcp_macros::{resource, tool};
use std::sync::atomic::{AtomicU64, Ordering};

static COUNTER: AtomicU64 = AtomicU64::new(0);

#[resource("counter://value")]
async fn counter() -> Result<String> {
    Ok(serde_json::json!({ "value": COUNTER.load(Ordering::SeqCst) }).to_string())
}

#[tool]
async fn increment() -> Result<u64> {
    Ok(COUNTER.fetch_add(1, Ordering::SeqCst) + 1)
}

#[tokio::main]
async fn main() -> Result<()> {
    FastMCP::new("subs").version("1.0.0").run().await
}
  • Resources made with #[resource] are subscribable automatically.
  • To emit notifications manually (e.g., from a background task), use server.subscription_manager().notify_uri(uri, content).await? where content is ResourceContent.

Protocol Shapes

  • Subscribe:
{"jsonrpc":"2.0","id":1,"method":"resources/subscribe","params":{"uri":"counter://value"}}
  • Notification (server → client):
{"jsonrpc":"2.0","method":"notifications/resources/updated","params":{"subscription_id":"<uuid>","uri":"counter://value","content":{"uri":"counter://value","mimeType":"application/json","text":"{\"value\":2}"},"timestamp":1699565000}}
  • Unsubscribe:
{"jsonrpc":"2.0","id":2,"method":"resources/unsubscribe","params":{"subscriptionId":"<uuid>"}}}
  • List:
{"jsonrpc":"2.0","id":3,"method":"resources/list_subscriptions"}

Server-Driven Updates

  • Background tasks can push updates on intervals or external triggers. Check SubscriptionManager::subscription_count_for_uri before expensive work.
  • SSE clients must include connectionId when posting messages; WebSocket clients can be targeted via metadata.connectionId in server-initiated requests (otherwise current connection).

Pitfalls

  • Unknown URI → ResourceNotFound / MethodNotFound.
  • Missing connectionId on SSE POSTs when multiple clients are connected will be rejected (400 Bad Request).
  • Validate payload schemas in tools/resources to avoid InvalidParams errors that break subscription flows.

Related: Transports GuideTroubleshooting for debugging tips • Examples for a full demo.

Back to Home

Clone this wiki locally