mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-03 02:22:24 +00:00
fix(mcp): use global fallback before raising missing per-user env var 412
A variable declared with both global and user scope is covered by the global value (globals win in the merge), so the tool-call path must resolve it from the global rather than raising a 412 when the user has not filled in the per-user value. Restrict the missing-var check to referenced user vars that lack a global fallback.
This commit is contained in:
parent
2fb11f18da
commit
2da1e5e6fd
2 changed files with 54 additions and 6 deletions
|
|
@ -1645,12 +1645,18 @@ class MCPServerManager:
|
|||
if not global_values and not user_specs:
|
||||
return static_headers
|
||||
|
||||
# Figure out which user-scoped vars are actually referenced.
|
||||
# Figure out which user-scoped vars are actually referenced. A var that
|
||||
# also carries a global value is always covered by that global (globals
|
||||
# win in the merge below), so it can never be genuinely "missing" even if
|
||||
# the user hasn't filled it in -- only vars without a global fallback do.
|
||||
referenced = collect_env_var_references(strings=(static_headers or {}).values())
|
||||
referenced_user_vars = referenced & user_var_names
|
||||
required_user_vars = {
|
||||
name for name in referenced_user_vars if name not in global_values
|
||||
}
|
||||
|
||||
user_values: Dict[str, str] = {}
|
||||
if referenced_user_vars:
|
||||
if required_user_vars:
|
||||
try:
|
||||
user_values = await self._load_user_env_vars(server, user_api_key_auth)
|
||||
except Exception as exc:
|
||||
|
|
@ -1669,7 +1675,7 @@ class MCPServerManager:
|
|||
|
||||
if raise_on_missing:
|
||||
missing = sorted(
|
||||
name for name in referenced_user_vars if not user_values.get(name)
|
||||
name for name in required_user_vars if not user_values.get(name)
|
||||
)
|
||||
if missing:
|
||||
# A cached negative must never produce a 412: cache
|
||||
|
|
@ -1681,9 +1687,7 @@ class MCPServerManager:
|
|||
server, user_api_key_auth, force_refresh=True
|
||||
)
|
||||
missing = sorted(
|
||||
name
|
||||
for name in referenced_user_vars
|
||||
if not user_values.get(name)
|
||||
name for name in required_user_vars if not user_values.get(name)
|
||||
)
|
||||
if missing:
|
||||
raise MCPMissingUserEnvVarsError(
|
||||
|
|
|
|||
|
|
@ -446,6 +446,50 @@ async def test_resolve_static_headers_stale_user_value_cannot_override_global(
|
|||
assert headers == {"X-DB-URL": "admin-db/alice"}
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_resolve_static_headers_dual_scope_var_uses_global_without_412(
|
||||
monkeypatch,
|
||||
):
|
||||
"""A var declared with both ``global`` and ``user`` scope is covered by the
|
||||
global value (globals win in the merge), so the tool-call path must resolve
|
||||
it from the global instead of raising a 412 when the user hasn't filled it
|
||||
in. This happens during a global-to-user (or user-to-global) migration."""
|
||||
from litellm.proxy._experimental.mcp_server.mcp_server_manager import (
|
||||
MCPServerManager,
|
||||
)
|
||||
from litellm.types.mcp_server.mcp_server_manager import MCPServer
|
||||
|
||||
manager = MCPServerManager()
|
||||
server = MCPServer(
|
||||
server_id="srv-5",
|
||||
name="srv5",
|
||||
transport="http",
|
||||
url="https://example.com",
|
||||
static_headers={"Authorization": "Bearer ${SHARED_TOKEN}"},
|
||||
env_vars=[
|
||||
{"name": "SHARED_TOKEN", "value": "global-secret", "scope": "global"},
|
||||
{"name": "SHARED_TOKEN", "value": "", "scope": "user"},
|
||||
],
|
||||
)
|
||||
|
||||
load_calls = []
|
||||
|
||||
async def fake_load_user_env_vars(
|
||||
server, user_api_key_auth, *, force_refresh=False
|
||||
):
|
||||
load_calls.append(force_refresh)
|
||||
return {}
|
||||
|
||||
monkeypatch.setattr(manager, "_load_user_env_vars", fake_load_user_env_vars)
|
||||
|
||||
headers = await manager._resolve_static_headers_with_env_vars(
|
||||
server, user_api_key_auth=object()
|
||||
)
|
||||
assert headers == {"Authorization": "Bearer global-secret"}
|
||||
# The global fully covers the reference, so no per-user lookup is needed.
|
||||
assert load_calls == []
|
||||
|
||||
|
||||
# ── health-check skip for per-user-env-var-backed headers ──────────────────
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue