@mcp-shark/mcp-shark 1.4.1 → 1.5.0

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.
Files changed (203) hide show
  1. package/README.md +84 -645
  2. package/bin/mcp-shark.js +30 -36
  3. package/mcp-server/index.js +115 -0
  4. package/mcp-server/lib/auditor/audit.js +34 -42
  5. package/mcp-server/lib/common/error.js +1 -1
  6. package/mcp-server/lib/server/external/all.js +5 -6
  7. package/mcp-server/lib/server/external/config.js +1 -3
  8. package/mcp-server/lib/server/external/kv.js +21 -40
  9. package/mcp-server/lib/server/external/single/request.js +3 -6
  10. package/mcp-server/lib/server/external/single/run.js +8 -19
  11. package/mcp-server/lib/server/internal/handlers/prompts-get.js +5 -7
  12. package/mcp-server/lib/server/internal/handlers/prompts-list.js +5 -4
  13. package/mcp-server/lib/server/internal/handlers/resources-list.js +5 -4
  14. package/mcp-server/lib/server/internal/handlers/resources-read.js +5 -4
  15. package/mcp-server/lib/server/internal/handlers/tools-call.js +8 -10
  16. package/mcp-server/lib/server/internal/handlers/tools-list.js +6 -5
  17. package/mcp-server/lib/server/internal/run.js +5 -17
  18. package/mcp-server/lib/server/internal/server.js +14 -15
  19. package/mcp-server/lib/server/internal/session.js +8 -13
  20. package/mcp-server/mcp-shark.js +16 -66
  21. package/package.json +23 -38
  22. package/ui/dist/assets/index-Cc-IUa83.css +1 -0
  23. package/ui/dist/assets/index-srLDlk97.js +35 -0
  24. package/ui/dist/index.html +17 -0
  25. package/ui/dist/og-image.png +0 -0
  26. package/ui/server/routes/backups/deleteBackup.js +54 -0
  27. package/ui/server/routes/backups/index.js +15 -0
  28. package/ui/server/routes/backups/listBackups.js +75 -0
  29. package/ui/server/routes/backups/restoreBackup.js +83 -0
  30. package/ui/server/routes/backups/viewBackup.js +47 -0
  31. package/ui/server/routes/composite/index.js +46 -0
  32. package/ui/server/routes/composite/servers.js +18 -0
  33. package/ui/server/routes/composite/setup.js +129 -0
  34. package/ui/server/routes/composite/status.js +7 -0
  35. package/ui/server/routes/composite/stop.js +39 -0
  36. package/ui/server/routes/composite/utils.js +45 -0
  37. package/ui/server/routes/config.js +34 -30
  38. package/ui/server/routes/conversations.js +3 -3
  39. package/ui/server/routes/help.js +2 -2
  40. package/ui/server/routes/logs.js +5 -5
  41. package/ui/server/routes/playground.js +91 -62
  42. package/ui/server/routes/requests.js +112 -108
  43. package/ui/server/routes/sessions.js +4 -4
  44. package/ui/server/routes/settings.js +199 -0
  45. package/ui/server/routes/smartscan/discover.js +7 -6
  46. package/ui/server/routes/smartscan/scans/clearCache.js +3 -2
  47. package/ui/server/routes/smartscan/scans/createBatchScans.js +4 -3
  48. package/ui/server/routes/smartscan/scans/createScan.js +2 -1
  49. package/ui/server/routes/smartscan/scans/getCachedResults.js +2 -1
  50. package/ui/server/routes/smartscan/scans/getScan.js +2 -1
  51. package/ui/server/routes/smartscan/scans/listScans.js +5 -4
  52. package/ui/server/routes/smartscan/scans.js +3 -3
  53. package/ui/server/routes/smartscan/token.js +4 -3
  54. package/ui/server/routes/smartscan/transport.js +1 -1
  55. package/ui/server/routes/smartscan.js +1 -1
  56. package/ui/server/routes/statistics.js +13 -10
  57. package/ui/server/utils/config-update.js +140 -112
  58. package/ui/server/utils/config.js +4 -4
  59. package/ui/server/utils/logger.js +2 -0
  60. package/ui/server/utils/paths.js +210 -2
  61. package/ui/server/utils/port.js +2 -2
  62. package/ui/server/utils/process.js +0 -67
  63. package/ui/server/utils/scan-cache/all-results.js +76 -59
  64. package/ui/server/utils/scan-cache/directory.js +1 -1
  65. package/ui/server/utils/scan-cache/file-operations.js +19 -16
  66. package/ui/server/utils/scan-cache/server-operations.js +14 -9
  67. package/ui/server/utils/serialization.js +9 -3
  68. package/ui/server/utils/smartscan-token.js +4 -3
  69. package/ui/server.js +87 -41
  70. package/ui/src/App.jsx +5 -5
  71. package/ui/src/CompositeLogs.jsx +20 -20
  72. package/ui/src/CompositeSetup.jsx +9 -9
  73. package/ui/src/HelpGuide/HelpGuideFooter.jsx +1 -0
  74. package/ui/src/HelpGuide/HelpGuideHeader.jsx +2 -1
  75. package/ui/src/HelpGuide.jsx +17 -4
  76. package/ui/src/IntroTour.jsx +19 -5
  77. package/ui/src/LogDetail.jsx +1 -0
  78. package/ui/src/LogTable.jsx +24 -6
  79. package/ui/src/PacketDetail.jsx +21 -16
  80. package/ui/src/PacketFilters.jsx +29 -14
  81. package/ui/src/PacketList.jsx +4 -5
  82. package/ui/src/SmartScan.jsx +5 -5
  83. package/ui/src/TabNavigation.jsx +5 -5
  84. package/ui/src/components/App/HelpButton.jsx +4 -0
  85. package/ui/src/components/App/TrafficTab.jsx +4 -4
  86. package/ui/src/components/App/useAppState.js +118 -24
  87. package/ui/src/components/BackupList.jsx +6 -2
  88. package/ui/src/components/CollapsibleSection.jsx +16 -2
  89. package/ui/src/components/ConfigViewerModal.jsx +17 -3
  90. package/ui/src/components/ConfirmationModal.jsx +20 -3
  91. package/ui/src/components/DetailsTab/BodySection.jsx +3 -1
  92. package/ui/src/components/DetailsTab/CollapsibleRequestResponse.jsx +14 -3
  93. package/ui/src/components/DetailsTab/InfoSection.jsx +4 -2
  94. package/ui/src/components/DetailsTab/RequestDetailsSection.jsx +7 -5
  95. package/ui/src/components/DetailsTab/ResponseDetailsSection.jsx +7 -5
  96. package/ui/src/components/DetectedPathsList.jsx +5 -2
  97. package/ui/src/components/FileInput.jsx +3 -1
  98. package/ui/src/components/GroupHeader.jsx +14 -0
  99. package/ui/src/components/GroupedByMcpView.jsx +3 -10
  100. package/ui/src/components/GroupedByServerView.jsx +1 -1
  101. package/ui/src/components/GroupedBySessionView.jsx +1 -1
  102. package/ui/src/components/HexTab.jsx +17 -4
  103. package/ui/src/components/LogsToolbar.jsx +3 -1
  104. package/ui/src/components/McpPlayground/LoadingModal.jsx +7 -3
  105. package/ui/src/components/McpPlayground/PromptsSection/PromptCallPanel.jsx +5 -0
  106. package/ui/src/components/McpPlayground/PromptsSection/PromptItem.jsx +52 -23
  107. package/ui/src/components/McpPlayground/PromptsSection/PromptsList.jsx +13 -11
  108. package/ui/src/components/McpPlayground/PromptsSection.jsx +2 -1
  109. package/ui/src/components/McpPlayground/ResourcesSection/ResourceCallPanel.jsx +3 -0
  110. package/ui/src/components/McpPlayground/ResourcesSection/ResourceItem.jsx +66 -34
  111. package/ui/src/components/McpPlayground/ResourcesSection/ResourcesList.jsx +13 -11
  112. package/ui/src/components/McpPlayground/ResourcesSection.jsx +2 -1
  113. package/ui/src/components/McpPlayground/ToolsSection/ToolCallPanel.jsx +5 -0
  114. package/ui/src/components/McpPlayground/ToolsSection/ToolItem.jsx +52 -23
  115. package/ui/src/components/McpPlayground/ToolsSection/ToolsList.jsx +13 -11
  116. package/ui/src/components/McpPlayground/ToolsSection.jsx +2 -1
  117. package/ui/src/components/McpPlayground/hooks/useMcpDataLoader.js +107 -0
  118. package/ui/src/components/McpPlayground/hooks/useMcpRequest.js +70 -0
  119. package/ui/src/components/McpPlayground/hooks/useMcpServerStatus.js +90 -0
  120. package/ui/src/components/McpPlayground/useMcpPlayground.js +118 -159
  121. package/ui/src/components/McpPlayground.jsx +105 -23
  122. package/ui/src/components/PacketDetailHeader.jsx +8 -3
  123. package/ui/src/components/PacketFilters/ExportControls.jsx +2 -1
  124. package/ui/src/components/PacketFilters/FilterInput.jsx +1 -1
  125. package/ui/src/components/RawTab.jsx +15 -2
  126. package/ui/src/components/RequestRow/OrphanedResponseRow.jsx +10 -2
  127. package/ui/src/components/RequestRow/RequestRowMain.jsx +12 -3
  128. package/ui/src/components/RequestRow/ResponseRow.jsx +11 -3
  129. package/ui/src/components/RequestRow.jsx +17 -9
  130. package/ui/src/components/ServerControl.jsx +3 -1
  131. package/ui/src/components/ServiceSelector.jsx +2 -0
  132. package/ui/src/components/SmartScan/AnalysisResult.jsx +2 -2
  133. package/ui/src/components/SmartScan/BatchResultsDisplay/BatchResultItem.jsx +2 -1
  134. package/ui/src/components/SmartScan/BatchResultsDisplay/BatchResultsHeader.jsx +1 -1
  135. package/ui/src/components/SmartScan/BatchResultsDisplay.jsx +9 -3
  136. package/ui/src/components/SmartScan/EmptyState.jsx +3 -0
  137. package/ui/src/components/SmartScan/ErrorDisplay.jsx +4 -2
  138. package/ui/src/components/SmartScan/ExpandableSection.jsx +4 -0
  139. package/ui/src/components/SmartScan/FindingsTable.jsx +46 -42
  140. package/ui/src/components/SmartScan/ListViewContent.jsx +2 -2
  141. package/ui/src/components/SmartScan/NotablePatternsSection.jsx +13 -8
  142. package/ui/src/components/SmartScan/OverallSummarySection.jsx +36 -29
  143. package/ui/src/components/SmartScan/RecommendationsSection.jsx +10 -8
  144. package/ui/src/components/SmartScan/ScanDetailHeader.jsx +2 -1
  145. package/ui/src/components/SmartScan/ScanDetailView.jsx +4 -4
  146. package/ui/src/components/SmartScan/ScanListView/ScanListHeader.jsx +2 -1
  147. package/ui/src/components/SmartScan/ScanListView/ScanListItem.jsx +15 -1
  148. package/ui/src/components/SmartScan/ScanOverviewSection.jsx +3 -1
  149. package/ui/src/components/SmartScan/ScanResultsDisplay.jsx +1 -1
  150. package/ui/src/components/SmartScan/ScanViewContent.jsx +2 -2
  151. package/ui/src/components/SmartScan/ScanningProgress.jsx +4 -2
  152. package/ui/src/components/SmartScan/ServerInfoSection.jsx +3 -1
  153. package/ui/src/components/SmartScan/ServerSelectionRow.jsx +4 -2
  154. package/ui/src/components/SmartScan/SingleResultDisplay.jsx +5 -3
  155. package/ui/src/components/SmartScan/SmartScanControls.jsx +11 -7
  156. package/ui/src/components/SmartScan/SmartScanHeader.jsx +1 -1
  157. package/ui/src/components/SmartScan/ViewModeTabs.jsx +2 -0
  158. package/ui/src/components/SmartScan/hooks/useCacheManagement.js +1 -2
  159. package/ui/src/components/SmartScan/hooks/useMcpDiscovery.js +22 -26
  160. package/ui/src/components/SmartScan/hooks/useScanList.js +10 -9
  161. package/ui/src/components/SmartScan/hooks/useScanOperations.js +23 -14
  162. package/ui/src/components/SmartScan/hooks/useServerStatus.js +2 -2
  163. package/ui/src/components/SmartScan/hooks/useTokenManagement.js +2 -2
  164. package/ui/src/components/SmartScan/scanDataUtils.js +22 -17
  165. package/ui/src/components/SmartScan/useSmartScan.js +4 -4
  166. package/ui/src/components/SmartScan/utils.js +3 -1
  167. package/ui/src/components/SmartScanIcons.jsx +6 -3
  168. package/ui/src/components/TabNavigation/DesktopTabs.jsx +8 -3
  169. package/ui/src/components/TabNavigation/MobileDropdown.jsx +3 -1
  170. package/ui/src/components/TabNavigation.jsx +8 -3
  171. package/ui/src/components/TabNavigationIcons.jsx +4 -4
  172. package/ui/src/components/TourOverlay.jsx +1 -1
  173. package/ui/src/components/TourTooltip/TourTooltipButtons.jsx +3 -0
  174. package/ui/src/components/TourTooltip/TourTooltipHeader.jsx +1 -0
  175. package/ui/src/components/TourTooltip/TourTooltipIcons.jsx +9 -0
  176. package/ui/src/components/TourTooltip/useTooltipPosition.js +63 -36
  177. package/ui/src/components/TourTooltip.jsx +11 -3
  178. package/ui/src/components/ViewModeTabs.jsx +3 -1
  179. package/ui/src/config/tourSteps.jsx +0 -2
  180. package/ui/src/hooks/useAnimation.js +15 -12
  181. package/ui/src/hooks/useConfigManagement.js +8 -8
  182. package/ui/src/hooks/useServiceExtraction.js +1 -1
  183. package/ui/src/index.css +3 -8
  184. package/ui/src/theme.js +3 -3
  185. package/ui/src/utils/hexUtils.js +11 -5
  186. package/ui/src/utils/mcpGroupingUtils.js +18 -10
  187. package/ui/src/utils/requestPairing.js +89 -0
  188. package/ui/src/utils/requestUtils.js +37 -105
  189. package/ui/vite.config.js +1 -1
  190. package/mcp-server/.editorconfig +0 -15
  191. package/mcp-server/.prettierignore +0 -11
  192. package/mcp-server/.prettierrc +0 -12
  193. package/mcp-server/README.md +0 -280
  194. package/mcp-server/commitlint.config.cjs +0 -42
  195. package/mcp-server/eslint.config.js +0 -131
  196. package/mcp-server/package-lock.json +0 -4784
  197. package/mcp-server/package.json +0 -30
  198. package/ui/README.md +0 -212
  199. package/ui/package-lock.json +0 -3574
  200. package/ui/package.json +0 -12
  201. package/ui/paths.js +0 -282
  202. package/ui/server/routes/backups.js +0 -251
  203. package/ui/server/routes/composite.js +0 -244
