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