Fix MCP connect docs base URL

This commit is contained in:
Yu Cheng 2026-08-05 16:09:22 -07:00
parent aa1180c0c9
commit 7ad1b5c32c
6 changed files with 120 additions and 3 deletions

View file

@ -0,0 +1,52 @@
import { describe, expect, it } from "vitest";
import { resolveMCPConnectBaseUrl } from "./mcpConnectBaseUrl";
describe("resolveMCPConnectBaseUrl", () => {
const fallbackBaseUrl = "https://runtime.litellm.test";
it("uses LITELLM_UI_API_DOC_BASE_URL when provided", () => {
expect(
resolveMCPConnectBaseUrl(
{
LITELLM_UI_API_DOC_BASE_URL: "https://docs.litellm.test",
},
fallbackBaseUrl,
),
).toBe("https://docs.litellm.test");
});
it("prefers LITELLM_UI_API_DOC_BASE_URL over PROXY_BASE_URL", () => {
expect(
resolveMCPConnectBaseUrl(
{
LITELLM_UI_API_DOC_BASE_URL: "https://docs.litellm.test",
PROXY_BASE_URL: "https://proxy.litellm.test",
},
fallbackBaseUrl,
),
).toBe("https://docs.litellm.test");
});
it("falls back to PROXY_BASE_URL when the docs url is missing", () => {
expect(
resolveMCPConnectBaseUrl(
{
PROXY_BASE_URL: "https://proxy.litellm.test",
},
fallbackBaseUrl,
),
).toBe("https://proxy.litellm.test");
});
it("falls back to the runtime base url when settings are empty", () => {
expect(
resolveMCPConnectBaseUrl(
{
LITELLM_UI_API_DOC_BASE_URL: " ",
PROXY_BASE_URL: "",
},
fallbackBaseUrl,
),
).toBe(fallbackBaseUrl);
});
});

View file

@ -0,0 +1,21 @@
export interface MCPConnectProxySettings {
PROXY_BASE_URL?: string | null;
LITELLM_UI_API_DOC_BASE_URL?: string | null;
}
export function resolveMCPConnectBaseUrl(
proxySettings: MCPConnectProxySettings | undefined,
fallbackBaseUrl: string,
): string {
const customDocBaseUrl = proxySettings?.LITELLM_UI_API_DOC_BASE_URL;
if (customDocBaseUrl && customDocBaseUrl.trim()) {
return customDocBaseUrl.trim();
}
const proxyBaseUrl = proxySettings?.PROXY_BASE_URL;
if (proxyBaseUrl && proxyBaseUrl.trim()) {
return proxyBaseUrl.trim();
}
return fallbackBaseUrl;
}

View file

@ -0,0 +1,31 @@
import { render } from "@testing-library/react";
import { describe, expect, it, vi } from "vitest";
import MCPConnect from "./mcp_connect";
vi.mock("@/components/networking", () => ({
getProxyBaseUrl: vi.fn(() => "https://runtime.litellm.test"),
}));
vi.mock("@/utils/dataUtils", () => ({
copyToClipboard: vi.fn(),
}));
describe("MCPConnect", () => {
it("renders connection snippets with the API docs base url when configured", () => {
const apiDocBaseUrl = "https://docs.litellm.test";
const proxyBaseUrl = "https://proxy.litellm.test";
const { container } = render(
<MCPConnect
proxySettings={{
LITELLM_UI_API_DOC_BASE_URL: apiDocBaseUrl,
PROXY_BASE_URL: proxyBaseUrl,
}}
/>,
);
expect(container.textContent).toContain(`${apiDocBaseUrl}/mcp`);
expect(container.textContent).toContain(`${apiDocBaseUrl}/v1/responses`);
expect(container.textContent).not.toContain(`${proxyBaseUrl}/mcp`);
});
});

View file

@ -6,6 +6,7 @@ import { TabPanel, TabPanels, TabGroup, TabList, Tab, Title as TremorTitle, Text
import { CopyIcon, Code, Terminal, Globe, CheckIcon, ExternalLinkIcon, KeyIcon, ServerIcon, Zap } from "lucide-react";
import { getProxyBaseUrl } from "@/components/networking";
import { copyToClipboard as utilCopyToClipboard } from "@/utils/dataUtils";
import { resolveMCPConnectBaseUrl, type MCPConnectProxySettings } from "./mcpConnectBaseUrl";
const { Title, Text } = Typography;
const { Panel } = Collapse;
@ -112,10 +113,11 @@ const FeatureCard: React.FC<FeatureCardProps> = ({
interface MCPConnectProps {
currentServerAccessGroups?: string[];
proxySettings?: MCPConnectProxySettings;
}
const MCPConnect: React.FC<MCPConnectProps> = ({ currentServerAccessGroups = [] }) => {
const proxyBaseUrl = getProxyBaseUrl();
const MCPConnect: React.FC<MCPConnectProps> = ({ currentServerAccessGroups = [], proxySettings }) => {
const proxyBaseUrl = resolveMCPConnectBaseUrl(proxySettings, getProxyBaseUrl());
const [copiedStates, setCopiedStates] = useState<Record<string, boolean>>({});
const [serverHeaders, setServerHeaders] = useState<Record<string, string[]>>({
openai: [],

View file

@ -27,6 +27,15 @@ vi.mock("@/components/molecules/notifications_manager", () => ({
},
}));
vi.mock("@/app/(dashboard)/hooks/proxySettings/useProxySettings", () => ({
__esModule: true,
default: vi.fn(() => ({
PROXY_BASE_URL: "",
PROXY_LOGOUT_URL: "",
LITELLM_UI_API_DOC_BASE_URL: null,
})),
}));
const createQueryClient = () =>
new QueryClient({
defaultOptions: {

View file

@ -20,6 +20,7 @@ import React, { useEffect, useState, useMemo, useCallback } from "react";
import { useQuery } from "@tanstack/react-query";
import { useMCPServers } from "@/app/(dashboard)/hooks/mcpServers/useMCPServers";
import { useMCPServerHealth } from "@/app/(dashboard)/hooks/mcpServers/useMCPServerHealth";
import useProxySettings from "@/app/(dashboard)/hooks/proxySettings/useProxySettings";
import NotificationsManager from "@/components/molecules/notifications_manager";
import { deleteMCPServer } from "@/components/networking";
import { MCPSubmissionsTab } from "./MCPSubmissionsTab";
@ -109,6 +110,7 @@ const readToolsOAuthServerId = (): string | null => {
const MCPServers: React.FC<MCPServerProps> = ({ accessToken, userRole, userID }) => {
const { data: mcpServers, isLoading: isLoadingServers, refetch } = useMCPServers();
const proxySettings = useProxySettings(accessToken);
// Fetch health status for all servers
const {
@ -707,7 +709,7 @@ const MCPServers: React.FC<MCPServerProps> = ({ accessToken, userRole, userID })
<MCPToolsetsTab accessToken={accessToken} userRole={userRole} />
</TabsContent>
<TabsContent value="connect">
<MCPConnect />
<MCPConnect proxySettings={proxySettings} />
</TabsContent>
{isAdminRole(userRole) && (
<TabsContent value="semantic-filter">