fix(proxy): hydrate MCP server registry from DB on startup when store_model_in_db is false - #31775
Conversation
…_model_in_db is false MCP servers created through the UI are persisted to the database independent of store_model_in_db, but the in-memory registry that GET /v1/mcp/server reads was hydrated from the database only through add_deployment, which runs solely when store_model_in_db is True. On a DB-backed single-instance proxy with store_model_in_db unset the registry started empty after a restart, so the MCP Servers page showed nothing until an add or edit triggered a reload. Hydrate the registry from the database on startup regardless of store_model_in_db via a new ProxyConfig.init_mcp_servers_from_db, honoring supported_db_objects.
Greptile SummaryThis PR fixes a bug where MCP servers added through the UI (and stored in
Confidence Score: 4/5Safe to merge — the change is a minimal, targeted startup hook with no impact on the request hot path. The new No files require special attention.
|
| Filename | Overview |
|---|---|
| litellm/proxy/proxy_server.py | Adds ProxyConfig.init_mcp_servers_from_db and calls it in initialize_scheduled_background_jobs when store_model_in_db is not True, ensuring the in-memory MCP registry is populated from the DB on startup even without the model-sync loop. |
| tests/test_litellm/proxy/test_proxy_server.py | Adds two regression tests: one verifying init_mcp_servers_from_db is awaited (and add_deployment is not called) during startup when store_model_in_db=False, and one verifying supported_db_objects allowlist is respected. Both use only mocks — no real network calls. |
Reviews (1): Last reviewed commit: "fix(proxy): hydrate MCP server registry ..." | Re-trigger Greptile
Greptile SummaryThis PR fixes a startup bug where MCP servers persisted to the database via the UI would disappear from the in-memory registry after a proxy restart when
Confidence Score: 5/5Safe to merge — the change is a targeted startup-only hydration call with no impact on the hot request path. The new method is a 4-line wrapper that mirrors logic already present in No files require special attention.
|
| Filename | Overview |
|---|---|
| litellm/proxy/proxy_server.py | Adds init_mcp_servers_from_db (4 lines) and one call in initialize_scheduled_background_jobs (3 lines); correctly gated after the DB store_model_in_db override check so the True path is untouched. |
| tests/test_litellm/proxy/test_proxy_server.py | Two new async regression tests: one verifies startup hydration on the store_model_in_db=False path, the other verifies supported_db_objects filtering — both are pure mock-based tests, no real network calls. |
Reviews (2): Last reviewed commit: "fix(proxy): hydrate MCP server registry ..." | Re-trigger Greptile
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
|
bugbot run |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit ed5429a. Configure here.
…_model_in_db is false (BerriAI#31775) MCP servers created through the UI are persisted to the database independent of store_model_in_db, but the in-memory registry that GET /v1/mcp/server reads was hydrated from the database only through add_deployment, which runs solely when store_model_in_db is True. On a DB-backed single-instance proxy with store_model_in_db unset the registry started empty after a restart, so the MCP Servers page showed nothing until an add or edit triggered a reload. Hydrate the registry from the database on startup regardless of store_model_in_db via a new ProxyConfig.init_mcp_servers_from_db, honoring supported_db_objects.
Relevant issues
Resolves LIT-4128
Reported internally: MCP servers added through the UI disappear from the MCP Servers page after a single-instance restart, and only reappear once a new server is added
Linear ticket
LIT-4128
Pre-Submission checklist
@greptileaiand received a Confidence Score of at least 4/5 before requesting a maintainer reviewThis is a backend fix (no dashboard changes)
The symptom looks like a UI problem, but the dashboard is not at fault. The MCP Servers page already fetches the list on mount through a React Query hook (
useMCPServers) and re-fetches after every write, so an empty page reflects whatGET /v1/mcp/serveractually returned rather than stale client state. On a cold start the endpoint itself returned an empty list even though the rows were still in the database, so a client-side change (a different hook, a hard refresh) would just re-fetch the same empty result. The fix is entirely server side inlitellm/proxy/proxy_server.py; there are no frontend editsRoot cause
MCP servers created through the UI are written straight to the database (
LiteLLM_MCPServerTable) and are independent ofstore_model_in_db, butGET /v1/mcp/serverreads only from the in-memory registry onglobal_mcp_server_manager, never from the database. That registry is hydrated from the database on startup exclusively throughadd_deployment -> _init_non_llm_objects_in_db -> _init_mcp_servers_in_db -> reload_servers_from_database, and the initialadd_deploymentcall plus its polling job run only inside theif store_model_in_db is True:guard inProxyStartupEvent.initialize_scheduled_background_jobsConfig-yaml servers load unconditionally on startup, but database servers ride the
store_model_in_dbmodel-sync loop. So on a DB-backed single-instance proxy wherestore_model_in_dbis not set, a restart leaves the registry empty, and the page stays empty until any MCP write endpoint (add, edit, delete, approve, reject) callsreload_servers_from_databaseand rebuilds the registry from the database. That write side effect is why adding one server makes every previously-added server reappearFix
Hydrate the MCP registry from the database on startup regardless of
store_model_in_db, so it matches how config servers already behave. A new publicProxyConfig.init_mcp_servers_from_dbwraps the existing loader and honorssupported_db_objects;initialize_scheduled_background_jobscalls it whenstore_model_in_dbis notTrue(when it isTrue, the existing model-sync loop already handles hydration, so nothing changes for those deployments)Screenshots / Proof of Fix
Single-instance proxy, Postgres attached,
store_model_in_dbunset (defaultFalse), started withpython litellm/proxy/proxy_cli.py --config config.yamlThe same is visible in the dashboard at
http://localhost:4000/ui/?page=mcp-servers: empty after a restart before this change, populated after a restart with itType
🐛 Bug Fix
Changes
litellm/proxy/proxy_server.py: addedProxyConfig.init_mcp_servers_from_dband a startup call to it ininitialize_scheduled_background_jobsfor thestore_model_in_dbis notTruepathtests/test_litellm/proxy/test_proxy_server.py: a regression test asserting the startup path hydrates MCP servers whenstore_model_in_dbisFalse(and does not runadd_deployment), plus a test thatinit_mcp_servers_from_dbhonorssupported_db_objects