@mcp-shark/mcp-shark 1.5.4 → 1.5.5
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 +32 -96
- package/bin/mcp-shark.js +1 -1
- package/core/configs/codex.js +68 -0
- package/core/configs/environment.js +51 -0
- package/{lib/common → core}/configs/index.js +16 -1
- package/core/constants/Defaults.js +15 -0
- package/core/constants/HttpStatus.js +14 -0
- package/core/constants/Server.js +20 -0
- package/core/constants/StatusCodes.js +25 -0
- package/core/constants/index.js +7 -0
- package/core/container/DependencyContainer.js +179 -0
- package/core/db/init.js +33 -0
- package/core/index.js +10 -0
- package/{mcp-server/lib/common/error.js → core/libraries/ErrorLibrary.js} +4 -0
- package/core/libraries/LoggerLibrary.js +91 -0
- package/core/libraries/SerializationLibrary.js +32 -0
- package/core/libraries/bootstrap-logger.js +19 -0
- package/core/libraries/errors/ApplicationError.js +97 -0
- package/core/libraries/index.js +17 -0
- package/{mcp-server/lib → core/mcp-server}/auditor/audit.js +77 -53
- package/core/mcp-server/index.js +192 -0
- package/{mcp-server/lib → core/mcp-server}/server/external/all.js +1 -1
- package/core/mcp-server/server/external/config.js +75 -0
- package/{mcp-server/lib → core/mcp-server}/server/external/single/client.js +1 -1
- package/{mcp-server/lib → core/mcp-server}/server/external/single/request.js +1 -1
- package/{mcp-server/lib → core/mcp-server}/server/external/single/run.js +20 -11
- package/{mcp-server/lib → core/mcp-server}/server/external/single/transport.js +1 -1
- package/{mcp-server/lib → core/mcp-server}/server/internal/handlers/error.js +1 -1
- package/core/mcp-server/server/internal/handlers/prompts-get.js +28 -0
- package/core/mcp-server/server/internal/handlers/prompts-list.js +21 -0
- package/core/mcp-server/server/internal/handlers/resources-list.js +21 -0
- package/core/mcp-server/server/internal/handlers/resources-read.js +28 -0
- package/core/mcp-server/server/internal/handlers/tools-call.js +44 -0
- package/core/mcp-server/server/internal/handlers/tools-list.js +23 -0
- package/core/mcp-server/server/internal/run.js +53 -0
- package/{mcp-server/lib → core/mcp-server}/server/internal/server.js +11 -1
- package/core/models/ConversationFilters.js +31 -0
- package/core/models/ExportFormat.js +8 -0
- package/core/models/RequestFilters.js +43 -0
- package/core/models/SessionFilters.js +23 -0
- package/core/models/index.js +8 -0
- package/core/repositories/AuditRepository.js +233 -0
- package/core/repositories/ConversationRepository.js +182 -0
- package/core/repositories/PacketRepository.js +237 -0
- package/core/repositories/SchemaRepository.js +107 -0
- package/core/repositories/SessionRepository.js +59 -0
- package/core/repositories/StatisticsRepository.js +54 -0
- package/core/repositories/index.js +10 -0
- package/core/services/AuditService.js +144 -0
- package/core/services/BackupService.js +222 -0
- package/core/services/ConfigDetectionService.js +89 -0
- package/core/services/ConfigFileService.js +210 -0
- package/core/services/ConfigPatchingService.js +137 -0
- package/core/services/ConfigService.js +250 -0
- package/core/services/ConfigTransformService.js +178 -0
- package/core/services/ConversationService.js +19 -0
- package/core/services/ExportService.js +117 -0
- package/core/services/LogService.js +64 -0
- package/core/services/McpClientService.js +235 -0
- package/core/services/McpDiscoveryService.js +107 -0
- package/core/services/RequestService.js +56 -0
- package/core/services/ScanCacheService.js +242 -0
- package/core/services/ScanService.js +167 -0
- package/core/services/ServerManagementService.js +206 -0
- package/core/services/SessionService.js +34 -0
- package/core/services/SettingsService.js +163 -0
- package/core/services/StatisticsService.js +64 -0
- package/core/services/TokenService.js +94 -0
- package/core/services/index.js +25 -0
- package/core/services/parsers/ConfigParserFactory.js +113 -0
- package/core/services/parsers/JsonConfigParser.js +66 -0
- package/core/services/parsers/LegacyJsonConfigParser.js +71 -0
- package/core/services/parsers/TomlConfigParser.js +87 -0
- package/core/services/parsers/index.js +4 -0
- package/{ui/server → core}/utils/scan-cache/directory.js +1 -1
- package/core/utils/validation.js +77 -0
- package/package.json +14 -11
- package/ui/dist/assets/index-CArYxKxS.js +35 -0
- package/ui/dist/index.html +1 -1
- package/ui/server/controllers/BackupController.js +129 -0
- package/ui/server/controllers/ConfigController.js +92 -0
- package/ui/server/controllers/ConversationController.js +41 -0
- package/ui/server/controllers/LogController.js +44 -0
- package/ui/server/controllers/McpClientController.js +60 -0
- package/ui/server/controllers/McpDiscoveryController.js +44 -0
- package/ui/server/controllers/RequestController.js +129 -0
- package/ui/server/controllers/ScanController.js +122 -0
- package/ui/server/controllers/ServerManagementController.js +134 -0
- package/ui/server/controllers/SessionController.js +57 -0
- package/ui/server/controllers/SettingsController.js +24 -0
- package/ui/server/controllers/StatisticsController.js +54 -0
- package/ui/server/controllers/TokenController.js +58 -0
- package/ui/server/controllers/index.js +17 -0
- package/ui/server/routes/backups/index.js +15 -9
- package/ui/server/routes/composite/index.js +62 -32
- package/ui/server/routes/composite/servers.js +20 -15
- package/ui/server/routes/config.js +13 -172
- package/ui/server/routes/conversations.js +9 -19
- package/ui/server/routes/help.js +4 -3
- package/ui/server/routes/logs.js +14 -26
- package/ui/server/routes/playground.js +11 -174
- package/ui/server/routes/requests.js +12 -232
- package/ui/server/routes/sessions.js +10 -21
- package/ui/server/routes/settings.js +10 -192
- package/ui/server/routes/smartscan.js +26 -15
- package/ui/server/routes/statistics.js +8 -79
- package/ui/server/setup.js +162 -0
- package/ui/server/swagger/paths/backups.js +151 -0
- package/ui/server/swagger/paths/components.js +76 -0
- package/ui/server/swagger/paths/config.js +117 -0
- package/ui/server/swagger/paths/conversations.js +29 -0
- package/ui/server/swagger/paths/help.js +82 -0
- package/ui/server/swagger/paths/logs.js +87 -0
- package/ui/server/swagger/paths/playground.js +49 -0
- package/ui/server/swagger/paths/requests.js +178 -0
- package/ui/server/swagger/paths/serverManagement.js +169 -0
- package/ui/server/swagger/paths/sessions.js +61 -0
- package/ui/server/swagger/paths/settings.js +31 -0
- package/ui/server/swagger/paths/smartScan/discovery.js +97 -0
- package/ui/server/swagger/paths/smartScan/index.js +13 -0
- package/ui/server/swagger/paths/smartScan/scans.js +151 -0
- package/ui/server/swagger/paths/smartScan/token.js +71 -0
- package/ui/server/swagger/paths/statistics.js +40 -0
- package/ui/server/swagger/paths.js +38 -0
- package/ui/server/swagger/swagger.js +37 -0
- package/ui/server/utils/cleanup.js +99 -0
- package/ui/server/utils/config.js +18 -96
- package/ui/server/utils/errorHandler.js +43 -0
- package/ui/server/utils/logger.js +2 -2
- package/ui/server/utils/paths.js +27 -30
- package/ui/server/utils/port.js +21 -21
- package/ui/server/utils/process.js +18 -10
- package/ui/server/utils/processState.js +17 -0
- package/ui/server/utils/signals.js +34 -0
- package/ui/server/websocket/broadcast.js +33 -0
- package/ui/server/websocket/handler.js +52 -0
- package/ui/server.js +51 -230
- package/ui/src/App.jsx +2 -0
- package/ui/src/CompositeSetup.jsx +23 -9
- package/ui/src/PacketFilters.jsx +17 -3
- package/ui/src/components/AlertModal.jsx +116 -0
- package/ui/src/components/App/ApiDocsButton.jsx +57 -0
- package/ui/src/components/App/useAppState.js +43 -1
- package/ui/src/components/BackupList.jsx +27 -3
- package/ui/src/utils/requestPairing.js +35 -36
- package/ui/src/utils/requestUtils.js +1 -0
- package/lib/common/db/init.js +0 -132
- package/lib/common/db/logger.js +0 -349
- package/lib/common/db/query.js +0 -403
- package/lib/common/logger.js +0 -90
- package/mcp-server/index.js +0 -138
- package/mcp-server/lib/server/external/config.js +0 -57
- package/mcp-server/lib/server/internal/handlers/prompts-get.js +0 -20
- package/mcp-server/lib/server/internal/handlers/prompts-list.js +0 -13
- package/mcp-server/lib/server/internal/handlers/resources-list.js +0 -13
- package/mcp-server/lib/server/internal/handlers/resources-read.js +0 -20
- package/mcp-server/lib/server/internal/handlers/tools-call.js +0 -35
- package/mcp-server/lib/server/internal/handlers/tools-list.js +0 -15
- package/mcp-server/lib/server/internal/run.js +0 -37
- package/mcp-server/mcp-shark.js +0 -22
- package/ui/dist/assets/index-CFHeMNwd.js +0 -35
- package/ui/server/routes/backups/deleteBackup.js +0 -54
- package/ui/server/routes/backups/listBackups.js +0 -75
- package/ui/server/routes/backups/restoreBackup.js +0 -83
- package/ui/server/routes/backups/viewBackup.js +0 -47
- package/ui/server/routes/composite/setup.js +0 -129
- package/ui/server/routes/composite/status.js +0 -7
- package/ui/server/routes/composite/stop.js +0 -39
- package/ui/server/routes/composite/utils.js +0 -45
- package/ui/server/routes/smartscan/discover.js +0 -118
- package/ui/server/routes/smartscan/scans/clearCache.js +0 -23
- package/ui/server/routes/smartscan/scans/createBatchScans.js +0 -124
- package/ui/server/routes/smartscan/scans/createScan.js +0 -43
- package/ui/server/routes/smartscan/scans/getCachedResults.js +0 -52
- package/ui/server/routes/smartscan/scans/getScan.js +0 -42
- package/ui/server/routes/smartscan/scans/listScans.js +0 -25
- package/ui/server/routes/smartscan/scans.js +0 -13
- package/ui/server/routes/smartscan/token.js +0 -57
- package/ui/server/utils/config-update.js +0 -240
- package/ui/server/utils/scan-cache/all-results.js +0 -197
- package/ui/server/utils/scan-cache/file-operations.js +0 -107
- package/ui/server/utils/scan-cache/hash.js +0 -47
- package/ui/server/utils/scan-cache/server-operations.js +0 -85
- package/ui/server/utils/scan-cache.js +0 -12
- package/ui/server/utils/smartscan-token.js +0 -43
- /package/{mcp-server/lib → core/mcp-server}/server/external/kv.js +0 -0
- /package/{mcp-server/lib → core/mcp-server}/server/internal/handlers/common.js +0 -0
- /package/{mcp-server/lib → core/mcp-server}/server/internal/session.js +0 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { colors, fonts } from '../theme';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Alert modal for displaying error or info messages
|
|
5
|
+
* Replaces browser alert() function with a proper modal
|
|
6
|
+
*/
|
|
7
|
+
function AlertModal({ isOpen, onClose, title, message, type = 'error' }) {
|
|
8
|
+
if (!isOpen) {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const isError = type === 'error';
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<dialog
|
|
16
|
+
open
|
|
17
|
+
aria-modal="true"
|
|
18
|
+
style={{
|
|
19
|
+
position: 'fixed',
|
|
20
|
+
top: 0,
|
|
21
|
+
left: 0,
|
|
22
|
+
right: 0,
|
|
23
|
+
bottom: 0,
|
|
24
|
+
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
|
25
|
+
display: 'flex',
|
|
26
|
+
alignItems: 'center',
|
|
27
|
+
justifyContent: 'center',
|
|
28
|
+
zIndex: 1000,
|
|
29
|
+
border: 'none',
|
|
30
|
+
margin: 0,
|
|
31
|
+
width: '100%',
|
|
32
|
+
height: '100%',
|
|
33
|
+
}}
|
|
34
|
+
onClick={onClose}
|
|
35
|
+
onKeyDown={(e) => {
|
|
36
|
+
if (e.key === 'Escape' || e.key === 'Enter') {
|
|
37
|
+
onClose();
|
|
38
|
+
}
|
|
39
|
+
}}
|
|
40
|
+
>
|
|
41
|
+
<div
|
|
42
|
+
role="document"
|
|
43
|
+
style={{
|
|
44
|
+
background: colors.bgCard,
|
|
45
|
+
borderRadius: '12px',
|
|
46
|
+
padding: '24px',
|
|
47
|
+
maxWidth: '500px',
|
|
48
|
+
width: '90%',
|
|
49
|
+
boxShadow: `0 4px 20px ${colors.shadowLg}`,
|
|
50
|
+
fontFamily: fonts.body,
|
|
51
|
+
}}
|
|
52
|
+
onClick={(e) => e.stopPropagation()}
|
|
53
|
+
onKeyDown={(e) => e.stopPropagation()}
|
|
54
|
+
>
|
|
55
|
+
<h3
|
|
56
|
+
style={{
|
|
57
|
+
margin: '0 0 12px 0',
|
|
58
|
+
fontSize: '18px',
|
|
59
|
+
fontWeight: '600',
|
|
60
|
+
color: isError ? colors.error : colors.textPrimary,
|
|
61
|
+
}}
|
|
62
|
+
>
|
|
63
|
+
{title || (isError ? 'Error' : 'Information')}
|
|
64
|
+
</h3>
|
|
65
|
+
<p
|
|
66
|
+
style={{
|
|
67
|
+
margin: '0 0 24px 0',
|
|
68
|
+
fontSize: '14px',
|
|
69
|
+
color: colors.textSecondary,
|
|
70
|
+
lineHeight: '1.5',
|
|
71
|
+
}}
|
|
72
|
+
>
|
|
73
|
+
{message}
|
|
74
|
+
</p>
|
|
75
|
+
<div
|
|
76
|
+
style={{
|
|
77
|
+
display: 'flex',
|
|
78
|
+
gap: '12px',
|
|
79
|
+
justifyContent: 'flex-end',
|
|
80
|
+
}}
|
|
81
|
+
>
|
|
82
|
+
<button
|
|
83
|
+
type="button"
|
|
84
|
+
onClick={onClose}
|
|
85
|
+
style={{
|
|
86
|
+
padding: '10px 20px',
|
|
87
|
+
background: isError ? colors.buttonDanger : colors.buttonPrimary,
|
|
88
|
+
border: 'none',
|
|
89
|
+
borderRadius: '8px',
|
|
90
|
+
color: colors.textInverse,
|
|
91
|
+
fontSize: '14px',
|
|
92
|
+
fontWeight: '500',
|
|
93
|
+
fontFamily: fonts.body,
|
|
94
|
+
cursor: 'pointer',
|
|
95
|
+
transition: 'all 0.2s',
|
|
96
|
+
}}
|
|
97
|
+
onMouseEnter={(e) => {
|
|
98
|
+
e.currentTarget.style.background = isError
|
|
99
|
+
? colors.buttonDangerHover
|
|
100
|
+
: colors.buttonPrimaryHover;
|
|
101
|
+
}}
|
|
102
|
+
onMouseLeave={(e) => {
|
|
103
|
+
e.currentTarget.style.background = isError
|
|
104
|
+
? colors.buttonDanger
|
|
105
|
+
: colors.buttonPrimary;
|
|
106
|
+
}}
|
|
107
|
+
>
|
|
108
|
+
OK
|
|
109
|
+
</button>
|
|
110
|
+
</div>
|
|
111
|
+
</div>
|
|
112
|
+
</dialog>
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export default AlertModal;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { IconApi } from '@tabler/icons-react';
|
|
2
|
+
import { colors, fonts } from '../../theme';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* API Documentation button component
|
|
6
|
+
* Opens Swagger/OpenAPI documentation in a new tab
|
|
7
|
+
*/
|
|
8
|
+
export default function ApiDocsButton() {
|
|
9
|
+
const handleClick = () => {
|
|
10
|
+
window.open('/api-docs', '_blank');
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<button
|
|
15
|
+
type="button"
|
|
16
|
+
onClick={handleClick}
|
|
17
|
+
data-tour="api-docs-button"
|
|
18
|
+
style={{
|
|
19
|
+
position: 'fixed',
|
|
20
|
+
bottom: '20px',
|
|
21
|
+
right: '80px',
|
|
22
|
+
background: colors.bgCard,
|
|
23
|
+
border: `1px solid ${colors.borderLight}`,
|
|
24
|
+
borderRadius: '50%',
|
|
25
|
+
width: '48px',
|
|
26
|
+
height: '48px',
|
|
27
|
+
padding: 0,
|
|
28
|
+
color: colors.textSecondary,
|
|
29
|
+
cursor: 'pointer',
|
|
30
|
+
fontFamily: fonts.body,
|
|
31
|
+
boxShadow: `0 4px 12px ${colors.shadowMd}`,
|
|
32
|
+
display: 'flex',
|
|
33
|
+
alignItems: 'center',
|
|
34
|
+
justifyContent: 'center',
|
|
35
|
+
zIndex: 9999,
|
|
36
|
+
transition: 'all 0.2s ease',
|
|
37
|
+
}}
|
|
38
|
+
onMouseEnter={(e) => {
|
|
39
|
+
e.currentTarget.style.background = colors.bgHover;
|
|
40
|
+
e.currentTarget.style.color = colors.textPrimary;
|
|
41
|
+
e.currentTarget.style.borderColor = colors.borderMedium;
|
|
42
|
+
e.currentTarget.style.transform = 'scale(1.1)';
|
|
43
|
+
e.currentTarget.style.boxShadow = `0 6px 16px ${colors.shadowLg}`;
|
|
44
|
+
}}
|
|
45
|
+
onMouseLeave={(e) => {
|
|
46
|
+
e.currentTarget.style.background = colors.bgCard;
|
|
47
|
+
e.currentTarget.style.color = colors.textSecondary;
|
|
48
|
+
e.currentTarget.style.borderColor = colors.borderLight;
|
|
49
|
+
e.currentTarget.style.transform = 'scale(1)';
|
|
50
|
+
e.currentTarget.style.boxShadow = `0 4px 12px ${colors.shadowMd}`;
|
|
51
|
+
}}
|
|
52
|
+
title="View API Documentation"
|
|
53
|
+
>
|
|
54
|
+
<IconApi size={20} stroke={1.5} />
|
|
55
|
+
</button>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
@@ -24,8 +24,24 @@ function appendFilterParams(queryParams, filters) {
|
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
const VALID_TABS = ['traffic', 'logs', 'setup', 'playground', 'smart-scan'];
|
|
28
|
+
const DEFAULT_TAB = 'traffic';
|
|
29
|
+
|
|
30
|
+
function getTabFromHash() {
|
|
31
|
+
const hash = window.location.hash.slice(1); // Remove '#'
|
|
32
|
+
const tab = hash.startsWith('/') ? hash.slice(1) : hash; // Remove leading '/'
|
|
33
|
+
return VALID_TABS.includes(tab) ? tab : DEFAULT_TAB;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function updateUrlHash(tab) {
|
|
37
|
+
const newHash = `#/${tab}`;
|
|
38
|
+
if (window.location.hash !== newHash) {
|
|
39
|
+
window.history.replaceState(null, '', newHash);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
27
43
|
export function useAppState() {
|
|
28
|
-
const [activeTab, setActiveTab] = useState(
|
|
44
|
+
const [activeTab, setActiveTab] = useState(() => getTabFromHash());
|
|
29
45
|
const [requests, setRequests] = useState([]);
|
|
30
46
|
const [selected, setSelected] = useState(null);
|
|
31
47
|
const [filters, setFilters] = useState({});
|
|
@@ -37,6 +53,32 @@ export function useAppState() {
|
|
|
37
53
|
const prevTabRef = useRef(activeTab);
|
|
38
54
|
const filtersRef = useRef(filters);
|
|
39
55
|
|
|
56
|
+
// Initialize URL hash on mount if missing
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
if (!window.location.hash || window.location.hash === '#') {
|
|
59
|
+
const initialTab = getTabFromHash();
|
|
60
|
+
updateUrlHash(initialTab);
|
|
61
|
+
}
|
|
62
|
+
}, []);
|
|
63
|
+
|
|
64
|
+
// Sync URL hash with activeTab
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
updateUrlHash(activeTab);
|
|
67
|
+
}, [activeTab]);
|
|
68
|
+
|
|
69
|
+
// Listen for hash changes (back/forward buttons)
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
const handleHashChange = () => {
|
|
72
|
+
const tabFromHash = getTabFromHash();
|
|
73
|
+
if (tabFromHash !== activeTab) {
|
|
74
|
+
setActiveTab(tabFromHash);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
window.addEventListener('hashchange', handleHashChange);
|
|
79
|
+
return () => window.removeEventListener('hashchange', handleHashChange);
|
|
80
|
+
}, [activeTab]);
|
|
81
|
+
|
|
40
82
|
const loadStatistics = async () => {
|
|
41
83
|
try {
|
|
42
84
|
const queryParams = new URLSearchParams();
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { IconEye, IconRefresh, IconRestore, IconTrash } from '@tabler/icons-react';
|
|
2
|
+
import { useState } from 'react';
|
|
2
3
|
import { colors, fonts, withOpacity } from '../theme';
|
|
4
|
+
import ConfirmationModal from './ConfirmationModal';
|
|
3
5
|
|
|
4
6
|
function BackupList({ backups, loadingBackups, onRefresh, onRestore, onView, onDelete }) {
|
|
7
|
+
const [showDeleteModal, setShowDeleteModal] = useState(false);
|
|
8
|
+
const [pendingDelete, setPendingDelete] = useState(null);
|
|
9
|
+
|
|
5
10
|
if (backups.length === 0) {
|
|
6
11
|
return null;
|
|
7
12
|
}
|
|
@@ -126,9 +131,8 @@ function BackupList({ backups, loadingBackups, onRefresh, onRestore, onView, onD
|
|
|
126
131
|
<button
|
|
127
132
|
type="button"
|
|
128
133
|
onClick={() => {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
}
|
|
134
|
+
setPendingDelete(backup.backupPath);
|
|
135
|
+
setShowDeleteModal(true);
|
|
132
136
|
}}
|
|
133
137
|
style={{
|
|
134
138
|
padding: '6px 12px',
|
|
@@ -189,6 +193,26 @@ function BackupList({ backups, loadingBackups, onRefresh, onRestore, onView, onD
|
|
|
189
193
|
</div>
|
|
190
194
|
))}
|
|
191
195
|
</div>
|
|
196
|
+
|
|
197
|
+
<ConfirmationModal
|
|
198
|
+
isOpen={showDeleteModal}
|
|
199
|
+
onClose={() => {
|
|
200
|
+
setShowDeleteModal(false);
|
|
201
|
+
setPendingDelete(null);
|
|
202
|
+
}}
|
|
203
|
+
onConfirm={() => {
|
|
204
|
+
if (pendingDelete) {
|
|
205
|
+
onDelete(pendingDelete);
|
|
206
|
+
}
|
|
207
|
+
setShowDeleteModal(false);
|
|
208
|
+
setPendingDelete(null);
|
|
209
|
+
}}
|
|
210
|
+
title="Delete Backup"
|
|
211
|
+
message="Are you sure you want to delete this backup? This action cannot be undone."
|
|
212
|
+
confirmText="Delete"
|
|
213
|
+
cancelText="Cancel"
|
|
214
|
+
danger={true}
|
|
215
|
+
/>
|
|
192
216
|
</div>
|
|
193
217
|
);
|
|
194
218
|
}
|
|
@@ -1,45 +1,44 @@
|
|
|
1
1
|
import { getJsonRpcMethod } from './requestUtils.js';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
// Session ID must match (or both null for initiation)
|
|
10
|
-
const sessionMatch = req.session_id === resp.session_id;
|
|
11
|
-
if (!sessionMatch) {
|
|
12
|
-
return false;
|
|
13
|
-
}
|
|
3
|
+
function matchesRequests(req, resp) {
|
|
4
|
+
// Session ID must match (or both null for initiation)
|
|
5
|
+
const sessionMatch = req.session_id === resp.session_id;
|
|
6
|
+
if (!sessionMatch) {
|
|
7
|
+
return false;
|
|
8
|
+
}
|
|
14
9
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
10
|
+
// JSON-RPC Method must match
|
|
11
|
+
const reqMethod = getJsonRpcMethod(req);
|
|
12
|
+
const respMethod = getJsonRpcMethod(resp);
|
|
18
13
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
if (req.jsonrpc_id && resp.jsonrpc_id) {
|
|
24
|
-
return req.jsonrpc_id === resp.jsonrpc_id;
|
|
25
|
-
}
|
|
26
|
-
// If no method and no ID, we can't match reliably
|
|
27
|
-
return false;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const methodMatch = reqMethod === respMethod;
|
|
31
|
-
if (!methodMatch) {
|
|
32
|
-
return false;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// If JSON-RPC ID exists, it must match (for more precise pairing)
|
|
14
|
+
// Both must have a method, and they must match
|
|
15
|
+
if (!reqMethod || !respMethod) {
|
|
16
|
+
// If either doesn't have a method, we can't match by method
|
|
17
|
+
// Fall back to JSON-RPC ID matching only
|
|
36
18
|
if (req.jsonrpc_id && resp.jsonrpc_id) {
|
|
37
19
|
return req.jsonrpc_id === resp.jsonrpc_id;
|
|
38
20
|
}
|
|
21
|
+
// If no method and no ID, we can't match reliably
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const methodMatch = reqMethod === respMethod;
|
|
26
|
+
if (!methodMatch) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// If JSON-RPC ID exists, it must match (for more precise pairing)
|
|
31
|
+
if (req.jsonrpc_id && resp.jsonrpc_id) {
|
|
32
|
+
return req.jsonrpc_id === resp.jsonrpc_id;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// If no JSON-RPC ID, match by session and method only
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
39
38
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
export function pairRequestsWithResponses(requests) {
|
|
40
|
+
const pairs = [];
|
|
41
|
+
const processed = new Set();
|
|
43
42
|
|
|
44
43
|
requests.forEach((request) => {
|
|
45
44
|
if (processed.has(request.frame_number)) {
|
|
@@ -52,7 +51,7 @@ export function pairRequestsWithResponses(requests) {
|
|
|
52
51
|
(r) =>
|
|
53
52
|
r.direction === 'response' &&
|
|
54
53
|
!processed.has(r.frame_number) &&
|
|
55
|
-
|
|
54
|
+
matchesRequests(request, r) &&
|
|
56
55
|
r.frame_number > request.frame_number
|
|
57
56
|
);
|
|
58
57
|
|
|
@@ -71,7 +70,7 @@ export function pairRequestsWithResponses(requests) {
|
|
|
71
70
|
(r) =>
|
|
72
71
|
r.direction === 'request' &&
|
|
73
72
|
!processed.has(r.frame_number) &&
|
|
74
|
-
|
|
73
|
+
matchesRequests(r, request) &&
|
|
75
74
|
r.frame_number < request.frame_number
|
|
76
75
|
);
|
|
77
76
|
|
package/lib/common/db/init.js
DELETED
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
import * as fs from 'node:fs';
|
|
2
|
-
import * as path from 'node:path';
|
|
3
|
-
import Database from 'better-sqlite3';
|
|
4
|
-
|
|
5
|
-
function createTables(db) {
|
|
6
|
-
db.exec(`
|
|
7
|
-
-- Packet capture table
|
|
8
|
-
-- Each HTTP request/response is stored as a packet for forensic analysis
|
|
9
|
-
CREATE TABLE IF NOT EXISTS packets (
|
|
10
|
-
frame_number INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
11
|
-
|
|
12
|
-
-- Timestamps (nanosecond precision)
|
|
13
|
-
timestamp_ns INTEGER NOT NULL, -- Unix timestamp in nanoseconds
|
|
14
|
-
timestamp_iso TEXT NOT NULL, -- ISO 8601 formatted timestamp for readability
|
|
15
|
-
|
|
16
|
-
-- Packet direction and protocol
|
|
17
|
-
direction TEXT NOT NULL CHECK(direction IN ('request', 'response')),
|
|
18
|
-
protocol TEXT NOT NULL DEFAULT 'HTTP',
|
|
19
|
-
|
|
20
|
-
-- Session identification (normalized from various header formats)
|
|
21
|
-
session_id TEXT, -- Normalized session ID (from mcp-session-id, Mcp-Session-Id, or X-MCP-Session-Id)
|
|
22
|
-
|
|
23
|
-
-- HTTP metadata
|
|
24
|
-
method TEXT, -- HTTP method (GET, POST, etc.)
|
|
25
|
-
url TEXT, -- Request URL/path
|
|
26
|
-
status_code INTEGER, -- HTTP status code (for responses)
|
|
27
|
-
|
|
28
|
-
-- Headers and body
|
|
29
|
-
headers_json TEXT NOT NULL, -- Full HTTP headers as JSON
|
|
30
|
-
body_raw TEXT, -- Raw body content
|
|
31
|
-
body_json TEXT, -- Parsed JSON body (if applicable)
|
|
32
|
-
|
|
33
|
-
-- JSON-RPC metadata (for correlation)
|
|
34
|
-
jsonrpc_id TEXT, -- JSON-RPC request ID
|
|
35
|
-
jsonrpc_method TEXT, -- JSON-RPC method (e.g., 'tools/list', 'tools/call')
|
|
36
|
-
jsonrpc_result TEXT, -- JSON-RPC result (for responses, as JSON string)
|
|
37
|
-
jsonrpc_error TEXT, -- JSON-RPC error (for error responses, as JSON string)
|
|
38
|
-
|
|
39
|
-
-- Packet metadata
|
|
40
|
-
length INTEGER NOT NULL, -- Total packet size in bytes
|
|
41
|
-
info TEXT, -- Summary info for quick viewing
|
|
42
|
-
|
|
43
|
-
-- Network metadata
|
|
44
|
-
user_agent TEXT, -- User agent string
|
|
45
|
-
remote_address TEXT, -- Remote IP address
|
|
46
|
-
host TEXT -- Host header value
|
|
47
|
-
);
|
|
48
|
-
|
|
49
|
-
-- Conversations table - correlates request/response pairs
|
|
50
|
-
CREATE TABLE IF NOT EXISTS conversations (
|
|
51
|
-
conversation_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
52
|
-
request_frame_number INTEGER NOT NULL,
|
|
53
|
-
response_frame_number INTEGER,
|
|
54
|
-
session_id TEXT,
|
|
55
|
-
jsonrpc_id TEXT,
|
|
56
|
-
method TEXT,
|
|
57
|
-
request_timestamp_ns INTEGER NOT NULL,
|
|
58
|
-
response_timestamp_ns INTEGER,
|
|
59
|
-
duration_ms REAL, -- Round-trip time in milliseconds
|
|
60
|
-
status TEXT DEFAULT 'pending' CHECK(status IN ('pending', 'completed', 'timeout', 'error')),
|
|
61
|
-
|
|
62
|
-
FOREIGN KEY (request_frame_number) REFERENCES packets(frame_number),
|
|
63
|
-
FOREIGN KEY (response_frame_number) REFERENCES packets(frame_number)
|
|
64
|
-
);
|
|
65
|
-
|
|
66
|
-
-- Sessions table - tracks session metadata
|
|
67
|
-
CREATE TABLE IF NOT EXISTS sessions (
|
|
68
|
-
session_id TEXT PRIMARY KEY,
|
|
69
|
-
first_seen_ns INTEGER NOT NULL,
|
|
70
|
-
last_seen_ns INTEGER NOT NULL,
|
|
71
|
-
packet_count INTEGER DEFAULT 0,
|
|
72
|
-
user_agent TEXT,
|
|
73
|
-
remote_address TEXT,
|
|
74
|
-
host TEXT
|
|
75
|
-
);
|
|
76
|
-
|
|
77
|
-
-- Create indexes for forensic analysis
|
|
78
|
-
CREATE INDEX IF NOT EXISTS idx_packets_timestamp ON packets(timestamp_ns);
|
|
79
|
-
CREATE INDEX IF NOT EXISTS idx_packets_session ON packets(session_id);
|
|
80
|
-
CREATE INDEX IF NOT EXISTS idx_packets_direction ON packets(direction);
|
|
81
|
-
CREATE INDEX IF NOT EXISTS idx_packets_jsonrpc_id ON packets(jsonrpc_id);
|
|
82
|
-
CREATE INDEX IF NOT EXISTS idx_packets_jsonrpc_method ON packets(jsonrpc_method);
|
|
83
|
-
CREATE INDEX IF NOT EXISTS idx_packets_method ON packets(method);
|
|
84
|
-
CREATE INDEX IF NOT EXISTS idx_packets_status_code ON packets(status_code);
|
|
85
|
-
CREATE INDEX IF NOT EXISTS idx_packets_session_timestamp ON packets(session_id, timestamp_ns);
|
|
86
|
-
|
|
87
|
-
CREATE INDEX IF NOT EXISTS idx_conversations_session ON conversations(session_id);
|
|
88
|
-
CREATE INDEX IF NOT EXISTS idx_conversations_jsonrpc_id ON conversations(jsonrpc_id);
|
|
89
|
-
CREATE INDEX IF NOT EXISTS idx_conversations_request_frame ON conversations(request_frame_number);
|
|
90
|
-
CREATE INDEX IF NOT EXISTS idx_conversations_response_frame ON conversations(response_frame_number);
|
|
91
|
-
CREATE INDEX IF NOT EXISTS idx_conversations_timestamp ON conversations(request_timestamp_ns);
|
|
92
|
-
|
|
93
|
-
CREATE INDEX IF NOT EXISTS idx_sessions_first_seen ON sessions(first_seen_ns);
|
|
94
|
-
CREATE INDEX IF NOT EXISTS idx_sessions_last_seen ON sessions(last_seen_ns);
|
|
95
|
-
`);
|
|
96
|
-
|
|
97
|
-
return db;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
export function initDb(dbConnectionString) {
|
|
101
|
-
const db = new Database(dbConnectionString);
|
|
102
|
-
db.pragma('journal_mode = WAL');
|
|
103
|
-
db.pragma('foreign_keys = ON');
|
|
104
|
-
|
|
105
|
-
return createTables(db);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Open or create a database file, ensuring the directory exists
|
|
110
|
-
* Creates tables if the database is new or ensures they exist
|
|
111
|
-
*/
|
|
112
|
-
export function openDb(dbPath) {
|
|
113
|
-
// Ensure the directory exists
|
|
114
|
-
const dbDir = path.dirname(dbPath);
|
|
115
|
-
if (!fs.existsSync(dbDir)) {
|
|
116
|
-
fs.mkdirSync(dbDir, { recursive: true });
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// Check if database file exists
|
|
120
|
-
const _dbExists = fs.existsSync(dbPath);
|
|
121
|
-
|
|
122
|
-
// Open or create the database
|
|
123
|
-
const db = new Database(dbPath);
|
|
124
|
-
db.pragma('journal_mode = WAL');
|
|
125
|
-
db.pragma('foreign_keys = ON');
|
|
126
|
-
|
|
127
|
-
// Create tables if database is new or tables don't exist
|
|
128
|
-
// Even if database exists, ensure tables exist (in case schema changed)
|
|
129
|
-
createTables(db);
|
|
130
|
-
|
|
131
|
-
return db;
|
|
132
|
-
}
|