import React from 'react'
import { useQuery } from '@tanstack/react-query'
import { Terminal, Clock, AlertCircle, CheckCircle, Image } from 'lucide-react'
import { format } from 'date-fns'
const fetchLogs = async () => {
const response = await fetch('/logs?limit=50')
if (!response.ok) throw new Error('Failed to fetch logs')
return response.json()
}
export default function ToolCalls() {
const { data: logsData, isLoading } = useQuery({
queryKey: ['logs'],
queryFn: fetchLogs,
refetchInterval: 2000
})
if (isLoading) {
return (
<div className="px-4 sm:px-6 lg:px-8">
<div className="animate-pulse">
<div className="h-8 bg-gray-300 dark:bg-gray-700 rounded w-1/4 mb-4"></div>
<div className="space-y-3">
{[...Array(5)].map((_, i) => (
<div key={i} className="h-24 bg-gray-300 dark:bg-gray-700 rounded"></div>
))}
</div>
</div>
</div>
)
}
const logs = logsData?.logs || []
return (
<div className="px-4 sm:px-6 lg:px-8">
<div className="sm:flex sm:items-center">
<div className="sm:flex-auto">
<h1 className="text-2xl font-semibold text-gray-900 dark:text-white">Tool Calls</h1>
<p className="mt-2 text-sm text-gray-700 dark:text-gray-300">
Monitor all tool calls made to the MCP server in real-time.
</p>
</div>
</div>
{/* Stats */}
<div className="mt-8 grid grid-cols-1 gap-5 sm:grid-cols-3">
<div className="bg-white dark:bg-gray-800 overflow-hidden shadow rounded-lg border dark:border-gray-700">
<div className="p-5">
<div className="flex items-center">
<div className="flex-shrink-0">
<CheckCircle className="w-6 h-6 text-green-600" />
</div>
<div className="ml-5 w-0 flex-1">
<dl>
<dt className="text-sm font-medium text-gray-500 dark:text-gray-400 truncate">
Successful Calls
</dt>
<dd className="text-lg font-semibold text-gray-900 dark:text-white">
{logs.filter((log: any) => !log.error).length}
</dd>
</dl>
</div>
</div>
</div>
</div>
<div className="bg-white dark:bg-gray-800 overflow-hidden shadow rounded-lg border dark:border-gray-700">
<div className="p-5">
<div className="flex items-center">
<div className="flex-shrink-0">
<AlertCircle className="w-6 h-6 text-red-600" />
</div>
<div className="ml-5 w-0 flex-1">
<dl>
<dt className="text-sm font-medium text-gray-500 dark:text-gray-400 truncate">
Failed Calls
</dt>
<dd className="text-lg font-semibold text-gray-900 dark:text-white">
{logs.filter((log: any) => log.error).length}
</dd>
</dl>
</div>
</div>
</div>
</div>
<div className="bg-white dark:bg-gray-800 overflow-hidden shadow rounded-lg border dark:border-gray-700">
<div className="p-5">
<div className="flex items-center">
<div className="flex-shrink-0">
<Clock className="w-6 h-6 text-blue-600" />
</div>
<div className="ml-5 w-0 flex-1">
<dl>
<dt className="text-sm font-medium text-gray-500 dark:text-gray-400 truncate">
Avg Duration
</dt>
<dd className="text-lg font-semibold text-gray-900 dark:text-white">
{logs.length > 0
? Math.round(logs.reduce((sum: number, log: any) => sum + log.duration_ms, 0) / logs.length)
: 0}ms
</dd>
</dl>
</div>
</div>
</div>
</div>
</div>
{/* Tool Calls List */}
<div className="mt-8">
{logs.length > 0 ? (
<div className="bg-white dark:bg-gray-800 shadow overflow-hidden sm:rounded-lg border dark:border-gray-700">
<div className="divide-y divide-gray-200 dark:divide-gray-700">
{logs.map((log: any, index: number) => (
<div key={log.id || index} className="px-6 py-4">
<div className="flex items-start justify-between">
<div className="flex items-start space-x-4 min-w-0 flex-1">
<div className="flex-shrink-0">
{log.error ? (
<div className="w-8 h-8 rounded-full bg-red-100 dark:bg-red-900/20 flex items-center justify-center">
<AlertCircle className="w-4 h-4 text-red-600" />
</div>
) : (
<div className="w-8 h-8 rounded-full bg-green-100 dark:bg-green-900/20 flex items-center justify-center">
<CheckCircle className="w-4 h-4 text-green-600" />
</div>
)}
</div>
<div className="min-w-0 flex-1">
<div className="flex items-center space-x-2">
<p className="text-sm font-medium text-gray-900 dark:text-white truncate">
{log.tool_name}
</p>
{log.images?.length > 0 && (
<span className="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-blue-100 text-blue-800 dark:bg-blue-900/20 dark:text-blue-300">
<Image className="w-3 h-3 mr-1" />
{log.images.length} image{log.images.length > 1 ? 's' : ''}
</span>
)}
</div>
<div className="mt-1 flex items-center text-sm text-gray-500 dark:text-gray-400 space-x-4">
<span>{format(new Date(log.timestamp), 'MMM d, h:mm:ss a')}</span>
<span>{log.duration_ms}ms</span>
{log.session_id && (
<span className="font-mono text-xs">
Session: {log.session_id.slice(0, 8)}...
</span>
)}
</div>
{log.error && (
<div className="mt-2 text-sm text-red-600 dark:text-red-400">
<p className="font-medium">{log.error.code}: {log.error.message}</p>
{log.error.details && (
<pre className="mt-1 text-xs bg-red-50 dark:bg-red-900/10 p-2 rounded overflow-x-auto">
{JSON.stringify(log.error.details, null, 2)}
</pre>
)}
</div>
)}
{/* Parameters */}
{log.params && Object.keys(log.params).length > 0 && (
<details className="mt-2">
<summary className="text-xs font-medium text-gray-500 dark:text-gray-400 cursor-pointer hover:text-gray-700 dark:hover:text-gray-300">
Parameters
</summary>
<pre className="mt-1 text-xs bg-gray-50 dark:bg-gray-900 p-2 rounded overflow-x-auto">
{JSON.stringify(log.params, null, 2)}
</pre>
</details>
)}
{/* Result */}
{log.result && !log.error && (
<details className="mt-2">
<summary className="text-xs font-medium text-gray-500 dark:text-gray-400 cursor-pointer hover:text-gray-700 dark:hover:text-gray-300">
Result
</summary>
<pre className="mt-1 text-xs bg-gray-50 dark:bg-gray-900 p-2 rounded overflow-x-auto">
{JSON.stringify(log.result, null, 2)}
</pre>
</details>
)}
</div>
</div>
</div>
</div>
))}
</div>
</div>
) : (
<div className="text-center py-12">
<div className="w-12 h-12 mx-auto bg-gray-100 dark:bg-gray-800 rounded-full flex items-center justify-center mb-4">
<Terminal className="w-6 h-6 text-gray-400" />
</div>
<h3 className="text-sm font-medium text-gray-900 dark:text-white mb-1">No tool calls yet</h3>
<p className="text-sm text-gray-500 dark:text-gray-400">
Tool call logs will appear here as they are made.
</p>
</div>
)}
</div>
</div>
)
}