@mcp-shark/mcp-shark 1.4.2 → 1.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +84 -645
- package/bin/mcp-shark.js +30 -36
- package/mcp-server/index.js +115 -0
- package/mcp-server/lib/auditor/audit.js +22 -38
- package/mcp-server/lib/common/error.js +1 -1
- package/mcp-server/lib/server/external/all.js +5 -6
- package/mcp-server/lib/server/external/config.js +1 -3
- package/mcp-server/lib/server/external/kv.js +4 -12
- package/mcp-server/lib/server/external/single/request.js +3 -6
- package/mcp-server/lib/server/external/single/run.js +8 -19
- package/mcp-server/lib/server/internal/handlers/prompts-get.js +3 -13
- package/mcp-server/lib/server/internal/handlers/prompts-list.js +2 -6
- package/mcp-server/lib/server/internal/handlers/resources-list.js +2 -6
- package/mcp-server/lib/server/internal/handlers/resources-read.js +3 -12
- package/mcp-server/lib/server/internal/handlers/tools-call.js +3 -9
- package/mcp-server/lib/server/internal/handlers/tools-list.js +2 -2
- package/mcp-server/lib/server/internal/run.js +4 -16
- package/mcp-server/lib/server/internal/server.js +6 -7
- package/mcp-server/lib/server/internal/session.js +2 -15
- package/mcp-server/mcp-shark.js +16 -66
- package/package.json +23 -38
- package/shared/logger.js +90 -0
- package/ui/dist/assets/index-Cc-IUa83.css +1 -0
- package/ui/dist/assets/index-srLDlk97.js +35 -0
- package/ui/dist/index.html +17 -0
- package/ui/dist/og-image.png +0 -0
- package/ui/server/routes/backups/deleteBackup.js +54 -0
- package/ui/server/routes/backups/index.js +15 -0
- package/ui/server/routes/backups/listBackups.js +75 -0
- package/ui/server/routes/backups/restoreBackup.js +83 -0
- package/ui/server/routes/backups/viewBackup.js +47 -0
- package/ui/server/routes/composite/index.js +46 -0
- package/ui/server/routes/composite/servers.js +18 -0
- package/ui/server/routes/composite/setup.js +129 -0
- package/ui/server/routes/composite/status.js +7 -0
- package/ui/server/routes/composite/stop.js +39 -0
- package/ui/server/routes/composite/utils.js +45 -0
- package/ui/server/routes/config.js +34 -30
- package/ui/server/routes/conversations.js +3 -3
- package/ui/server/routes/help.js +2 -2
- package/ui/server/routes/logs.js +5 -5
- package/ui/server/routes/playground.js +45 -47
- package/ui/server/routes/requests.js +112 -108
- package/ui/server/routes/sessions.js +4 -4
- package/ui/server/routes/settings.js +199 -0
- package/ui/server/routes/smartscan/discover.js +7 -6
- package/ui/server/routes/smartscan/scans/clearCache.js +3 -2
- package/ui/server/routes/smartscan/scans/createBatchScans.js +4 -3
- package/ui/server/routes/smartscan/scans/createScan.js +2 -1
- package/ui/server/routes/smartscan/scans/getCachedResults.js +2 -1
- package/ui/server/routes/smartscan/scans/getScan.js +2 -1
- package/ui/server/routes/smartscan/scans/listScans.js +5 -4
- package/ui/server/routes/smartscan/scans.js +3 -3
- package/ui/server/routes/smartscan/token.js +4 -3
- package/ui/server/routes/smartscan/transport.js +1 -1
- package/ui/server/routes/smartscan.js +1 -1
- package/ui/server/routes/statistics.js +13 -10
- package/ui/server/utils/config-update.js +7 -6
- package/ui/server/utils/config.js +4 -4
- package/ui/server/utils/logger.js +2 -0
- package/ui/server/utils/paths.js +210 -2
- package/ui/server/utils/port.js +2 -2
- package/ui/server/utils/process.js +0 -67
- package/ui/server/utils/scan-cache/all-results.js +76 -59
- package/ui/server/utils/scan-cache/directory.js +1 -1
- package/ui/server/utils/scan-cache/file-operations.js +19 -16
- package/ui/server/utils/scan-cache/server-operations.js +14 -9
- package/ui/server/utils/serialization.js +9 -3
- package/ui/server/utils/smartscan-token.js +4 -3
- package/ui/server.js +86 -41
- package/ui/src/App.jsx +5 -5
- package/ui/src/CompositeLogs.jsx +20 -20
- package/ui/src/CompositeSetup.jsx +9 -9
- package/ui/src/HelpGuide/HelpGuideFooter.jsx +1 -0
- package/ui/src/HelpGuide/HelpGuideHeader.jsx +2 -1
- package/ui/src/HelpGuide.jsx +17 -4
- package/ui/src/IntroTour.jsx +19 -5
- package/ui/src/LogDetail.jsx +1 -0
- package/ui/src/LogTable.jsx +24 -6
- package/ui/src/PacketDetail.jsx +21 -16
- package/ui/src/PacketFilters.jsx +29 -14
- package/ui/src/PacketList.jsx +4 -5
- package/ui/src/SmartScan.jsx +5 -5
- package/ui/src/TabNavigation.jsx +5 -5
- package/ui/src/components/App/HelpButton.jsx +4 -0
- package/ui/src/components/App/TrafficTab.jsx +4 -4
- package/ui/src/components/App/useAppState.js +118 -24
- package/ui/src/components/BackupList.jsx +6 -2
- package/ui/src/components/CollapsibleSection.jsx +16 -2
- package/ui/src/components/ConfigViewerModal.jsx +17 -3
- package/ui/src/components/ConfirmationModal.jsx +20 -3
- package/ui/src/components/DetailsTab/BodySection.jsx +3 -1
- package/ui/src/components/DetailsTab/CollapsibleRequestResponse.jsx +14 -3
- package/ui/src/components/DetailsTab/InfoSection.jsx +4 -2
- package/ui/src/components/DetailsTab/RequestDetailsSection.jsx +7 -5
- package/ui/src/components/DetailsTab/ResponseDetailsSection.jsx +7 -5
- package/ui/src/components/DetectedPathsList.jsx +5 -2
- package/ui/src/components/FileInput.jsx +3 -1
- package/ui/src/components/GroupHeader.jsx +14 -0
- package/ui/src/components/GroupedByMcpView.jsx +3 -4
- package/ui/src/components/GroupedByServerView.jsx +1 -1
- package/ui/src/components/GroupedBySessionView.jsx +1 -1
- package/ui/src/components/HexTab.jsx +17 -4
- package/ui/src/components/LogsToolbar.jsx +3 -1
- package/ui/src/components/McpPlayground/LoadingModal.jsx +7 -3
- package/ui/src/components/McpPlayground/PromptsSection/PromptCallPanel.jsx +5 -0
- package/ui/src/components/McpPlayground/PromptsSection/PromptItem.jsx +6 -2
- package/ui/src/components/McpPlayground/PromptsSection/PromptsList.jsx +4 -4
- package/ui/src/components/McpPlayground/PromptsSection.jsx +2 -1
- package/ui/src/components/McpPlayground/ResourcesSection/ResourceCallPanel.jsx +3 -0
- package/ui/src/components/McpPlayground/ResourcesSection/ResourceItem.jsx +6 -2
- package/ui/src/components/McpPlayground/ResourcesSection/ResourcesList.jsx +4 -4
- package/ui/src/components/McpPlayground/ResourcesSection.jsx +2 -1
- package/ui/src/components/McpPlayground/ToolsSection/ToolCallPanel.jsx +5 -0
- package/ui/src/components/McpPlayground/ToolsSection/ToolItem.jsx +6 -2
- package/ui/src/components/McpPlayground/ToolsSection/ToolsList.jsx +4 -4
- package/ui/src/components/McpPlayground/ToolsSection.jsx +2 -1
- package/ui/src/components/McpPlayground/hooks/useMcpDataLoader.js +9 -9
- package/ui/src/components/McpPlayground/hooks/useMcpRequest.js +10 -5
- package/ui/src/components/McpPlayground/hooks/useMcpServerStatus.js +43 -23
- package/ui/src/components/McpPlayground/useMcpPlayground.js +72 -44
- package/ui/src/components/McpPlayground.jsx +5 -2
- package/ui/src/components/PacketDetailHeader.jsx +8 -3
- package/ui/src/components/PacketFilters/ExportControls.jsx +2 -1
- package/ui/src/components/PacketFilters/FilterInput.jsx +1 -1
- package/ui/src/components/RawTab.jsx +15 -2
- package/ui/src/components/RequestRow/OrphanedResponseRow.jsx +10 -2
- package/ui/src/components/RequestRow/RequestRowMain.jsx +12 -3
- package/ui/src/components/RequestRow/ResponseRow.jsx +11 -3
- package/ui/src/components/RequestRow.jsx +17 -9
- package/ui/src/components/ServerControl.jsx +3 -1
- package/ui/src/components/ServiceSelector.jsx +2 -0
- package/ui/src/components/SmartScan/AnalysisResult.jsx +2 -2
- package/ui/src/components/SmartScan/BatchResultsDisplay/BatchResultItem.jsx +2 -1
- package/ui/src/components/SmartScan/BatchResultsDisplay/BatchResultsHeader.jsx +1 -1
- package/ui/src/components/SmartScan/BatchResultsDisplay.jsx +9 -3
- package/ui/src/components/SmartScan/EmptyState.jsx +3 -0
- package/ui/src/components/SmartScan/ErrorDisplay.jsx +4 -2
- package/ui/src/components/SmartScan/ExpandableSection.jsx +4 -0
- package/ui/src/components/SmartScan/FindingsTable.jsx +46 -42
- package/ui/src/components/SmartScan/ListViewContent.jsx +2 -2
- package/ui/src/components/SmartScan/NotablePatternsSection.jsx +13 -8
- package/ui/src/components/SmartScan/OverallSummarySection.jsx +36 -29
- package/ui/src/components/SmartScan/RecommendationsSection.jsx +10 -8
- package/ui/src/components/SmartScan/ScanDetailHeader.jsx +2 -1
- package/ui/src/components/SmartScan/ScanDetailView.jsx +4 -4
- package/ui/src/components/SmartScan/ScanListView/ScanListHeader.jsx +2 -1
- package/ui/src/components/SmartScan/ScanListView/ScanListItem.jsx +15 -1
- package/ui/src/components/SmartScan/ScanOverviewSection.jsx +3 -1
- package/ui/src/components/SmartScan/ScanResultsDisplay.jsx +1 -1
- package/ui/src/components/SmartScan/ScanViewContent.jsx +2 -2
- package/ui/src/components/SmartScan/ScanningProgress.jsx +4 -2
- package/ui/src/components/SmartScan/ServerInfoSection.jsx +3 -1
- package/ui/src/components/SmartScan/ServerSelectionRow.jsx +4 -2
- package/ui/src/components/SmartScan/SingleResultDisplay.jsx +5 -3
- package/ui/src/components/SmartScan/SmartScanControls.jsx +11 -7
- package/ui/src/components/SmartScan/SmartScanHeader.jsx +1 -1
- package/ui/src/components/SmartScan/ViewModeTabs.jsx +2 -0
- package/ui/src/components/SmartScan/hooks/useCacheManagement.js +1 -2
- package/ui/src/components/SmartScan/hooks/useMcpDiscovery.js +22 -26
- package/ui/src/components/SmartScan/hooks/useScanList.js +10 -9
- package/ui/src/components/SmartScan/hooks/useScanOperations.js +23 -14
- package/ui/src/components/SmartScan/hooks/useServerStatus.js +2 -2
- package/ui/src/components/SmartScan/hooks/useTokenManagement.js +2 -2
- package/ui/src/components/SmartScan/scanDataUtils.js +22 -17
- package/ui/src/components/SmartScan/useSmartScan.js +4 -4
- package/ui/src/components/SmartScan/utils.js +3 -1
- package/ui/src/components/SmartScanIcons.jsx +6 -3
- package/ui/src/components/TabNavigation/DesktopTabs.jsx +8 -3
- package/ui/src/components/TabNavigation/MobileDropdown.jsx +3 -1
- package/ui/src/components/TabNavigation.jsx +8 -3
- package/ui/src/components/TabNavigationIcons.jsx +4 -4
- package/ui/src/components/TourOverlay.jsx +1 -1
- package/ui/src/components/TourTooltip/TourTooltipButtons.jsx +3 -0
- package/ui/src/components/TourTooltip/TourTooltipHeader.jsx +1 -0
- package/ui/src/components/TourTooltip/TourTooltipIcons.jsx +9 -0
- package/ui/src/components/TourTooltip/useTooltipPosition.js +63 -36
- package/ui/src/components/TourTooltip.jsx +11 -3
- package/ui/src/components/ViewModeTabs.jsx +3 -1
- package/ui/src/config/tourSteps.jsx +0 -2
- package/ui/src/hooks/useAnimation.js +15 -12
- package/ui/src/hooks/useConfigManagement.js +8 -8
- package/ui/src/hooks/useServiceExtraction.js +1 -1
- package/ui/src/index.css +3 -8
- package/ui/src/theme.js +3 -3
- package/ui/src/utils/hexUtils.js +11 -5
- package/ui/src/utils/mcpGroupingUtils.js +18 -10
- package/ui/src/utils/requestPairing.js +89 -0
- package/ui/src/utils/requestUtils.js +32 -101
- package/ui/vite.config.js +1 -1
- package/mcp-server/.editorconfig +0 -15
- package/mcp-server/.prettierignore +0 -11
- package/mcp-server/.prettierrc +0 -12
- package/mcp-server/README.md +0 -280
- package/mcp-server/commitlint.config.cjs +0 -42
- package/mcp-server/eslint.config.js +0 -131
- package/mcp-server/package-lock.json +0 -4784
- package/mcp-server/package.json +0 -30
- package/ui/README.md +0 -212
- package/ui/package-lock.json +0 -3574
- package/ui/package.json +0 -12
- package/ui/paths.js +0 -282
- package/ui/server/routes/backups.js +0 -251
- package/ui/server/routes/composite.js +0 -260
|
@@ -1,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,
|
package/ui/server/routes/help.js
CHANGED
|
@@ -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 = (
|
|
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 = (
|
|
28
|
+
router.reset = (_req, res) => {
|
|
29
29
|
const state = {
|
|
30
30
|
dismissed: false,
|
|
31
31
|
tourCompleted: false,
|
package/ui/server/routes/logs.js
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
export function createLogsRoutes(mcpSharkLogs,
|
|
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 = (
|
|
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 = (
|
|
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
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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: '
|
|
121
|
-
message:
|
|
123
|
+
error: 'Unsupported method',
|
|
124
|
+
message: `Method ${method} is not supported`,
|
|
122
125
|
});
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 = (
|
|
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
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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 (
|
|
124
|
+
} catch (_e) {
|
|
116
125
|
// Ignore
|
|
117
126
|
}
|
|
118
|
-
|
|
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
|
-
|
|
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
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
)
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
content = JSON.stringify(serializeBigInt(requests), null, 2);
|
|
221
|
-
contentType
|
|
222
|
-
|
|
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
|
};
|