How to Implement Subscriptions for Real-Time Updates in MCP

Implement real-time subscriptions in Model Context Protocol (MCP) servers by registering Subscribe and Unsubscribe RPC handlers, tracking active sessions in a Map<uri, Set<sessionId>>, and pushing notifications/resources/updated messages through session-scoped intervals.

The Model Context Protocol (MCP) enables servers to push live resource updates to connected clients through a subscription mechanism. This guide explains how to implement subscriptions for real-time updates in MCP using the reference implementation found in the modelcontextprotocol/servers repository, specifically within the Everything example server.

Architecture of MCP Subscriptions

Core Components

The subscription system relies on three coordinated mechanisms working together to manage client connections and message delivery:

  1. Subscription request handling – Registers Subscribe and Unsubscribe RPCs to process client requests.
  2. Session-scoped update loops – A timer that periodically emits notifications/resources/updated for each URI a client is subscribed to.
  3. Toggle tool – The toggle-subscriber-updates tool allows clients to start or stop the periodic notifications for their session.

Data Structures

The canonical tracking mechanism uses two primary data structures defined in src/everything/resources/subscriptions.ts:

  • subscriptions: A Map<string, Set<string>> mapping resource URIs to sets of active session IDs.
  • subsUpdateIntervals: A Map<string, NodeJS.Timeout> storing timer handles for each session's update loop.

Key Implementation Files

src/everything/resources/subscriptions.ts

This file contains the core subscription logic, including request handlers and the simulated update loop. The setSubscriptionHandlers function registers the Subscribe and Unsubscribe RPCs, while sendSimulatedResourceUpdates walks through the subscriptions map to emit notifications.

src/everything/tools/toggle-subscriber-updates.ts

This tool exposes a user-facing interface to control the update loop. When invoked, it calls beginSimulatedResourceUpdates or stopSimulatedResourceUpdates to manage the 5-second interval for the requesting session.

src/everything/server/index.ts

The server factory wires everything together. It instantiates the McpServer, enables the resources.subscribe capability, registers all handlers, and ensures cleanup routines stop active update loops when sessions terminate.

Step-by-Step Implementation

Registering Subscription Handlers

First, enable subscription capabilities and register the RPC handlers in your server initialization:

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp";
import { setSubscriptionHandlers } from "./resources/subscriptions";

const server = new McpServer({
  name: "my-server",
  version: "1.0.0",
  capabilities: {
    resources: {
      subscribe: true  // Enable subscription support
    }
  }
});

setSubscriptionHandlers(server);  // Registers Subscribe/Unsubscribe RPCs

Managing Update Loops

Implement the session-scoped update mechanism to push notifications. The sendSimulatedResourceUpdates function in subscriptions.ts demonstrates the pattern:

const sendSimulatedResourceUpdates = async (sessionId: string) => {
  // Iterate through all subscribed URIs
  for (const [uri, sessionIds] of subscriptions.entries()) {
    if (sessionIds.has(sessionId)) {
      // Send notification to specific session
      await server.server.notification({
        method: "notifications/resources/updated",
        params: { uri }
      });
    }
  }
};

Handling Cleanup

Ensure proper resource cleanup when sessions disconnect. Store interval handles and clear them on session end:

const subsUpdateIntervals = new Map<string, NodeJS.Timeout>();

const beginSimulatedResourceUpdates = (sessionId: string) => {
  const interval = setInterval(() => {
    sendSimulatedResourceUpdates(sessionId);
  }, 5000); // 5-second interval
  
  subsUpdateIntervals.set(sessionId, interval);
};

// Cleanup on disconnect
const cleanupSession = (sessionId: string) => {
  stopSimulatedResourceUpdates(sessionId);
  // Remove session from all subscription sets
  for (const sessionIds of subscriptions.values()) {
    sessionIds.delete(sessionId);
  }
};

Complete Code Example

Here is the complete pattern for implementing subscriptions for real-time updates in MCP:

// 1. Register the subscription handlers (usually in your server factory)
import { setSubscriptionHandlers } from "./resources/subscriptions";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp";

const server = new McpServer({
  name: "example-server",
  version: "1.0.0",
  capabilities: {
    resources: { subscribe: true }
  }
});

setSubscriptionHandlers(server);   // Enables Subscribe/Unsubscribe RPCs

// 2. (Optional) Expose a tool to start/stop simulated updates
import { registerToggleSubscriberUpdatesTool } from "./tools/toggle-subscriber-updates";
registerToggleSubscriberUpdatesTool(server);

// 3. Client-side – subscribe to a resource URI
await client.request("resources/subscribe", {
  params: { uri: "myapp://example/resource/1" }
});

// 4. Client-side – turn on the simulated updater
await client.callTool("toggle-subscriber-updates");

// 5. Server pushes notifications every 5 seconds:
// {
//   method: "notifications/resources/updated",
//   params: { uri: "myapp://example/resource/1" }
// }

Key implementation details to remember:

  • Include resources: { subscribe: true } in the server capabilities configuration.
  • Call setSubscriptionHandlers before any client connects to register the RPC handlers with the transport layer.
  • Use the toggle-subscriber-updates tool or implement custom timer logic if you require a different update cadence.

Summary

  • Enable capabilities: Set resources.subscribe: true in your McpServer configuration to signal subscription support to clients.
  • Register handlers: Use setSubscriptionHandlers from src/everything/resources/subscriptions.ts to process Subscribe and Unsubscribe RPCs.
  • Track sessions: Maintain a Map<uri, Set<sessionId>> to record which clients are listening to which resource URIs.
  • Push updates: Implement sendSimulatedResourceUpdates to emit notifications/resources/updated messages via server.server.notification.
  • Manage lifecycle: Store interval handles in subsUpdateIntervals and clear them in cleanup routines when sessions disconnect.

Frequently Asked Questions

How do I enable subscription capabilities in an MCP server?

Add resources: { subscribe: true } to the capabilities object when instantiating McpServer. This signals to clients that the server supports the resources/subscribe and resources/unsubscribe RPCs. Then call setSubscriptionHandlers(server) to register the actual request handlers before the server starts accepting connections.

What is the purpose of the toggle-subscriber-updates tool?

The toggle-subscriber-updates tool provides a user-facing mechanism to start or stop the simulated update loop for a specific session. When invoked, it calls beginSimulatedResourceUpdates or stopSimulatedResourceUpdates, which manage a 5-second interval that periodically triggers sendSimulatedResourceUpdates to push notifications to subscribed clients.

How does the server handle client disconnections in subscriptions?

The server maintains a subsUpdateIntervals map storing NodeJS.Timeout handles for each active session. When a session ends, the cleanup routine calls stopSimulatedResourceUpdates to clear the interval, then iterates through the subscriptions map to remove the disconnected session ID from all URI subscriber sets, preventing memory leaks and ghost notifications.

Can I customize the notification interval for resource updates?

Yes, while the reference implementation uses a fixed 5-second interval in beginSimulatedResourceUpdates, you can modify the setInterval duration or replace the timer-based approach entirely. For production use, you might implement event-driven updates instead of polling, or adjust the interval based on resource volatility by passing a custom delay parameter to your update loop initialization function.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →