Notifications
A notification is a one-way message your server pushes to a connected client; change notifications tell clients that a list or a resource they cached is stale.
Send a list-changed notification
Start from a server with one tool.
import { McpServer } from '@modelcontextprotocol/server';
const jobs = ['nightly-backup'];
const server = new McpServer({ name: 'jobs', version: '1.0.0' });
server.registerTool('list-jobs', { description: 'List the configured jobs' }, async () => ({
content: [{ type: 'text', text: jobs.join('\n') }]
}));Every notification on this page is observed by an in-memory Client connected to the server above — Test a server shows that wiring — which logs each notification method it receives. Push a tool-list change yourself when the tool set changes for a reason the registration API cannot see.
server.sendToolListChanged();The client receives one notifications/tools/list_changed and re-fetches tools/list:
notifications/tools/list_changedsendPromptListChanged() and sendResourceListChanged() are the prompt and resource siblings.
Let registration changes notify for you
Hold on to the handle registerTool returns — every mutation through it sends the matching list-changed on its own.
const report = server.registerTool('run-report', { description: 'Run the weekly report' }, async () => ({
content: [{ type: 'text', text: 'report queued' }]
}));
report.update({ description: 'Run the weekly report and email it' });
report.disable();Registering, updating, and disabling each sent a notification of its own — three more, none explicit:
notifications/tools/list_changed
notifications/tools/list_changed
notifications/tools/list_changedenable() and remove() notify the same way, and the handles returned by registerResource and registerPrompt send notifications/resources/list_changed and notifications/prompts/list_changed. Most servers never call a send*ListChanged() method directly.
Advertise the listChanged capability
McpServer advertised tools: { listChanged: true } the moment you registered a tool, and does the same for prompts and resources. Only the low-level Server needs the capability declared up front.
const lowLevel = new Server({ name: 'jobs', version: '1.0.0' }, { capabilities: { tools: { listChanged: true } } });The low-level Server refuses to send a notification its capabilities do not cover — sendToolListChanged() throws without a tools capability — and clients use the listChanged flag to decide which notification types to ask for.
Publish a resource update through the handler
On a 2026-07-28 connection, change notifications reach a client only on a subscriptions/listen stream the client opens — see Protocol versions. Behind createMcpHandler the McpServer instance is per-request, so publish through the handler, not the instance: notify is a typed facade over the handler's open subscription streams.
const handler = createMcpHandler(() => buildJobsServer());
// After config://app changes:
handler.notify.resourceUpdated('config://app');Every client whose stream listed config://app receives notifications/resources/updated carrying that URI; notify.toolsChanged(), notify.promptsChanged(), and notify.resourcesChanged() publish the three list-changed types the same way. Per-resource updates have one extra gate: the server your factory builds must advertise resources: { subscribe: true }.
TIP
On stdio, serveStdio routes the instance's own send*ListChanged() and sendResourceUpdated() calls onto its open subscription stream — no notify facade needed.
Coming from 2025-era subscriptions
A 2025-era connection delivers per-resource updates without a listen stream — Subscriptions covers that path, and Protocol versions the era split.
Pick an event bus for multi-process deployments
The bus option accepts any ServerEventBus; InMemoryServerEventBus is what createMcpHandler builds when you omit it.
const bus = new InMemoryServerEventBus();
const shared = createMcpHandler(() => buildJobsServer(), { bus });The in-memory bus never leaves the process, so one process needs nothing more. Run more than one and you implement the two-method ServerEventBus interface — publish and subscribe — over your own pub/sub backend, then pass the same instance to every handler; see Sessions, state, and scaling.
Recap
sendToolListChanged(),sendPromptListChanged(), andsendResourceListChanged()push a list-changed notification to connected clients.- Registering, updating, enabling, disabling, or removing through a registration handle sends the matching list-changed for you.
McpServeradvertiseslistChangedas you register; only the low-levelServerdeclares it up front.- Behind
createMcpHandler, publish throughhandler.notify; delivery reaches every open subscription stream that opted in. - One process needs no
bus; more than one shares aServerEventBus.