@@ -1,7 +1,7 @@
1
- import { useState, useEffect, useRef } from 'react';
2
- import { colors } from './theme';
3
- import LogsToolbar from './components/LogsToolbar';
1
+ import { useEffect, useRef, useState } from 'react';
4
2
  import LogsDisplay from './components/LogsDisplay';
3
+ import LogsToolbar from './components/LogsToolbar';
4
+ import { colors } from './theme';
5
5
 
6
6
  function CompositeLogs() {
7
7
  const [logs, setLogs] = useState([]);
@@ -11,27 +11,23 @@ function CompositeLogs() {
11
11
  const logEndRef = useRef(null);
12
12
  const wsRef = useRef(null);
13
13
 
14
- const scrollToTop = () => {
14
+ useEffect(() => {
15
15
  if (autoScroll && logEndRef.current) {
16
16
  logEndRef.current.scrollIntoView({ behavior: 'smooth', block: 'start' });
17
17
  }
18
- };
18
+ }, [autoScroll]);
19
19
 
20
20
  useEffect(() => {
21
- scrollToTop();
22
- }, [logs, autoScroll]);
23
-
24
- const loadLogs = async () => {
25
- try {
26
- const response = await fetch('/api/composite/logs?limit=5000');
27
- const data = await response.json();
28
- setLogs(data);
29
- } catch (error) {
30
- console.error('Failed to load logs:', error);
31
- }
32
- };
21
+ const loadLogs = async () => {
22
+ try {
23
+ const response = await fetch('/api/composite/logs?limit=5000');
24
+ const data = await response.json();
25
+ setLogs(data);
26
+ } catch (error) {
27
+ console.error('Failed to load logs:', error);
28
+ }
29
+ };
33
30
 
34
- useEffect(() => {
35
31
  loadLogs();
36
32
 
37
33
  const wsUrl = import.meta.env.DEV
@@ -106,8 +102,12 @@ function CompositeLogs() {
106
102
  };
107
103
 
108
104
  const filteredLogs = logs.filter((log) => {
109
- if (logType !== 'all' && log.type !== logType) return false;
110
- if (filter && !log.line.toLowerCase().includes(filter.toLowerCase())) return false;
105
+ if (logType !== 'all' && log.type !== logType) {
106
+ return false;
107
+ }
108
+ if (filter && !log.line.toLowerCase().includes(filter.toLowerCase())) {
109
+ return false;
110
+ }
111
111
  return true;
112
112
  });
113
113
 
@@ -1,14 +1,14 @@
1
- import { useState, useEffect } from 'react';
2
- import { colors } from './theme';
3
- import SetupHeader from './components/SetupHeader';
4
- import ConfigFileSection from './components/ConfigFileSection';
5
- import WhatThisDoesSection from './components/WhatThisDoesSection';
6
- import ServerControl from './components/ServerControl';
7
- import MessageDisplay from './components/MessageDisplay';
1
+ import { useEffect, useState } from 'react';
8
2
  import BackupList from './components/BackupList';
3
+ import ConfigFileSection from './components/ConfigFileSection';
9
4
  import ConfigViewerModal from './components/ConfigViewerModal';
10
- import { useServiceExtraction } from './hooks/useServiceExtraction';
5
+ import MessageDisplay from './components/MessageDisplay';
6
+ import ServerControl from './components/ServerControl';
7
+ import SetupHeader from './components/SetupHeader';
8
+ import WhatThisDoesSection from './components/WhatThisDoesSection';
11
9
  import { useConfigManagement } from './hooks/useConfigManagement';
10
+ import { useServiceExtraction } from './hooks/useServiceExtraction';
11
+ import { colors } from './theme';
12
12
 
13
13
  function CompositeSetup() {
14
14
  const [fileContent, setFileContent] = useState('');
@@ -181,7 +181,7 @@ function CompositeSetup() {
181
181
  if (res.ok) {
182
182
  const msg = data.message || 'MCP Shark server stopped';
183
183
  setMessage(
184
- data.message && data.message.includes('restored')
184
+ data.message?.includes('restored')
185
185
  ? 'MCP Shark server stopped and original config file restored'
186
186
  : msg
187
187
  );
@@ -32,6 +32,7 @@ export default function HelpGuideFooter({ dontShowAgain, setDontShowAgain, onClo
32
32
  Don't show this again
33
33
  </label>
34
34
  <button
35
+ type="button"
35
36
  onClick={onClose}
36
37
  style={{
37
38
  background: colors.buttonPrimary,
@@ -1,5 +1,5 @@
1
- import { colors, fonts } from '../theme';
2
1
  import { IconHelp, IconX } from '@tabler/icons-react';
2
+ import { colors, fonts } from '../theme';
3
3
 
4
4
  export default function HelpGuideHeader({ onClose }) {
5
5
  return (
@@ -29,6 +29,7 @@ export default function HelpGuideHeader({ onClose }) {
29
29
  </h2>
30
30
  </div>
31
31
  <button
32
+ type="button"
32
33
  onClick={onClose}
33
34
  style={{
34
35
  background: 'transparent',
@@ -1,8 +1,8 @@
1
1
  import { useState } from 'react';
2
- import { colors } from './theme';
3
- import HelpGuideHeader from './HelpGuide/HelpGuideHeader';
4
2
  import HelpGuideContent from './HelpGuide/HelpGuideContent';
5
3
  import HelpGuideFooter from './HelpGuide/HelpGuideFooter';
4
+ import HelpGuideHeader from './HelpGuide/HelpGuideHeader';
5
+ import { colors } from './theme';
6
6
 
7
7
  function HelpGuide({ onClose }) {
8
8
  const [dontShowAgain, setDontShowAgain] = useState(false);
@@ -19,7 +19,9 @@ function HelpGuide({ onClose }) {
19
19
  };
20
20
 
21
21
  return (
22
- <div
22
+ <dialog
23
+ open
24
+ aria-modal="true"
23
25
  style={{
24
26
  position: 'fixed',
25
27
  top: 0,
@@ -32,10 +34,20 @@ function HelpGuide({ onClose }) {
32
34
  alignItems: 'center',
33
35
  justifyContent: 'center',
34
36
  padding: '20px',
37
+ border: 'none',
38
+ margin: 0,
39
+ width: '100%',
40
+ height: '100%',
35
41
  }}
36
42
  onClick={handleClose}
43
+ onKeyDown={(e) => {
44
+ if (e.key === 'Escape') {
45
+ handleClose();
46
+ }
47
+ }}
37
48
  >
38
49
  <div
50
+ role="document"
39
51
  style={{
40
52
  background: colors.bgCard,
41
53
  border: `1px solid ${colors.borderLight}`,
@@ -47,6 +59,7 @@ function HelpGuide({ onClose }) {
47
59
  boxShadow: '0 8px 32px rgba(0, 0, 0, 0.5)',
48
60
  }}
49
61
  onClick={(e) => e.stopPropagation()}
62
+ onKeyDown={(e) => e.stopPropagation()}
50
63
  >
51
64
  <HelpGuideHeader onClose={handleClose} />
52
65
  <div style={{ padding: '24px' }}>
@@ -58,7 +71,7 @@ function HelpGuide({ onClose }) {
58
71
  />
59
72
  </div>
60
73
  </div>
61
- </div>
74
+ </dialog>
62
75
  );
63
76
  }
64
77
 
@@ -1,4 +1,4 @@
1
- import { useState, useEffect, useRef } from 'react';
1
+ import { useEffect, useRef, useState } from 'react';
2
2
  import TourOverlay from './components/TourOverlay';
3
3
  import TourTooltip from './components/TourTooltip';
4
4
 
@@ -9,7 +9,9 @@ function IntroTour({ steps, onComplete, onSkip, onStepChange }) {
9
9
  const overlayRef = useRef(null);
10
10
 
11
11
  useEffect(() => {
12
- if (!highlightedElement) return;
12
+ if (!highlightedElement) {
13
+ return;
14
+ }
13
15
 
14
16
  const updatePosition = () => {
15
17
  if (highlightedElement) {
@@ -31,10 +33,14 @@ function IntroTour({ steps, onComplete, onSkip, onStepChange }) {
31
33
  }, [highlightedElement]);
32
34
 
33
35
  useEffect(() => {
34
- if (steps.length === 0) return;
36
+ if (steps.length === 0) {
37
+ return;
38
+ }
35
39
 
36
40
  const step = steps[currentStep];
37
- if (!step) return;
41
+ if (!step) {
42
+ return;
43
+ }
38
44
 
39
45
  if (onStepChange) {
40
46
  onStepChange(currentStep);
@@ -95,7 +101,9 @@ function IntroTour({ steps, onComplete, onSkip, onStepChange }) {
95
101
 
96
102
  const handleSkip = () => {
97
103
  handleComplete();
98
- if (onSkip) onSkip();
104
+ if (onSkip) {
105
+ onSkip();
106
+ }
99
107
  };
100
108
 
101
109
  if (steps.length === 0 || currentStep >= steps.length) {
@@ -108,6 +116,7 @@ function IntroTour({ steps, onComplete, onSkip, onStepChange }) {
108
116
  <>
109
117
  <div
110
118
  ref={overlayRef}
119
+ role="presentation"
111
120
  style={{
112
121
  position: 'fixed',
113
122
  top: 0,
@@ -118,6 +127,11 @@ function IntroTour({ steps, onComplete, onSkip, onStepChange }) {
118
127
  pointerEvents: 'auto',
119
128
  }}
120
129
  onClick={handleSkip}
130
+ onKeyDown={(e) => {
131
+ if (e.key === 'Escape') {
132
+ handleSkip();
133
+ }
134
+ }}
121
135
  >
122
136
  <TourOverlay elementRect={elementRect} />
123
137
  </div>
@@ -16,6 +16,7 @@ function LogDetail({ log, onClose }) {
16
16
  >
17
17
  <h3 style={{ fontSize: '14px', fontWeight: 'normal' }}>Details</h3>
18
18
  <button
19
+ type="button"
19
20
  onClick={onClose}
20
21
  style={{
21
22
  background: 'none',
@@ -1,10 +1,16 @@
1
- import { colors, fonts, withOpacity } from './theme';
1
+ import { colors, fonts } from './theme';
2
2
 
3
3
  function LogTable({ logs, selected, onSelect }) {
4
4
  const getStatusColor = (status) => {
5
- if (status === 'error') return colors.error;
6
- if (status === 'success') return colors.success;
7
- if (status === 'pending') return colors.warning;
5
+ if (status === 'error') {
6
+ return colors.error;
7
+ }
8
+ if (status === 'success') {
9
+ return colors.success;
10
+ }
11
+ if (status === 'pending') {
12
+ return colors.warning;
13
+ }
8
14
  return colors.textTertiary;
9
15
  };
10
16
 
@@ -144,6 +150,14 @@ function LogTable({ logs, selected, onSelect }) {
144
150
  <tr
145
151
  key={log.id}
146
152
  onClick={() => onSelect(log)}
153
+ onKeyDown={(e) => {
154
+ if (e.key === 'Enter' || e.key === ' ') {
155
+ e.preventDefault();
156
+ onSelect(log);
157
+ }
158
+ }}
159
+ tabIndex={0}
160
+ aria-label={`Select log entry ${log.id}`}
147
161
  style={{
148
162
  cursor: 'pointer',
149
163
  background: selected?.id === log.id ? colors.bgSelected : colors.bgCard,
@@ -151,10 +165,14 @@ function LogTable({ logs, selected, onSelect }) {
151
165
  transition: 'background-color 0.15s ease',
152
166
  }}
153
167
  onMouseEnter={(e) => {
154
- if (selected?.id !== log.id) e.currentTarget.style.background = colors.bgHover;
168
+ if (selected?.id !== log.id) {
169
+ e.currentTarget.style.background = colors.bgHover;
170
+ }
155
171
  }}
156
172
  onMouseLeave={(e) => {
157
- if (selected?.id !== log.id) e.currentTarget.style.background = colors.bgCard;
173
+ if (selected?.id !== log.id) {
174
+ e.currentTarget.style.background = colors.bgCard;
175
+ }
158
176
  }}
159
177
  >
160
178
  <td
@@ -1,12 +1,12 @@
1
- import { useState, useEffect, useRef } from 'react';
2
- import { colors, fonts } from './theme';
3
- import PacketDetailHeader from './components/PacketDetailHeader';
4
- import TabNavigation from './components/TabNavigation';
1
+ import { useEffect, useRef, useState } from 'react';
5
2
  import DetailsTab from './components/DetailsTab';
6
3
  import HexTab from './components/HexTab';
4
+ import PacketDetailHeader from './components/PacketDetailHeader';
7
5
  import RawTab from './components/RawTab';
8
- import { generateHexDump, createFullRequestText } from './utils/hexUtils.js';
6
+ import TabNavigation from './components/TabNavigation';
7
+ import { colors } from './theme';
9
8
  import { fadeIn } from './utils/animations';
9
+ import { createFullRequestText, generateHexDump } from './utils/hexUtils.js';
10
10
 
11
11
  function RequestDetail({ request, onClose, requests = [] }) {
12
12
  const [activeTab, setActiveTab] = useState('details');
@@ -20,7 +20,9 @@ function RequestDetail({ request, onClose, requests = [] }) {
20
20
  }
21
21
  }, [activeTab]);
22
22
 
23
- if (!request) return null;
23
+ if (!request) {
24
+ return null;
25
+ }
24
26
 
25
27
  // Helper function to extract JSON-RPC method
26
28
  const getJsonRpcMethod = (req) => {
@@ -38,7 +40,7 @@ function RequestDetail({ request, onClose, requests = [] }) {
38
40
  if (body && typeof body === 'object' && body.method) {
39
41
  return body.method;
40
42
  }
41
- } catch (e) {
43
+ } catch (_e) {
42
44
  // Failed to parse
43
45
  }
44
46
  }
@@ -48,7 +50,7 @@ function RequestDetail({ request, onClose, requests = [] }) {
48
50
  if (body && typeof body === 'object' && body.method) {
49
51
  return body.method;
50
52
  }
51
- } catch (e) {
53
+ } catch (_e) {
52
54
  // Failed to parse
53
55
  }
54
56
  }
@@ -61,7 +63,9 @@ function RequestDetail({ request, onClose, requests = [] }) {
61
63
  const findMatchingPair = () => {
62
64
  const matches = (req, resp) => {
63
65
  // Session ID must match
64
- if (req.session_id !== resp.session_id) return false;
66
+ if (req.session_id !== resp.session_id) {
67
+ return false;
68
+ }
65
69
 
66
70
  // JSON-RPC Method must match
67
71
  const reqMethod = getJsonRpcMethod(req);
@@ -78,7 +82,9 @@ function RequestDetail({ request, onClose, requests = [] }) {
78
82
  return false;
79
83
  }
80
84
 
81
- if (reqMethod !== respMethod) return false;
85
+ if (reqMethod !== respMethod) {
86
+ return false;
87
+ }
82
88
 
83
89
  // If JSON-RPC ID exists, it must match
84
90
  if (req.jsonrpc_id && resp.jsonrpc_id) {
@@ -94,13 +100,12 @@ function RequestDetail({ request, onClose, requests = [] }) {
94
100
  (r) =>
95
101
  r.direction === 'response' && matches(request, r) && r.frame_number > request.frame_number
96
102
  );
97
- } else {
98
- // Find the corresponding request
99
- return requests.find(
100
- (r) =>
101
- r.direction === 'request' && matches(r, request) && r.frame_number < request.frame_number
102
- );
103
103
  }
104
+ // Find the corresponding request
105
+ return requests.find(
106
+ (r) =>
107
+ r.direction === 'request' && matches(r, request) && r.frame_number < request.frame_number
108
+ );
104
109
  };
105
110
 
106
111
  const matchingPair = findMatchingPair();
@@ -1,13 +1,13 @@
1
+ import { IconSearch, IconTrash } from '@tabler/icons-react';
2
+ import anime from 'animejs';
1
3
  import { useEffect, useRef, useState } from 'react';
4
+ import ConfirmationModal from './components/ConfirmationModal';
5
+ import ExportControls from './components/PacketFilters/ExportControls';
6
+ import FilterInput from './components/PacketFilters/FilterInput';
2
7
  import { colors, fonts } from './theme';
3
8
  import { fadeIn } from './utils/animations';
4
- import FilterInput from './components/PacketFilters/FilterInput';
5
- import ExportControls from './components/PacketFilters/ExportControls';
6
- import ConfirmationModal from './components/ConfirmationModal';
7
- import { IconTrash, IconSearch } from '@tabler/icons-react';
8
- import anime from 'animejs';
9
9
 
10
- function RequestFilters({ filters, onFilterChange, stats, onExport, onClear }) {
10
+ function RequestFilters({ filters, onFilterChange, stats, onClear }) {
11
11
  const filtersRef = useRef(null);
12
12
  const [showClearModal, setShowClearModal] = useState(false);
13
13
 
@@ -20,13 +20,27 @@ function RequestFilters({ filters, onFilterChange, stats, onExport, onClear }) {
20
20
  const handleExport = async (format = 'json') => {
21
21
  try {
22
22
  const queryParams = new URLSearchParams();
23
- if (filters.search) queryParams.append('search', filters.search);
24
- if (filters.serverName) queryParams.append('serverName', filters.serverName);
25
- if (filters.sessionId) queryParams.append('sessionId', filters.sessionId);
26
- if (filters.method) queryParams.append('method', filters.method);
27
- if (filters.jsonrpcMethod) queryParams.append('jsonrpcMethod', filters.jsonrpcMethod);
28
- if (filters.statusCode) queryParams.append('statusCode', filters.statusCode);
29
- if (filters.jsonrpcId) queryParams.append('jsonrpcId', filters.jsonrpcId);
23
+ if (filters.search) {
24
+ queryParams.append('search', filters.search);
25
+ }
26
+ if (filters.serverName) {
27
+ queryParams.append('serverName', filters.serverName);
28
+ }
29
+ if (filters.sessionId) {
30
+ queryParams.append('sessionId', filters.sessionId);
31
+ }
32
+ if (filters.method) {
33
+ queryParams.append('method', filters.method);
34
+ }
35
+ if (filters.jsonrpcMethod) {
36
+ queryParams.append('jsonrpcMethod', filters.jsonrpcMethod);
37
+ }
38
+ if (filters.statusCode) {
39
+ queryParams.append('statusCode', filters.statusCode);
40
+ }
41
+ if (filters.jsonrpcId) {
42
+ queryParams.append('jsonrpcId', filters.jsonrpcId);
43
+ }
30
44
  queryParams.append('format', format);
31
45
 
32
46
  const response = await fetch(`/api/requests/export?${queryParams}`);
@@ -127,7 +141,7 @@ function RequestFilters({ filters, onFilterChange, stats, onExport, onClear }) {
127
141
  onChange={(e) =>
128
142
  onFilterChange({
129
143
  ...filters,
130
- statusCode: e.target.value ? parseInt(e.target.value) : null,
144
+ statusCode: e.target.value ? Number.parseInt(e.target.value) : null,
131
145
  })
132
146
  }
133
147
  style={{ width: '120px' }}
@@ -144,6 +158,7 @@ function RequestFilters({ filters, onFilterChange, stats, onExport, onClear }) {
144
158
  <ExportControls stats={stats} onExport={handleExport} />
145
159
 
146
160
  <button
161
+ type="button"
147
162
  onClick={() => setShowClearModal(true)}
148
163
  style={{
149
164
  padding: '8px 14px',
@@ -1,13 +1,12 @@
1
- import { useState, useEffect, useMemo, useRef } from 'react';
2
- import { colors, fonts } from './theme';
1
+ import { useEffect, useMemo, useRef, useState } from 'react';
2
+ import GroupedByMcpView from './components/GroupedByMcpView';
3
3
  import RequestRow from './components/RequestRow';
4
4
  import TableHeader from './components/TableHeader';
5
5
  import ViewModeTabs from './components/ViewModeTabs';
6
- import GroupedByMcpView from './components/GroupedByMcpView';
6
+ import { colors, fonts } from './theme';
7
+ import { staggerIn } from './utils/animations';
7
8
  import { groupByMcpSessionAndCategory } from './utils/mcpGroupingUtils.js';
8
9
  import { pairRequestsWithResponses } from './utils/requestUtils.js';
9
- import { staggerIn } from './utils/animations';
10
- import anime from 'animejs';
11
10
 
12
11
  function RequestList({ requests, selected, onSelect, firstRequestTime }) {
13
12
  const [viewMode, setViewMode] = useState('general');
@@ -1,11 +1,11 @@
1
1
  import { useState } from 'react';
2
- import { colors } from './theme';
3
- import SmartScanHeader from './components/SmartScan/SmartScanHeader';
2
+ import ListViewContent from './components/SmartScan/ListViewContent';
3
+ import ScanViewContent from './components/SmartScan/ScanViewContent';
4
4
  import SmartScanControls from './components/SmartScan/SmartScanControls';
5
+ import SmartScanHeader from './components/SmartScan/SmartScanHeader';
5
6
  import ViewModeTabs from './components/SmartScan/ViewModeTabs';
6
- import ScanViewContent from './components/SmartScan/ScanViewContent';
7
- import ListViewContent from './components/SmartScan/ListViewContent';
8
7
  import { useSmartScan } from './components/SmartScan/useSmartScan';
8
+ import { colors } from './theme';
9
9
 
10
10
  function SmartScan() {
11
11
  const [viewMode, setViewMode] = useState('scan'); // 'scan' or 'list'
@@ -151,7 +151,7 @@ function SmartScan() {
151
151
  const serverName =
152
152
  matchingScan?.serverName || scanData.serverName || 'Unknown Server';
153
153
 
154
- if (scanData && scanData.data && typeof scanData.data === 'object') {
154
+ if (scanData?.data && typeof scanData.data === 'object') {
155
155
  const actualScan = scanData.data;
156
156
  setSelectedScan({
157
157
  ...actualScan,
@@ -1,15 +1,15 @@
1
1
  import { useEffect, useRef, useState } from 'react';
2
- import { colors, fonts } from './theme';
3
2
  import { SharkLogo } from './components/SharkLogo';
3
+ import DesktopTabs from './components/TabNavigation/DesktopTabs';
4
+ import MobileDropdown from './components/TabNavigation/MobileDropdown';
4
5
  import {
5
- NetworkIcon,
6
6
  LogsIcon,
7
- SettingsIcon,
7
+ NetworkIcon,
8
8
  PlaygroundIcon,
9
+ SettingsIcon,
9
10
  ShieldIcon,
10
11
  } from './components/TabNavigationIcons';
11
- import MobileDropdown from './components/TabNavigation/MobileDropdown';
12
- import DesktopTabs from './components/TabNavigation/DesktopTabs';
12
+ import { colors, fonts } from './theme';
13
13
 
14
14
  function TabNavigation({ activeTab, onTabChange }) {
15
15
  const tabs = [
@@ -10,7 +10,10 @@ const TourIcon = ({ size = 16, color = 'currentColor' }) => (
10
10
  strokeWidth="2"
11
11
  strokeLinecap="round"
12
12
  strokeLinejoin="round"
13
+ role="img"
14
+ aria-label="Tour icon"
13
15
  >
16
+ <title>Tour icon</title>
14
17
  <path d="M12 2L2 7l10 5 10-5-10-5z" />
15
18
  <path d="M2 17l10 5 10-5" />
16
19
  <path d="M2 12l10 5 10-5" />
@@ -20,6 +23,7 @@ const TourIcon = ({ size = 16, color = 'currentColor' }) => (
20
23
  export default function HelpButton({ onClick }) {
21
24
  return (
22
25
  <button
26
+ type="button"
23
27
  onClick={onClick}
24
28
  data-tour="help-button"
25
29
  style={{
@@ -1,9 +1,9 @@
1
- import { useRef, useEffect } from 'react';
2
- import { colors } from '../../theme';
3
- import { slideInRight } from '../../utils/animations';
4
- import RequestList from '../../PacketList';
1
+ import { useEffect, useRef } from 'react';
5
2
  import RequestDetail from '../../PacketDetail';
6
3
  import RequestFilters from '../../PacketFilters';
4
+ import RequestList from '../../PacketList';
5
+ import { colors } from '../../theme';
6
+ import { slideInRight } from '../../utils/animations';
7
7
 
8
8
  export default function TrafficTab({
9
9
  requests,