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():

Registering subscription callbacks on the server.
 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.

Notifying subscribers of a resource update.
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(): Sends notifications/tools/list_changed.

  • notify_resources_list_changed(): Sends notifications/resources/list_changed.

  • notify_prompts_list_changed(): Sends notifications/prompts/list_changed.

Example of triggering list notifications:

Triggering list change notifications for tools, resources, and prompts.
 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.

Registering client-side notification handlers.
 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.

  1. Server Setup: The server enables the resource subscription capability.

  2. Registration: The server registers a resource and subscription handlers.

  3. Subscription: The client sends a resources/subscribe request.

  4. Notification: The server calls notify_resource_updated() when the counter increments.

  5. Handling: The client’s registered callback processes the update.

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