Repository navigation
[Infra] Merge dev branch #26197
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Infra] Merge dev branch #26197
Changes from 7 commits
9deefc0
99f007f
b6de470
1702b51
7656e26
c2b7c4b
bb46d36
8d52b1e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -1336,18 +1336,24 @@ async def add_session_mcp_server( | |||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| return _redact_mcp_credentials(temp_record) | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| def _get_cached_temporary_mcp_server_or_404(server_id: str) -> MCPServer: | ||||||||||||||||||||||||||||||||||||
| def _get_cached_temporary_mcp_server_or_404( | ||||||||||||||||||||||||||||||||||||
| server_id: str, request: Optional[Request] = None | ||||||||||||||||||||||||||||||||||||
| ) -> MCPServer: | ||||||||||||||||||||||||||||||||||||
| server = get_cached_temporary_mcp_server(server_id) | ||||||||||||||||||||||||||||||||||||
| if server is None: | ||||||||||||||||||||||||||||||||||||
| # Fall back to real DB/config server (e.g. for the user-side OAuth flow | ||||||||||||||||||||||||||||||||||||
| # which calls these endpoints with a real server_id, not a temp session id). | ||||||||||||||||||||||||||||||||||||
| from litellm.proxy._experimental.mcp_server.mcp_server_manager import ( | ||||||||||||||||||||||||||||||||||||
| global_mcp_server_manager, | ||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||
| from litellm.proxy.auth.ip_address_utils import IPAddressUtils | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| client_ip = IPAddressUtils.get_mcp_client_ip(request) if request else None | ||||||||||||||||||||||||||||||||||||
| server = global_mcp_server_manager.get_mcp_server_by_id( | ||||||||||||||||||||||||||||||||||||
| server_id | ||||||||||||||||||||||||||||||||||||
| ) or global_mcp_server_manager.get_mcp_server_by_name(server_id) | ||||||||||||||||||||||||||||||||||||
| ) or global_mcp_server_manager.get_mcp_server_by_name( | ||||||||||||||||||||||||||||||||||||
| server_id, client_ip=client_ip | ||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||
| if server is None: | ||||||||||||||||||||||||||||||||||||
| raise HTTPException( | ||||||||||||||||||||||||||||||||||||
| status_code=status.HTTP_404_NOT_FOUND, | ||||||||||||||||||||||||||||||||||||
|
|
@@ -1358,10 +1364,12 @@ def _get_cached_temporary_mcp_server_or_404(server_id: str) -> MCPServer: | |||||||||||||||||||||||||||||||||||
| @router.get( | ||||||||||||||||||||||||||||||||||||
| "/server/oauth/{server_id}/authorize", | ||||||||||||||||||||||||||||||||||||
| include_in_schema=False, | ||||||||||||||||||||||||||||||||||||
| dependencies=[Depends(user_api_key_auth)], | ||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||
|
Comment on lines
1364
to
1368
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Adding If the intent is that only pre-authenticated LiteLLM users can initiate the OAuth proxy flow (i.e. the MCP client always carries the LiteLLM key as a bearer token), this deserves a comment explaining the design and ideally a feature flag ( Rule Used: What: avoid backwards-incompatible changes without... (source) |
||||||||||||||||||||||||||||||||||||
| async def mcp_authorize( | ||||||||||||||||||||||||||||||||||||
| request: Request, | ||||||||||||||||||||||||||||||||||||
| server_id: str, | ||||||||||||||||||||||||||||||||||||
| user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth), | ||||||||||||||||||||||||||||||||||||
|
Comment on lines
1364
to
+1372
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
FastAPI deduplicates by callable per request, so having
Suggested change
|
||||||||||||||||||||||||||||||||||||
| client_id: Optional[str] = None, | ||||||||||||||||||||||||||||||||||||
| redirect_uri: str = Query(...), | ||||||||||||||||||||||||||||||||||||
| state: str = "", | ||||||||||||||||||||||||||||||||||||
|
|
@@ -1370,7 +1378,7 @@ async def mcp_authorize( | |||||||||||||||||||||||||||||||||||
| response_type: Optional[str] = None, | ||||||||||||||||||||||||||||||||||||
| scope: Optional[str] = None, | ||||||||||||||||||||||||||||||||||||
| ): | ||||||||||||||||||||||||||||||||||||
| mcp_server = _get_cached_temporary_mcp_server_or_404(server_id) | ||||||||||||||||||||||||||||||||||||
| mcp_server = _get_cached_temporary_mcp_server_or_404(server_id, request=request) | ||||||||||||||||||||||||||||||||||||
| # Use the server's stored client_id when the caller doesn't supply one | ||||||||||||||||||||||||||||||||||||
| resolved_client_id = mcp_server.client_id or client_id or "" | ||||||||||||||||||||||||||||||||||||
| if not resolved_client_id: | ||||||||||||||||||||||||||||||||||||
|
|
@@ -1399,10 +1407,12 @@ async def mcp_authorize( | |||||||||||||||||||||||||||||||||||
| @router.post( | ||||||||||||||||||||||||||||||||||||
| "/server/oauth/{server_id}/token", | ||||||||||||||||||||||||||||||||||||
| include_in_schema=False, | ||||||||||||||||||||||||||||||||||||
| dependencies=[Depends(user_api_key_auth)], | ||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||
| async def mcp_token( | ||||||||||||||||||||||||||||||||||||
| request: Request, | ||||||||||||||||||||||||||||||||||||
| server_id: str, | ||||||||||||||||||||||||||||||||||||
| user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth), | ||||||||||||||||||||||||||||||||||||
| grant_type: str = Form(...), | ||||||||||||||||||||||||||||||||||||
| code: Optional[str] = Form(None), | ||||||||||||||||||||||||||||||||||||
| redirect_uri: Optional[str] = Form(None), | ||||||||||||||||||||||||||||||||||||
|
|
@@ -1412,7 +1422,7 @@ async def mcp_token( | |||||||||||||||||||||||||||||||||||
| refresh_token: Optional[str] = Form(None), | ||||||||||||||||||||||||||||||||||||
| scope: Optional[str] = Form(None), | ||||||||||||||||||||||||||||||||||||
| ): | ||||||||||||||||||||||||||||||||||||
| mcp_server = _get_cached_temporary_mcp_server_or_404(server_id) | ||||||||||||||||||||||||||||||||||||
| mcp_server = _get_cached_temporary_mcp_server_or_404(server_id, request=request) | ||||||||||||||||||||||||||||||||||||
| resolved_client_id = mcp_server.client_id or client_id or "" | ||||||||||||||||||||||||||||||||||||
| if not resolved_client_id: | ||||||||||||||||||||||||||||||||||||
| raise HTTPException( | ||||||||||||||||||||||||||||||||||||
|
|
@@ -1441,9 +1451,14 @@ async def mcp_token( | |||||||||||||||||||||||||||||||||||
| @router.post( | ||||||||||||||||||||||||||||||||||||
| "/server/oauth/{server_id}/register", | ||||||||||||||||||||||||||||||||||||
| include_in_schema=False, | ||||||||||||||||||||||||||||||||||||
| dependencies=[Depends(user_api_key_auth)], | ||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||
| async def mcp_register(request: Request, server_id: str): | ||||||||||||||||||||||||||||||||||||
| mcp_server = _get_cached_temporary_mcp_server_or_404(server_id) | ||||||||||||||||||||||||||||||||||||
| async def mcp_register( | ||||||||||||||||||||||||||||||||||||
| request: Request, | ||||||||||||||||||||||||||||||||||||
| server_id: str, | ||||||||||||||||||||||||||||||||||||
| user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth), | ||||||||||||||||||||||||||||||||||||
| ): | ||||||||||||||||||||||||||||||||||||
| mcp_server = _get_cached_temporary_mcp_server_or_404(server_id, request=request) | ||||||||||||||||||||||||||||||||||||
| request_data = await _read_request_body(request=request) | ||||||||||||||||||||||||||||||||||||
| data: dict = {**request_data} | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
>→>=on user budget checkThe original condition
user_budget < user_object.spendblocked requests only when spend strictly exceeds the budget. The new conditionuser_spend >= user_budgetalso blocks when spend equals the budget exactly. A user who has spent precisely their budget will now be rejected on the next request, whereas previously they could still make one more call. This is likely intentional stricter enforcement, but it's worth documenting in a comment since it's a visible behavior change for anyone sitting exactly at their limit.