Keep both sets of tests: upstream's OAuth2 token injection test and
our case-insensitive tool matching tests. Use upstream's version of
the bedrock output_config test (more comprehensive).
* fix(proxy): make async_post_call_response_headers_hook consistent across all endpoints
The response headers hook had 5 gaps that prevented callbacks from
reliably extracting routing metadata across endpoint types:
1. Hook never fired for /audio/transcriptions (endpoint bypasses
base_process_llm_request)
2. custom_llm_provider not accessible in hook data for any endpoint
3. custom_llm_provider not stamped in ResponsesAPIResponse._hidden_params
(unlike chat completions)
4. model_info under inconsistent keys (metadata vs litellm_metadata)
5. request_headers always None at all call sites
This adds a litellm_call_info parameter to the hook that normalizes
routing metadata (custom_llm_provider, model_info, api_base, model_id)
regardless of endpoint type. Also stamps custom_llm_provider on
Responses API responses, adds the hook call to the transcription
handler, and passes request_headers at all call sites.
Supersedes PR #21385.
* fix(proxy): address review feedback — safer backwards compat and None guards
- Replace try/except TypeError with inspect.signature() check for
litellm_call_info backwards compatibility. This avoids masking real
TypeErrors inside callback implementations and prevents double
invocation with inconsistent parameters.
- Use (data.get("key") or {}) instead of data.get("key", {}) to guard
against keys that exist with an explicit None value, which would
cause AttributeError on the subsequent .get() call.
* fix(proxy): cache inspect.signature result for callback compat check
Move the inspect.signature() call into a module-level helper with a
dict cache keyed by callback identity. Avoids repeated introspection
per request per callback in the hot path.
* fix(proxy): use class identity for signature cache key
Key the _CALLBACK_ACCEPTS_CALL_INFO cache by id(type(cb)) instead of
id(cb) to avoid stale entries from Python address reuse after GC.
All instances of the same callback class share the same method
signature, so class identity is both safer and more cache-efficient.
- Thread api_version through HTTP handlers to Azure realtime endpoints
- Make expires_at optional in RealtimeClientSecretResponse
- Fix test token expiry times to be in the future
- Populate user_id and team_id in minimal_auth for spend tracking
Made-with: Cursor
- user_dashboard.tsx: Replace userInfoCall with userGetInfoV2 for spend data,
remove keys/teams logic (keys come from props/useKeys hook, teams from fetchTeams)
- user_info_view.tsx: Replace userInfoCall with userGetInfoV2, flatten data
structure from nested {user_info: {...}} to flat response, fetch team details
separately using teamInfoCall, remove keys display (Virtual Keys section)
- Update user_dashboard.test.tsx and user_info_view.test.tsx mocks
Co-authored-by: yuneng-jiang <yuneng-jiang@users.noreply.github.com>
- Add UserInfoV2Response type and userGetInfoV2() function in networking.tsx
- Migrate useCurrentUser hook from userInfoCall to userGetInfoV2
- Update useCurrentUser.test.ts to test new v2 API integration
- The hook no longer needs userRole since the endpoint handles auth itself
Co-authored-by: yuneng-jiang <yuneng-jiang@users.noreply.github.com>
- Add UserInfoV2Response type in _types.py (returns only user object, no keys/teams)
- Add /v2/user/info endpoint handler with proper access control:
- Proxy admins can query any user
- Team admins can query users in their teams
- Internal users can query themselves only
- Returns 404 for unauthorized/not-found (not 403)
- Add /v2/user/info to info_routes in LiteLLMRoutes
- Add route check passthrough in route_checks.py
- Add get_user_v2() method to Python client
Co-authored-by: yuneng-jiang <yuneng-jiang@users.noreply.github.com>
- Add break after match in user_api_key_auth.py loop to avoid unnecessary
iterations over remaining mapped routes
- Guard against normalized_route being empty when route == root_path exactly,
which would otherwise match every mapped route via startswith("")
- Apply same empty-string guard in pass_through_endpoints.py for consistency
- Azure Model Router transform_response: let parent extract actual model from raw response
- common_request_processing: skip model override for Azure Model Router requests
- proxy_server: skip streaming chunk model restamp for Azure Model Router
- Add _is_azure_model_router_request helper
- Add tests for non-streaming and streaming
Made-with: Cursor
* fix(mcp): OAuth2 chat connect - tools fetch, auth flow, and status fixes
- schema.prisma: add missing MCP table fields (approval_status, submitted_by, submitted_at, reviewed_at, review_notes) to prevent destructive migrations
- rest_endpoints.py: inject user OAuth token via extra_headers for OAuth2 servers so tools list is populated; add server name->UUID resolution so MCPConnectPicker name lookups work
- mcp_registry.json: fix Atlassian defaults (transport: http, url: .../v1/mcp)
- ChatPage.tsx: read mcpOauthReturn param to init sidebarView="apps" on OAuth return, clean up param after mount
- MCPAppsPanel.tsx: auto-add OAuth2 servers to selectedServers when credential detected; onConnect also enables server for chat; disconnect removes from selectedServers
- mcp_servers.tsx: sort servers by created_at DESC
- useUserMcpOAuthFlow.tsx: append mcpOauthReturn=apps to return URL so Apps panel is mounted on return
* address greptile review feedback (greploop iteration 1)
* fix(mcp): inject stored OAuth2 token when fetching tools via /responses API
When a user has connected an OAuth2 MCP server (e.g. Atlassian) and then
uses the /responses endpoint with that server, tool listing was failing
because the stored per-user OAuth token was never injected.
Two fixes:
1. server.py: add _get_user_oauth_extra_headers_from_db() helper; call it
in _get_tools_from_mcp_servers when oauth2_headers is None for an OAuth2
server, falling back to the user's stored token in LiteLLM_MCPUserCredentials
2. litellm_proxy_mcp_handler.py: also intercept MCP tools whose server_url
matches */mcp/<server_name> (e.g. http://localhost:4000/mcp/atlassian_test)
by rewriting them to litellm_proxy/mcp/<server_name> so they go through
the internal handler (and get the OAuth token injected) instead of being
forwarded to OpenAI raw where localhost is unreachable
* address greptile review feedback (greploop iteration 2)
* test(mcp): add unit test for OAuth2 token injection in _get_tools_from_mcp_servers
Verifies that when _get_tools_from_mcp_servers is called for an OAuth2 MCP
server without oauth2_headers in the request, the implementation:
- calls _prefetch_oauth_creds_for_user once (not per-server) to avoid N+1 queries
- passes the stored token as extra_headers={"Authorization": "Bearer ..."} to
_get_tools_from_server so the upstream OAuth2 MCP server authenticates correctly
* address greptile review feedback (greploop iteration 3)
* address greptile review feedback (greploop iteration 4)
* address greptile review feedback (greploop iteration 5)
* redesign credentials table to use Tremor table layout matching Keys page
* fix: /server/oauth authorize 422 - make client_id optional, fall back to real DB server
* fix: mcp_token client_id optional, resolve from server record
* fix: look up real server by UUID (get_mcp_server_by_id) before falling back to name
* Update litellm/responses/mcp/litellm_proxy_mcp_handler.py
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
* fix: address greptile feedback - client_id guards, dict spread, helper refactor, tests
- mcp_management_endpoints: raise 400 when resolved_client_id is empty in
mcp_authorize and mcp_token instead of forwarding "" to upstream
- litellm_proxy_mcp_handler: use {**tool, "server_url": ...} spread instead
of dict(tool) + mutation for shallow copy safety
- rest_endpoints: extract _oauth2_server_ids set comprehension to a named
_get_oauth2_server_ids() helper for clarity; add Set to typing imports
- test_rest_endpoints: add tests for name→UUID resolution path,
access-denied when resolved UUID not in allowed list, and OAuth2 user
token injection for single-server requests; fix fake_get_tools signature
to accept extra_headers kwarg
---------
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>