@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.
154 lines (144 loc) • 4.3 kB
JSX
import { useEffect, useState } from 'react';
import CompositeSetup from './CompositeSetup';
import CompositeLogs from './CompositeLogs';
import McpPlayground from './components/McpPlayground';
import SmartScan from './SmartScan';
import TabNavigation from './TabNavigation';
import IntroTour from './IntroTour';
import HelpButton from './components/App/HelpButton';
import TrafficTab from './components/App/TrafficTab';
import { colors } from './theme';
import { tourSteps } from './config/tourSteps.jsx';
import { fadeIn } from './utils/animations';
import { useAppState } from './components/App/useAppState';
function App() {
const {
activeTab,
setActiveTab,
requests,
selected,
setSelected,
filters,
setFilters,
stats,
firstRequestTime,
showTour,
setShowTour,
prevTabRef,
loadRequests,
} = useAppState();
const [tourKey, setTourKey] = useState(0);
useEffect(() => {
if (prevTabRef.current !== activeTab) {
const tabContent = document.querySelector('[data-tab-content]');
if (tabContent) {
fadeIn(tabContent, { duration: 300 });
}
prevTabRef.current = activeTab;
}
}, [activeTab, prevTabRef]);
return (
<div
style={{
display: 'flex',
height: '100vh',
flexDirection: 'column',
background: colors.bgPrimary,
}}
>
{showTour && (
<IntroTour
key={tourKey}
steps={tourSteps}
onComplete={() => setShowTour(false)}
onSkip={() => setShowTour(false)}
onStepChange={(stepIndex) => {
const step = tourSteps[stepIndex];
if (step) {
if (
step.target === '[data-tour="setup-tab"]' ||
step.target === '[data-tour="detected-editors"]' ||
step.target === '[data-tour="select-file"]' ||
step.target === '[data-tour="start-button"]'
) {
if (activeTab !== 'setup') {
setActiveTab('setup');
}
} else if (step.target === '[data-tour="traffic-tab"]') {
if (activeTab !== 'traffic') {
setActiveTab('traffic');
}
} else if (step.target === '[data-tour="smart-scan-tab"]') {
if (activeTab !== 'smart-scan') {
setActiveTab('smart-scan');
}
}
}
}}
/>
)}
<div style={{ position: 'relative' }} data-tour="tabs">
<TabNavigation activeTab={activeTab} onTabChange={setActiveTab} />
</div>
<HelpButton
onClick={() => {
if (showTour) {
setShowTour(false);
setTourKey((prev) => prev + 1);
setTimeout(() => {
setShowTour(true);
}, 100);
} else {
setTourKey((prev) => prev + 1);
setShowTour(true);
}
}}
/>
{activeTab === 'traffic' && (
<TrafficTab
requests={requests}
selected={selected}
onSelect={setSelected}
filters={filters}
onFilterChange={setFilters}
stats={stats}
firstRequestTime={firstRequestTime}
onClear={() => {
setSelected(null);
loadRequests();
}}
/>
)}
{activeTab === 'logs' && (
<div
data-tab-content
style={{ flex: 1, overflow: 'hidden', display: 'flex', flexDirection: 'column' }}
>
<CompositeLogs />
</div>
)}
{activeTab === 'setup' && (
<div
data-tab-content
style={{ flex: 1, overflow: 'hidden', width: '100%', height: '100%' }}
>
<CompositeSetup />
</div>
)}
{activeTab === 'playground' && (
<div
data-tab-content
style={{ flex: 1, overflow: 'hidden', width: '100%', height: '100%' }}
>
<McpPlayground />
</div>
)}
{activeTab === 'smart-scan' && (
<div data-tab-content style={{ flex: 1, overflow: 'auto', width: '100%', height: '100%' }}>
<SmartScan />
</div>
)}
</div>
);
}
export default App;