@mcp-shark/mcp-shark 1.4.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 (212) hide show
  1. package/LICENSE +85 -0
  2. package/README.md +724 -0
  3. package/bin/mcp-shark.js +93 -0
  4. package/mcp-server/.editorconfig +15 -0
  5. package/mcp-server/.prettierignore +11 -0
  6. package/mcp-server/.prettierrc +12 -0
  7. package/mcp-server/README.md +280 -0
  8. package/mcp-server/commitlint.config.cjs +42 -0
  9. package/mcp-server/eslint.config.js +131 -0
  10. package/mcp-server/lib/auditor/audit.js +228 -0
  11. package/mcp-server/lib/common/error.js +15 -0
  12. package/mcp-server/lib/server/external/all.js +32 -0
  13. package/mcp-server/lib/server/external/config.js +59 -0
  14. package/mcp-server/lib/server/external/kv.js +102 -0
  15. package/mcp-server/lib/server/external/single/client.js +35 -0
  16. package/mcp-server/lib/server/external/single/request.js +49 -0
  17. package/mcp-server/lib/server/external/single/run.js +75 -0
  18. package/mcp-server/lib/server/external/single/transport.js +57 -0
  19. package/mcp-server/lib/server/internal/handlers/common.js +20 -0
  20. package/mcp-server/lib/server/internal/handlers/error.js +7 -0
  21. package/mcp-server/lib/server/internal/handlers/prompts-get.js +22 -0
  22. package/mcp-server/lib/server/internal/handlers/prompts-list.js +12 -0
  23. package/mcp-server/lib/server/internal/handlers/resources-list.js +12 -0
  24. package/mcp-server/lib/server/internal/handlers/resources-read.js +19 -0
  25. package/mcp-server/lib/server/internal/handlers/tools-call.js +37 -0
  26. package/mcp-server/lib/server/internal/handlers/tools-list.js +14 -0
  27. package/mcp-server/lib/server/internal/run.js +49 -0
  28. package/mcp-server/lib/server/internal/server.js +63 -0
  29. package/mcp-server/lib/server/internal/session.js +39 -0
  30. package/mcp-server/mcp-shark.js +72 -0
  31. package/mcp-server/package-lock.json +4784 -0
  32. package/mcp-server/package.json +30 -0
  33. package/package.json +103 -0
  34. package/ui/README.md +212 -0
  35. package/ui/index.html +16 -0
  36. package/ui/package-lock.json +3574 -0
  37. package/ui/package.json +12 -0
  38. package/ui/paths.js +282 -0
  39. package/ui/public/og-image.png +0 -0
  40. package/ui/server/routes/backups.js +251 -0
  41. package/ui/server/routes/composite.js +244 -0
  42. package/ui/server/routes/config.js +175 -0
  43. package/ui/server/routes/conversations.js +25 -0
  44. package/ui/server/routes/help.js +43 -0
  45. package/ui/server/routes/logs.js +32 -0
  46. package/ui/server/routes/playground.js +152 -0
  47. package/ui/server/routes/requests.js +235 -0
  48. package/ui/server/routes/sessions.js +27 -0
  49. package/ui/server/routes/smartscan/discover.js +117 -0
  50. package/ui/server/routes/smartscan/scans/clearCache.js +22 -0
  51. package/ui/server/routes/smartscan/scans/createBatchScans.js +123 -0
  52. package/ui/server/routes/smartscan/scans/createScan.js +42 -0
  53. package/ui/server/routes/smartscan/scans/getCachedResults.js +51 -0
  54. package/ui/server/routes/smartscan/scans/getScan.js +41 -0
  55. package/ui/server/routes/smartscan/scans/listScans.js +24 -0
  56. package/ui/server/routes/smartscan/scans.js +13 -0
  57. package/ui/server/routes/smartscan/token.js +56 -0
  58. package/ui/server/routes/smartscan/transport.js +53 -0
  59. package/ui/server/routes/smartscan.js +24 -0
  60. package/ui/server/routes/statistics.js +83 -0
  61. package/ui/server/utils/config-update.js +212 -0
  62. package/ui/server/utils/config.js +98 -0
  63. package/ui/server/utils/paths.js +23 -0
  64. package/ui/server/utils/port.js +28 -0
  65. package/ui/server/utils/process.js +80 -0
  66. package/ui/server/utils/scan-cache/all-results.js +180 -0
  67. package/ui/server/utils/scan-cache/directory.js +35 -0
  68. package/ui/server/utils/scan-cache/file-operations.js +104 -0
  69. package/ui/server/utils/scan-cache/hash.js +47 -0
  70. package/ui/server/utils/scan-cache/server-operations.js +80 -0
  71. package/ui/server/utils/scan-cache.js +12 -0
  72. package/ui/server/utils/serialization.js +13 -0
  73. package/ui/server/utils/smartscan-token.js +42 -0
  74. package/ui/server.js +199 -0
  75. package/ui/src/App.jsx +153 -0
  76. package/ui/src/CompositeLogs.jsx +164 -0
  77. package/ui/src/CompositeSetup.jsx +285 -0
  78. package/ui/src/HelpGuide/HelpGuideContent.jsx +118 -0
  79. package/ui/src/HelpGuide/HelpGuideFooter.jsx +58 -0
  80. package/ui/src/HelpGuide/HelpGuideHeader.jsx +56 -0
  81. package/ui/src/HelpGuide.jsx +65 -0
  82. package/ui/src/IntroTour.jsx +140 -0
  83. package/ui/src/LogDetail.jsx +122 -0
  84. package/ui/src/LogTable.jsx +242 -0
  85. package/ui/src/PacketDetail.jsx +190 -0
  86. package/ui/src/PacketFilters.jsx +222 -0
  87. package/ui/src/PacketList.jsx +183 -0
  88. package/ui/src/SmartScan.jsx +178 -0
  89. package/ui/src/TabNavigation.jsx +143 -0
  90. package/ui/src/components/App/HelpButton.jsx +64 -0
  91. package/ui/src/components/App/TrafficTab.jsx +69 -0
  92. package/ui/src/components/App/useAppState.js +163 -0
  93. package/ui/src/components/BackupList.jsx +192 -0
  94. package/ui/src/components/CollapsibleSection.jsx +82 -0
  95. package/ui/src/components/ConfigFileSection.jsx +84 -0
  96. package/ui/src/components/ConfigViewerModal.jsx +141 -0
  97. package/ui/src/components/ConfirmationModal.jsx +129 -0
  98. package/ui/src/components/DetailsTab/BodySection.jsx +27 -0
  99. package/ui/src/components/DetailsTab/CollapsibleRequestResponse.jsx +70 -0
  100. package/ui/src/components/DetailsTab/HeadersSection.jsx +25 -0
  101. package/ui/src/components/DetailsTab/InfoSection.jsx +28 -0
  102. package/ui/src/components/DetailsTab/NetworkInfoSection.jsx +63 -0
  103. package/ui/src/components/DetailsTab/ProtocolInfoSection.jsx +75 -0
  104. package/ui/src/components/DetailsTab/RequestDetailsSection.jsx +46 -0
  105. package/ui/src/components/DetailsTab/ResponseDetailsSection.jsx +66 -0
  106. package/ui/src/components/DetailsTab.jsx +31 -0
  107. package/ui/src/components/DetectedPathsList.jsx +171 -0
  108. package/ui/src/components/FileInput.jsx +144 -0
  109. package/ui/src/components/GroupHeader.jsx +76 -0
  110. package/ui/src/components/GroupedByMcpView.jsx +103 -0
  111. package/ui/src/components/GroupedByServerView.jsx +134 -0
  112. package/ui/src/components/GroupedBySessionView.jsx +127 -0
  113. package/ui/src/components/GroupedViews.jsx +2 -0
  114. package/ui/src/components/HexTab.jsx +188 -0
  115. package/ui/src/components/LogsDisplay.jsx +93 -0
  116. package/ui/src/components/LogsToolbar.jsx +193 -0
  117. package/ui/src/components/McpPlayground/LoadingModal.jsx +113 -0
  118. package/ui/src/components/McpPlayground/PromptsSection/PromptCallPanel.jsx +125 -0
  119. package/ui/src/components/McpPlayground/PromptsSection/PromptItem.jsx +48 -0
  120. package/ui/src/components/McpPlayground/PromptsSection/PromptsList.jsx +45 -0
  121. package/ui/src/components/McpPlayground/PromptsSection.jsx +106 -0
  122. package/ui/src/components/McpPlayground/ResourcesSection/ResourceCallPanel.jsx +89 -0
  123. package/ui/src/components/McpPlayground/ResourcesSection/ResourceItem.jsx +59 -0
  124. package/ui/src/components/McpPlayground/ResourcesSection/ResourcesList.jsx +45 -0
  125. package/ui/src/components/McpPlayground/ResourcesSection.jsx +91 -0
  126. package/ui/src/components/McpPlayground/ToolsSection/ToolCallPanel.jsx +125 -0
  127. package/ui/src/components/McpPlayground/ToolsSection/ToolItem.jsx +48 -0
  128. package/ui/src/components/McpPlayground/ToolsSection/ToolsList.jsx +45 -0
  129. package/ui/src/components/McpPlayground/ToolsSection.jsx +107 -0
  130. package/ui/src/components/McpPlayground/common/EmptyState.jsx +17 -0
  131. package/ui/src/components/McpPlayground/common/ErrorState.jsx +17 -0
  132. package/ui/src/components/McpPlayground/common/LoadingState.jsx +17 -0
  133. package/ui/src/components/McpPlayground/useMcpPlayground.js +280 -0
  134. package/ui/src/components/McpPlayground.jsx +171 -0
  135. package/ui/src/components/MessageDisplay.jsx +28 -0
  136. package/ui/src/components/PacketDetailHeader.jsx +88 -0
  137. package/ui/src/components/PacketFilters/ExportControls.jsx +126 -0
  138. package/ui/src/components/PacketFilters/FilterInput.jsx +59 -0
  139. package/ui/src/components/RawTab.jsx +142 -0
  140. package/ui/src/components/RequestRow/OrphanedResponseRow.jsx +155 -0
  141. package/ui/src/components/RequestRow/RequestRowMain.jsx +240 -0
  142. package/ui/src/components/RequestRow/ResponseRow.jsx +158 -0
  143. package/ui/src/components/RequestRow.jsx +70 -0
  144. package/ui/src/components/ServerControl.jsx +133 -0
  145. package/ui/src/components/ServiceSelector.jsx +209 -0
  146. package/ui/src/components/SetupHeader.jsx +30 -0
  147. package/ui/src/components/SharkLogo.jsx +21 -0
  148. package/ui/src/components/SmartScan/AnalysisResult.jsx +64 -0
  149. package/ui/src/components/SmartScan/BatchResultsDisplay/BatchResultItem.jsx +215 -0
  150. package/ui/src/components/SmartScan/BatchResultsDisplay/BatchResultsHeader.jsx +94 -0
  151. package/ui/src/components/SmartScan/BatchResultsDisplay.jsx +26 -0
  152. package/ui/src/components/SmartScan/DebugInfoSection.jsx +53 -0
  153. package/ui/src/components/SmartScan/EmptyState.jsx +57 -0
  154. package/ui/src/components/SmartScan/ErrorDisplay.jsx +48 -0
  155. package/ui/src/components/SmartScan/ExpandableSection.jsx +93 -0
  156. package/ui/src/components/SmartScan/FindingsTable.jsx +257 -0
  157. package/ui/src/components/SmartScan/ListViewContent.jsx +75 -0
  158. package/ui/src/components/SmartScan/NotablePatternsSection.jsx +75 -0
  159. package/ui/src/components/SmartScan/OverallSummarySection.jsx +72 -0
  160. package/ui/src/components/SmartScan/RawDataSection.jsx +52 -0
  161. package/ui/src/components/SmartScan/RecommendationsSection.jsx +78 -0
  162. package/ui/src/components/SmartScan/ScanDetailHeader.jsx +92 -0
  163. package/ui/src/components/SmartScan/ScanDetailView.jsx +141 -0
  164. package/ui/src/components/SmartScan/ScanListView/ScanListHeader.jsx +49 -0
  165. package/ui/src/components/SmartScan/ScanListView/ScanListItem.jsx +201 -0
  166. package/ui/src/components/SmartScan/ScanListView.jsx +73 -0
  167. package/ui/src/components/SmartScan/ScanOverviewSection.jsx +123 -0
  168. package/ui/src/components/SmartScan/ScanResultsDisplay.jsx +35 -0
  169. package/ui/src/components/SmartScan/ScanViewContent.jsx +68 -0
  170. package/ui/src/components/SmartScan/ScanningProgress.jsx +47 -0
  171. package/ui/src/components/SmartScan/ServerInfoSection.jsx +43 -0
  172. package/ui/src/components/SmartScan/ServerSelectionRow.jsx +207 -0
  173. package/ui/src/components/SmartScan/SingleResultDisplay.jsx +269 -0
  174. package/ui/src/components/SmartScan/SmartScanControls.jsx +290 -0
  175. package/ui/src/components/SmartScan/SmartScanHeader.jsx +77 -0
  176. package/ui/src/components/SmartScan/ViewModeTabs.jsx +57 -0
  177. package/ui/src/components/SmartScan/hooks/useCacheManagement.js +34 -0
  178. package/ui/src/components/SmartScan/hooks/useMcpDiscovery.js +121 -0
  179. package/ui/src/components/SmartScan/hooks/useScanList.js +193 -0
  180. package/ui/src/components/SmartScan/hooks/useScanOperations.js +87 -0
  181. package/ui/src/components/SmartScan/hooks/useServerStatus.js +26 -0
  182. package/ui/src/components/SmartScan/hooks/useTokenManagement.js +53 -0
  183. package/ui/src/components/SmartScan/scanDataUtils.js +98 -0
  184. package/ui/src/components/SmartScan/useSmartScan.js +72 -0
  185. package/ui/src/components/SmartScan/utils.js +19 -0
  186. package/ui/src/components/SmartScanIcons.jsx +58 -0
  187. package/ui/src/components/TabNavigation/DesktopTabs.jsx +111 -0
  188. package/ui/src/components/TabNavigation/MobileDropdown.jsx +140 -0
  189. package/ui/src/components/TabNavigation.jsx +97 -0
  190. package/ui/src/components/TabNavigationIcons.jsx +40 -0
  191. package/ui/src/components/TableHeader.jsx +164 -0
  192. package/ui/src/components/TourOverlay.jsx +117 -0
  193. package/ui/src/components/TourTooltip/TourTooltipButtons.jsx +117 -0
  194. package/ui/src/components/TourTooltip/TourTooltipHeader.jsx +70 -0
  195. package/ui/src/components/TourTooltip/TourTooltipIcons.jsx +45 -0
  196. package/ui/src/components/TourTooltip/useTooltipPosition.js +108 -0
  197. package/ui/src/components/TourTooltip.jsx +83 -0
  198. package/ui/src/components/ViewModeTabs.jsx +91 -0
  199. package/ui/src/components/WhatThisDoesSection.jsx +61 -0
  200. package/ui/src/config/tourSteps.jsx +141 -0
  201. package/ui/src/hooks/useAnimation.js +92 -0
  202. package/ui/src/hooks/useConfigManagement.js +124 -0
  203. package/ui/src/hooks/useServiceExtraction.js +51 -0
  204. package/ui/src/index.css +42 -0
  205. package/ui/src/main.jsx +10 -0
  206. package/ui/src/theme.js +65 -0
  207. package/ui/src/utils/animations.js +170 -0
  208. package/ui/src/utils/groupingUtils.js +93 -0
  209. package/ui/src/utils/hexUtils.js +24 -0
  210. package/ui/src/utils/mcpGroupingUtils.js +262 -0
  211. package/ui/src/utils/requestUtils.js +297 -0
  212. package/ui/vite.config.js +18 -0
