From 197fda79e4abd89de0aba2eb74cab42afb682589 Mon Sep 17 00:00:00 2001 From: Artur Sidorenko Date: Mon, 2 Nov 2026 11:02:46 +0100 Subject: [PATCH] fix(mcp): seed the catalog revision row so the catalog cache works before the first MCP server write litellm_bump_mcp_catalog_revision creates the mcp_catalog row in LiteLLM_Config only on the first write to LiteLLM_MCPServerTable. Until an admin changes an MCP server, get_mcp_catalog_revision returns None, which the catalog treats as "always reload": every MCP request reloads and deep-copies every server row. Seed the row in a migration that runs after the trigger migration; ON CONFLICT DO NOTHING keeps a revision the trigger already bumped. --- .../migration.sql | 8 +++++ .../test_mcp_catalog_revision_seed.py | 36 +++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 litellm-proxy-extras/litellm_proxy_extras/migrations/20261102000000_seed_mcp_catalog_revision/migration.sql create mode 100644 tests/unit/litellm_proxy_extras/test_mcp_catalog_revision_seed.py diff --git a/litellm-proxy-extras/litellm_proxy_extras/migrations/20261102000000_seed_mcp_catalog_revision/migration.sql b/litellm-proxy-extras/litellm_proxy_extras/migrations/20261102000000_seed_mcp_catalog_revision/migration.sql new file mode 100644 index 00000000000..2517f58072c --- /dev/null +++ b/litellm-proxy-extras/litellm_proxy_extras/migrations/20261102000000_seed_mcp_catalog_revision/migration.sql @@ -0,0 +1,8 @@ +SET LOCAL lock_timeout = '10s'; + +-- Seed the MCP catalog revision row. litellm_bump_mcp_catalog_revision only creates it on +-- the first write to "LiteLLM_MCPServerTable"; until then get_mcp_catalog_revision returns +-- None and every MCP request reloads the whole server catalog from the database. +INSERT INTO "LiteLLM_Config" ("param_name", "reload_revision") +VALUES ('mcp_catalog', 1) +ON CONFLICT ("param_name") DO NOTHING; diff --git a/tests/unit/litellm_proxy_extras/test_mcp_catalog_revision_seed.py b/tests/unit/litellm_proxy_extras/test_mcp_catalog_revision_seed.py new file mode 100644 index 00000000000..d9162e450b1 --- /dev/null +++ b/tests/unit/litellm_proxy_extras/test_mcp_catalog_revision_seed.py @@ -0,0 +1,36 @@ +"""The MCP catalog revision row must exist as soon as the trigger that bumps it does. + +``litellm_bump_mcp_catalog_revision`` only creates the row on the first write to +``LiteLLM_MCPServerTable``. Until then ``get_mcp_catalog_revision`` returns None, and the +catalog treats None as "always reload": every MCP request reloads every server row. +""" + +import re +from pathlib import Path +from typing import Final + +from litellm.proxy.mcp_server.db import MCP_CATALOG_REVISION_PARAM_NAME + +MIGRATIONS: Final = Path(__file__).resolve().parents[3] / "litellm-proxy-extras" / "litellm_proxy_extras" / "migrations" +TRIGGER: Final = "20260923000000_add_mcp_catalog_revision_trigger" +SEED: Final = "20261102000000_seed_mcp_catalog_revision" + + +def _statements(name: str) -> str: + sql: Final = (MIGRATIONS / name / "migration.sql").read_text(encoding="utf-8") + return " ".join(re.sub(r"--[^\n]*", "", sql).split()) + + +def test_the_seed_runs_after_the_migration_that_installs_the_trigger() -> None: + assert sorted([SEED, TRIGGER]) == [TRIGGER, SEED] + + +def test_the_seed_creates_the_row_the_catalog_reads() -> None: + assert ( + f'INSERT INTO "LiteLLM_Config" ("param_name", "reload_revision") ' + f"VALUES ('{MCP_CATALOG_REVISION_PARAM_NAME}', 1)" in _statements(SEED) + ) + + +def test_the_seed_keeps_a_revision_the_trigger_already_bumped() -> None: + assert _statements(SEED).endswith('ON CONFLICT ("param_name") DO NOTHING;')