fix(mcp): deserialise MCPToolset from Redis cache hit, replace fastapi import in test

This commit is contained in:
Ishaan Jaffer 2026-03-23 14:14:11 -07:00
parent 1cff05e87d
commit feacbedf57
2 changed files with 18 additions and 3 deletions

View file

@ -891,16 +891,27 @@ class MCPServerManager:
) -> Optional[Any]:
"""Return a toolset by name, cached in ``user_api_key_cache`` (Redis-backed
``DualCache`` in production) to avoid a DB hit on every routed request.
Serialisation note: the cache value is stored as a plain JSON-safe dict via
``model_dump(mode="json")`` so that Redis round-trips correctly in multi-worker
deployments. On a cache hit we reconstruct the ``MCPToolset`` Pydantic object
so callers can always use attribute access (e.g. ``toolset.toolset_id``).
"""
from litellm.constants import DEFAULT_MANAGEMENT_OBJECT_IN_MEMORY_CACHE_TTL
from litellm.proxy.proxy_server import user_api_key_cache
from litellm.types.mcp_server.mcp_toolset import MCPToolset
cache_key = f"toolset_name:{toolset_name}"
cached = await user_api_key_cache.async_get_cache(key=cache_key)
if cached is not None:
# Sentinel value used to cache "not found" so we don't re-query for
# names that don't exist.
return None if cached == "__not_found__" else cached
if cached == "__not_found__":
return None
# Redis deserialises JSON back as a plain dict — reconstruct the model.
if isinstance(cached, dict):
return MCPToolset(**cached)
return cached
from litellm.proxy._experimental.mcp_server.toolset_db import (
get_mcp_toolset_by_name,
@ -909,7 +920,11 @@ class MCPServerManager:
toolset = await get_mcp_toolset_by_name(prisma_client, toolset_name)
await user_api_key_cache.async_set_cache(
key=cache_key,
value=toolset if toolset is not None else "__not_found__",
value=(
toolset.model_dump(mode="json")
if toolset is not None
else "__not_found__"
),
ttl=DEFAULT_MANAGEMENT_OBJECT_IN_MEMORY_CACHE_TTL,
)
return toolset

View file

@ -84,7 +84,7 @@ class TestApplyToolsetScope:
@pytest.mark.asyncio
async def test_non_admin_no_object_permission_raises_403(self):
"""Non-admin key with object_permission=None is denied (no grants configured)."""
from fastapi import HTTPException
from starlette.exceptions import HTTPException
from litellm.proxy._experimental.mcp_server.server import _apply_toolset_scope