diff --git a/ui/litellm-dashboard/src/components/playground/compareUI/components/UnifiedSelector.tsx b/ui/litellm-dashboard/src/components/playground/compareUI/components/UnifiedSelector.tsx
index c531eed3737..9d4af25067b 100644
--- a/ui/litellm-dashboard/src/components/playground/compareUI/components/UnifiedSelector.tsx
+++ b/ui/litellm-dashboard/src/components/playground/compareUI/components/UnifiedSelector.tsx
@@ -32,7 +32,7 @@ export function UnifiedSelector({
(option?.label ?? "").toLowerCase().includes(input.toLowerCase())
}
options={options}
- className="w-48"
+ className="w-48 md:w-64 lg:w-72"
notFoundContent={
loading ? (
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 24112ca1666..c3c623c25c3 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
@@ -4,6 +4,7 @@ import { TokenUsage } from "../chat_ui/ResponseMetrics";
import { VectorStoreSearchResponse } from "../chat_ui/types";
import { getProxyBaseUrl } from "@/components/networking";
import { MCPServer } from "../../mcp_tools/types";
+import { MCPEvent } from "../chat_ui/MCPEventsDisplay";
export async function makeOpenAIChatCompletionRequest(
chatHistory: { role: string; content: string | any[] }[],
@@ -27,11 +28,12 @@ export async function makeOpenAIChatCompletionRequest(
customBaseUrl?: string,
mcpServers?: MCPServer[],
mcpServerToolRestrictions?: Record,
+ onMCPEvent?: (event: MCPEvent) => void,
) {
// base url should be the current base_url
const isLocal = process.env.NODE_ENV === "development";
if (isLocal !== true) {
- console.log = function () {};
+ console.log = function () { };
}
console.log("isLocal:", isLocal);
const proxyBaseUrl = customBaseUrl || getProxyBaseUrl();
@@ -57,6 +59,14 @@ export async function makeOpenAIChatCompletionRequest(
let fullResponseContent = "";
let fullReasoningContent = "";
+ // Track MCP metadata cumulatively across chunks
+ let mcpMetadata: {
+ mcp_list_tools?: any[];
+ mcp_tool_calls?: any[];
+ mcp_call_results?: any[];
+ } = {};
+ let mcpListToolsProcessed = false;
+
// Build tools array
const tools: any[] = [];
@@ -158,6 +168,51 @@ export async function makeOpenAIChatCompletionRequest(
onSearchResults(delta.provider_specific_fields.search_results);
}
+ // Check for MCP metadata in provider_specific_fields
+ if (delta && delta.provider_specific_fields) {
+ const providerFields = delta.provider_specific_fields;
+
+ // Merge MCP metadata cumulatively (don't overwrite)
+ if (providerFields.mcp_list_tools && !mcpMetadata.mcp_list_tools) {
+ mcpMetadata.mcp_list_tools = providerFields.mcp_list_tools;
+ // Process mcp_list_tools immediately when found (typically in first chunk)
+ if (onMCPEvent && !mcpListToolsProcessed) {
+ mcpListToolsProcessed = true;
+ const toolsEvent: MCPEvent = {
+ type: "response.output_item.done",
+ item_id: "mcp_list_tools", // Add item_id to prevent duplicate detection issues
+ item: {
+ type: "mcp_list_tools",
+ tools: providerFields.mcp_list_tools.map((tool: any) => ({
+ name: tool.function?.name || tool.name || "",
+ description: tool.function?.description || tool.description || "",
+ input_schema: tool.function?.parameters || tool.input_schema || {},
+ })),
+ },
+ timestamp: Date.now(),
+ };
+ onMCPEvent(toolsEvent);
+ console.log("MCP list_tools event sent:", toolsEvent);
+ }
+ }
+
+ if (providerFields.mcp_tool_calls) {
+ mcpMetadata.mcp_tool_calls = providerFields.mcp_tool_calls;
+ }
+
+ if (providerFields.mcp_call_results) {
+ mcpMetadata.mcp_call_results = providerFields.mcp_call_results;
+ }
+
+ if (providerFields.mcp_list_tools || providerFields.mcp_tool_calls || providerFields.mcp_call_results) {
+ console.log("MCP metadata found in chunk:", {
+ mcp_list_tools: providerFields.mcp_list_tools ? "present" : "absent",
+ mcp_tool_calls: providerFields.mcp_tool_calls ? "present" : "absent",
+ mcp_call_results: providerFields.mcp_call_results ? "present" : "absent",
+ });
+ }
+ }
+
// Check for usage data using type assertion
const chunkWithUsage = chunk as any;
if (chunkWithUsage.usage && onUsageData) {
@@ -182,6 +237,37 @@ export async function makeOpenAIChatCompletionRequest(
}
}
+ // Process remaining MCP metadata (mcp_tool_calls and mcp_call_results) after stream completes
+ // Note: mcp_list_tools is already processed when found in the first chunk
+ if (onMCPEvent && (mcpMetadata.mcp_tool_calls || mcpMetadata.mcp_call_results)) {
+ // Convert mcp_tool_calls and mcp_call_results to MCPEvent[]
+ if (mcpMetadata.mcp_tool_calls && mcpMetadata.mcp_tool_calls.length > 0) {
+ mcpMetadata.mcp_tool_calls.forEach((toolCall: any, index: number) => {
+ const functionName = toolCall.function?.name || toolCall.name || "";
+ const functionArgs = toolCall.function?.arguments || toolCall.arguments || "{}";
+
+ // Find corresponding result
+ const result = mcpMetadata.mcp_call_results?.find(
+ (r: any) => r.tool_call_id === toolCall.id || r.tool_call_id === toolCall.call_id
+ ) || mcpMetadata.mcp_call_results?.[index];
+
+ const callEvent: MCPEvent = {
+ type: "response.output_item.done",
+ item: {
+ type: "mcp_call",
+ name: functionName,
+ arguments: typeof functionArgs === "string" ? functionArgs : JSON.stringify(functionArgs),
+ output: result?.result ? (typeof result.result === "string" ? result.result : JSON.stringify(result.result)) : undefined,
+ },
+ item_id: toolCall.id || toolCall.call_id,
+ timestamp: Date.now(),
+ };
+ onMCPEvent(callEvent);
+ console.log("MCP call event sent:", callEvent);
+ });
+ }
+ }
+
const endTime = Date.now();
const totalLatency = endTime - startTime;
if (onTotalLatency) {