An MCP connection can stay healthy while its tool catalog becomes stale.
Imagine a server that initially exposes a setup tool. Calling it makes another tool available, and the server sends a tool-list change notification. If the agent keeps using the catalog it fetched at connection time, its next model request still describes the old tools.
AIClaw 3.7.23 closes that gap in the desktop Go kernel. It listens for catalog changes, fetches an updated list, and installs it before the next model request—even within the same agent turn.
This is shipped behavior in v3.7.23, not a proposal. This article focuses on dynamic discovery in the desktop kernel; the earlier custom-tools article covered the broader extension model.
A notification marks the catalog as stale
The signal is the MCP notification named notifications/tools/list_changed.
AIClaw does not install new tools from the transport's reader goroutine. The notification increments a change generation. Later, RefreshTools checks whether that generation has already been applied.
That distinction has two useful effects:
- An unchanged catalog does not trigger another
tools/listrequest at every model step. - Concurrent users of a shared connection coalesce the refresh work instead of independently fetching the same catalog.
The client also reads paginated lists using nextCursor. It rejects empty or duplicate tool names, a cursor that does not advance, and catalogs exceeding its page or tool limits. It publishes the fetched catalog only after the list is complete and no newer notification has invalidated that fetch.
The implementation is in mcpclient/client.go.
Install the update at the next model step
Receiving a notification and updating the model's available tools are separate operations.
The agent's sampling loop calls refreshMCP before calling the model. When a connection has a newer catalog, the session removes that server's previous tool registrations, mounts the new catalog, and refreshes its system instructions.
A simplified sequence looks like this:
- The model calls an existing MCP tool.
- The server changes its catalog and sends a notification.
- The current tool call returns.
- Before the next model request, AIClaw refreshes and installs the catalog.
- The model can see and call the newly available tool.
An already admitted call is not interrupted by a transport notification. The catalog update takes effect at the sampling boundary. It does not rewrite a model request that is already in flight.
See the refresh boundary and session catalog installation.
Code Mode needs the update too
Refreshing only the direct tool definitions would leave Code Mode behind.
In Code Mode, MCP tools sit behind the executable tool bridge. AIClaw updates the underlying registry, rebuilds that bridge and its descriptions, and refreshes the instructions used for the next model request.
The desktop host receives a session/toolsUpdated event as well. The renderer applies it to the active session's tool information, so the displayed catalog can follow the kernel's catalog. Events for another session do not replace the active chat's tool list.
A failed refresh keeps the last usable catalog
A temporary tools/list failure should not turn a connected server into an empty tool list.
AIClaw retains the previous catalog, reports the refresh failure, and applies a one-second retry cooldown. The refresh operation has a five-second timeout. If notifications keep arriving during a fetch, it makes at most three attempts within that operation before backing off.
Keeping old definitions is a recovery strategy, not a guarantee that every old tool will still work. A server may already have removed a tool. The old catalog simply remains available while discovery recovers.
There is also a latency tradeoff: a pending refresh can delay the next model request. The five-second timeout bounds one refresh operation; it is not a claim that discovery is free.
Transport support and boundaries
The release handles notifications over stdio and in HTTP response SSE streams. For Streamable HTTP servers that advertise capabilities.tools.listChanged, it also opens the optional GET notification stream. Closing the transport cancels that stream.
The older HTTP+SSE transport with separate SSE and message endpoints remains unsupported. This change also depends on receiving notifications; it does not continuously poll servers that silently change their tools.
See the HTTP transport.
What was verified
For this article, I reran four targeted Go test functions with the race detector. All passed; the agent integration test includes both direct and Code Mode subtests.
The controlled tests cover coalesced refreshes, pagination, retaining the catalog after failure, retry recovery, notification/RPC response separation, and cancellation of the HTTP notification stream. The agent test changes a fixture server's tool list after a tool call, verifies that the next model request contains the new definition instead of the old one, invokes that new tool in the same turn, and checks the host update event.
These are local tests using a fixture MCP server and a scripted model. They do not establish compatibility with every third-party server or constitute a live production-model test.
The reproducible checks are in client refresh tests and the two-mode agent regression.
If an MCP server changes its tool surface while work is running, a connected socket is only half the contract. The next model request needs an accurate description of what it can call. This release makes that update an explicit part of the agent loop.
Top comments (0)