FIELD NOTES / 5 MIN READ

MCP Tools Can Change Mid-Session. How Does Your Bot Find Out Now?

The current Model Context Protocol specification, 2026-07-28, removed the always-on connection that used to push tool-list-changed notifications to every client. A bot now only hears about a changed tool list if it explicitly opened a subscriptions/listen stream and asked for that notification type. If it didn't, the tool list it is holding can go stale with no warning, and the fix is to either subscribe or re-poll tools/list on a schedule.

AI-assisted editorial: researched and drafted with AI using the primary sources linked below. Examples are illustrative; they are not measured product results. Editorial policy.

Key takeaways

  • In the 2026-07-28 spec, a server will not push a tools list_changed notification unless the client opened a subscriptions/listen stream and asked for toolsListChanged — silence no longer means nothing changed.
  • Servers still running 2025-11-25 or earlier push list_changed notifications automatically over the session's open connection; that behavior does not carry forward to 2026-07-28 and should not be assumed.
  • A list_changed notification is a cue to re-fetch, not a diff — your bot still has to call tools/list again and compare, because the notification carries no detail about what changed.

The short answer

If your bot talks to an MCP server on the current protocol revision, 2026-07-28, and it never opened a subscriptions/listen stream asking for toolsListChanged, it will not be told when the server's tool list changes. It has to go find out for itself, either by polling tools/list on its own schedule or by opening that stream.

This is a real behavior change, not a theoretical one. The previous model let a server announce tool changes over the connection it already had open with a client. The current model requires the client to ask first, and the server only delivers to clients that asked.

What changed: unsolicited notifications became opt-in subscriptions

The 2026-07-28 revision made the MCP protocol core stateless. The spec's changelog describes this directly: the Streamable HTTP transport no longer carries protocol-level sessions, and the old initialize/initialized handshake that used to establish a persistent connection is gone entirely. Every request now declares its own protocol version and capabilities instead of relying on a session that was set up once and reused.

That statelessness is what forces notifications to change shape. The changelog spells out the replacement mechanism directly: the server-push model is replaced by subscriptions/listen, described as a single long-lived POST-response stream for opted-in server-to-client change notifications, where the client names the specific types it wants and the server tags matching notifications accordingly.

The official architecture documentation confirms the mechanics from the client's side: change notifications are opt-in, so a client has to open a long-lived subscriptions/listen stream and name the event types it wants, such as toolsListChanged, before the server will deliver notifications/tools/list_changed to it. The tools specification page states the same rule for servers: a server that declared the listChanged capability should send the notification to clients that have opened a subscriptions/listen stream with toolsListChanged set to true — not to every connected client, because there is no longer a fixed set of "connected" clients in the old sense.

One detail matters for anyone building a dashboard or audit log on top of this: the notification itself carries no payload describing what changed. It's a bare signal. The tools page shows the message as just a method name with no params. A bot that receives it still has to call tools/list again and diff the result against what it already has cached.

Sources: Key Changes - Model Context Protocol, Architecture overview - Model Context Protocol, Tools - Model Context Protocol.

A worked example (illustrative)

Say your bot connects to a fictional MCP server, 'shared-drive', that exposes one tool per folder a user has shared with it: read_folder_minutes, read_folder_specs, and so on. A teammate shares a new folder mid-afternoon, and the server now wants to expose read_folder_roadmap as a new tool.

If your bot opened a subscriptions/listen stream at startup and asked for toolsListChanged, it receives a bare notifications/tools/list_changed message on that stream within moments of the share happening. Your bot's job is then simple: call tools/list, see the new tool, and decide whether to make it available to the model in the next turn.

If your bot never opened that stream — maybe because the client library you used defaults to not subscribing, or because you built against an older MCP client that doesn't implement subscriptions/listen at all — the new tool simply does not exist for your bot until it happens to call tools/list again for some other reason, or until you restart the connection. There is no error, no timeout, nothing to catch in a try/except block. The absence is silent by design.

This is also where cross-call state interacts with tool-list changes: because 2026-07-28 removed sessions, any handle your bot was passing between calls (a basket_id or similar) has to be an explicit argument the model threads through, not something tied to the connection that disappears if the tool list refreshes. The two problems — missed notifications and lost session state — are separate, but they show up in the same migration.

Sources: Architecture overview - Model Context Protocol.

Where this breaks: legacy servers and the SDK boundary

Not every MCP server your bot talks to has moved to 2026-07-28 yet. Server-side SDK migration guidance from the official TypeScript SDK documentation notes that a server can be built to serve both eras from one endpoint, defaulting to a legacy stateless mode for older traffic while also handling the new revision — which means the same server might behave differently depending on which protocol version a given request declares.

The same SDK documentation is explicit about the split: the 2026-07-28 revision delivers tools, prompts, and resources list_changed notifications, along with resource updates, only on a subscriptions/listen stream the client opened, and the server never sends an unrequested notification type — while it also notes that the older, 2025-era unsolicited delivery model is unchanged on legacy connections. If your bot's MCP client library was written before this split existed, check whether it opens a subscriptions/listen stream automatically or leaves that to you; many early client implementations did not.

The transport specification adds one more wrinkle worth knowing before you debug a 'missing notification': over Streamable HTTP, there are no client-to-server notifications in the core protocol at all except cancellation signaled by closing the stream, and change notifications only ever arrive on the response stream of a subscriptions/listen request your bot initiated, never bundled into the response of an unrelated tool call. If your bot is only listening to the response stream of each tools/call, it will never see a list_changed notification no matter how long it waits, because that notification lives on a different stream entirely.

Sources: Supporting protocol revision 2026-07-28, Streamable HTTP - Model Context Protocol.

What to build into your bot today

Treat tool-list freshness as something your bot owns, not something the server guarantees it will hear about. Two paths get there, and most builders need both.

  • If your MCP client library supports subscriptions/listen, open it at connection time and ask for toolsListChanged explicitly — don't assume a default subscribes you.
  • Regardless of subscription status, re-fetch tools/list on a fixed interval (minutes, not hours, for servers you know change often) so a missed or unsent notification doesn't leave your bot working from a stale tool set indefinitely.
  • On receiving notifications/tools/list_changed, always re-call tools/list before acting — the notification has no payload, so there's nothing to act on until you fetch the current list.
  • Check which protocol revision each server you connect to actually speaks before relying on unsolicited push behavior; a server still on 2025-11-25 or earlier pushes automatically, one on 2026-07-28 does not unless you subscribed.

Primary sources

Sources checked 2026-10-04. Standards and product documentation can change; follow the linked version when implementing.

  1. Key Changes - Model Context Protocol — Model Context Protocol
  2. Architecture overview - Model Context Protocol — Model Context Protocol
  3. Tools - Model Context Protocol — Model Context Protocol
  4. Streamable HTTP - Model Context Protocol — Model Context Protocol
  5. Supporting protocol revision 2026-07-28 — Model Context Protocol TypeScript SDK

BotBento is in development. Suggest a correction.