Notifications and Subscriptions
Notifications and subscriptions enable real-time communication between MCP servers and clients. While the standard request-response cycle is suitable for most interactions, notifications allow either side to push updates asynchronously, and subscriptions specifically allow clients to stay informed about changes to resources.
Subscriptions Overview
In MCP, subscriptions are specifically tied to resources. A client can “subscribe” to a resource URI to indicate interest in receiving updates whenever that resource changes.
Key benefits include: - Reduced latency for data updates. - Lower overhead compared to polling. - Real-time synchronization for telemetry, logs, or shared state.
Resource Subscriptions
Server-side Subscription Callbacks
Servers can track when clients subscribe to or unsubscribe from resources. This is useful for managing underlying data sources, such as opening a database connection or starting a hardware sensor only when active subscribers exist.
You can register subscription handlers using on_subscribe() and on_unsubscribe():
1 // ========== SUBSCRIPTION HANDLERS ==========
2 // Track subscriptions
3 std::cout << "[Server] Registering subscription callbacks\n";
4 server.on_subscribe([](const std::string& uri) {
5 std::cout << "[Server] Client subscribed to: " << uri << '\n';
6 });
7
8 server.on_unsubscribe([](const std::string& uri) {
9 std::cout << "[Server] Client unsubscribed from: " << uri << '\n';
10 });
Resource Change Notifications
When a resource’s content is updated, the server should notify all active subscribers. The notify_resource_updated() method sends a notifications/resources/updated message to all clients that have an active subscription for the given URI.
1 std::cout << "[Server] Triggering notify_resource_updated\n";
2 co_await server.notify_resource_updated(resource_uri);
List Change Notifications
Beyond individual resource updates, servers can notify clients when their entire collection of tools, resources, or prompts has changed. This is common when new capabilities are dynamically added or removed at runtime.
The following methods are available for list change notifications:
notify_tools_list_changed(): Sendsnotifications/tools/list_changed.notify_resources_list_changed(): Sendsnotifications/resources/list_changed.notify_prompts_list_changed(): Sendsnotifications/prompts/list_changed.
Example of triggering list notifications:
1 std::cout << "[Server] Triggering notify_tools_list_changed\n";
2 co_await server.notify_tools_list_changed();
3
4 // Wait for notification
5 wait_timer.expires_after(std::chrono::milliseconds(100));
6 co_await wait_timer.async_wait(asio::use_awaitable);
7
8 std::cout << "[Server] Triggering notify_resources_list_changed\n";
9 co_await server.notify_resources_list_changed();
10
11 // Wait for notification
12 wait_timer.expires_after(std::chrono::milliseconds(100));
13 co_await wait_timer.async_wait(asio::use_awaitable);
14
15 std::cout << "[Server] Triggering notify_prompts_list_changed\n";
16 co_await server.notify_prompts_list_changed();
17
18 // Wait for notification
19 wait_timer.expires_after(std::chrono::milliseconds(100));
20 co_await wait_timer.async_wait(asio::use_awaitable);
Client-side Notification Handling
Clients must explicitly register handlers to process incoming notifications. This is done using the on_notification() method, which maps a notification method name to a callback function.
1 // Handle resource updated notifications
2 client.on_notification(
3 "notifications/resources/updated", [](const nlohmann::json& params) {
4 std::cout
5 << "[Client] Received resource updated notification: " << params.dump()
6 << '\n';
7 });
8
9 // Handle tools list changed notifications
10 client.on_notification(
11 "notifications/tools/list_changed", [](const nlohmann::json& params) {
12 std::cout << "[Client] Received tools list changed notification\n";
13 });
14
15 // Handle resources list changed notifications
16 client.on_notification(
17 "notifications/resources/list_changed", [](const nlohmann::json& params) {
18 std::cout << "[Client] Received resources list changed notification\n";
19 });
20
21 // Handle prompts list changed notifications
22 client.on_notification(
23 "notifications/prompts/list_changed", [](const nlohmann::json& params) {
24 std::cout << "[Client] Received prompts list changed notification\n";
25 });
Example Walkthrough
The following example demonstrates a complete flow where a client subscribes to a counter resource, and the server periodically notifies the client of updates.
Architecture of Notifications
Notifications in MCP are one-way messages that do not expect a response. This makes them ideal for high-frequency updates or background events where the overhead of a full request-response cycle would be prohibitive.
Methods: Notification methods are prefixed with
notifications/.Parameters: Notifications can carry arbitrary JSON parameters.
Delivery: Notifications are delivered asynchronously over the established transport.
Subscription State Management
While the mcp-cpp-sdk handles the underlying message routing, the server application is responsible for managing the logic associated with subscriptions.
Persistence: Subscriptions are typically session-based and do not persist across reconnections unless re-established by the client.
Scalability: When notifying thousands of clients, consider the performance impact of JSON serialization. The SDK optimizes this where possible by reusing serialized buffers.
Example Walkthrough
The following example demonstrates a complete flow where a client subscribes to a counter resource, and the server periodically notifies the client of updates.
Server Setup: The server enables the resource subscription capability.
Registration: The server registers a resource and subscription handlers.
Subscription: The client sends a
resources/subscriberequest.Notification: The server calls
notify_resource_updated()when the counter increments.Handling: The client’s registered callback processes the update.
Unsubscription: The client eventually unsubscribes to stop receiving updates.
Advanced Notification Patterns
Custom Notifications
While MCP defines several standard notification methods, you can also send custom notifications using the underlying protocol layer if your client and server agree on the method names. However, for most resource-related needs, the standard updated and list_changed methods are preferred.
Throttling and Debouncing
For rapidly changing resources, it is recommended to throttle or debounce notifications on the server side to avoid overwhelming the client. For example, if a log file is being updated multiple times per millisecond, sending a single notification every 100ms is often more efficient.
For a complete working example, see examples/features/notifications_subscriptions.cpp in the source tree.
See Also
Resources: Deep dive into how resources work.
Server Context: Using the request context for logging and progress.
Transports: How notifications are carried over different transport layers.