UNPKG

@mcp-shark/mcp-shark

Version:

Aggregate multiple Model Context Protocol (MCP) servers into a single unified interface with a powerful monitoring UI. Prov deep visibility into every request and response.

164 lines (143 loc) 5.09 kB
import { useState, useEffect, useRef } from 'react'; export function useAppState() { const [activeTab, setActiveTab] = useState('traffic'); const [requests, setRequests] = useState([]); const [selected, setSelected] = useState(null); const [filters, setFilters] = useState({}); const [stats, setStats] = useState(null); const [firstRequestTime, setFirstRequestTime] = useState(null); const [showTour, setShowTour] = useState(false); const [tourDismissed, setTourDismissed] = useState(true); const wsRef = useRef(null); const prevTabRef = useRef(activeTab); const loadStatistics = async () => { try { const queryParams = new URLSearchParams(); if (filters.search) queryParams.append('search', filters.search); if (filters.serverName) queryParams.append('serverName', filters.serverName); if (filters.sessionId) queryParams.append('sessionId', filters.sessionId); if (filters.method) queryParams.append('method', filters.method); if (filters.jsonrpcMethod) queryParams.append('jsonrpcMethod', filters.jsonrpcMethod); if (filters.statusCode) queryParams.append('statusCode', filters.statusCode); if (filters.jsonrpcId) queryParams.append('jsonrpcId', filters.jsonrpcId); const statsResponse = await fetch(`/api/statistics?${queryParams}`); const statsData = await statsResponse.json(); setStats(statsData); } catch (error) { console.error('Failed to load statistics:', error); } }; const loadRequests = async () => { try { const queryParams = new URLSearchParams(); if (filters.search) queryParams.append('search', filters.search); if (filters.serverName) queryParams.append('serverName', filters.serverName); if (filters.sessionId) queryParams.append('sessionId', filters.sessionId); if (filters.method) queryParams.append('method', filters.method); if (filters.jsonrpcMethod) queryParams.append('jsonrpcMethod', filters.jsonrpcMethod); if (filters.statusCode) queryParams.append('statusCode', filters.statusCode); if (filters.jsonrpcId) queryParams.append('jsonrpcId', filters.jsonrpcId); queryParams.append('limit', '5000'); const response = await fetch(`/api/requests?${queryParams}`); const data = await response.json(); setRequests(data); if (data.length > 0) { const oldest = data[data.length - 1]?.timestamp_iso; if (oldest) { setFirstRequestTime(oldest); } } // Also load statistics when loading requests await loadStatistics(); } catch (error) { console.error('Failed to load requests:', error); } }; useEffect(() => { const checkTourState = async () => { try { const response = await fetch('/api/help/state'); const data = await response.json(); setTourDismissed(data.dismissed || data.tourCompleted); if (!data.dismissed && !data.tourCompleted) { setTimeout(() => { setShowTour(true); }, 500); } } catch (error) { console.error('Failed to load tour state:', error); setTimeout(() => { setShowTour(true); }, 500); setTourDismissed(false); } }; checkTourState(); loadRequests(); const wsUrl = import.meta.env.DEV ? 'ws://localhost:9853' : `${window.location.protocol === 'https:' ? 'wss:' : 'ws:'}//${window.location.host}`; try { const ws = new WebSocket(wsUrl); wsRef.current = ws; ws.onmessage = (e) => { const msg = JSON.parse(e.data); if (msg.type === 'update') { setRequests(msg.data); if (msg.data.length > 0) { const oldest = msg.data[msg.data.length - 1]?.timestamp_iso; if (oldest) { setFirstRequestTime(oldest); } } // Update statistics when new data arrives via WebSocket loadStatistics(); } }; ws.onerror = () => { // Silently handle WebSocket errors - server may not be running }; ws.onclose = () => { // Connection closed - will attempt to reconnect on next mount if needed }; } catch (err) { // Silently handle WebSocket creation errors } return () => { if (wsRef.current && wsRef.current.readyState === WebSocket.OPEN) { wsRef.current.close(); } }; }, []); useEffect(() => { loadRequests(); }, [filters]); // Periodically update statistics when on traffic tab useEffect(() => { if (activeTab !== 'traffic') { return; } // Update statistics every 2 seconds const interval = setInterval(() => { loadStatistics(); }, 2000); return () => clearInterval(interval); }, [activeTab, filters]); return { activeTab, setActiveTab, requests, selected, setSelected, filters, setFilters, stats, firstRequestTime, showTour, setShowTour, tourDismissed, prevTabRef, wsRef, loadRequests, }; }