@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.
Files changed (204) 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 +22 -38
  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 +4 -12
  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 +3 -13
  12. package/mcp-server/lib/server/internal/handlers/prompts-list.js +2 -6
  13. package/mcp-server/lib/server/internal/handlers/resources-list.js +2 -6
  14. package/mcp-server/lib/server/internal/handlers/resources-read.js +3 -12
  15. package/mcp-server/lib/server/internal/handlers/tools-call.js +3 -9
  16. package/mcp-server/lib/server/internal/handlers/tools-list.js +2 -2
  17. package/mcp-server/lib/server/internal/run.js +4 -16
  18. package/mcp-server/lib/server/internal/server.js +6 -7
  19. package/mcp-server/lib/server/internal/session.js +2 -15
  20. package/mcp-server/mcp-shark.js +16 -66
  21. package/package.json +23 -38
  22. package/shared/logger.js +90 -0
  23. package/ui/dist/assets/index-Cc-IUa83.css +1 -0
  24. package/ui/dist/assets/index-srLDlk97.js +35 -0
  25. package/ui/dist/index.html +17 -0
  26. package/ui/dist/og-image.png +0 -0
  27. package/ui/server/routes/backups/deleteBackup.js +54 -0
  28. package/ui/server/routes/backups/index.js +15 -0
  29. package/ui/server/routes/backups/listBackups.js +75 -0
  30. package/ui/server/routes/backups/restoreBackup.js +83 -0
  31. package/ui/server/routes/backups/viewBackup.js +47 -0
  32. package/ui/server/routes/composite/index.js +46 -0
  33. package/ui/server/routes/composite/servers.js +18 -0
  34. package/ui/server/routes/composite/setup.js +129 -0
  35. package/ui/server/routes/composite/status.js +7 -0
  36. package/ui/server/routes/composite/stop.js +39 -0
  37. package/ui/server/routes/composite/utils.js +45 -0
  38. package/ui/server/routes/config.js +34 -30
  39. package/ui/server/routes/conversations.js +3 -3
  40. package/ui/server/routes/help.js +2 -2
  41. package/ui/server/routes/logs.js +5 -5
  42. package/ui/server/routes/playground.js +45 -47
  43. package/ui/server/routes/requests.js +112 -108
  44. package/ui/server/routes/sessions.js +4 -4
  45. package/ui/server/routes/settings.js +199 -0
  46. package/ui/server/routes/smartscan/discover.js +7 -6
  47. package/ui/server/routes/smartscan/scans/clearCache.js +3 -2
  48. package/ui/server/routes/smartscan/scans/createBatchScans.js +4 -3
  49. package/ui/server/routes/smartscan/scans/createScan.js +2 -1
  50. package/ui/server/routes/smartscan/scans/getCachedResults.js +2 -1
  51. package/ui/server/routes/smartscan/scans/getScan.js +2 -1
  52. package/ui/server/routes/smartscan/scans/listScans.js +5 -4
  53. package/ui/server/routes/smartscan/scans.js +3 -3
  54. package/ui/server/routes/smartscan/token.js +4 -3
  55. package/ui/server/routes/smartscan/transport.js +1 -1
  56. package/ui/server/routes/smartscan.js +1 -1
  57. package/ui/server/routes/statistics.js +13 -10
  58. package/ui/server/utils/config-update.js +7 -6
  59. package/ui/server/utils/config.js +4 -4
  60. package/ui/server/utils/logger.js +2 -0
  61. package/ui/server/utils/paths.js +210 -2
  62. package/ui/server/utils/port.js +2 -2
  63. package/ui/server/utils/process.js +0 -67
  64. package/ui/server/utils/scan-cache/all-results.js +76 -59
  65. package/ui/server/utils/scan-cache/directory.js +1 -1
  66. package/ui/server/utils/scan-cache/file-operations.js +19 -16
  67. package/ui/server/utils/scan-cache/server-operations.js +14 -9
  68. package/ui/server/utils/serialization.js +9 -3
  69. package/ui/server/utils/smartscan-token.js +4 -3
  70. package/ui/server.js +86 -41
  71. package/ui/src/App.jsx +5 -5
  72. package/ui/src/CompositeLogs.jsx +20 -20
  73. package/ui/src/CompositeSetup.jsx +9 -9
  74. package/ui/src/HelpGuide/HelpGuideFooter.jsx +1 -0
  75. package/ui/src/HelpGuide/HelpGuideHeader.jsx +2 -1
  76. package/ui/src/HelpGuide.jsx +17 -4
  77. package/ui/src/IntroTour.jsx +19 -5
  78. package/ui/src/LogDetail.jsx +1 -0
  79. package/ui/src/LogTable.jsx +24 -6
  80. package/ui/src/PacketDetail.jsx +21 -16
  81. package/ui/src/PacketFilters.jsx +29 -14
  82. package/ui/src/PacketList.jsx +4 -5
  83. package/ui/src/SmartScan.jsx +5 -5
  84. package/ui/src/TabNavigation.jsx +5 -5
  85. package/ui/src/components/App/HelpButton.jsx +4 -0
  86. package/ui/src/components/App/TrafficTab.jsx +4 -4
  87. package/ui/src/components/App/useAppState.js +118 -24
  88. package/ui/src/components/BackupList.jsx +6 -2
  89. package/ui/src/components/CollapsibleSection.jsx +16 -2
  90. package/ui/src/components/ConfigViewerModal.jsx +17 -3
  91. package/ui/src/components/ConfirmationModal.jsx +20 -3
  92. package/ui/src/components/DetailsTab/BodySection.jsx +3 -1
  93. package/ui/src/components/DetailsTab/CollapsibleRequestResponse.jsx +14 -3
  94. package/ui/src/components/DetailsTab/InfoSection.jsx +4 -2
  95. package/ui/src/components/DetailsTab/RequestDetailsSection.jsx +7 -5
  96. package/ui/src/components/DetailsTab/ResponseDetailsSection.jsx +7 -5
  97. package/ui/src/components/DetectedPathsList.jsx +5 -2
  98. package/ui/src/components/FileInput.jsx +3 -1
  99. package/ui/src/components/GroupHeader.jsx +14 -0
  100. package/ui/src/components/GroupedByMcpView.jsx +3 -4
  101. package/ui/src/components/GroupedByServerView.jsx +1 -1
  102. package/ui/src/components/GroupedBySessionView.jsx +1 -1
  103. package/ui/src/components/HexTab.jsx +17 -4
  104. package/ui/src/components/LogsToolbar.jsx +3 -1
  105. package/ui/src/components/McpPlayground/LoadingModal.jsx +7 -3
  106. package/ui/src/components/McpPlayground/PromptsSection/PromptCallPanel.jsx +5 -0
  107. package/ui/src/components/McpPlayground/PromptsSection/PromptItem.jsx +6 -2
  108. package/ui/src/components/McpPlayground/PromptsSection/PromptsList.jsx +4 -4
  109. package/ui/src/components/McpPlayground/PromptsSection.jsx +2 -1
  110. package/ui/src/components/McpPlayground/ResourcesSection/ResourceCallPanel.jsx +3 -0
  111. package/ui/src/components/McpPlayground/ResourcesSection/ResourceItem.jsx +6 -2
  112. package/ui/src/components/McpPlayground/ResourcesSection/ResourcesList.jsx +4 -4
  113. package/ui/src/components/McpPlayground/ResourcesSection.jsx +2 -1
  114. package/ui/src/components/McpPlayground/ToolsSection/ToolCallPanel.jsx +5 -0
  115. package/ui/src/components/McpPlayground/ToolsSection/ToolItem.jsx +6 -2
  116. package/ui/src/components/McpPlayground/ToolsSection/ToolsList.jsx +4 -4
  117. package/ui/src/components/McpPlayground/ToolsSection.jsx +2 -1
  118. package/ui/src/components/McpPlayground/hooks/useMcpDataLoader.js +9 -9
  119. package/ui/src/components/McpPlayground/hooks/useMcpRequest.js +10 -5
  120. package/ui/src/components/McpPlayground/hooks/useMcpServerStatus.js +43 -23
  121. package/ui/src/components/McpPlayground/useMcpPlayground.js +72 -44
  122. package/ui/src/components/McpPlayground.jsx +5 -2
  123. package/ui/src/components/PacketDetailHeader.jsx +8 -3
  124. package/ui/src/components/PacketFilters/ExportControls.jsx +2 -1
  125. package/ui/src/components/PacketFilters/FilterInput.jsx +1 -1
  126. package/ui/src/components/RawTab.jsx +15 -2
  127. package/ui/src/components/RequestRow/OrphanedResponseRow.jsx +10 -2
  128. package/ui/src/components/RequestRow/RequestRowMain.jsx +12 -3
  129. package/ui/src/components/RequestRow/ResponseRow.jsx +11 -3
  130. package/ui/src/components/RequestRow.jsx +17 -9
  131. package/ui/src/components/ServerControl.jsx +3 -1
  132. package/ui/src/components/ServiceSelector.jsx +2 -0
  133. package/ui/src/components/SmartScan/AnalysisResult.jsx +2 -2
  134. package/ui/src/components/SmartScan/BatchResultsDisplay/BatchResultItem.jsx +2 -1
  135. package/ui/src/components/SmartScan/BatchResultsDisplay/BatchResultsHeader.jsx +1 -1
  136. package/ui/src/components/SmartScan/BatchResultsDisplay.jsx +9 -3
  137. package/ui/src/components/SmartScan/EmptyState.jsx +3 -0
  138. package/ui/src/components/SmartScan/ErrorDisplay.jsx +4 -2
  139. package/ui/src/components/SmartScan/ExpandableSection.jsx +4 -0
  140. package/ui/src/components/SmartScan/FindingsTable.jsx +46 -42
  141. package/ui/src/components/SmartScan/ListViewContent.jsx +2 -2
  142. package/ui/src/components/SmartScan/NotablePatternsSection.jsx +13 -8
  143. package/ui/src/components/SmartScan/OverallSummarySection.jsx +36 -29
  144. package/ui/src/components/SmartScan/RecommendationsSection.jsx +10 -8
  145. package/ui/src/components/SmartScan/ScanDetailHeader.jsx +2 -1
  146. package/ui/src/components/SmartScan/ScanDetailView.jsx +4 -4
  147. package/ui/src/components/SmartScan/ScanListView/ScanListHeader.jsx +2 -1
  148. package/ui/src/components/SmartScan/ScanListView/ScanListItem.jsx +15 -1
  149. package/ui/src/components/SmartScan/ScanOverviewSection.jsx +3 -1
  150. package/ui/src/components/SmartScan/ScanResultsDisplay.jsx +1 -1
  151. package/ui/src/components/SmartScan/ScanViewContent.jsx +2 -2
  152. package/ui/src/components/SmartScan/ScanningProgress.jsx +4 -2
  153. package/ui/src/components/SmartScan/ServerInfoSection.jsx +3 -1
  154. package/ui/src/components/SmartScan/ServerSelectionRow.jsx +4 -2
  155. package/ui/src/components/SmartScan/SingleResultDisplay.jsx +5 -3
  156. package/ui/src/components/SmartScan/SmartScanControls.jsx +11 -7
  157. package/ui/src/components/SmartScan/SmartScanHeader.jsx +1 -1
  158. package/ui/src/components/SmartScan/ViewModeTabs.jsx +2 -0
  159. package/ui/src/components/SmartScan/hooks/useCacheManagement.js +1 -2
  160. package/ui/src/components/SmartScan/hooks/useMcpDiscovery.js +22 -26
  161. package/ui/src/components/SmartScan/hooks/useScanList.js +10 -9
  162. package/ui/src/components/SmartScan/hooks/useScanOperations.js +23 -14
  163. package/ui/src/components/SmartScan/hooks/useServerStatus.js +2 -2
  164. package/ui/src/components/SmartScan/hooks/useTokenManagement.js +2 -2
  165. package/ui/src/components/SmartScan/scanDataUtils.js +22 -17
  166. package/ui/src/components/SmartScan/useSmartScan.js +4 -4
  167. package/ui/src/components/SmartScan/utils.js +3 -1
  168. package/ui/src/components/SmartScanIcons.jsx +6 -3
  169. package/ui/src/components/TabNavigation/DesktopTabs.jsx +8 -3
  170. package/ui/src/components/TabNavigation/MobileDropdown.jsx +3 -1
  171. package/ui/src/components/TabNavigation.jsx +8 -3
  172. package/ui/src/components/TabNavigationIcons.jsx +4 -4
  173. package/ui/src/components/TourOverlay.jsx +1 -1
  174. package/ui/src/components/TourTooltip/TourTooltipButtons.jsx +3 -0
  175. package/ui/src/components/TourTooltip/TourTooltipHeader.jsx +1 -0
  176. package/ui/src/components/TourTooltip/TourTooltipIcons.jsx +9 -0
  177. package/ui/src/components/TourTooltip/useTooltipPosition.js +63 -36
  178. package/ui/src/components/TourTooltip.jsx +11 -3
  179. package/ui/src/components/ViewModeTabs.jsx +3 -1
  180. package/ui/src/config/tourSteps.jsx +0 -2
  181. package/ui/src/hooks/useAnimation.js +15 -12
  182. package/ui/src/hooks/useConfigManagement.js +8 -8
  183. package/ui/src/hooks/useServiceExtraction.js +1 -1
  184. package/ui/src/index.css +3 -8
  185. package/ui/src/theme.js +3 -3
  186. package/ui/src/utils/hexUtils.js +11 -5
  187. package/ui/src/utils/mcpGroupingUtils.js +18 -10
  188. package/ui/src/utils/requestPairing.js +89 -0
  189. package/ui/src/utils/requestUtils.js +32 -101
  190. package/ui/vite.config.js +1 -1
  191. package/mcp-server/.editorconfig +0 -15
  192. package/mcp-server/.prettierignore +0 -11
  193. package/mcp-server/.prettierrc +0 -12
  194. package/mcp-server/README.md +0 -280
  195. package/mcp-server/commitlint.config.cjs +0 -42
  196. package/mcp-server/eslint.config.js +0 -131
  197. package/mcp-server/package-lock.json +0 -4784
  198. package/mcp-server/package.json +0 -30
  199. package/ui/README.md +0 -212
  200. package/ui/package-lock.json +0 -3574
  201. package/ui/package.json +0 -12
  202. package/ui/paths.js +0 -282
  203. package/ui/server/routes/backups.js +0 -251
  204. package/ui/server/routes/composite.js +0 -260
