From ab5854d0bfd543ea7a219da3b6a3a68a8bb4d4e1 Mon Sep 17 00:00:00 2001 From: Ishaan Jaffer Date: Sat, 21 Mar 2026 19:01:27 -0700 Subject: [PATCH] feat(mcp): generate correct server_url for toolsets in playground API calls --- .../playground/llm_calls/chat_completion.tsx | 37 ++++++++++------ .../playground/llm_calls/responses_api.tsx | 42 ++++++++++++------- 2 files changed, 53 insertions(+), 26 deletions(-) diff --git a/ui/litellm-dashboard/src/components/playground/llm_calls/chat_completion.tsx b/ui/litellm-dashboard/src/components/playground/llm_calls/chat_completion.tsx index 3197c9409ce..b2281eaadce 100644 --- a/ui/litellm-dashboard/src/components/playground/llm_calls/chat_completion.tsx +++ b/ui/litellm-dashboard/src/components/playground/llm_calls/chat_completion.tsx @@ -3,7 +3,7 @@ import { ChatCompletionMessageParam } from "openai/resources/chat/completions"; import { TokenUsage } from "../chat_ui/ResponseMetrics"; import { VectorStoreSearchResponse } from "../chat_ui/types"; import { getProxyBaseUrl } from "@/components/networking"; -import { MCPServer, type MCPEvent } from "../../mcp_tools/types"; +import { MCPServer, MCPToolset, type MCPEvent } from "../../mcp_tools/types"; export async function makeOpenAIChatCompletionRequest( chatHistory: { role: string; content: string | any[] }[], @@ -30,6 +30,7 @@ export async function makeOpenAIChatCompletionRequest( mcpServerToolRestrictions?: Record, onMCPEvent?: (event: MCPEvent) => void, mockTestFallbacks?: boolean, + mcpToolsets?: MCPToolset[], ) { // base url should be the current base_url const isLocal = process.env.NODE_ENV === "development"; @@ -82,19 +83,31 @@ export async function makeOpenAIChatCompletionRequest( require_approval: "never", }); } else { - // Individual servers selected - create one entry per server + // Individual servers/toolsets selected - create one entry per item selectedMCPServers.forEach((serverId) => { - const server = mcpServers?.find((s) => s.server_id === serverId); - const serverName = server?.alias || server?.server_name || serverId; - const allowedTools = mcpServerToolRestrictions?.[serverId] || []; + if (serverId.startsWith("toolset:")) { + const toolsetId = serverId.slice("toolset:".length); + const toolset = mcpToolsets?.find((t) => t.toolset_id === toolsetId); + const toolsetName = toolset?.toolset_name || toolsetId; + tools.push({ + type: "mcp", + server_label: toolsetName, + server_url: `litellm_proxy/mcp/${encodeURIComponent(toolsetName)}`, + require_approval: "never", + }); + } else { + const server = mcpServers?.find((s) => s.server_id === serverId); + const serverName = server?.alias || server?.server_name || serverId; + const allowedTools = mcpServerToolRestrictions?.[serverId] || []; - tools.push({ - type: "mcp", - server_label: "litellm", - server_url: `litellm_proxy/mcp/${serverName}`, - require_approval: "never", - ...(allowedTools.length > 0 ? { allowed_tools: allowedTools } : {}), - }); + tools.push({ + type: "mcp", + server_label: "litellm", + server_url: `litellm_proxy/mcp/${serverName}`, + require_approval: "never", + ...(allowedTools.length > 0 ? { allowed_tools: allowedTools } : {}), + }); + } }); } } diff --git a/ui/litellm-dashboard/src/components/playground/llm_calls/responses_api.tsx b/ui/litellm-dashboard/src/components/playground/llm_calls/responses_api.tsx index 48d0efca6ee..4e88a356cf3 100644 --- a/ui/litellm-dashboard/src/components/playground/llm_calls/responses_api.tsx +++ b/ui/litellm-dashboard/src/components/playground/llm_calls/responses_api.tsx @@ -4,7 +4,7 @@ import { TokenUsage } from "../chat_ui/ResponseMetrics"; import { getProxyBaseUrl } from "@/components/networking"; import NotificationManager from "@/components/molecules/notifications_manager"; import type { MCPEvent } from "../../mcp_tools/types"; -import { MCPServer } from "../../mcp_tools/types"; +import { MCPServer, MCPToolset } from "../../mcp_tools/types"; import { CodeInterpreterResult, CodeInterpreterState, @@ -37,6 +37,7 @@ export async function makeOpenAIResponsesRequest( customBaseUrl?: string, mcpServers?: MCPServer[], mcpServerToolRestrictions?: Record, + mcpToolsets?: MCPToolset[], ) { if (!accessToken) { throw new Error("Virtual Key is required"); @@ -102,21 +103,34 @@ export async function makeOpenAIResponsesRequest( require_approval: "never", }); } else { - // Individual servers selected - create one entry per server + // Individual servers/toolsets selected - create one entry per item selectedMCPServers.forEach((serverId) => { - const server = mcpServers?.find((s) => s.server_id === serverId); - // Use server_name for both routing and labelling. server_name is the - // unique registered identifier; aliases can collide across servers. - const routeName = server?.server_name || serverId; - const allowedTools = mcpServerToolRestrictions?.[serverId] || []; + if (serverId.startsWith("toolset:")) { + // Toolset: same /{name}/mcp pattern as individual servers + const toolsetId = serverId.slice("toolset:".length); + const toolset = mcpToolsets?.find((t) => t.toolset_id === toolsetId); + const toolsetName = toolset?.toolset_name || toolsetId; + tools.push({ + type: "mcp", + server_label: toolsetName, + server_url: `${proxyBaseUrl}/mcp/${encodeURIComponent(toolsetName)}`, + require_approval: "never", + }); + } else { + const server = mcpServers?.find((s) => s.server_id === serverId); + // Use server_name for both routing and labelling. server_name is the + // unique registered identifier; aliases can collide across servers. + const routeName = server?.server_name || serverId; + const allowedTools = mcpServerToolRestrictions?.[serverId] || []; - tools.push({ - type: "mcp", - server_label: routeName, // unique per request — collisions cause silent tool-routing failures - server_url: `${proxyBaseUrl}/mcp/${encodeURIComponent(routeName)}`, - require_approval: "never", - ...(allowedTools.length > 0 ? { allowed_tools: allowedTools } : {}), - }); + tools.push({ + type: "mcp", + server_label: routeName, // unique per request — collisions cause silent tool-routing failures + server_url: `${proxyBaseUrl}/mcp/${encodeURIComponent(routeName)}`, + require_approval: "never", + ...(allowedTools.length > 0 ? { allowed_tools: allowedTools } : {}), + }); + } }); } }