mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-12 23:01:41 +00:00
The MCP sub-app is attached with app.mount("/mcp", ...) and a Starlette
mount never matches its bare prefix, so POST /mcp fell through to the
router's redirect_slashes 307. Behind a TLS-terminating ingress whose
peer address is not in uvicorn's forwarded-allow-ips (default: loopback
only) the redirect Location is built from the socket scheme as http://,
and MCP clients strip the Authorization header on the cross-origin
follow, so reconnects fail with ECONNRESET right after a successful
OAuth flow. The redirect also fires before auth, so the bare spelling
never returns the RFC 9728 WWW-Authenticate challenge that OAuth
clients need to start the flow.
Add an explicit /mcp route beside the existing /toolset/{name}/mcp and
/{name}/mcp spellings, forwarding to handle_streamable_http_mcp with
the same scope rewrite those routes already use (path=/mcp,
_original_path preserved for OAuth challenge URL selection). When the
mcp package is unavailable the route 404s, matching what the bare
sub-app serves on /mcp/ in that state. /mcp/, /mcp/{server},
/{server}/mcp and /toolset/{name}/mcp spellings are unchanged; the
exact-match route and the mount have disjoint match sets so
registration order cannot matter.
158 lines
3.9 KiB
Python
158 lines
3.9 KiB
Python
"""Path allowlist for the UI backend (control plane) component.
|
|
|
|
The backend exposes management/admin endpoints consumed by the UI: keys, users,
|
|
teams, orgs, customers, budgets, tags, workflows, model management, spend &
|
|
analytics, settings (router/cache/cost-tracking/fallbacks), SSO/onboarding,
|
|
audit logs, debug, enterprise admin, and UI bootstrap helpers (logo, favicon,
|
|
.well-known config).
|
|
|
|
Anything LLM data-plane is dropped — those run on the gateway component.
|
|
"""
|
|
|
|
BACKEND_PATH_PREFIXES: tuple[str, ...] = (
|
|
# Identity / access
|
|
"/key/",
|
|
"/v2/key/",
|
|
"/user/",
|
|
"/v2/user/",
|
|
"/team/",
|
|
"/v2/team/",
|
|
"/organization/",
|
|
"/v2/organization/",
|
|
"/customer/",
|
|
"/end_user/",
|
|
"/sso/",
|
|
"/login",
|
|
"/v2/login",
|
|
"/v3/login",
|
|
"/logout",
|
|
"/token",
|
|
"/onboarding/",
|
|
"/audit",
|
|
"/oauth/",
|
|
"/invitation/",
|
|
"/jwt/",
|
|
# Models & routing config
|
|
"/model/",
|
|
"/v1/model/info",
|
|
"/v2/model/",
|
|
"/model_group",
|
|
"/model_access_group/",
|
|
"/model_hub/",
|
|
"/v1/access_group",
|
|
"/access_group/",
|
|
"/router/",
|
|
"/router_settings",
|
|
"/adaptive_router/",
|
|
"/auto_router/",
|
|
"/fallback",
|
|
"/fallbacks",
|
|
"/cache_settings",
|
|
"/coordination_redis/",
|
|
"/cost_tracking",
|
|
"/cost/",
|
|
"/credentials",
|
|
"/credential",
|
|
"/provider/budgets",
|
|
# Tools / agents (registry & policy admin)
|
|
"/v1/tool/",
|
|
"/v1/agents",
|
|
# Guardrails admin
|
|
"/v2/guardrails/",
|
|
# MCP server admin + BYOK OAuth flow (UI-initiated) + dynamic per-server endpoints
|
|
"/v1/mcp/",
|
|
"/test/",
|
|
"/{mcp_server_name}/",
|
|
# Budgets / tags / workflows / memory mgmt
|
|
"/budget/",
|
|
"/tag/",
|
|
"/workflow/",
|
|
"/v1/workflows/",
|
|
"/project/",
|
|
"/memory/",
|
|
"/mcp/",
|
|
# Control plane (see the List Endpoints + Tables standard). Every resource
|
|
# eventually moves under this prefix, so allowlist it once rather than
|
|
# per-resource.
|
|
"/management/v1/",
|
|
# Spend / analytics
|
|
"/spend/",
|
|
"/analytics/",
|
|
"/global/",
|
|
"/user_agent",
|
|
"/usage/",
|
|
"/daily/",
|
|
# Deployment-wide gateway request counts. Scoped to the analytics read rather
|
|
# than all of /gateway/, which stays free for data-plane routes.
|
|
"/gateway/daily/",
|
|
# CloudZero cost-export admin (init / settings / export / dry-run / delete)
|
|
"/cloudzero/",
|
|
# Caching admin
|
|
"/cache/",
|
|
"/caching/",
|
|
# Callbacks / hooks
|
|
"/active/callbacks",
|
|
"/callbacks",
|
|
"/team_callback",
|
|
# Rust data-plane gateway → proxy control-plane API (logging today, auth later)
|
|
"/v1/rust_control_plane/",
|
|
# Alerting / email / IP allowlist
|
|
"/alerting/",
|
|
"/email/",
|
|
"/add/allowed_ip",
|
|
"/delete/allowed_ip",
|
|
"/get/",
|
|
# Enterprise admin
|
|
"/enterprise/",
|
|
# Debug / config / profiling
|
|
"/debug/",
|
|
"/config/",
|
|
"/memory-usage-in-mem-cache",
|
|
"/otel-spans",
|
|
"/lazy/",
|
|
"/in_product_nudges",
|
|
# Admin reload / schedule
|
|
"/reload/",
|
|
"/schedule/",
|
|
"/settings",
|
|
"/update/",
|
|
"/upload/",
|
|
# Dev / admin utilities
|
|
"/utils/",
|
|
# UI bootstrap helpers (assets the dashboard fetches)
|
|
"/get_logo_url",
|
|
"/get_image",
|
|
"/get_favicon",
|
|
"/.well-known/",
|
|
"/litellm/.well-known/",
|
|
"/ui_discovery/",
|
|
"/ui-config",
|
|
"/sso_settings",
|
|
"/public/",
|
|
"/robots.txt",
|
|
# Health (k8s probes)
|
|
"/health",
|
|
# Plugin system
|
|
"/api/plugins",
|
|
"/plugin-proxy/",
|
|
)
|
|
|
|
BACKEND_EXACT_PATHS: frozenset[str] = frozenset(
|
|
{
|
|
"/",
|
|
"/routes",
|
|
"/openapi.json",
|
|
"/docs",
|
|
"/docs/oauth2-redirect",
|
|
"/redoc",
|
|
"/fallback/login",
|
|
"/mcp", # bare spelling of the aggregate MCP endpoint; /mcp/ prefix covers the rest
|
|
}
|
|
)
|
|
|
|
BACKEND_MOUNT_PATHS: frozenset[str] = frozenset(
|
|
{
|
|
"/swagger", # API documentation static assets belong to the backend
|
|
"/mcp", # lazily-mounted MCP sub-app serves on the backend component
|
|
}
|
|
)
|