MCP’s notifications/tools/list_changed lets a server tell a connected client that its tool list is no longer what the client last saw — a tool was added, removed, or redefined, so whatever the client cached from the last tools/list call is stale. It’s a small notification with no payload of its own, and its entire job is to say go re-fetch, not to describe what actually changed.

How it actually works

A server declares support for this up front, during capability negotiation: its tools capability can carry listChanged: true. That’s the only thing a client learns in advance — whether this server might ever send the notification at all. From there, whenever the server’s tool set actually changes, it sends a bare notifications/tools/list_changed message with no fields. The client’s only correct response is to call tools/list again and replace whatever it had cached. The notification doesn’t say which tool changed, what the new schema looks like, or whether something was added versus removed — just that the list the client is holding should no longer be trusted.

That’s a deliberately thin design, and it matches the pattern MCP uses elsewhere: resource subscriptions work the same way for a single resource’s content, and progress notifications use the same fire-and-forget shape for a different signal. None of them carry a diff. They exist to tell the other side something changed, go look, and leave the looking to a request the client already knows how to make.

What actually triggers it

A server’s tool list isn’t always fixed at startup, and this notification only matters for the ones where it isn’t. A few real cases:

  • Feature flags or plan tiers. A SaaS-backed server might expose a smaller tool set to a free-tier account and a larger one once a feature is enabled, without restarting the connection.
  • Authorization state. A server that only reveals write tools once a user has granted a write scope can add those tools to the list the moment the broader grant lands, instead of returning them disabled or erroring on every call.
  • Dynamic backends. A server wrapping a plugin system, a workspace with configurable integrations, or a catalog that changes server-side can genuinely gain or lose tools during a long-lived session.
  • A gateway reconfiguration. If you connect through something like an MCP gateway that lets an admin turn individual tools on or off per user or per team, the gateway’s own tool surface for that connection can change mid-session as policy changes — and the same notification is the honest way to tell a connected client about it.

The common thread: this notification only matters for a server whose tool list can actually move after the handshake. A static server — the overwhelming majority of what’s in gate’s MCP server directory — declares one fixed tool list at startup and never sends this notification at all, because nothing about its tools ever changes.

What it doesn’t give you

It’s worth being precise about what this notification is not, because it’s easy to assume it does more:

  • It’s not a security signal. A server can add a tool, remove one, or quietly change a tool’s description and arguments between two tools/list calls, and the notification treats all of those identically. That overlaps with what we covered on rug pulls and tool drift — a server that redefines a tool after you’ve approved it is using the exact same mechanism a well-behaved dynamic server uses. The notification can’t tell a client which case it’s looking at.
  • It’s not a guarantee the client will act on it. Nothing in the spec forces a client to re-fetch immediately, or at all. A client that ignores the notification keeps operating on a stale list until something else forces a refresh — a reconnect, a restart, a manual action.
  • It doesn’t cover resources or prompts. Tools, resources, and prompts each have their own list_changed notification and their own capability flag, declared independently. Supporting one says nothing about whether a server supports the others — see how the three primitives differ if you haven’t connected the dots between them yet.
  • It doesn’t tell an in-flight conversation what to do with a tool call already in motion. If a model decided to call a tool based on the list it had, and that tool disappears before the call lands, the notification doesn’t resolve the race — the call still has to fail or succeed on its own terms.

Why most clients barely surface this

In day-to-day use through Claude or Cursor, you won’t see “tool list changed” as a UI event. Most chat clients that support the notification simply re-fetch the list quietly and update what’s available for the next turn, with no visible signal that anything happened. That’s reasonable for the common case — a tool quietly appearing or disappearing between turns is low-drama when the tool set is genuinely under the server’s own control. It’s a different story when you’re the one deciding which servers to trust with that kind of movement in the first place, which is really a question about the server’s behavior, not about the notification itself.

If you build a server

Only declare listChanged: true if your tool list can genuinely change during a session — declaring it and never sending the notification is harmless but pointless, and sending it when your list is actually static just adds noise a client has to process for nothing. When you do send it, make sure the next tools/list response is actually ready to serve the new state; sending the notification before the change has propagated internally just means a client re-fetches and gets the stale list back anyway. And treat a tool’s identity as its name plus its schema together — if you’re changing what an existing tool name means rather than adding or removing a tool, that’s the versioning problem we covered in MCP server versioning, and this notification doesn’t make that problem safer, just more visible.

Where gate fits: a server that keeps rewriting its own tool list mid-session is exactly the kind of behavior worth watching, not just accepting silently. Every tool call that passes through a gateway lands in a plain-language log, so a tool that appeared out of nowhere — and then got called — is something you can actually trace back, and gate’s free MCP security scanner checks a server’s current tool descriptions before you connect, which is the first read, not the last.

The bottom line

notifications/tools/list_changed is MCP’s way of saying “re-fetch the list, something moved” — nothing about what changed, nothing about why, and no promise a client acts on it right away. It matters for servers whose tools genuinely depend on plan tier, auth state, or configuration that can shift mid-session; it’s irrelevant for the static tool list most servers ship. Either way, the notification tells you a list moved. Whether that movement is routine or worth a second look is a judgment about the server, not something the protocol decides for you.