Repository navigation
feat(api): rebuild the agent when the toolsets' tools change - #218
Merged
Merged
Conversation
The API built the agent once at startup and kept its tool list, and the views cache read the bundles once, both for the life of the process. A toolset redeployed after that reached the model with its old descriptions, and the UI with its old views, until the API restarted. Every MCP_AGENT_API_REFRESH_SECONDS (default 1800, 0 for never) the app builds the agent again and swaps it in when the tools' names, descriptions or arguments changed. A failed build keeps the current agent. The views cache re-reads the bundles on the same interval, keeping any a toolset did not answer for. keep_fresh is public, for a host with its own lifespan. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ciaransweet
marked this pull request as ready for review
October 28, 2026 10:06
j08lue
approved these changes
Oct 28, 2026
j08lue
left a comment
Member
There was a problem hiding this comment.
I don't fully understand how this works (create_app), but it looks clean.
| how you say "not yet", and that surfaces as `503`. An agent rebuilt behind it, | ||
| on a model change or a reconnect, is picked up without remounting. | ||
|
|
||
| **Your lifespan owns the refresh.** `create_app` rebuilds the agent when the |
Member
There was a problem hiding this comment.
Your lifespan owns the refresh
This cryptic / condensed LLM-speak freaks me out.
ciaransweet
pushed a commit
that referenced
this pull request
Oct 28, 2026
🤖 I have created a release *beep* *boop* --- ## [0.12.1](mcp-toolsets-runtime-v0.12.0...mcp-toolsets-runtime-v0.12.1) (2026-10-28) ### Features * **api:** rebuild the agent when the toolsets' tools change ([#218](#218)) ([e1b5707](e1b5707)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: ds-release-bot[bot] <116609932+ds-release-bot[bot]@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
create_appbuilt the agent once at startup and kept its tool list for the life of the process.ViewCachedid the same with the view bundles. A toolset redeployed after the API started reached the model with its old descriptions, and the UI with its old views, until something restarted the API.This happened in production on dss (ecmwf/dss-agentic-ai-services#157). The knowledge-base toolset gained an
announcementssection, but the agent still had the oldsearch_knowledgedescription, so for two days it never searched announcements. Locally the bug can't be seen, because a fresh process always lists the current tools.What
mcp_agent_api.refresh(new, about 60 lines)MCP_AGENT_API_REFRESH_SECONDSsets the interval: default1800,0for never.fingerprint(built)hashes what the model sees of the tools: their names, descriptions and arguments.keep_fresh(app, build, every)runs the same build as startup on that interval, and swapsapp.state.builtonly when the fingerprint changed. A failed build keeps the current agent; a toolset that's down makeslist_toolsraise, so it never silently drops that toolset's tools.create_appStarts
keep_freshbeside the lifespan'syieldand cancels it on shutdown.ViewCacheRe-reads the bundles at most once per interval. A toolset that doesn't answer a re-read keeps the bundles it had.
Behaviour
A turn already running finishes on the agent it started with, because the routes read the agent once per request. Threads carry on across a swap, because the checkpointer is shared. Each refresh costs one connections request plus one
tools/listper toolset.A host with its own lifespan (dss) starts the same task itself. The example is in Mounting the routes into your own application in
CONSUMING.md. Behaviour changes by default: existingcreate_appdeployments start refreshing every 30 minutes, and0restores the old behaviour.In dss
dss builds its own app around
create_router, so it starts the refresh in its own lifespan. Once it takes this release,packages/dss-agent/src/dss_agent/api.pychanges like this:MCP_AGENT_API_REFRESH_SECONDScan then be set in the chart's API values; left unset, it refreshes every 30 minutes.Tests
create_app: an unchanged tool list and a failed build both keep the current agent, and a changed description swaps it.0builds the agent once.611 passed. Lint clean.
🤖 Generated with Claude Code