Skip to main content
MCP supports bidirectional notifications between clients and servers. Notifications are one-way JSON-RPC messages that don’t require a response. Server → Client: Servers send notifications to signal events like tools/list_changed, resources/list_changed, or custom events. Client → Server: Clients send notifications like roots/list_changed when their roots change.
For information on sending notifications from your server, see Server Notifications.

Receiving Notifications (Server → Client)

Register a notification handler on your session using the on method:
import { MCPClient } from "mcp-use";

const client = new MCPClient({
  mcpServers: {
    myServer: { url: "http://localhost:3000/mcp" }
  }
});

const session = await client.createSession("myServer");

// Register notification handler
session.on("notification", async (notification) => {
  console.log(`Received: ${notification.method}`, notification.params);
});

Notification Structure

Notifications follow the JSON-RPC 2.0 format without an id field:
interface Notification {
  method: string;           // The notification method name
  params?: Record<string, any>;  // Optional parameters
}

Standard MCP Notifications

MCP defines several standard notification types that servers may send:

Tools List Changed

Sent when the server’s available tools have changed:
session.on("notification", async (notification) => {
  if (notification.method === "notifications/tools/list_changed") {
    console.log("Tools list updated - refreshing...");
    // Refresh tools cache
    const tools = await session.connector.listTools();
    console.log(`Now have ${tools.length} tools`);
  }
});

Resources List Changed

Sent when resources have been added, removed, or modified:
session.on("notification", async (notification) => {
  if (notification.method === "notifications/resources/list_changed") {
    console.log("Resources updated");
    const resources = await session.connector.listAllResources();
    // Update your UI or cache
  }
});

Prompts List Changed

Sent when prompts have changed:
session.on("notification", async (notification) => {
  if (notification.method === "notifications/prompts/list_changed") {
    console.log("Prompts updated");
    const prompts = await session.connector.listPrompts();
    // Update your UI or cache
  }
});

Custom Notifications

Servers can send custom notifications with any method name:
session.on("notification", async (notification) => {
  switch (notification.method) {
    case "custom/heartbeat":
      console.log(`Heartbeat #${notification.params?.count}`);
      break;
      
    case "custom/user-joined":
      console.log(`User joined: ${notification.params?.username}`);
      break;
      
    case "custom/data-updated":
      console.log("Data updated:", notification.params);
      break;
      
    default:
      console.log(`Unknown notification: ${notification.method}`);
  }
});

Sending Notifications (Client → Server)

Roots Changed

Roots represent directories or files that the client has access to. When you update roots, the client automatically sends a notifications/roots/list_changed notification to the server:
import { MCPClient, Root } from "mcp-use";

const client = new MCPClient({
  mcpServers: {
    myServer: { url: "http://localhost:3000/mcp" }
  }
});

const session = await client.createSession("myServer");

// Set roots - this sends notifications/roots/list_changed to the server
await session.setRoots([
  { uri: "file:///home/user/project", name: "My Project" },
  { uri: "file:///home/user/data", name: "Data Directory" }
]);

// Get current roots
const roots = session.getRoots();
console.log(`Current roots: ${roots.length}`);

Initial Roots at Connection

You can provide initial roots when creating the connector:
import { HttpConnector } from "mcp-use";

const connector = new HttpConnector("http://localhost:3000/mcp", {
  roots: [
    { uri: "file:///workspace", name: "Workspace" }
  ]
});

const session = new MCPSession(connector);
await session.connect();
await session.initialize();

// Later, update roots
await session.setRoots([
  { uri: "file:///workspace", name: "Workspace" },
  { uri: "file:///tmp/scratch", name: "Scratch" }
]);

Root Type

interface Root {
  uri: string;      // Must start with "file://"
  name?: string;    // Optional human-readable name
}

Practical Examples

Building a Real-Time Dashboard

import { MCPClient, Notification } from "mcp-use";

const client = new MCPClient({
  mcpServers: {
    dashboard: { url: "http://localhost:3000/mcp" }
  }
});

const session = await client.createSession("dashboard");

// State
let connectedUsers = 0;
let lastHeartbeat: Date | null = null;

session.on("notification", async (notification: Notification) => {
  switch (notification.method) {
    case "custom/heartbeat":
      lastHeartbeat = new Date();
      connectedUsers = notification.params?.connectedClients ?? 0;
      updateStatusUI();
      break;
      
    case "custom/alert":
      showAlert(notification.params?.message);
      break;
      
    case "notifications/tools/list_changed":
      await refreshToolsList();
      break;
  }
});

function updateStatusUI() {
  console.log(`Status: ${connectedUsers} users, last heartbeat: ${lastHeartbeat}`);
}

Handling Connection Events

session.on("notification", async (notification) => {
  if (notification.method === "custom/welcome") {
    console.log("Connected to server!");
    console.log("Server time:", notification.params?.serverTime);
    console.log("Available tools:", notification.params?.availableTools);
  }
});

Progress Updates

session.on("notification", async (notification) => {
  if (notification.method === "custom/progress") {
    const { taskId, progress, status } = notification.params ?? {};
    console.log(`Task ${taskId}: ${progress}% - ${status}`);
    
    // Update progress bar UI
    updateProgressBar(taskId, progress);
  }
});

Multiple Handlers

You can register multiple notification handlers:
// Handler for logging
session.on("notification", async (notification) => {
  console.log("[LOG]", notification.method, notification.params);
});

// Handler for specific notifications
session.on("notification", async (notification) => {
  if (notification.method === "custom/important") {
    await handleImportantNotification(notification);
  }
});

// Handler for UI updates
session.on("notification", async (notification) => {
  if (notification.method.startsWith("ui/")) {
    updateUI(notification);
  }
});

Error Handling

Errors in notification handlers are caught and logged, but don’t stop other handlers:
session.on("notification", async (notification) => {
  try {
    // Process notification
    await processNotification(notification);
  } catch (error) {
    console.error("Error processing notification:", error);
    // Handle error - maybe retry or log for debugging
  }
});

Browser Usage

Notifications work the same way in browser environments:
import { MCPClient } from "mcp-use/browser";

const client = new MCPClient({
  mcpServers: {
    api: { url: "https://api.example.com/mcp" }
  }
});

const session = await client.createSession("api");

session.on("notification", (notification) => {
  // Update React state, Vue refs, etc.
  setNotifications(prev => [...prev, notification]);
});

TypeScript Types

Import the Notification type for type-safe handlers:
import { MCPClient, Notification, NotificationHandler } from "mcp-use";

// Type-safe handler
const handler: NotificationHandler = async (notification: Notification) => {
  // notification.method is string
  // notification.params is Record<string, any> | undefined
};

session.on("notification", handler);

Requirements

Notifications require:
  1. Stateful Connection: The server must maintain sessions (stateful mode)
  2. Active Session: The client must have an active, initialized session
  3. Streaming Transport: Either SSE or Streamable HTTP transport
Notifications don’t work in stateless edge environments where sessions aren’t maintained between requests.

Best Practices

  1. Handle Unknown Notifications: Always have a fallback for notifications you don’t recognize
  2. Keep Handlers Fast: Notification handlers should be quick - offload heavy work
  3. Use TypeScript: Leverage the Notification type for better code quality
  4. Log Notifications: During development, log all notifications to understand server behavior
  5. Clean Up: If your session disconnects, handlers won’t receive new notifications

Next Steps