@@ -0,0 +1,290 @@
1
+ import { useState, useRef } from 'react';
2
+ import { colors, fonts } from '../../theme';
3
+ import { CheckIcon, LoadingSpinner, CacheIcon } from '../SmartScanIcons';
4
+ import { ExternalLinkIcon } from '../SmartScanIcons';
5
+ import { IconTrash } from '@tabler/icons-react';
6
+ import ConfirmationModal from '../ConfirmationModal';
7
+
8
+ export default function SmartScanControls({
9
+ apiToken,
10
+ setApiToken,
11
+ saveToken,
12
+ loadingData,
13
+ discoverMcpData,
14
+ discoveredServers,
15
+ selectedServers,
16
+ setSelectedServers,
17
+ runScan,
18
+ scanning,
19
+ clearCache,
20
+ clearingCache,
21
+ }) {
22
+ const saveTokenTimeoutRef = useRef(null);
23
+ const [showClearCacheModal, setShowClearCacheModal] = useState(false);
24
+
25
+ const handleTokenChange = (newToken) => {
26
+ setApiToken(newToken);
27
+ if (saveTokenTimeoutRef.current) {
28
+ clearTimeout(saveTokenTimeoutRef.current);
29
+ }
30
+ if (newToken) {
31
+ saveTokenTimeoutRef.current = setTimeout(() => {
32
+ saveToken(newToken);
33
+ }, 1000);
34
+ } else {
35
+ saveToken('');
36
+ }
37
+ };
38
+
39
+ return (
40
+ <div
41
+ style={{
42
+ display: 'flex',
43
+ alignItems: 'center',
44
+ gap: '20px',
45
+ flexWrap: 'wrap',
46
+ flex: 1,
47
+ justifyContent: 'flex-end',
48
+ }}
49
+ >
50
+ {/* API Token Section */}
51
+ <div
52
+ style={{
53
+ display: 'flex',
54
+ alignItems: 'center',
55
+ gap: '8px',
56
+ }}
57
+ >
58
+ <label
59
+ style={{
60
+ fontSize: '12px',
61
+ fontWeight: '600',
62
+ color: colors.textSecondary,
63
+ fontFamily: fonts.body,
64
+ whiteSpace: 'nowrap',
65
+ }}
66
+ >
67
+ API Token:
68
+ </label>
69
+ <div style={{ position: 'relative', width: '200px' }}>
70
+ <input
71
+ type="password"
72
+ value={apiToken}
73
+ onChange={(e) => handleTokenChange(e.target.value)}
74
+ placeholder="sk_..."
75
+ style={{
76
+ width: '100%',
77
+ padding: '8px 10px',
78
+ paddingRight: apiToken ? '28px' : '10px',
79
+ border: `1px solid ${apiToken ? colors.accentGreen : colors.borderMedium}`,
80
+ borderRadius: '8px',
81
+ fontSize: '12px',
82
+ fontFamily: fonts.body,
83
+ background: colors.bgCard,
84
+ color: colors.textPrimary,
85
+ boxSizing: 'border-box',
86
+ transition: 'all 0.2s ease',
87
+ }}
88
+ onFocus={(e) => {
89
+ e.currentTarget.style.borderColor = colors.accentBlue;
90
+ e.currentTarget.style.boxShadow = `0 0 0 2px ${colors.accentBlue}20`;
91
+ }}
92
+ onBlur={(e) => {
93
+ e.currentTarget.style.borderColor = apiToken
94
+ ? colors.accentGreen
95
+ : colors.borderMedium;
96
+ e.currentTarget.style.boxShadow = 'none';
97
+ }}
98
+ />
99
+ {apiToken && (
100
+ <div
101
+ style={{
102
+ position: 'absolute',
103
+ right: '8px',
104
+ top: '50%',
105
+ transform: 'translateY(-50%)',
106
+ }}
107
+ >
108
+ <CheckIcon size={12} color={colors.accentGreen} />
109
+ </div>
110
+ )}
111
+ </div>
112
+ <a
113
+ href="https://smart.mcpshark.sh/tokens"
114
+ target="_blank"
115
+ rel="noopener noreferrer"
116
+ style={{
117
+ display: 'inline-flex',
118
+ alignItems: 'center',
119
+ gap: '4px',
120
+ fontSize: '11px',
121
+ color: colors.accentBlue,
122
+ textDecoration: 'none',
123
+ fontFamily: fonts.body,
124
+ fontWeight: '500',
125
+ whiteSpace: 'nowrap',
126
+ }}
127
+ onMouseEnter={(e) => {
128
+ e.currentTarget.style.textDecoration = 'underline';
129
+ }}
130
+ onMouseLeave={(e) => {
131
+ e.currentTarget.style.textDecoration = 'none';
132
+ }}
133
+ >
134
+ <span>Get token</span>
135
+ <ExternalLinkIcon size={10} color={colors.accentBlue} />
136
+ </a>
137
+ </div>
138
+
139
+ {/* Discover MCP Data Section */}
140
+ <div
141
+ style={{
142
+ display: 'flex',
143
+ alignItems: 'center',
144
+ gap: '8px',
145
+ }}
146
+ >
147
+ <label
148
+ style={{
149
+ fontSize: '12px',
150
+ fontWeight: '600',
151
+ color: colors.textSecondary,
152
+ fontFamily: fonts.body,
153
+ whiteSpace: 'nowrap',
154
+ }}
155
+ >
156
+ Servers:
157
+ </label>
158
+ <button
159
+ onClick={discoverMcpData}
160
+ disabled={loadingData}
161
+ style={{
162
+ padding: '8px 14px',
163
+ background: !loadingData ? colors.buttonPrimary : colors.buttonSecondary,
164
+ color: !loadingData ? colors.textInverse : colors.textTertiary,
165
+ border: 'none',
166
+ borderRadius: '6px',
167
+ fontSize: '12px',
168
+ fontWeight: '600',
169
+ fontFamily: fonts.body,
170
+ cursor: !loadingData ? 'pointer' : 'not-allowed',
171
+ transition: 'all 0.2s ease',
172
+ display: 'flex',
173
+ alignItems: 'center',
174
+ gap: '6px',
175
+ whiteSpace: 'nowrap',
176
+ }}
177
+ onMouseEnter={(e) => {
178
+ if (!loadingData) {
179
+ e.currentTarget.style.background = colors.buttonPrimaryHover;
180
+ e.currentTarget.style.transform = 'translateY(-1px)';
181
+ }
182
+ }}
183
+ onMouseLeave={(e) => {
184
+ if (!loadingData) {
185
+ e.currentTarget.style.background = colors.buttonPrimary;
186
+ e.currentTarget.style.transform = 'translateY(0)';
187
+ }
188
+ }}
189
+ >
190
+ {loadingData ? (
191
+ <>
192
+ <LoadingSpinner size={12} />
193
+ <span>Discovering...</span>
194
+ </>
195
+ ) : (
196
+ <>
197
+ <CheckIcon size={12} color={colors.textInverse} />
198
+ <span>Discover</span>
199
+ </>
200
+ )}
201
+ </button>
202
+ {discoveredServers.length > 0 && (
203
+ <div
204
+ style={{
205
+ display: 'flex',
206
+ alignItems: 'center',
207
+ gap: '6px',
208
+ padding: '6px 10px',
209
+ background: colors.bgTertiary,
210
+ borderRadius: '8px',
211
+ fontSize: '11px',
212
+ fontWeight: '600',
213
+ color: colors.textPrimary,
214
+ fontFamily: fonts.body,
215
+ }}
216
+ >
217
+ <CheckIcon size={12} color={colors.accentGreen} />
218
+ <span>
219
+ {discoveredServers.length} server{discoveredServers.length !== 1 ? 's' : ''}
220
+ </span>
221
+ </div>
222
+ )}
223
+ </div>
224
+
225
+ {/* Clear Cache Button */}
226
+ <button
227
+ onClick={() => setShowClearCacheModal(true)}
228
+ disabled={clearingCache}
229
+ style={{
230
+ padding: '8px 14px',
231
+ background: colors.buttonSecondary,
232
+ border: `1px solid ${colors.borderLight}`,
233
+ color: colors.textSecondary,
234
+ cursor: clearingCache ? 'not-allowed' : 'pointer',
235
+ fontSize: '12px',
236
+ fontWeight: '500',
237
+ fontFamily: fonts.body,
238
+ borderRadius: '8px',
239
+ transition: 'all 0.2s ease',
240
+ display: 'flex',
241
+ alignItems: 'center',
242
+ gap: '6px',
243
+ whiteSpace: 'nowrap',
244
+ opacity: clearingCache ? 0.5 : 1,
245
+ }}
246
+ onMouseEnter={(e) => {
247
+ if (!clearingCache) {
248
+ e.currentTarget.style.background = colors.buttonSecondaryHover;
249
+ e.currentTarget.style.color = colors.textPrimary;
250
+ }
251
+ }}
252
+ onMouseLeave={(e) => {
253
+ if (!clearingCache) {
254
+ e.currentTarget.style.background = colors.buttonSecondary;
255
+ e.currentTarget.style.color = colors.textSecondary;
256
+ }
257
+ }}
258
+ title="Clear cached scan results"
259
+ >
260
+ {clearingCache ? (
261
+ <>
262
+ <LoadingSpinner size={12} />
263
+ <span>Clearing...</span>
264
+ </>
265
+ ) : (
266
+ <>
267
+ <IconTrash size={14} stroke={1.5} />
268
+ <span>Clear Cache</span>
269
+ </>
270
+ )}
271
+ </button>
272
+
273
+ <ConfirmationModal
274
+ isOpen={showClearCacheModal}
275
+ onClose={() => setShowClearCacheModal(false)}
276
+ onConfirm={async () => {
277
+ const result = await clearCache();
278
+ if (result.success) {
279
+ setShowClearCacheModal(false);
280
+ }
281
+ }}
282
+ title="Clear Scan Cache?"
283
+ message="Are you sure you want to clear all cached scan results? This will force fresh scans for all servers on the next scan."
284
+ confirmText="Clear Cache"
285
+ cancelText="Cancel"
286
+ danger={false}
287
+ />
288
+ </div>
289
+ );
290
+ }
@@ -0,0 +1,77 @@
1
+ import { ShieldIcon, ExternalLinkIcon } from '../SmartScanIcons';
2
+ import { colors, fonts } from '../../theme';
3
+
4
+ export default function SmartScanHeader() {
5
+ return (
6
+ <div
7
+ style={{
8
+ display: 'flex',
9
+ alignItems: 'center',
10
+ gap: '12px',
11
+ marginRight: 'auto',
12
+ }}
13
+ >
14
+ <div
15
+ style={{
16
+ display: 'flex',
17
+ alignItems: 'center',
18
+ justifyContent: 'center',
19
+ width: '40px',
20
+ height: '40px',
21
+ borderRadius: '10px',
22
+ background: `linear-gradient(135deg, ${colors.accentBlue}20, ${colors.accentGreen}20)`,
23
+ border: `2px solid ${colors.accentBlue}40`,
24
+ flexShrink: 0,
25
+ }}
26
+ >
27
+ <ShieldIcon size={20} color={colors.accentBlue} />
28
+ </div>
29
+ <div>
30
+ <h1
31
+ style={{
32
+ fontSize: '18px',
33
+ fontWeight: '700',
34
+ color: colors.textPrimary,
35
+ fontFamily: fonts.body,
36
+ margin: 0,
37
+ letterSpacing: '-0.2px',
38
+ }}
39
+ >
40
+ <a
41
+ href="https://smart.mcpshark.sh/#get-started"
42
+ target="_blank"
43
+ rel="noopener noreferrer"
44
+ style={{
45
+ color: colors.textPrimary,
46
+ textDecoration: 'none',
47
+ display: 'inline-flex',
48
+ alignItems: 'center',
49
+ gap: '6px',
50
+ transition: 'color 0.2s ease',
51
+ }}
52
+ onMouseEnter={(e) => {
53
+ e.currentTarget.style.color = colors.accentBlue;
54
+ }}
55
+ onMouseLeave={(e) => {
56
+ e.currentTarget.style.color = colors.textPrimary;
57
+ }}
58
+ >
59
+ Smart Scan
60
+ <ExternalLinkIcon size={12} color="currentColor" />
61
+ </a>
62
+ </h1>
63
+ <p
64
+ style={{
65
+ fontSize: '11px',
66
+ color: colors.textSecondary,
67
+ fontFamily: fonts.body,
68
+ margin: 0,
69
+ lineHeight: '1.3',
70
+ }}
71
+ >
72
+ AI-powered security analysis for Model Context Protocol (MCP) servers
73
+ </p>
74
+ </div>
75
+ </div>
76
+ );
77
+ }
@@ -0,0 +1,57 @@
1
+ import { colors, fonts } from '../../theme';
2
+
3
+ export default function ViewModeTabs({ viewMode, setViewMode, onSwitchToScan, onSwitchToList }) {
4
+ return (
5
+ <div
6
+ style={{
7
+ display: 'flex',
8
+ gap: '8px',
9
+ border: `1px solid ${colors.borderLight}`,
10
+ borderRadius: '8px',
11
+ padding: '4px',
12
+ background: colors.bgSecondary,
13
+ }}
14
+ >
15
+ <button
16
+ onClick={() => {
17
+ setViewMode('scan');
18
+ onSwitchToScan?.();
19
+ }}
20
+ style={{
21
+ padding: '6px 14px',
22
+ background: viewMode === 'scan' ? colors.bgCard : 'transparent',
23
+ border: 'none',
24
+ color: viewMode === 'scan' ? colors.textPrimary : colors.textSecondary,
25
+ borderRadius: '6px',
26
+ fontSize: '12px',
27
+ fontFamily: fonts.body,
28
+ fontWeight: viewMode === 'scan' ? '600' : '400',
29
+ cursor: 'pointer',
30
+ transition: 'all 0.2s',
31
+ }}
32
+ >
33
+ Scan Servers
34
+ </button>
35
+ <button
36
+ onClick={() => {
37
+ setViewMode('list');
38
+ onSwitchToList?.();
39
+ }}
40
+ style={{
41
+ padding: '6px 14px',
42
+ background: viewMode === 'list' ? colors.bgCard : 'transparent',
43
+ border: 'none',
44
+ color: viewMode === 'list' ? colors.textPrimary : colors.textSecondary,
45
+ borderRadius: '6px',
46
+ fontSize: '12px',
47
+ fontFamily: fonts.body,
48
+ fontWeight: viewMode === 'list' ? '600' : '400',
49
+ cursor: 'pointer',
50
+ transition: 'all 0.2s',
51
+ }}
52
+ >
53
+ View All Scans
54
+ </button>
55
+ </div>
56
+ );
57
+ }
@@ -0,0 +1,34 @@
1
+ import { useState } from 'react';
2
+
3
+ export function useCacheManagement(discoveredServers, discoverMcpData, setError) {
4
+ const [clearingCache, setClearingCache] = useState(false);
5
+
6
+ const clearCache = async () => {
7
+ setClearingCache(true);
8
+ setError(null);
9
+ try {
10
+ const response = await fetch('/api/smartscan/cache/clear', {
11
+ method: 'POST',
12
+ });
13
+ const data = await response.json();
14
+ if (response.ok) {
15
+ if (discoveredServers.length > 0) {
16
+ await discoverMcpData();
17
+ }
18
+ return { success: true, message: data.message };
19
+ } else {
20
+ throw new Error(data.error || 'Failed to clear cache');
21
+ }
22
+ } catch (err) {
23
+ setError(err.message || 'Failed to clear cache');
24
+ return { success: false, error: err.message };
25
+ } finally {
26
+ setClearingCache(false);
27
+ }
28
+ };
29
+
30
+ return {
31
+ clearingCache,
32
+ clearCache,
33
+ };
34
+ }
@@ -0,0 +1,121 @@
1
+ import { useState } from 'react';
2
+
3
+ export function useMcpDiscovery(setError) {
4
+ const [mcpData, setMcpData] = useState(null);
5
+ const [discoveredServers, setDiscoveredServers] = useState([]);
6
+ const [selectedServers, setSelectedServers] = useState(new Set());
7
+ const [loadingData, setLoadingData] = useState(false);
8
+ const [sessionId, setSessionId] = useState(null);
9
+
10
+ const makeMcpRequest = async (method, params = {}) => {
11
+ try {
12
+ const headers = { 'Content-Type': 'application/json' };
13
+ if (sessionId) {
14
+ headers['Mcp-Session-Id'] = sessionId;
15
+ }
16
+
17
+ const response = await fetch('/api/playground/proxy', {
18
+ method: 'POST',
19
+ headers,
20
+ body: JSON.stringify({ method, params }),
21
+ });
22
+
23
+ const data = await response.json();
24
+
25
+ const responseSessionId =
26
+ response.headers.get('Mcp-Session-Id') ||
27
+ response.headers.get('mcp-session-id') ||
28
+ data._sessionId;
29
+ if (responseSessionId && responseSessionId !== sessionId) {
30
+ setSessionId(responseSessionId);
31
+ }
32
+
33
+ if (!response.ok) {
34
+ throw new Error(data.error?.message || data.message || 'Request failed');
35
+ }
36
+
37
+ return data.result || data;
38
+ } catch (err) {
39
+ throw err;
40
+ }
41
+ };
42
+
43
+ const discoverMcpData = async () => {
44
+ setLoadingData(true);
45
+ setError(null);
46
+ setMcpData(null);
47
+ setDiscoveredServers([]);
48
+
49
+ try {
50
+ const response = await fetch('/api/smartscan/discover');
51
+ if (!response.ok) {
52
+ const errorData = await response.json();
53
+ throw new Error(errorData.message || 'Failed to discover servers');
54
+ }
55
+
56
+ const data = await response.json();
57
+ if (!data.success || !data.servers || data.servers.length === 0) {
58
+ throw new Error(
59
+ 'No MCP servers found in config. Please configure servers in the Setup tab.'
60
+ );
61
+ }
62
+
63
+ setDiscoveredServers(data.servers);
64
+
65
+ if (data.servers.length > 0) {
66
+ setSelectedServers(new Set(data.servers.map((s) => s.name)));
67
+
68
+ try {
69
+ const cachedResponse = await fetch('/api/smartscan/cached-results', {
70
+ method: 'POST',
71
+ headers: {
72
+ 'Content-Type': 'application/json',
73
+ },
74
+ body: JSON.stringify({
75
+ servers: data.servers,
76
+ }),
77
+ });
78
+
79
+ if (cachedResponse.ok) {
80
+ const cachedData = await cachedResponse.json();
81
+ if (cachedData.success && cachedData.results) {
82
+ const validCachedResults = cachedData.results.filter((r) => r.success && r.cached);
83
+ if (validCachedResults.length > 0) {
84
+ // This will be handled by the parent component
85
+ }
86
+ }
87
+ }
88
+ } catch (err) {
89
+ console.debug('Could not load cached results:', err);
90
+ }
91
+ }
92
+
93
+ if (data.servers.length > 0) {
94
+ const firstServer = data.servers[0];
95
+ setMcpData({
96
+ server: {
97
+ name: firstServer.name,
98
+ description: `Discovered from MCP config`,
99
+ },
100
+ tools: firstServer.tools || [],
101
+ resources: firstServer.resources || [],
102
+ prompts: firstServer.prompts || [],
103
+ });
104
+ }
105
+ } catch (err) {
106
+ setError(err.message || 'Failed to discover MCP server data');
107
+ } finally {
108
+ setLoadingData(false);
109
+ }
110
+ };
111
+
112
+ return {
113
+ mcpData,
114
+ discoveredServers,
115
+ selectedServers,
116
+ setSelectedServers,
117
+ loadingData,
118
+ discoverMcpData,
119
+ makeMcpRequest,
120
+ };
121
+ }