mirror of
https://github.com/BerriAI/litellm.git
synced 2025-04-26 03:04:13 +00:00
endpoints to list and call tools
This commit is contained in:
parent
b381dde9ac
commit
e0cff7595c
5 changed files with 182 additions and 32 deletions
147
ui/litellm-dashboard/src/components/mcp_tools/index.tsx
Normal file
147
ui/litellm-dashboard/src/components/mcp_tools/index.tsx
Normal file
|
@ -0,0 +1,147 @@
|
|||
import React, { useState } from 'react';
|
||||
import { useQuery, useMutation } from '@tanstack/react-query';
|
||||
import { DataTable } from '../view_logs/table';
|
||||
import { columns, ToolTestPanel } from './columns';
|
||||
import { MCPTool, MCPToolsViewerProps, CallMCPToolResponse } from './types';
|
||||
import { listMCPTools, callMCPTool } from '../networking';
|
||||
|
||||
export default function MCPToolsViewer({
|
||||
accessToken,
|
||||
userRole,
|
||||
userID,
|
||||
}: MCPToolsViewerProps) {
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
const [selectedTool, setSelectedTool] = useState<MCPTool | null>(null);
|
||||
const [toolResult, setToolResult] = useState<CallMCPToolResponse | null>(null);
|
||||
const [toolError, setToolError] = useState<Error | null>(null);
|
||||
|
||||
// Query to fetch MCP tools
|
||||
const { data: mcpTools, isLoading: isLoadingTools } = useQuery({
|
||||
queryKey: ['mcpTools'],
|
||||
queryFn: () => {
|
||||
if (!accessToken) throw new Error('Access Token required');
|
||||
return listMCPTools(accessToken);
|
||||
},
|
||||
enabled: !!accessToken,
|
||||
});
|
||||
|
||||
// Mutation for calling a tool
|
||||
const { mutate: executeTool, isPending: isCallingTool } = useMutation({
|
||||
mutationFn: (args: { tool: MCPTool; arguments: Record<string, any> }) => {
|
||||
if (!accessToken) throw new Error('Access Token required');
|
||||
return callMCPTool(
|
||||
accessToken,
|
||||
args.tool.name,
|
||||
args.arguments
|
||||
);
|
||||
},
|
||||
onSuccess: (data) => {
|
||||
setToolResult(data);
|
||||
setToolError(null);
|
||||
},
|
||||
onError: (error: Error) => {
|
||||
setToolError(error);
|
||||
setToolResult(null);
|
||||
},
|
||||
});
|
||||
|
||||
// Add onToolSelect handler to each tool
|
||||
const toolsData = React.useMemo(() => {
|
||||
if (!mcpTools) return [];
|
||||
|
||||
return mcpTools.map(tool => ({
|
||||
...tool,
|
||||
onToolSelect: (tool: MCPTool) => {
|
||||
setSelectedTool(tool);
|
||||
setToolResult(null);
|
||||
setToolError(null);
|
||||
}
|
||||
}));
|
||||
}, [mcpTools]);
|
||||
|
||||
// Filter tools based on search term
|
||||
const filteredTools = React.useMemo(() => {
|
||||
return toolsData.filter(tool => {
|
||||
const searchLower = searchTerm.toLowerCase();
|
||||
return (
|
||||
tool.name.toLowerCase().includes(searchLower) ||
|
||||
tool.description.toLowerCase().includes(searchLower) ||
|
||||
tool.mcp_info.server_name.toLowerCase().includes(searchLower)
|
||||
);
|
||||
});
|
||||
}, [toolsData, searchTerm]);
|
||||
|
||||
// Handle tool call submission
|
||||
const handleToolSubmit = (args: Record<string, any>) => {
|
||||
if (!selectedTool) return;
|
||||
|
||||
executeTool({
|
||||
tool: selectedTool,
|
||||
arguments: args,
|
||||
});
|
||||
};
|
||||
|
||||
if (!accessToken || !userRole || !userID) {
|
||||
return <div className="p-6 text-center text-gray-500">Missing required authentication parameters.</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="w-full p-6">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<h1 className="text-xl font-semibold">MCP Tools</h1>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-lg shadow">
|
||||
<div className="border-b px-6 py-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="relative w-64">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search tools..."
|
||||
className="w-full px-3 py-2 pl-8 border rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
/>
|
||||
<svg
|
||||
className="absolute left-2.5 top-2.5 h-4 w-4 text-gray-500"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div className="text-sm text-gray-500">
|
||||
{filteredTools.length} tool{filteredTools.length !== 1 ? "s" : ""} available
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<DataTable
|
||||
columns={columns}
|
||||
data={filteredTools}
|
||||
isLoading={isLoadingTools}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Tool Test Panel - Show when a tool is selected */}
|
||||
{selectedTool && (
|
||||
<div className="fixed inset-0 bg-gray-800 bg-opacity-75 flex items-center justify-center z-50 p-4">
|
||||
<ToolTestPanel
|
||||
tool={selectedTool}
|
||||
onSubmit={handleToolSubmit}
|
||||
isLoading={isCallingTool}
|
||||
result={toolResult}
|
||||
error={toolError}
|
||||
onClose={() => setSelectedTool(null)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue