fix(mcp): add expression to uv.lock and guard the v2 bridge import

The v2 resolver graft pulls in the expression library (via the outbound_credentials types). It
was declared in pyproject but uv.lock was never regenerated, so the runtime image (built with
uv sync --frozen) shipped without it; configuring any MCP server then crashed the proxy on
startup with ModuleNotFoundError: expression. Regenerate uv.lock to include expression, and
guard the bridge import in oauth2_token_cache so a missing v2 dependency degrades to v1 instead
of taking down the whole MCP feature.
This commit is contained in:
Tin Chi Lo 2026-06-18 11:50:35 -07:00
parent e8ff27dd9a
commit ae218ae9d8
2 changed files with 32 additions and 7 deletions

View file

@ -27,11 +27,21 @@ from litellm.proxy.common_utils.encrypt_decrypt_utils import (
encrypt_value_helper,
)
from litellm.proxy._experimental.mcp_server.auth import token_exchange
from litellm.proxy._experimental.mcp_server.v2_resolver_bridge import (
resolve_v2_auth_value,
)
from litellm.types.llms.custom_http import httpxSpecialProvider
# The v2 resolver bridge pulls in the v2 gateway package (and its `expression` dependency).
# Guard the import so a missing optional dep degrades this graft to v1 rather than taking down
# the entire MCP feature on startup.
try:
from litellm.proxy._experimental.mcp_server.v2_resolver_bridge import (
resolve_v2_auth_value,
)
except ImportError as _e:
verbose_logger.warning(
"v2 MCP resolver bridge unavailable (%s); using v1 resolution", _e
)
resolve_v2_auth_value = None # type: ignore[assignment]
if TYPE_CHECKING:
from litellm.types.mcp_server.mcp_server_manager import MCPServer
@ -290,9 +300,10 @@ async def resolve_mcp_auth(
"""
if mcp_auth_header:
return mcp_auth_header
v2_auth_value = await resolve_v2_auth_value(server)
if v2_auth_value is not None:
return v2_auth_value
if resolve_v2_auth_value is not None:
v2_auth_value = await resolve_v2_auth_value(server)
if v2_auth_value is not None:
return v2_auth_value
if server.has_token_exchange_config:
if subject_token:
return await token_exchange.mcp_token_exchange_handler.exchange_token(

16
uv.lock generated
View file

@ -9,7 +9,7 @@ resolution-markers = [
]
[options]
exclude-newer = "2026-06-14T15:53:04.946308996Z"
exclude-newer = "0001-01-01T00:00:00Z" # This has no effect and is included for backwards compatibility when using relative exclude-newer values.
exclude-newer-span = "P3D"
[manifest]
@ -1477,6 +1477,18 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017", size = 28317, upload-time = "2025-09-01T09:48:08.5Z" },
]
[[package]]
name = "expression"
version = "5.6.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/43/c7/bb061623b5815566bda69f5e9d156e38a97ebb383b8db3d2dedb26415466/expression-5.6.0.tar.gz", hash = "sha256:454f6fe138347194a43c7f878d958efe9b84b9cc770e462010c7a52e18058065", size = 59147, upload-time = "2025-02-19T09:37:37.432Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/6b/a2/656b8bebe495117342a8676ccabf52b3885ce11a856c8dfe1fbbdc250d2d/expression-5.6.0-py3-none-any.whl", hash = "sha256:f5c62e38186c9287e088dee9cf3939b0bbde21cb4c59571872154a53d33dd7c0", size = 69673, upload-time = "2025-02-19T09:37:35.476Z" },
]
[[package]]
name = "fakeredis"
version = "2.34.1"
@ -3285,6 +3297,7 @@ proxy = [
{ name = "backoff" },
{ name = "boto3" },
{ name = "cryptography" },
{ name = "expression" },
{ name = "fastapi" },
{ name = "fastapi-sso" },
{ name = "granian" },
@ -3446,6 +3459,7 @@ requires-dist = [
{ name = "ddtrace", marker = "extra == 'proxy-runtime'", specifier = ">=2.19.0,<3.0" },
{ name = "detect-secrets", marker = "extra == 'proxy-runtime'", specifier = ">=1.5.0,<2.0" },
{ name = "diskcache", marker = "extra == 'caching'", specifier = ">=5.6.3,<6.0" },
{ name = "expression", marker = "extra == 'proxy'", specifier = ">=5.6.0,<6.0" },
{ name = "fastapi", marker = "extra == 'proxy'", specifier = ">=0.136.3,<1.0" },
{ name = "fastapi-sso", marker = "extra == 'proxy'", specifier = ">=0.19.0,<1.0" },
{ name = "fastuuid", specifier = ">=0.14.0,<1.0" },