diff --git a/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/ChatUI.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/ChatUI.test.tsx
index 036eb9f2872..3311546aa8e 100644
--- a/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/ChatUI.test.tsx
+++ b/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/ChatUI.test.tsx
@@ -249,6 +249,30 @@ describe("ChatUI", () => {
});
});
+ it("should enable the MCP tools selector for interactions", async () => {
+ render(
+ ,
+ );
+
+ await waitFor(() => {
+ expect(screen.getByText("Test Key")).toBeInTheDocument();
+ });
+
+ const mcpInput = () => screen.getByLabelText("Select MCP servers");
+
+ await selectComboboxOption("Select an endpoint", "/v1beta/interactions");
+
+ await waitFor(() => {
+ expect(mcpInput()).not.toBeDisabled();
+ });
+ });
+
it("should show Simulate failure to test fallbacks in Model Settings when chat endpoint is selected", async () => {
const user = userEvent.setup();
render(
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/ChatUI.tsx b/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/ChatUI.tsx
index 56905c5dfe4..2b8f76aeaf3 100644
--- a/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/ChatUI.tsx
+++ b/ui/litellm-dashboard/src/app/(dashboard)/playground/components/chat_ui/ChatUI.tsx
@@ -103,6 +103,7 @@ const MCP_SUPPORTED_ENDPOINTS = new Set([
EndpointType.RESPONSES,
EndpointType.MCP,
EndpointType.ANTHROPIC_MESSAGES,
+ EndpointType.INTERACTIONS,
]);
const CUSTOM_MODEL_DEBOUNCE_WAIT_MS = 500;
@@ -1070,6 +1071,11 @@ const ChatUI: React.FC = ({
selectedTags,
signal,
customProxyBaseUrl || undefined,
+ undefined,
+ selectedMCPServers,
+ mcpServers,
+ mcpServerToolRestrictions,
+ mcpToolsets,
);
}
}
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/playground/llm_calls/interactions_api.tsx b/ui/litellm-dashboard/src/app/(dashboard)/playground/llm_calls/interactions_api.tsx
index 39d998fe7eb..d03880bbe70 100644
--- a/ui/litellm-dashboard/src/app/(dashboard)/playground/llm_calls/interactions_api.tsx
+++ b/ui/litellm-dashboard/src/app/(dashboard)/playground/llm_calls/interactions_api.tsx
@@ -1,5 +1,7 @@
import NotificationManager from "@/components/molecules/notifications_manager";
import { getGlobalLitellmHeaderName, getProxyBaseUrl } from "@/components/networking";
+import { buildMcpToolBlocks } from "@/components/llm_calls/mcp_tool_blocks";
+import type { MCPServer, MCPToolset } from "@/components/mcp_tools/types";
export async function makeInteractionsRequest(
input: string,
@@ -10,6 +12,10 @@ export async function makeInteractionsRequest(
signal?: AbortSignal,
customBaseUrl?: string,
previousInteractionId?: string,
+ selectedMCPServers?: string[],
+ mcpServers?: MCPServer[],
+ mcpServerToolRestrictions?: Record,
+ mcpToolsets?: MCPToolset[],
): Promise {
if (!accessToken) {
throw new Error("Virtual Key is required");
@@ -32,10 +38,18 @@ export async function makeInteractionsRequest(
headers["x-litellm-tags"] = tags.join(",");
}
+ const tools = buildMcpToolBlocks({
+ selectedMCPServers,
+ mcpServers,
+ mcpToolsets,
+ mcpServerToolRestrictions,
+ });
+
const body: Record = {
model: selectedModel,
input,
stream: true,
+ ...(tools.length > 0 ? { tools } : {}),
};
if (previousInteractionId) {
body.previous_interaction_id = previousInteractionId;
diff --git a/ui/litellm-dashboard/src/components/llm_calls/chat_completion.test.tsx b/ui/litellm-dashboard/src/components/llm_calls/chat_completion.test.tsx
index 10252adecd0..9a9e7a42b08 100644
--- a/ui/litellm-dashboard/src/components/llm_calls/chat_completion.test.tsx
+++ b/ui/litellm-dashboard/src/components/llm_calls/chat_completion.test.tsx
@@ -172,23 +172,22 @@ describe("chat_completion", () => {
const callArgs = mockCreate.mock.calls[0][0];
expect(callArgs.tool_choice).toBe("auto");
- expect(callArgs.tools).toHaveLength(2);
-
- // Check first tool
- const firstTool = callArgs.tools[0];
- expect(firstTool.type).toBe("mcp");
- expect(firstTool.server_label).toBe("litellm");
- expect(firstTool.server_url).toBe("litellm_proxy/mcp/alpha");
- expect(firstTool.require_approval).toBe("never");
- expect(firstTool.allowed_tools).toEqual(["toolA", "toolB"]);
-
- // Check second tool
- const secondTool = callArgs.tools[1];
- expect(secondTool.type).toBe("mcp");
- expect(secondTool.server_label).toBe("litellm");
- expect(secondTool.server_url).toBe("litellm_proxy/mcp/Beta");
- expect(secondTool.require_approval).toBe("never");
- expect(secondTool.allowed_tools).toEqual(["toolC"]);
+ expect(callArgs.tools).toEqual([
+ {
+ type: "mcp",
+ server_label: "Alpha",
+ server_url: "litellm_proxy/mcp/Alpha",
+ require_approval: "never",
+ allowed_tools: ["toolA", "toolB"],
+ },
+ {
+ type: "mcp",
+ server_label: "Beta",
+ server_url: "litellm_proxy/mcp/Beta",
+ require_approval: "never",
+ allowed_tools: ["toolC"],
+ },
+ ]);
});
it("should include mock_testing_fallbacks in request body when mockTestFallbacks is true", async () => {
diff --git a/ui/litellm-dashboard/src/components/llm_calls/chat_completion.tsx b/ui/litellm-dashboard/src/components/llm_calls/chat_completion.tsx
index 549f309b4ea..c4e7e1f4533 100644
--- a/ui/litellm-dashboard/src/components/llm_calls/chat_completion.tsx
+++ b/ui/litellm-dashboard/src/components/llm_calls/chat_completion.tsx
@@ -4,6 +4,7 @@ import { TokenUsage } from "../chat_ui/ResponseMetrics";
import { VectorStoreSearchResponse } from "../chat_ui/types";
import { getProxyBaseUrl } from "@/components/networking";
import { MCPServer, MCPToolset, type MCPEvent } from "@/components/mcp_tools/types";
+import { buildMcpToolBlocks } from "./mcp_tool_blocks";
const completionAsSingleChunk = (completion: ChatCompletion): ChatCompletionChunk =>
({
@@ -81,48 +82,12 @@ export async function makeOpenAIChatCompletionRequest(
} = {};
let mcpListToolsProcessed = false;
- // Build tools array
- const tools: any[] = [];
-
- // Add MCP servers if selected
- if (selectedMCPServers && selectedMCPServers.length > 0) {
- if (selectedMCPServers.includes("__all__")) {
- // All MCP Servers selected
- tools.push({
- type: "mcp",
- server_label: "litellm",
- server_url: "litellm_proxy/mcp",
- require_approval: "never",
- });
- } else {
- // Individual servers/toolsets selected - create one entry per item
- selectedMCPServers.forEach((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 } : {}),
- });
- }
- });
- }
- }
+ const tools = buildMcpToolBlocks({
+ selectedMCPServers,
+ mcpServers,
+ mcpToolsets,
+ mcpServerToolRestrictions,
+ });
const requestBody = {
model: selectedModel,
diff --git a/ui/litellm-dashboard/src/components/llm_calls/responses_api.test.tsx b/ui/litellm-dashboard/src/components/llm_calls/responses_api.test.tsx
index a897e6fc4cc..7f54e67c300 100644
--- a/ui/litellm-dashboard/src/components/llm_calls/responses_api.test.tsx
+++ b/ui/litellm-dashboard/src/components/llm_calls/responses_api.test.tsx
@@ -280,14 +280,14 @@ describe("responses_api", () => {
{
type: "mcp",
server_label: "Alpha",
- server_url: "https://example.com/mcp/Alpha",
+ server_url: "litellm_proxy/mcp/Alpha",
require_approval: "never",
allowed_tools: ["toolA"],
},
{
type: "mcp",
server_label: "Beta",
- server_url: "https://example.com/mcp/Beta",
+ server_url: "litellm_proxy/mcp/Beta",
require_approval: "never",
allowed_tools: ["toolB", "toolC"],
},
diff --git a/ui/litellm-dashboard/src/components/llm_calls/responses_api.tsx b/ui/litellm-dashboard/src/components/llm_calls/responses_api.tsx
index f356b2cb2c3..6eb72fad46b 100644
--- a/ui/litellm-dashboard/src/components/llm_calls/responses_api.tsx
+++ b/ui/litellm-dashboard/src/components/llm_calls/responses_api.tsx
@@ -11,6 +11,7 @@ import {
handleCodeInterpreterCall,
handleCodeInterpreterOutput,
} from "./code_interpreter_handler";
+import { buildMcpToolBlocks } from "./mcp_tool_blocks";
export type { CodeInterpreterResult } from "./code_interpreter_handler";
@@ -134,53 +135,15 @@ export async function makeOpenAIResponsesRequest(
};
});
- // Build tools array
- const tools: any[] = [];
+ const tools: Array> = [
+ ...buildMcpToolBlocks({
+ selectedMCPServers,
+ mcpServers,
+ mcpToolsets,
+ mcpServerToolRestrictions,
+ }),
+ ];
- // Add MCP servers if selected
- if (selectedMCPServers && selectedMCPServers.length > 0) {
- if (selectedMCPServers.includes("__all__")) {
- // All MCP Servers selected
- tools.push({
- type: "mcp",
- server_label: "litellm",
- server_url: `${proxyBaseUrl}/mcp`,
- require_approval: "never",
- });
- } else {
- // Individual servers/toolsets selected - create one entry per item
- selectedMCPServers.forEach((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 } : {}),
- });
- }
- });
- }
- }
-
- // Add code_interpreter tool if enabled (OpenAI auto-creates container)
if (codeInterpreterEnabled) {
tools.push({
type: "code_interpreter",