fix(ui): honor LITELLM_UI_API_DOC_BASE_URL on the MCP Servers Connect and Toolsets tabs - #35587
Conversation
… and Toolsets tabs The Connect tab and the toolset endpoint URLs built every copy-paste snippet from getProxyBaseUrl(), so an operator who sets LITELLM_UI_API_DOC_BASE_URL to the public gateway URL still handed users the proxy base Adds a useDocBaseUrl hook that prefers a non-blank LITELLM_UI_API_DOC_BASE_URL and falls back to getProxyBaseUrl(), keeping today's behavior (including the worker-url override) when the variable is unset. toolsetEndpointUrl now takes its base explicitly since it is called from a column cell renderer where a hook is unsafe; the base is injected through the existing MCPToolsetTableColumnsDeps Fixes BerriAI#35583
Greptile SummaryThis PR introduces a shared documentation-base hook and applies it to MCP connection snippets and toolset endpoint displays and copy actions
Confidence Score: 4/5The URL normalization defect should be fixed before merging because valid environment formatting can produce unusable copied MCP endpoints The new resolver checks a trimmed value but returns the raw configuration, and every changed consumer appends paths directly, allowing surrounding whitespace or a trailing slash to propagate into user-facing endpoint URLs Files Needing Attention: ui/litellm-dashboard/src/app/(dashboard)/hooks/proxySettings/useDocBaseUrl.ts
|
| Filename | Overview |
|---|---|
| ui/litellm-dashboard/src/app/(dashboard)/hooks/proxySettings/useDocBaseUrl.ts | Adds the shared precedence resolver, but returns whitespace and trailing slashes unchanged despite checking a trimmed value |
| ui/litellm-dashboard/src/app/(dashboard)/mcp-servers/_components/mcp_connect.tsx | Consistently switches MCP and Responses snippets to the resolved documentation base, while inheriting its normalization defect |
| ui/litellm-dashboard/src/app/(dashboard)/mcp-servers/_components/MCPToolsetsTab.tsx | Uses the documentation base for the usage guide and injects it into table dependencies |
| ui/litellm-dashboard/src/app/(dashboard)/mcp-servers/_components/MCPToolsetTableColumns.tsx | Correctly receives the base as a dependency for display and clipboard URLs, but directly appends paths to an unnormalized base |
| ui/litellm-dashboard/src/app/(dashboard)/hooks/proxySettings/useDocBaseUrl.test.ts | Covers precedence and blank values but does not verify normalization of accepted values |
| ui/litellm-dashboard/src/app/(dashboard)/mcp-servers/_components/MCPToolsetsTab.test.tsx | Covers configured and fallback URLs for both usage-guide and table output using clean base values |
Reviews (1): Last reviewed commit: "fix(ui): honor LITELLM_UI_API_DOC_BASE_U..." | Re-trigger Greptile
| import useProxySettings from "@/app/(dashboard)/hooks/proxySettings/useProxySettings"; | ||
|
|
||
| export function resolveDocBaseUrl(docBaseUrl: string | null | undefined, fallback: string): string { | ||
| return docBaseUrl && docBaseUrl.trim() ? docBaseUrl : fallback; |
There was a problem hiding this comment.
Unnormalized documentation base URL
If LITELLM_UI_API_DOC_BASE_URL contains surrounding whitespace or a trailing slash, this returns the raw value and the changed consumers append paths directly, producing copied endpoints such as https://gateway.example.com/mcp or https://gateway.example.com//toolset/name/mcp that clients can reject or route incorrectly.
| return docBaseUrl && docBaseUrl.trim() ? docBaseUrl : fallback; | |
| return docBaseUrl?.trim().replace(/\/+$/, "") || fallback; |
Knowledge Base Used: Admin dashboard (ui/litellm-dashboard)
TLDR
Problem this solves:
How it solves it:
Relevant issues
Fixes #35583
Linear ticket
Pre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
@greptileaito re-request a review after pushing changes)Delays in PR merge?
If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-review).
Screenshots / Proof of Fix
Steps to capture before/after, with PROXY_BASE_URL and LITELLM_UI_API_DOC_BASE_URL deliberately pointed at different hosts:
export PROXY_BASE_URL=http://localhost:4000andexport LITELLM_UI_API_DOC_BASE_URL=https://gateway.example.com, thenpython litellm/proxy/proxy_cli.py --config litellm/proxy/dev_config.yaml --detailed_debug --reload --use_v2_migration_resolver 2>&1 | tee litellm.lognpm run devinui/litellm-dashboardhttp://localhost:4000/mcp; after,https://gateway.example.com/mcphttps://gateway.example.com/toolset/...http://localhost:4000Type
🐛 Bug Fix
Changes
useDocBaseUrllives atapp/(dashboard)/hooks/proxySettings/useDocBaseUrl.tsnext touseProxySettings. It exports a pureresolveDocBaseUrl(docBaseUrl, fallback)that trims and prefers the doc base, plus the hook that composesuseAuthorized,useProxySettingsandgetProxyBaseUrl. Taking the fallback as a parameter keeps the trim-and-prefer semantics shareable without forcing one chain on every callerFalling back to
getProxyBaseUrl()rather than straight toPROXY_BASE_URLpreserves the worker-url override innetworking.tsx, so the only behavior change is the one the issue asks fortoolsetEndpointUrlnow takes its base as a parameter because it is called from a TanStack columncellrenderer where calling a hook is unsafe; the resolved base is injected through the existingMCPToolsetTableColumnsDepsobject, matching how that file already receives its dependencieschat_ui/CodeSnippets.tsxkeeps its own inlined chain (LITELLM_UI_API_DOC_BASE_URL > PROXY_BASE_URL > window.location.origin). Those snippets are not worker-aware today and making them so is a separate changeTests were written first and each was watched fail against the unfixed code:
useDocBaseUrl.test.tspins the resolution order and the blank/whitespace/null guards,mcp_connect.test.tsxasserts the rendered Server URL follows the doc base when set and the proxy base when not,MCPToolsetsTab.test.tsxcovers the usage-guide snippet and the per-row endpoint URL, and the existingMCPToolsetTableColumns.test.tsxwas updated to inject a doc base distinct from the mocked proxy base so an inverted priority fails itFinal Attestation