@mcp-shark/mcp-shark 1.4.2 → 1.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +84 -645
- package/bin/mcp-shark.js +30 -36
- package/mcp-server/index.js +115 -0
- package/mcp-server/lib/auditor/audit.js +22 -38
- package/mcp-server/lib/common/error.js +1 -1
- package/mcp-server/lib/server/external/all.js +5 -6
- package/mcp-server/lib/server/external/config.js +1 -3
- package/mcp-server/lib/server/external/kv.js +4 -12
- package/mcp-server/lib/server/external/single/request.js +3 -6
- package/mcp-server/lib/server/external/single/run.js +8 -19
- package/mcp-server/lib/server/internal/handlers/prompts-get.js +3 -13
- package/mcp-server/lib/server/internal/handlers/prompts-list.js +2 -6
- package/mcp-server/lib/server/internal/handlers/resources-list.js +2 -6
- package/mcp-server/lib/server/internal/handlers/resources-read.js +3 -12
- package/mcp-server/lib/server/internal/handlers/tools-call.js +3 -9
- package/mcp-server/lib/server/internal/handlers/tools-list.js +2 -2
- package/mcp-server/lib/server/internal/run.js +4 -16
- package/mcp-server/lib/server/internal/server.js +6 -7
- package/mcp-server/lib/server/internal/session.js +2 -15
- package/mcp-server/mcp-shark.js +16 -66
- package/package.json +23 -38
- package/shared/logger.js +90 -0
- package/ui/dist/assets/index-Cc-IUa83.css +1 -0
- package/ui/dist/assets/index-srLDlk97.js +35 -0
- package/ui/dist/index.html +17 -0
- package/ui/dist/og-image.png +0 -0
- package/ui/server/routes/backups/deleteBackup.js +54 -0
- package/ui/server/routes/backups/index.js +15 -0
- package/ui/server/routes/backups/listBackups.js +75 -0
- package/ui/server/routes/backups/restoreBackup.js +83 -0
- package/ui/server/routes/backups/viewBackup.js +47 -0
- package/ui/server/routes/composite/index.js +46 -0
- package/ui/server/routes/composite/servers.js +18 -0
- package/ui/server/routes/composite/setup.js +129 -0
- package/ui/server/routes/composite/status.js +7 -0
- package/ui/server/routes/composite/stop.js +39 -0
- package/ui/server/routes/composite/utils.js +45 -0
- package/ui/server/routes/config.js +34 -30
- package/ui/server/routes/conversations.js +3 -3
- package/ui/server/routes/help.js +2 -2
- package/ui/server/routes/logs.js +5 -5
- package/ui/server/routes/playground.js +45 -47
- package/ui/server/routes/requests.js +112 -108
- package/ui/server/routes/sessions.js +4 -4
- package/ui/server/routes/settings.js +199 -0
- package/ui/server/routes/smartscan/discover.js +7 -6
- package/ui/server/routes/smartscan/scans/clearCache.js +3 -2
- package/ui/server/routes/smartscan/scans/createBatchScans.js +4 -3
- package/ui/server/routes/smartscan/scans/createScan.js +2 -1
- package/ui/server/routes/smartscan/scans/getCachedResults.js +2 -1
- package/ui/server/routes/smartscan/scans/getScan.js +2 -1
- package/ui/server/routes/smartscan/scans/listScans.js +5 -4
- package/ui/server/routes/smartscan/scans.js +3 -3
- package/ui/server/routes/smartscan/token.js +4 -3
- package/ui/server/routes/smartscan/transport.js +1 -1
- package/ui/server/routes/smartscan.js +1 -1
- package/ui/server/routes/statistics.js +13 -10
- package/ui/server/utils/config-update.js +7 -6
- package/ui/server/utils/config.js +4 -4
- package/ui/server/utils/logger.js +2 -0
- package/ui/server/utils/paths.js +210 -2
- package/ui/server/utils/port.js +2 -2
- package/ui/server/utils/process.js +0 -67
- package/ui/server/utils/scan-cache/all-results.js +76 -59
- package/ui/server/utils/scan-cache/directory.js +1 -1
- package/ui/server/utils/scan-cache/file-operations.js +19 -16
- package/ui/server/utils/scan-cache/server-operations.js +14 -9
- package/ui/server/utils/serialization.js +9 -3
- package/ui/server/utils/smartscan-token.js +4 -3
- package/ui/server.js +86 -41
- package/ui/src/App.jsx +5 -5
- package/ui/src/CompositeLogs.jsx +20 -20
- package/ui/src/CompositeSetup.jsx +9 -9
- package/ui/src/HelpGuide/HelpGuideFooter.jsx +1 -0
- package/ui/src/HelpGuide/HelpGuideHeader.jsx +2 -1
- package/ui/src/HelpGuide.jsx +17 -4
- package/ui/src/IntroTour.jsx +19 -5
- package/ui/src/LogDetail.jsx +1 -0
- package/ui/src/LogTable.jsx +24 -6
- package/ui/src/PacketDetail.jsx +21 -16
- package/ui/src/PacketFilters.jsx +29 -14
- package/ui/src/PacketList.jsx +4 -5
- package/ui/src/SmartScan.jsx +5 -5
- package/ui/src/TabNavigation.jsx +5 -5
- package/ui/src/components/App/HelpButton.jsx +4 -0
- package/ui/src/components/App/TrafficTab.jsx +4 -4
- package/ui/src/components/App/useAppState.js +118 -24
- package/ui/src/components/BackupList.jsx +6 -2
- package/ui/src/components/CollapsibleSection.jsx +16 -2
- package/ui/src/components/ConfigViewerModal.jsx +17 -3
- package/ui/src/components/ConfirmationModal.jsx +20 -3
- package/ui/src/components/DetailsTab/BodySection.jsx +3 -1
- package/ui/src/components/DetailsTab/CollapsibleRequestResponse.jsx +14 -3
- package/ui/src/components/DetailsTab/InfoSection.jsx +4 -2
- package/ui/src/components/DetailsTab/RequestDetailsSection.jsx +7 -5
- package/ui/src/components/DetailsTab/ResponseDetailsSection.jsx +7 -5
- package/ui/src/components/DetectedPathsList.jsx +5 -2
- package/ui/src/components/FileInput.jsx +3 -1
- package/ui/src/components/GroupHeader.jsx +14 -0
- package/ui/src/components/GroupedByMcpView.jsx +3 -4
- package/ui/src/components/GroupedByServerView.jsx +1 -1
- package/ui/src/components/GroupedBySessionView.jsx +1 -1
- package/ui/src/components/HexTab.jsx +17 -4
- package/ui/src/components/LogsToolbar.jsx +3 -1
- package/ui/src/components/McpPlayground/LoadingModal.jsx +7 -3
- package/ui/src/components/McpPlayground/PromptsSection/PromptCallPanel.jsx +5 -0
- package/ui/src/components/McpPlayground/PromptsSection/PromptItem.jsx +6 -2
- package/ui/src/components/McpPlayground/PromptsSection/PromptsList.jsx +4 -4
- package/ui/src/components/McpPlayground/PromptsSection.jsx +2 -1
- package/ui/src/components/McpPlayground/ResourcesSection/ResourceCallPanel.jsx +3 -0
- package/ui/src/components/McpPlayground/ResourcesSection/ResourceItem.jsx +6 -2
- package/ui/src/components/McpPlayground/ResourcesSection/ResourcesList.jsx +4 -4
- package/ui/src/components/McpPlayground/ResourcesSection.jsx +2 -1
- package/ui/src/components/McpPlayground/ToolsSection/ToolCallPanel.jsx +5 -0
- package/ui/src/components/McpPlayground/ToolsSection/ToolItem.jsx +6 -2
- package/ui/src/components/McpPlayground/ToolsSection/ToolsList.jsx +4 -4
- package/ui/src/components/McpPlayground/ToolsSection.jsx +2 -1
- package/ui/src/components/McpPlayground/hooks/useMcpDataLoader.js +9 -9
- package/ui/src/components/McpPlayground/hooks/useMcpRequest.js +10 -5
- package/ui/src/components/McpPlayground/hooks/useMcpServerStatus.js +43 -23
- package/ui/src/components/McpPlayground/useMcpPlayground.js +72 -44
- package/ui/src/components/McpPlayground.jsx +5 -2
- package/ui/src/components/PacketDetailHeader.jsx +8 -3
- package/ui/src/components/PacketFilters/ExportControls.jsx +2 -1
- package/ui/src/components/PacketFilters/FilterInput.jsx +1 -1
- package/ui/src/components/RawTab.jsx +15 -2
- package/ui/src/components/RequestRow/OrphanedResponseRow.jsx +10 -2
- package/ui/src/components/RequestRow/RequestRowMain.jsx +12 -3
- package/ui/src/components/RequestRow/ResponseRow.jsx +11 -3
- package/ui/src/components/RequestRow.jsx +17 -9
- package/ui/src/components/ServerControl.jsx +3 -1
- package/ui/src/components/ServiceSelector.jsx +2 -0
- package/ui/src/components/SmartScan/AnalysisResult.jsx +2 -2
- package/ui/src/components/SmartScan/BatchResultsDisplay/BatchResultItem.jsx +2 -1
- package/ui/src/components/SmartScan/BatchResultsDisplay/BatchResultsHeader.jsx +1 -1
- package/ui/src/components/SmartScan/BatchResultsDisplay.jsx +9 -3
- package/ui/src/components/SmartScan/EmptyState.jsx +3 -0
- package/ui/src/components/SmartScan/ErrorDisplay.jsx +4 -2
- package/ui/src/components/SmartScan/ExpandableSection.jsx +4 -0
- package/ui/src/components/SmartScan/FindingsTable.jsx +46 -42
- package/ui/src/components/SmartScan/ListViewContent.jsx +2 -2
- package/ui/src/components/SmartScan/NotablePatternsSection.jsx +13 -8
- package/ui/src/components/SmartScan/OverallSummarySection.jsx +36 -29
- package/ui/src/components/SmartScan/RecommendationsSection.jsx +10 -8
- package/ui/src/components/SmartScan/ScanDetailHeader.jsx +2 -1
- package/ui/src/components/SmartScan/ScanDetailView.jsx +4 -4
- package/ui/src/components/SmartScan/ScanListView/ScanListHeader.jsx +2 -1
- package/ui/src/components/SmartScan/ScanListView/ScanListItem.jsx +15 -1
- package/ui/src/components/SmartScan/ScanOverviewSection.jsx +3 -1
- package/ui/src/components/SmartScan/ScanResultsDisplay.jsx +1 -1
- package/ui/src/components/SmartScan/ScanViewContent.jsx +2 -2
- package/ui/src/components/SmartScan/ScanningProgress.jsx +4 -2
- package/ui/src/components/SmartScan/ServerInfoSection.jsx +3 -1
- package/ui/src/components/SmartScan/ServerSelectionRow.jsx +4 -2
- package/ui/src/components/SmartScan/SingleResultDisplay.jsx +5 -3
- package/ui/src/components/SmartScan/SmartScanControls.jsx +11 -7
- package/ui/src/components/SmartScan/SmartScanHeader.jsx +1 -1
- package/ui/src/components/SmartScan/ViewModeTabs.jsx +2 -0
- package/ui/src/components/SmartScan/hooks/useCacheManagement.js +1 -2
- package/ui/src/components/SmartScan/hooks/useMcpDiscovery.js +22 -26
- package/ui/src/components/SmartScan/hooks/useScanList.js +10 -9
- package/ui/src/components/SmartScan/hooks/useScanOperations.js +23 -14
- package/ui/src/components/SmartScan/hooks/useServerStatus.js +2 -2
- package/ui/src/components/SmartScan/hooks/useTokenManagement.js +2 -2
- package/ui/src/components/SmartScan/scanDataUtils.js +22 -17
- package/ui/src/components/SmartScan/useSmartScan.js +4 -4
- package/ui/src/components/SmartScan/utils.js +3 -1
- package/ui/src/components/SmartScanIcons.jsx +6 -3
- package/ui/src/components/TabNavigation/DesktopTabs.jsx +8 -3
- package/ui/src/components/TabNavigation/MobileDropdown.jsx +3 -1
- package/ui/src/components/TabNavigation.jsx +8 -3
- package/ui/src/components/TabNavigationIcons.jsx +4 -4
- package/ui/src/components/TourOverlay.jsx +1 -1
- package/ui/src/components/TourTooltip/TourTooltipButtons.jsx +3 -0
- package/ui/src/components/TourTooltip/TourTooltipHeader.jsx +1 -0
- package/ui/src/components/TourTooltip/TourTooltipIcons.jsx +9 -0
- package/ui/src/components/TourTooltip/useTooltipPosition.js +63 -36
- package/ui/src/components/TourTooltip.jsx +11 -3
- package/ui/src/components/ViewModeTabs.jsx +3 -1
- package/ui/src/config/tourSteps.jsx +0 -2
- package/ui/src/hooks/useAnimation.js +15 -12
- package/ui/src/hooks/useConfigManagement.js +8 -8
- package/ui/src/hooks/useServiceExtraction.js +1 -1
- package/ui/src/index.css +3 -8
- package/ui/src/theme.js +3 -3
- package/ui/src/utils/hexUtils.js +11 -5
- package/ui/src/utils/mcpGroupingUtils.js +18 -10
- package/ui/src/utils/requestPairing.js +89 -0
- package/ui/src/utils/requestUtils.js +32 -101
- package/ui/vite.config.js +1 -1
- package/mcp-server/.editorconfig +0 -15
- package/mcp-server/.prettierignore +0 -11
- package/mcp-server/.prettierrc +0 -12
- package/mcp-server/README.md +0 -280
- package/mcp-server/commitlint.config.cjs +0 -42
- package/mcp-server/eslint.config.js +0 -131
- package/mcp-server/package-lock.json +0 -4784
- package/mcp-server/package.json +0 -30
- package/ui/README.md +0 -212
- package/ui/package-lock.json +0 -3574
- package/ui/package.json +0 -12
- package/ui/paths.js +0 -282
- package/ui/server/routes/backups.js +0 -251
- package/ui/server/routes/composite.js +0 -260
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
// Tabler Icons for Tab Navigation
|
|
2
2
|
// Using @tabler/icons-react - install with: npm install @tabler/icons-react
|
|
3
3
|
import {
|
|
4
|
-
|
|
4
|
+
IconBrandStackoverflow,
|
|
5
|
+
IconChevronDown,
|
|
5
6
|
IconFileText,
|
|
7
|
+
IconMenu2,
|
|
8
|
+
IconNetwork,
|
|
6
9
|
IconSettings,
|
|
7
|
-
IconBrandStackoverflow,
|
|
8
10
|
IconShield,
|
|
9
|
-
IconMenu2,
|
|
10
|
-
IconChevronDown,
|
|
11
11
|
} from '@tabler/icons-react';
|
|
12
12
|
|
|
13
13
|
// Wrapper components to match existing API
|
|
@@ -19,6 +19,7 @@ export default function TourTooltipButtons({
|
|
|
19
19
|
}}
|
|
20
20
|
>
|
|
21
21
|
<button
|
|
22
|
+
type="button"
|
|
22
23
|
onClick={(e) => {
|
|
23
24
|
e.stopPropagation();
|
|
24
25
|
onSkip();
|
|
@@ -52,6 +53,7 @@ export default function TourTooltipButtons({
|
|
|
52
53
|
<div style={{ display: 'flex', gap: '8px', pointerEvents: 'auto' }}>
|
|
53
54
|
{currentStep > 0 && (
|
|
54
55
|
<button
|
|
56
|
+
type="button"
|
|
55
57
|
onClick={(e) => {
|
|
56
58
|
e.stopPropagation();
|
|
57
59
|
onPrevious();
|
|
@@ -82,6 +84,7 @@ export default function TourTooltipButtons({
|
|
|
82
84
|
</button>
|
|
83
85
|
)}
|
|
84
86
|
<button
|
|
87
|
+
type="button"
|
|
85
88
|
onClick={(e) => {
|
|
86
89
|
e.stopPropagation();
|
|
87
90
|
onNext();
|
|
@@ -8,7 +8,10 @@ export const CloseIcon = ({ size = 16, color = 'currentColor' }) => (
|
|
|
8
8
|
strokeWidth="2"
|
|
9
9
|
strokeLinecap="round"
|
|
10
10
|
strokeLinejoin="round"
|
|
11
|
+
role="img"
|
|
12
|
+
aria-label="Close icon"
|
|
11
13
|
>
|
|
14
|
+
<title>Close icon</title>
|
|
12
15
|
<line x1="18" y1="6" x2="6" y2="18" />
|
|
13
16
|
<line x1="6" y1="6" x2="18" y2="18" />
|
|
14
17
|
</svg>
|
|
@@ -24,7 +27,10 @@ export const ChevronRight = ({ size = 16, color = 'currentColor' }) => (
|
|
|
24
27
|
strokeWidth="2"
|
|
25
28
|
strokeLinecap="round"
|
|
26
29
|
strokeLinejoin="round"
|
|
30
|
+
role="img"
|
|
31
|
+
aria-label="Chevron right icon"
|
|
27
32
|
>
|
|
33
|
+
<title>Chevron right icon</title>
|
|
28
34
|
<polyline points="9 18 15 12 9 6" />
|
|
29
35
|
</svg>
|
|
30
36
|
);
|
|
@@ -39,7 +45,10 @@ export const ChevronLeft = ({ size = 16, color = 'currentColor' }) => (
|
|
|
39
45
|
strokeWidth="2"
|
|
40
46
|
strokeLinecap="round"
|
|
41
47
|
strokeLinejoin="round"
|
|
48
|
+
role="img"
|
|
49
|
+
aria-label="Chevron left icon"
|
|
42
50
|
>
|
|
51
|
+
<title>Chevron left icon</title>
|
|
43
52
|
<polyline points="15 18 9 12 15 6" />
|
|
44
53
|
</svg>
|
|
45
54
|
);
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
2
|
|
|
3
|
-
export function useTooltipPosition(elementRect, step,
|
|
3
|
+
export function useTooltipPosition(elementRect, step, _currentStep) {
|
|
4
4
|
const [tooltipPosition, setTooltipPosition] = useState({ x: 0, y: 0 });
|
|
5
5
|
const [isDragging, setIsDragging] = useState(false);
|
|
6
6
|
const [dragOffset, setDragOffset] = useState({ x: 0, y: 0 });
|
|
7
7
|
|
|
8
8
|
useEffect(() => {
|
|
9
9
|
setTooltipPosition({ x: 0, y: 0 });
|
|
10
|
-
}, [
|
|
10
|
+
}, []);
|
|
11
11
|
|
|
12
12
|
const handleMouseDown = (e, tooltipRef) => {
|
|
13
13
|
e.preventDefault();
|
|
@@ -23,7 +23,9 @@ export function useTooltipPosition(elementRect, step, currentStep) {
|
|
|
23
23
|
};
|
|
24
24
|
|
|
25
25
|
useEffect(() => {
|
|
26
|
-
if (!isDragging)
|
|
26
|
+
if (!isDragging) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
27
29
|
|
|
28
30
|
const handleMouseMove = (e) => {
|
|
29
31
|
setTooltipPosition({
|
|
@@ -46,7 +48,9 @@ export function useTooltipPosition(elementRect, step, currentStep) {
|
|
|
46
48
|
}, [isDragging, dragOffset]);
|
|
47
49
|
|
|
48
50
|
const calculatePosition = () => {
|
|
49
|
-
if (!elementRect)
|
|
51
|
+
if (!elementRect) {
|
|
52
|
+
return { left: 0, top: 0, transform: 'none' };
|
|
53
|
+
}
|
|
50
54
|
|
|
51
55
|
const tooltipWidth = 350;
|
|
52
56
|
const tooltipHeight = 200;
|
|
@@ -62,42 +66,65 @@ export function useTooltipPosition(elementRect, step, currentStep) {
|
|
|
62
66
|
}
|
|
63
67
|
|
|
64
68
|
const position = step.position || 'bottom';
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
left = elementRect.right + spacing;
|
|
76
|
-
top = elementRect.top + elementRect.height / 2;
|
|
77
|
-
transform = 'translateY(-50%)';
|
|
78
|
-
if (left + tooltipWidth > window.innerWidth - 10) {
|
|
79
|
-
left = elementRect.left - tooltipWidth - spacing;
|
|
69
|
+
|
|
70
|
+
const calculatePosition = (position, elementRect, tooltipWidth, tooltipHeight, spacing) => {
|
|
71
|
+
if (position === 'left') {
|
|
72
|
+
const baseLeft = elementRect.left - tooltipWidth - spacing;
|
|
73
|
+
const left = baseLeft < 10 ? elementRect.right + spacing : baseLeft;
|
|
74
|
+
return {
|
|
75
|
+
left,
|
|
76
|
+
top: elementRect.top + elementRect.height / 2,
|
|
77
|
+
transform: 'translateY(-50%)',
|
|
78
|
+
};
|
|
80
79
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
80
|
+
|
|
81
|
+
if (position === 'right') {
|
|
82
|
+
const baseLeft = elementRect.right + spacing;
|
|
83
|
+
const left =
|
|
84
|
+
baseLeft + tooltipWidth > window.innerWidth - 10
|
|
85
|
+
? elementRect.left - tooltipWidth - spacing
|
|
86
|
+
: baseLeft;
|
|
87
|
+
return {
|
|
88
|
+
left,
|
|
89
|
+
top: elementRect.top + elementRect.height / 2,
|
|
90
|
+
transform: 'translateY(-50%)',
|
|
91
|
+
};
|
|
87
92
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
93
|
+
|
|
94
|
+
if (position === 'top') {
|
|
95
|
+
const baseTop = elementRect.top - tooltipHeight - spacing;
|
|
96
|
+
const top = baseTop < 10 ? elementRect.bottom + spacing : baseTop;
|
|
97
|
+
return {
|
|
98
|
+
left: elementRect.left + elementRect.width / 2,
|
|
99
|
+
top,
|
|
100
|
+
transform: 'translate(-50%, 0)',
|
|
101
|
+
};
|
|
94
102
|
}
|
|
95
|
-
}
|
|
96
103
|
|
|
97
|
-
|
|
98
|
-
|
|
104
|
+
// bottom (default)
|
|
105
|
+
const baseTop = elementRect.bottom + spacing;
|
|
106
|
+
const top =
|
|
107
|
+
baseTop + tooltipHeight > window.innerHeight - 10
|
|
108
|
+
? elementRect.top - tooltipHeight - spacing
|
|
109
|
+
: baseTop;
|
|
110
|
+
return {
|
|
111
|
+
left: elementRect.left + elementRect.width / 2,
|
|
112
|
+
top,
|
|
113
|
+
transform: 'translate(-50%, 0)',
|
|
114
|
+
};
|
|
115
|
+
};
|
|
99
116
|
|
|
100
|
-
|
|
117
|
+
const rawPosition = calculatePosition(
|
|
118
|
+
position,
|
|
119
|
+
elementRect,
|
|
120
|
+
tooltipWidth,
|
|
121
|
+
tooltipHeight,
|
|
122
|
+
spacing
|
|
123
|
+
);
|
|
124
|
+
const left = Math.max(10, Math.min(rawPosition.left, window.innerWidth - tooltipWidth - 10));
|
|
125
|
+
const top = Math.max(10, Math.min(rawPosition.top, window.innerHeight - tooltipHeight - 10));
|
|
126
|
+
|
|
127
|
+
return { left, top, transform: rawPosition.transform };
|
|
101
128
|
};
|
|
102
129
|
|
|
103
130
|
return {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useRef } from 'react';
|
|
2
2
|
import { colors, fonts } from '../theme';
|
|
3
|
-
import TourTooltipHeader from './TourTooltip/TourTooltipHeader';
|
|
4
3
|
import TourTooltipButtons from './TourTooltip/TourTooltipButtons';
|
|
4
|
+
import TourTooltipHeader from './TourTooltip/TourTooltipHeader';
|
|
5
5
|
import { useTooltipPosition } from './TourTooltip/useTooltipPosition';
|
|
6
6
|
|
|
7
7
|
function TourTooltip({ elementRect, step, currentStep, totalSteps, onNext, onPrevious, onSkip }) {
|
|
@@ -17,9 +17,17 @@ function TourTooltip({ elementRect, step, currentStep, totalSteps, onNext, onPre
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
return (
|
|
20
|
-
<
|
|
20
|
+
<button
|
|
21
|
+
type="button"
|
|
21
22
|
ref={tooltipRef}
|
|
22
23
|
onMouseDown={(e) => handleMouseDown(e, tooltipRef)}
|
|
24
|
+
onKeyDown={(e) => {
|
|
25
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
26
|
+
e.preventDefault();
|
|
27
|
+
handleMouseDown(e, tooltipRef);
|
|
28
|
+
}
|
|
29
|
+
}}
|
|
30
|
+
aria-label="Draggable tooltip"
|
|
23
31
|
style={{
|
|
24
32
|
position: 'fixed',
|
|
25
33
|
left: `${position.left}px`,
|
|
@@ -76,7 +84,7 @@ function TourTooltip({ elementRect, step, currentStep, totalSteps, onNext, onPre
|
|
|
76
84
|
onPrevious={onPrevious}
|
|
77
85
|
onSkip={onSkip}
|
|
78
86
|
/>
|
|
79
|
-
</
|
|
87
|
+
</button>
|
|
80
88
|
);
|
|
81
89
|
}
|
|
82
90
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { colors, fonts } from '../theme';
|
|
2
1
|
import { IconList, IconNetwork } from '@tabler/icons-react';
|
|
2
|
+
import { colors, fonts } from '../theme';
|
|
3
3
|
|
|
4
4
|
function ViewModeTabs({ viewMode, onViewModeChange }) {
|
|
5
5
|
return (
|
|
@@ -15,6 +15,7 @@ function ViewModeTabs({ viewMode, onViewModeChange }) {
|
|
|
15
15
|
}}
|
|
16
16
|
>
|
|
17
17
|
<button
|
|
18
|
+
type="button"
|
|
18
19
|
onClick={() => onViewModeChange('general')}
|
|
19
20
|
style={{
|
|
20
21
|
padding: '10px 18px',
|
|
@@ -49,6 +50,7 @@ function ViewModeTabs({ viewMode, onViewModeChange }) {
|
|
|
49
50
|
General List
|
|
50
51
|
</button>
|
|
51
52
|
<button
|
|
53
|
+
type="button"
|
|
52
54
|
onClick={() => onViewModeChange('groupedByMcp')}
|
|
53
55
|
style={{
|
|
54
56
|
padding: '10px 18px',
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { useEffect, useRef } from 'react';
|
|
2
1
|
import anime from 'animejs';
|
|
2
|
+
import { useEffect, useRef } from 'react';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* React hook for animating elements on mount/unmount
|
|
@@ -18,7 +18,7 @@ export const useAnimation = (animationFn, deps = []) => {
|
|
|
18
18
|
anime.remove(elementRef.current);
|
|
19
19
|
}
|
|
20
20
|
};
|
|
21
|
-
}, deps);
|
|
21
|
+
}, [animationFn, ...deps]);
|
|
22
22
|
|
|
23
23
|
return elementRef;
|
|
24
24
|
};
|
|
@@ -28,6 +28,7 @@ export const useAnimation = (animationFn, deps = []) => {
|
|
|
28
28
|
*/
|
|
29
29
|
export const useMountAnimation = (options = {}) => {
|
|
30
30
|
const elementRef = useRef(null);
|
|
31
|
+
const { duration, easing } = options;
|
|
31
32
|
|
|
32
33
|
useEffect(() => {
|
|
33
34
|
if (elementRef.current) {
|
|
@@ -35,12 +36,12 @@ export const useMountAnimation = (options = {}) => {
|
|
|
35
36
|
targets: elementRef.current,
|
|
36
37
|
opacity: [0, 1],
|
|
37
38
|
translateY: [20, 0],
|
|
38
|
-
duration:
|
|
39
|
-
easing:
|
|
39
|
+
duration: duration || 400,
|
|
40
|
+
easing: easing || 'easeOutExpo',
|
|
40
41
|
...options,
|
|
41
42
|
});
|
|
42
43
|
}
|
|
43
|
-
}, []);
|
|
44
|
+
}, [duration, easing, options]);
|
|
44
45
|
|
|
45
46
|
return elementRef;
|
|
46
47
|
};
|
|
@@ -50,6 +51,7 @@ export const useMountAnimation = (options = {}) => {
|
|
|
50
51
|
*/
|
|
51
52
|
export const useUnmountAnimation = (shouldUnmount, onComplete, options = {}) => {
|
|
52
53
|
const elementRef = useRef(null);
|
|
54
|
+
const { duration, easing } = options;
|
|
53
55
|
|
|
54
56
|
useEffect(() => {
|
|
55
57
|
if (shouldUnmount && elementRef.current) {
|
|
@@ -57,13 +59,13 @@ export const useUnmountAnimation = (shouldUnmount, onComplete, options = {}) =>
|
|
|
57
59
|
targets: elementRef.current,
|
|
58
60
|
opacity: [1, 0],
|
|
59
61
|
translateY: [0, -20],
|
|
60
|
-
duration:
|
|
61
|
-
easing:
|
|
62
|
+
duration: duration || 300,
|
|
63
|
+
easing: easing || 'easeInExpo',
|
|
62
64
|
complete: onComplete,
|
|
63
65
|
...options,
|
|
64
66
|
});
|
|
65
67
|
}
|
|
66
|
-
}, [shouldUnmount, onComplete]);
|
|
68
|
+
}, [shouldUnmount, onComplete, duration, easing, options]);
|
|
67
69
|
|
|
68
70
|
return elementRef;
|
|
69
71
|
};
|
|
@@ -73,6 +75,7 @@ export const useUnmountAnimation = (shouldUnmount, onComplete, options = {}) =>
|
|
|
73
75
|
*/
|
|
74
76
|
export const useStaggerAnimation = (items, options = {}) => {
|
|
75
77
|
const containerRef = useRef(null);
|
|
78
|
+
const { duration, delay, easing } = options;
|
|
76
79
|
|
|
77
80
|
useEffect(() => {
|
|
78
81
|
if (containerRef.current && items.length > 0) {
|
|
@@ -80,13 +83,13 @@ export const useStaggerAnimation = (items, options = {}) => {
|
|
|
80
83
|
targets: containerRef.current.children,
|
|
81
84
|
opacity: [0, 1],
|
|
82
85
|
translateY: [20, 0],
|
|
83
|
-
duration:
|
|
84
|
-
delay: anime.stagger(
|
|
85
|
-
easing:
|
|
86
|
+
duration: duration || 400,
|
|
87
|
+
delay: anime.stagger(delay || 50),
|
|
88
|
+
easing: easing || 'easeOutExpo',
|
|
86
89
|
...options,
|
|
87
90
|
});
|
|
88
91
|
}
|
|
89
|
-
}, [items.length]);
|
|
92
|
+
}, [items.length, duration, delay, easing, options]);
|
|
90
93
|
|
|
91
94
|
return containerRef;
|
|
92
95
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useCallback, useEffect, useState } from 'react';
|
|
2
2
|
|
|
3
3
|
export function useConfigManagement() {
|
|
4
4
|
const [detectedPaths, setDetectedPaths] = useState([]);
|
|
@@ -12,7 +12,7 @@ export function useConfigManagement() {
|
|
|
12
12
|
const [backupContent, setBackupContent] = useState(null);
|
|
13
13
|
const [loadingBackup, setLoadingBackup] = useState(false);
|
|
14
14
|
|
|
15
|
-
const detectConfigPaths = async () => {
|
|
15
|
+
const detectConfigPaths = useCallback(async () => {
|
|
16
16
|
setDetecting(true);
|
|
17
17
|
try {
|
|
18
18
|
const res = await fetch('/api/config/detect');
|
|
@@ -23,9 +23,9 @@ export function useConfigManagement() {
|
|
|
23
23
|
} finally {
|
|
24
24
|
setDetecting(false);
|
|
25
25
|
}
|
|
26
|
-
};
|
|
26
|
+
}, []);
|
|
27
27
|
|
|
28
|
-
const loadBackups = async () => {
|
|
28
|
+
const loadBackups = useCallback(async () => {
|
|
29
29
|
setLoadingBackups(true);
|
|
30
30
|
try {
|
|
31
31
|
const res = await fetch('/api/config/backups');
|
|
@@ -36,7 +36,7 @@ export function useConfigManagement() {
|
|
|
36
36
|
} finally {
|
|
37
37
|
setLoadingBackups(false);
|
|
38
38
|
}
|
|
39
|
-
};
|
|
39
|
+
}, []);
|
|
40
40
|
|
|
41
41
|
const handleViewConfig = async (filePath) => {
|
|
42
42
|
setLoadingConfig(true);
|
|
@@ -49,7 +49,7 @@ export function useConfigManagement() {
|
|
|
49
49
|
} else {
|
|
50
50
|
setConfigContent(null);
|
|
51
51
|
}
|
|
52
|
-
} catch (
|
|
52
|
+
} catch (_err) {
|
|
53
53
|
setConfigContent(null);
|
|
54
54
|
} finally {
|
|
55
55
|
setLoadingConfig(false);
|
|
@@ -69,7 +69,7 @@ export function useConfigManagement() {
|
|
|
69
69
|
} else {
|
|
70
70
|
setBackupContent(null);
|
|
71
71
|
}
|
|
72
|
-
} catch (
|
|
72
|
+
} catch (_err) {
|
|
73
73
|
setBackupContent(null);
|
|
74
74
|
} finally {
|
|
75
75
|
setLoadingBackup(false);
|
|
@@ -98,7 +98,7 @@ export function useConfigManagement() {
|
|
|
98
98
|
useEffect(() => {
|
|
99
99
|
detectConfigPaths();
|
|
100
100
|
loadBackups();
|
|
101
|
-
}, []);
|
|
101
|
+
}, [detectConfigPaths, loadBackups]);
|
|
102
102
|
|
|
103
103
|
return {
|
|
104
104
|
detectedPaths,
|
package/ui/src/index.css
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
@import url(
|
|
1
|
+
@import url("https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&family=Roboto+Mono:wght@400;500&display=swap");
|
|
2
2
|
|
|
3
3
|
* {
|
|
4
4
|
margin: 0;
|
|
@@ -7,12 +7,7 @@
|
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
body {
|
|
10
|
-
font-family:
|
|
11
|
-
'Roboto',
|
|
12
|
-
-apple-system,
|
|
13
|
-
BlinkMacSystemFont,
|
|
14
|
-
'Segoe UI',
|
|
15
|
-
sans-serif;
|
|
10
|
+
font-family: "Roboto", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
16
11
|
background: #ffffff;
|
|
17
12
|
color: #202124;
|
|
18
13
|
overflow: hidden;
|
|
@@ -23,7 +18,7 @@ body {
|
|
|
23
18
|
code,
|
|
24
19
|
pre,
|
|
25
20
|
.monospace {
|
|
26
|
-
font-family:
|
|
21
|
+
font-family: "Roboto Mono", "JetBrains Mono", "Fira Code", "Consolas", monospace;
|
|
27
22
|
}
|
|
28
23
|
|
|
29
24
|
#root {
|
package/ui/src/theme.js
CHANGED
|
@@ -53,9 +53,9 @@ export function withOpacity(color, opacity) {
|
|
|
53
53
|
// Remove # if present
|
|
54
54
|
const hex = color.replace('#', '');
|
|
55
55
|
// Convert to RGB
|
|
56
|
-
const r = parseInt(hex.substring(0, 2), 16);
|
|
57
|
-
const g = parseInt(hex.substring(2, 4), 16);
|
|
58
|
-
const b = parseInt(hex.substring(4, 6), 16);
|
|
56
|
+
const r = Number.parseInt(hex.substring(0, 2), 16);
|
|
57
|
+
const g = Number.parseInt(hex.substring(2, 4), 16);
|
|
58
|
+
const b = Number.parseInt(hex.substring(4, 6), 16);
|
|
59
59
|
return `rgba(${r}, ${g}, ${b}, ${opacity})`;
|
|
60
60
|
}
|
|
61
61
|
|
package/ui/src/utils/hexUtils.js
CHANGED
|
@@ -1,16 +1,22 @@
|
|
|
1
1
|
export function generateHexDump(text) {
|
|
2
|
-
if (!text)
|
|
2
|
+
if (!text) {
|
|
3
|
+
return [];
|
|
4
|
+
}
|
|
3
5
|
const bytes = new TextEncoder().encode(text);
|
|
4
6
|
const lines = [];
|
|
5
|
-
|
|
6
|
-
|
|
7
|
+
const chunkSize = 16;
|
|
8
|
+
const totalChunks = Math.ceil(bytes.length / chunkSize);
|
|
9
|
+
|
|
10
|
+
for (const chunkIndex of Array.from({ length: totalChunks }, (_, idx) => idx)) {
|
|
11
|
+
const startOffset = chunkIndex * chunkSize;
|
|
12
|
+
const chunk = bytes.slice(startOffset, startOffset + chunkSize);
|
|
7
13
|
const hex = Array.from(chunk)
|
|
8
14
|
.map((b) => b.toString(16).padStart(2, '0'))
|
|
9
15
|
.join(' ');
|
|
10
16
|
const ascii = Array.from(chunk)
|
|
11
17
|
.map((b) => (b >= 32 && b < 127 ? String.fromCharCode(b) : '.'))
|
|
12
18
|
.join('');
|
|
13
|
-
const offset =
|
|
19
|
+
const offset = startOffset.toString(16).padStart(8, '0');
|
|
14
20
|
lines.push({ offset, hex, ascii });
|
|
15
21
|
}
|
|
16
22
|
return lines;
|
|
@@ -20,5 +26,5 @@ export function createFullRequestText(headers, bodyRaw) {
|
|
|
20
26
|
const headersText = Object.entries(headers)
|
|
21
27
|
.map(([key, value]) => `${key}: ${value}`)
|
|
22
28
|
.join('\r\n');
|
|
23
|
-
return headersText + (bodyRaw ?
|
|
29
|
+
return headersText + (bodyRaw ? `\r\n\r\n${bodyRaw}` : '');
|
|
24
30
|
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { pairRequestsWithResponses } from './requestUtils.js';
|
|
2
1
|
import {
|
|
3
|
-
|
|
4
|
-
IconTool,
|
|
2
|
+
IconBell,
|
|
5
3
|
IconDatabase,
|
|
6
4
|
IconMessage,
|
|
7
|
-
IconBell,
|
|
8
|
-
IconUser,
|
|
9
5
|
IconPackage,
|
|
6
|
+
IconRefresh,
|
|
7
|
+
IconTool,
|
|
8
|
+
IconUser,
|
|
10
9
|
} from '@tabler/icons-react';
|
|
10
|
+
import { pairRequestsWithResponses } from './requestUtils.js';
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* MCP Method Categories based on the protocol specification
|
|
@@ -27,7 +27,9 @@ export const MCP_METHOD_CATEGORIES = {
|
|
|
27
27
|
* Categorize an MCP method into its protocol category
|
|
28
28
|
*/
|
|
29
29
|
export function categorizeMcpMethod(method) {
|
|
30
|
-
if (!method)
|
|
30
|
+
if (!method) {
|
|
31
|
+
return MCP_METHOD_CATEGORIES.OTHER;
|
|
32
|
+
}
|
|
31
33
|
|
|
32
34
|
// Lifecycle methods
|
|
33
35
|
if (method === 'initialize' || method === 'notifications/initialized') {
|
|
@@ -112,7 +114,9 @@ export function groupByMcpSessionAndCategory(requests) {
|
|
|
112
114
|
|
|
113
115
|
pairs.forEach((pair) => {
|
|
114
116
|
const request = pair.request || pair.response;
|
|
115
|
-
if (!request)
|
|
117
|
+
if (!request) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
116
120
|
|
|
117
121
|
const sessionId = request.session_id || '__NO_SESSION__';
|
|
118
122
|
const method = getJsonRpcMethod(request);
|
|
@@ -140,7 +144,7 @@ export function groupByMcpSessionAndCategory(requests) {
|
|
|
140
144
|
});
|
|
141
145
|
|
|
142
146
|
return Array.from(sessionGroups.entries())
|
|
143
|
-
.map(([
|
|
147
|
+
.map(([_sessionId, session]) => ({
|
|
144
148
|
sessionId: session.sessionId,
|
|
145
149
|
firstTimestamp: session.firstTimestamp,
|
|
146
150
|
categories: Array.from(session.categories.entries())
|
|
@@ -182,7 +186,9 @@ export function groupByMcpCategory(requests) {
|
|
|
182
186
|
|
|
183
187
|
pairs.forEach((pair) => {
|
|
184
188
|
const request = pair.request || pair.response;
|
|
185
|
-
if (!request)
|
|
189
|
+
if (!request) {
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
186
192
|
|
|
187
193
|
const method = getJsonRpcMethod(request);
|
|
188
194
|
const category = categorizeMcpMethod(method || '');
|
|
@@ -226,7 +232,9 @@ export function groupByMcpCategory(requests) {
|
|
|
226
232
|
* Based on the protocol specification
|
|
227
233
|
*/
|
|
228
234
|
export function getMethodDescription(method) {
|
|
229
|
-
if (!method)
|
|
235
|
+
if (!method) {
|
|
236
|
+
return 'Unknown operation';
|
|
237
|
+
}
|
|
230
238
|
|
|
231
239
|
const descriptions = {
|
|
232
240
|
// Lifecycle
|