@@ -1,12 +1,12 @@
1
- import { serializeBigInt } from '../utils/serialization.js';
2
1
  import { queryConversations } from 'mcp-shark-common/db/query.js';
2
+ import { serializeBigInt } from '../utils/serialization.js';
3
3
 
4
4
  export function createConversationsRoutes(db) {
5
5
  const router = {};
6
6
 
7
7
  router.getConversations = (req, res) => {
8
- const limit = parseInt(req.query.limit) || 1000;
9
- const offset = parseInt(req.query.offset) || 0;
8
+ const limit = Number.parseInt(req.query.limit) || 1000;
9
+ const offset = Number.parseInt(req.query.offset) || 0;
10
10
  const filters = {
11
11
  sessionId: req.query.sessionId || null,
12
12
  method: req.query.method || null,
@@ -3,7 +3,7 @@ import { readHelpState, writeHelpState } from 'mcp-shark-common/configs/index.js
3
3
  export function createHelpRoutes() {
4
4
  const router = {};
5
5
 
6
- router.getState = (req, res) => {
6
+ router.getState = (_req, res) => {
7
7
  const state = readHelpState();
8
8
  res.json({
9
9
  dismissed: state.dismissed || false,
@@ -25,7 +25,7 @@ export function createHelpRoutes() {
25
25
  }
26
26
  };
27
27
 
28
- router.reset = (req, res) => {
28
+ router.reset = (_req, res) => {
29
29
  const state = {
30
30
  dismissed: false,
31
31
  tourCompleted: false,
@@ -1,19 +1,19 @@
1
- export function createLogsRoutes(mcpSharkLogs, broadcastLogUpdate) {
1
+ export function createLogsRoutes(mcpSharkLogs, _broadcastLogUpdate) {
2
2
  const router = {};
3
3
 
4
4
  router.getLogs = (req, res) => {
5
- const limit = parseInt(req.query.limit) || 1000;
6
- const offset = parseInt(req.query.offset) || 0;
5
+ const limit = Number.parseInt(req.query.limit) || 1000;
6
+ const offset = Number.parseInt(req.query.offset) || 0;
7
7
  const logs = [...mcpSharkLogs].reverse().slice(offset, offset + limit);
8
8
  res.json(logs);
9
9
  };
10
10
 
11
- router.clearLogs = (req, res) => {
11
+ router.clearLogs = (_req, res) => {
12
12
  mcpSharkLogs.length = 0;
13
13
  res.json({ success: true, message: 'Logs cleared' });
14
14
  };
15
15
 
16
- router.exportLogs = (req, res) => {
16
+ router.exportLogs = (_req, res) => {
17
17
  try {
18
18
  const logsText = mcpSharkLogs
19
19
  .map((log) => `[${log.timestamp}] [${log.type.toUpperCase()}] ${log.line}`)
@@ -1,5 +1,6 @@
1
1
  import { Client } from '@modelcontextprotocol/sdk/client/index.js';
2
2
  import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
3
+ import logger from '../utils/logger.js';
3
4
 
4
5
  const MCP_SERVER_BASE_URL = 'http://localhost:9851/mcp';
5
6
 
@@ -79,56 +80,53 @@ export function createPlaygroundRoutes() {
79
80
 
80
81
  const { client } = await getClient(serverName, sessionId);
81
82
 
82
- let result;
83
- switch (method) {
84
- case 'tools/list':
85
- result = await client.listTools();
86
- break;
87
- case 'tools/call':
88
- if (!params?.name) {
89
- return res.status(400).json({
90
- error: 'Invalid request',
91
- message: 'Tool name is required',
83
+ const executeMethod = async () => {
84
+ switch (method) {
85
+ case 'tools/list':
86
+ return await client.listTools();
87
+ case 'tools/call':
88
+ if (!params?.name) {
89
+ return res.status(400).json({
90
+ error: 'Invalid request',
91
+ message: 'Tool name is required',
92
+ });
93
+ }
94
+ return await client.callTool({
95
+ name: params.name,
96
+ arguments: params.arguments || {},
92
97
  });
93
- }
94
- result = await client.callTool({
95
- name: params.name,
96
- arguments: params.arguments || {},
97
- });
98
- break;
99
- case 'prompts/list':
100
- result = await client.listPrompts();
101
- break;
102
- case 'prompts/get':
103
- if (!params?.name) {
104
- return res.status(400).json({
105
- error: 'Invalid request',
106
- message: 'Prompt name is required',
98
+ case 'prompts/list':
99
+ return await client.listPrompts();
100
+ case 'prompts/get':
101
+ if (!params?.name) {
102
+ return res.status(400).json({
103
+ error: 'Invalid request',
104
+ message: 'Prompt name is required',
105
+ });
106
+ }
107
+ return await client.getPrompt({
108
+ name: params.name,
109
+ arguments: params.arguments || {},
107
110
  });
108
- }
109
- result = await client.getPrompt({
110
- name: params.name,
111
- arguments: params.arguments || {},
112
- });
113
- break;
114
- case 'resources/list':
115
- result = await client.listResources();
116
- break;
117
- case 'resources/read':
118
- if (!params?.uri) {
111
+ case 'resources/list':
112
+ return await client.listResources();
113
+ case 'resources/read':
114
+ if (!params?.uri) {
115
+ return res.status(400).json({
116
+ error: 'Invalid request',
117
+ message: 'Resource URI is required',
118
+ });
119
+ }
120
+ return await client.readResource({ uri: params.uri });
121
+ default:
119
122
  return res.status(400).json({
120
- error: 'Invalid request',
121
- message: 'Resource URI is required',
123
+ error: 'Unsupported method',
124
+ message: `Method ${method} is not supported`,
122
125
  });
123
- }
124
- result = await client.readResource({ uri: params.uri });
125
- break;
126
- default:
127
- return res.status(400).json({
128
- error: 'Unsupported method',
129
- message: `Method ${method} is not supported`,
130
- });
131
- }
126
+ }
127
+ };
128
+
129
+ const result = await executeMethod();
132
130
 
133
131
  // Return session ID in response
134
132
  res.setHeader('Mcp-Session-Id', sessionId);
@@ -137,7 +135,7 @@ export function createPlaygroundRoutes() {
137
135
  _sessionId: sessionId,
138
136
  });
139
137
  } catch (error) {
140
- console.error('Error in playground proxy:', error);
138
+ logger.error({ error: error.message }, 'Error in playground proxy');
141
139
 
142
140
  // Check if it's a connection error
143
141
  if (error.message?.includes('ECONNREFUSED') || error.message?.includes('connect')) {
@@ -1,22 +1,25 @@
1
- import { serializeBigInt } from '../utils/serialization.js';
2
1
  import { queryRequests } from 'mcp-shark-common/db/query.js';
2
+ import logger from '../utils/logger.js';
3
+ import { serializeBigInt } from '../utils/serialization.js';
4
+
5
+ const sanitizeSearch = (value) => {
6
+ if (value !== undefined && value !== null) {
7
+ const trimmed = String(value).trim();
8
+ return trimmed.length > 0 ? trimmed : null;
9
+ }
10
+ return null;
11
+ };
3
12
 
4
13
  export function createRequestsRoutes(db) {
5
14
  const router = {};
6
15
 
7
16
  router.getRequests = (req, res) => {
8
17
  try {
9
- const limit = parseInt(req.query.limit) || 1000;
10
- const offset = parseInt(req.query.offset) || 0;
18
+ const limit = Number.parseInt(req.query.limit) || 1000;
19
+ const offset = Number.parseInt(req.query.offset) || 0;
11
20
 
12
21
  // Sanitize search parameter - convert empty strings to null
13
- let search = req.query.search;
14
- if (search !== undefined && search !== null) {
15
- search = String(search).trim();
16
- search = search.length > 0 ? search : null;
17
- } else {
18
- search = null;
19
- }
22
+ const search = sanitizeSearch(req.query.search);
20
23
 
21
24
  // Build filters object, ensuring all values are properly typed
22
25
  const filters = {
@@ -24,7 +27,7 @@ export function createRequestsRoutes(db) {
24
27
  direction: (req.query.direction && String(req.query.direction).trim()) || null,
25
28
  method: (req.query.method && String(req.query.method).trim()) || null,
26
29
  jsonrpcMethod: (req.query.jsonrpcMethod && String(req.query.jsonrpcMethod).trim()) || null,
27
- statusCode: req.query.statusCode ? parseInt(req.query.statusCode) : null,
30
+ statusCode: req.query.statusCode ? Number.parseInt(req.query.statusCode) : null,
28
31
  jsonrpcId: (req.query.jsonrpcId && String(req.query.jsonrpcId).trim()) || null,
29
32
  search: search,
30
33
  serverName: (req.query.serverName && String(req.query.serverName).trim()) || null,
@@ -44,21 +47,21 @@ export function createRequestsRoutes(db) {
44
47
  const requests = queryRequests(db, filters);
45
48
  res.json(serializeBigInt(requests));
46
49
  } catch (error) {
47
- console.error('Error in getRequests:', error);
50
+ logger.error({ error: error.message }, 'Error in getRequests');
48
51
  res.status(500).json({ error: 'Failed to query requests', details: error.message });
49
52
  }
50
53
  };
51
54
 
52
55
  router.getRequest = (req, res) => {
53
56
  const stmt = db.prepare('SELECT * FROM packets WHERE frame_number = ?');
54
- const request = stmt.get(parseInt(req.params.frameNumber));
57
+ const request = stmt.get(Number.parseInt(req.params.frameNumber));
55
58
  if (!request) {
56
59
  return res.status(404).json({ error: 'Request not found' });
57
60
  }
58
61
  res.json(serializeBigInt(request));
59
62
  };
60
63
 
61
- router.clearRequests = (req, res) => {
64
+ router.clearRequests = (_req, res) => {
62
65
  try {
63
66
  // Disable foreign key constraints temporarily to avoid constraint violations
64
67
  db.exec('PRAGMA foreign_keys = OFF');
@@ -87,19 +90,25 @@ export function createRequestsRoutes(db) {
87
90
  ];
88
91
 
89
92
  // Delete from each table that exists
90
- let clearedCount = 0;
91
- const clearedTables = [];
92
- trafficTables.forEach((table) => {
93
- if (existingTables.includes(table)) {
94
- try {
95
- db.exec(`DELETE FROM ${table}`);
96
- clearedCount++;
97
- clearedTables.push(table);
98
- } catch (err) {
99
- console.warn(`Error clearing table ${table}:`, err.message);
93
+ const clearResults = trafficTables.reduce(
94
+ (acc, table) => {
95
+ if (existingTables.includes(table)) {
96
+ try {
97
+ db.exec(`DELETE FROM ${table}`);
98
+ return {
99
+ count: acc.count + 1,
100
+ tables: [...acc.tables, table],
101
+ };
102
+ } catch (err) {
103
+ logger.warn({ table, error: err.message }, 'Error clearing table');
104
+ }
100
105
  }
101
- }
102
- });
106
+ return acc;
107
+ },
108
+ { count: 0, tables: [] }
109
+ );
110
+ const clearedCount = clearResults.count;
111
+ const clearedTables = clearResults.tables;
103
112
 
104
113
  // Re-enable foreign key constraints
105
114
  db.exec('PRAGMA foreign_keys = ON');
@@ -112,10 +121,10 @@ export function createRequestsRoutes(db) {
112
121
  // Make sure to re-enable foreign keys even if there's an error
113
122
  try {
114
123
  db.exec('PRAGMA foreign_keys = ON');
115
- } catch (e) {
124
+ } catch (_e) {
116
125
  // Ignore
117
126
  }
118
- console.error('Error clearing requests:', error);
127
+ logger.error({ error: error.message }, 'Error clearing requests');
119
128
  res.status(500).json({ error: 'Failed to clear traffic', details: error.message });
120
129
  }
121
130
  };
@@ -123,20 +132,14 @@ export function createRequestsRoutes(db) {
123
132
  router.exportRequests = (req, res) => {
124
133
  try {
125
134
  // Sanitize search parameter - convert empty strings to null
126
- let search = req.query.search;
127
- if (search !== undefined && search !== null) {
128
- search = String(search).trim();
129
- search = search.length > 0 ? search : null;
130
- } else {
131
- search = null;
132
- }
135
+ const search = sanitizeSearch(req.query.search);
133
136
 
134
137
  const filters = {
135
138
  sessionId: req.query.sessionId || null,
136
139
  direction: req.query.direction || null,
137
140
  method: req.query.method || null,
138
141
  jsonrpcMethod: req.query.jsonrpcMethod || null,
139
- statusCode: req.query.statusCode ? parseInt(req.query.statusCode) : null,
142
+ statusCode: req.query.statusCode ? Number.parseInt(req.query.statusCode) : null,
140
143
  jsonrpcId: req.query.jsonrpcId || null,
141
144
  search: search,
142
145
  serverName: req.query.serverName || null,
@@ -149,78 +152,79 @@ export function createRequestsRoutes(db) {
149
152
  const requests = queryRequests(db, filters);
150
153
  const format = req.query.format || 'json';
151
154
 
152
- let content, contentType, extension;
153
-
154
- if (format === 'csv') {
155
- const headers = [
156
- 'Frame',
157
- 'Time',
158
- 'Source',
159
- 'Destination',
160
- 'Protocol',
161
- 'Length',
162
- 'Method',
163
- 'Status',
164
- 'JSON-RPC Method',
165
- 'Session ID',
166
- 'Server Name',
167
- ];
168
- const rows = requests.map((req) => [
169
- req.frame_number || '',
170
- req.timestamp_iso || '',
171
- req.request?.host || '',
172
- req.request?.host || '',
173
- 'HTTP',
174
- req.length || '',
175
- req.request?.method || '',
176
- req.response?.status_code || '',
177
- req.jsonrpc_method || '',
178
- req.session_id || '',
179
- req.server_name || '',
180
- ]);
181
-
182
- content = [
183
- headers.join(','),
184
- ...rows.map((row) =>
185
- row.map((cell) => `"${String(cell).replace(/"/g, '""')}"`).join(',')
186
- ),
187
- ].join('\n');
188
- contentType = 'text/csv';
189
- extension = 'csv';
190
- } else if (format === 'txt') {
191
- content = requests
192
- .map((req, idx) => {
193
- const lines = [
194
- `=== Request/Response #${idx + 1} (Frame ${req.frame_number || 'N/A'}) ===`,
195
- `Time: ${req.timestamp_iso || 'N/A'}`,
196
- `Session ID: ${req.session_id || 'N/A'}`,
197
- `Server: ${req.server_name || 'N/A'}`,
198
- `Direction: ${req.direction || 'N/A'}`,
199
- `Method: ${req.request?.method || 'N/A'}`,
200
- `Status: ${req.response?.status_code || 'N/A'}`,
201
- `JSON-RPC Method: ${req.jsonrpc_method || 'N/A'}`,
202
- `JSON-RPC ID: ${req.jsonrpc_id || 'N/A'}`,
203
- `Length: ${req.length || 0} bytes`,
204
- '',
205
- 'Request:',
206
- JSON.stringify(req.request || {}, null, 2),
207
- '',
208
- 'Response:',
209
- JSON.stringify(req.response || {}, null, 2),
210
- '',
211
- '---',
212
- '',
213
- ];
214
- return lines.join('\n');
215
- })
216
- .join('\n');
217
- contentType = 'text/plain';
218
- extension = 'txt';
219
- } else {
220
- content = JSON.stringify(serializeBigInt(requests), null, 2);
221
- contentType = 'application/json';
222
- extension = 'json';
223
- }
155
+ const formatExport = (requests, format) => {
156
+ if (format === 'csv') {
157
+ const headers = [
158
+ 'Frame',
159
+ 'Time',
160
+ 'Source',
161
+ 'Destination',
162
+ 'Protocol',
163
+ 'Length',
164
+ 'Method',
165
+ 'Status',
166
+ 'JSON-RPC Method',
167
+ 'Session ID',
168
+ 'Server Name',
169
+ ];
170
+ const rows = requests.map((req) => [
171
+ req.frame_number || '',
172
+ req.timestamp_iso || '',
173
+ req.request?.host || '',
174
+ req.request?.host || '',
175
+ 'HTTP',
176
+ req.length || '',
177
+ req.request?.method || '',
178
+ req.response?.status_code || '',
179
+ req.jsonrpc_method || '',
180
+ req.session_id || '',
181
+ req.server_name || '',
182
+ ]);
183
+
184
+ const content = [
185
+ headers.join(','),
186
+ ...rows.map((row) =>
187
+ row.map((cell) => `"${String(cell).replace(/"/g, '""')}"`).join(',')
188
+ ),
189
+ ].join('\n');
190
+ return { content, contentType: 'text/csv', extension: 'csv' };
191
+ }
192
+
193
+ if (format === 'txt') {
194
+ const content = requests
195
+ .map((req, idx) => {
196
+ const lines = [
197
+ `=== Request/Response #${idx + 1} (Frame ${req.frame_number || 'N/A'}) ===`,
198
+ `Time: ${req.timestamp_iso || 'N/A'}`,
199
+ `Session ID: ${req.session_id || 'N/A'}`,
200
+ `Server: ${req.server_name || 'N/A'}`,
201
+ `Direction: ${req.direction || 'N/A'}`,
202
+ `Method: ${req.request?.method || 'N/A'}`,
203
+ `Status: ${req.response?.status_code || 'N/A'}`,
204
+ `JSON-RPC Method: ${req.jsonrpc_method || 'N/A'}`,
205
+ `JSON-RPC ID: ${req.jsonrpc_id || 'N/A'}`,
206
+ `Length: ${req.length || 0} bytes`,
207
+ '',
208
+ 'Request:',
209
+ JSON.stringify(req.request || {}, null, 2),
210
+ '',
211
+ 'Response:',
212
+ JSON.stringify(req.response || {}, null, 2),
213
+ '',
214
+ '---',
215
+ '',
216
+ ];
217
+ return lines.join('\n');
218
+ })
219
+ .join('\n');
220
+ return { content, contentType: 'text/plain', extension: 'txt' };
221
+ }
222
+
223
+ const content = JSON.stringify(serializeBigInt(requests), null, 2);
224
+ return { content, contentType: 'application/json', extension: 'json' };
225
+ };
226
+
227
+ const { content, contentType, extension } = formatExport(requests, format);
224
228
 
225
229
  const filename = `mcp-shark-traffic-${new Date().toISOString().replace(/[:.]/g, '-')}.${extension}`;
226
230
  res.setHeader('Content-Type', contentType);
@@ -1,12 +1,12 @@
1
+ import { getSessionRequests, getSessions } from 'mcp-shark-common/db/query.js';
1
2
  import { serializeBigInt } from '../utils/serialization.js';
2
- import { getSessions, getSessionRequests } from 'mcp-shark-common/db/query.js';
3
3
 
4
4
  export function createSessionsRoutes(db) {
5
5
  const router = {};
6
6
 
7
7
  router.getSessions = (req, res) => {
8
- const limit = parseInt(req.query.limit) || 1000;
9
- const offset = parseInt(req.query.offset) || 0;
8
+ const limit = Number.parseInt(req.query.limit) || 1000;
9
+ const offset = Number.parseInt(req.query.offset) || 0;
10
10
  const filters = {
11
11
  startTime: req.query.startTime ? BigInt(req.query.startTime) : null,
12
12
  endTime: req.query.endTime ? BigInt(req.query.endTime) : null,
@@ -18,7 +18,7 @@ export function createSessionsRoutes(db) {
18
18
  };
19
19
 
20
20
  router.getSessionRequests = (req, res) => {
21
- const limit = parseInt(req.query.limit) || 10000;
21
+ const limit = Number.parseInt(req.query.limit) || 10000;
22
22
  const requests = getSessionRequests(db, req.params.sessionId, limit);
23
23
  res.json(serializeBigInt(requests));
24
24
  };