Skip to main content
MCP supports bidirectional notifications between servers and clients. Notifications are one-way JSON-RPC messages that don’t expect a response. Server → Client: Send notifications to signal events, status changes, or broadcast messages. Client → Server: Receive notifications when clients update their roots or other state.
For information on handling notifications in your client application, see Client Notifications.
Check out the complete notifications example for a working implementation with progress tracking, custom broadcasts, and resource updates.
Notifications only work in stateful mode where clients maintain persistent sessions. In stateless edge environments, notifications are not supported.

Sending Notifications

Broadcasting to All Clients

Send a notification to all connected clients:
import { MCPServer } from "mcp-use/server";

const server = new MCPServer({
  name: "my-server",
  version: "1.0.0"
});

// Later, send to all connected clients
await server.sendNotification("custom/status-update", {
  status: "ready",
  timestamp: Date.now()
});

// you can also send to a speocific client by session ID
await server.sendNotificationToSession(sessionId, "custom/status-update", {
  status: "ready",
  timestamp: Date.now()
});

Sending to the current client

Send a notification to the current client:
server.tool({
  name: 'send-notification',
  description: 'Send a notification to the current client',
}, async (params, ctx) => {
  await ctx.sendNotification("custom/welcome", { message: "Hello, client!" });
});

Sending to a Specific Client

Send a notification to a specific session:
// Get all active session IDs
const sessions = server.getActiveSessions();
console.log(`${sessions.length} clients connected`);

// Send to a specific session
if (sessions.length > 0) {
  const success = await server.sendNotificationToSession(
    sessions[0],
    "custom/welcome",
    { message: "Hello, client!" }
  );
  
  if (!success) {
    console.log("Session not found or expired");
  }
}

Built-in Notification Types

MCP defines standard notification methods that clients may handle:

Tools List Changed

Notify clients when the available tools have changed:
// Convenience method
await server.sendToolsListChanged();

// Or using the general notification method
await server.sendNotification("notifications/tools/list_changed");

Resources List Changed

Notify clients when resources have been updated:
// Convenience method
await server.sendResourcesListChanged();

// Or using the general notification method
await server.sendNotification("notifications/resources/list_changed");

Prompts List Changed

Notify clients when prompts have changed:
// Convenience method
await server.sendPromptsListChanged();

// Or using the general notification method
await server.sendNotification("notifications/prompts/list_changed");

Progress Notifications

Progress notifications (notifications/progress) are used to keep clients informed during long-running operations and prevent timeouts. When a client calls a tool with a progressToken, the server can send progress updates. Automatic Progress in Sampling When using ctx.sample() in tool callbacks, progress notifications are automatically sent every 5 seconds:
import { text } from 'mcp-use/server';

server.tool({
  name: 'long_llm_task',
  cb: async (params, ctx?: ToolContext) => {
    if (!ctx) return text('No context')
    
    // Progress notifications are sent automatically every 5 seconds
    const result = await ctx.sample({
      messages: [{ role: 'user', content: { type: 'text', text: params.prompt } }]
    })
    
    return text(result.content.text)
  }
})
Manual Progress Reporting You can also send progress notifications manually using ctx.reportProgress():
import { text } from 'mcp-use/server';

server.tool({
  name: 'process_data',
  cb: async (params, ctx?: ToolContext) => {
    if (ctx?.reportProgress) {
      await ctx.reportProgress(0, 100, 'Starting...')
      
      // Process data in stages
      await processStage1()
      await ctx.reportProgress(33, 100, 'Stage 1 complete')
      
      await processStage2()
      await ctx.reportProgress(66, 100, 'Stage 2 complete')
      
      await processStage3()
      await ctx.reportProgress(100, 100, 'Complete')
    }
    
    return text('Done')
  }
})
How Progress Prevents Timeouts According to the MCP specification, when a client has resetTimeoutOnProgress: true enabled:
  • Each progress notification resets the client’s timeout counter
  • This allows operations to run indefinitely as long as progress is reported
  • The default timeout is 60 seconds, but with progress notifications, operations can take much longer
Progress Notification Format Progress notifications follow this structure:
{
  method: "notifications/progress",
  params: {
    progressToken: number,  // Token from the original request
    progress: number,       // Current progress value
    total?: number,         // Total progress (if known)
    message?: string        // Optional status message
  }
}

Custom Notifications

You can send custom notifications with any method name and parameters:
// Use a namespace prefix for custom notifications
await server.sendNotification("custom/user-joined", {
  userId: "user-123",
  username: "alice",
  joinedAt: new Date().toISOString()
});

await server.sendNotification("custom/data-updated", {
  resourceId: "res-456",
  changes: ["field1", "field2"]
});

Receiving Notifications

Roots Changed

Clients can notify the server when their available roots (directories/files) change. Register a handler to respond to these changes:
import { MCPServer } from "mcp-use/server";

const server = new MCPServer({
  name: "my-server",
  version: "1.0.0"
});

// Handle roots/list_changed from clients
server.onRootsChanged(async (roots) => {
  console.log(`Client updated roots: ${roots.length} root(s)`);
  
  roots.forEach(root => {
    console.log(`  - ${root.name || "unnamed"}: ${root.uri}`);
  });
  
  // You can use the new roots to adjust server behavior
  // For example, update which files the server can access
});

Requesting Roots from a Client

You can also request the current roots from a specific client session:
const sessions = server.getActiveSessions();

if (sessions.length > 0) {
  const roots = await server.listRoots(sessions[0]);
  
  if (roots) {
    console.log(`Client has ${roots.length} roots:`);
    roots.forEach(r => console.log(`  - ${r.uri}`));
  }
}

Root Type

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

Next Steps