@mcp-shark/mcp-shark 1.4.2 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/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
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { getDatabaseFile, getWorkingDirectory } from 'mcp-shark-common/configs/index.js';
|
|
5
|
+
import logger from '../utils/logger.js';
|
|
6
|
+
import { getScanResultsDirectory } from '../utils/scan-cache/directory.js';
|
|
7
|
+
|
|
8
|
+
const SMART_SCAN_TOKEN_NAME = 'smart-scan-token.json';
|
|
9
|
+
|
|
10
|
+
function getSmartScanTokenPath() {
|
|
11
|
+
return join(getWorkingDirectory(), SMART_SCAN_TOKEN_NAME);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function getTokenMetadata() {
|
|
15
|
+
try {
|
|
16
|
+
const tokenPath = getSmartScanTokenPath();
|
|
17
|
+
if (existsSync(tokenPath)) {
|
|
18
|
+
const content = readFileSync(tokenPath, 'utf8');
|
|
19
|
+
const data = JSON.parse(content);
|
|
20
|
+
const stats = statSync(tokenPath);
|
|
21
|
+
return {
|
|
22
|
+
token: data.token || null,
|
|
23
|
+
updatedAt: data.updatedAt || stats.mtime.toISOString(),
|
|
24
|
+
path: tokenPath,
|
|
25
|
+
exists: true,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
token: null,
|
|
30
|
+
updatedAt: null,
|
|
31
|
+
path: tokenPath,
|
|
32
|
+
exists: false,
|
|
33
|
+
};
|
|
34
|
+
} catch (error) {
|
|
35
|
+
logger.error({ error: error.message }, 'Error reading Smart Scan token metadata');
|
|
36
|
+
return {
|
|
37
|
+
token: null,
|
|
38
|
+
updatedAt: null,
|
|
39
|
+
path: getSmartScanTokenPath(),
|
|
40
|
+
exists: false,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function getBackupCount() {
|
|
46
|
+
try {
|
|
47
|
+
const homeDir = homedir();
|
|
48
|
+
const backupDirs = [join(homeDir, '.cursor'), join(homeDir, '.codeium', 'windsurf')];
|
|
49
|
+
|
|
50
|
+
const newFormatCount = backupDirs.reduce((count, dir) => {
|
|
51
|
+
if (existsSync(dir)) {
|
|
52
|
+
const files = readdirSync(dir);
|
|
53
|
+
const matchingFiles = files.filter((file) => {
|
|
54
|
+
return /^\.(.+)-mcpshark\.\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}\.json$/.test(file);
|
|
55
|
+
});
|
|
56
|
+
return count + matchingFiles.length;
|
|
57
|
+
}
|
|
58
|
+
return count;
|
|
59
|
+
}, 0);
|
|
60
|
+
|
|
61
|
+
// Also count old .backup format
|
|
62
|
+
const commonPaths = [
|
|
63
|
+
join(homeDir, '.cursor', 'mcp.json'),
|
|
64
|
+
join(homeDir, '.codeium', 'windsurf', 'mcp_config.json'),
|
|
65
|
+
];
|
|
66
|
+
const oldFormatCount = commonPaths.reduce((count, configPath) => {
|
|
67
|
+
if (existsSync(`${configPath}.backup`)) {
|
|
68
|
+
return count + 1;
|
|
69
|
+
}
|
|
70
|
+
return count;
|
|
71
|
+
}, 0);
|
|
72
|
+
|
|
73
|
+
return newFormatCount + oldFormatCount;
|
|
74
|
+
} catch (error) {
|
|
75
|
+
logger.error({ error: error.message }, 'Error counting backups');
|
|
76
|
+
return 0;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function toDisplayPath(absolutePath) {
|
|
81
|
+
const homeDir = homedir();
|
|
82
|
+
return absolutePath.replace(homeDir, '~');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function createSettingsRoutes() {
|
|
86
|
+
const router = {};
|
|
87
|
+
|
|
88
|
+
router.getSettings = (_req, res) => {
|
|
89
|
+
try {
|
|
90
|
+
const homeDir = homedir();
|
|
91
|
+
const workingDir = getWorkingDirectory();
|
|
92
|
+
const databasePath = getDatabaseFile();
|
|
93
|
+
const scanResultsDir = getScanResultsDirectory();
|
|
94
|
+
const tokenPath = getSmartScanTokenPath();
|
|
95
|
+
const tokenMetadata = getTokenMetadata();
|
|
96
|
+
|
|
97
|
+
const cursorConfigPath = join(homeDir, '.cursor', 'mcp.json');
|
|
98
|
+
const windsurfConfigPath = join(homeDir, '.codeium', 'windsurf', 'mcp_config.json');
|
|
99
|
+
|
|
100
|
+
const cursorBackupDir = join(homeDir, '.cursor');
|
|
101
|
+
const windsurfBackupDir = join(homeDir, '.codeium', 'windsurf');
|
|
102
|
+
|
|
103
|
+
const settings = {
|
|
104
|
+
paths: {
|
|
105
|
+
workingDirectory: {
|
|
106
|
+
absolute: workingDir,
|
|
107
|
+
display: toDisplayPath(workingDir),
|
|
108
|
+
exists: existsSync(workingDir),
|
|
109
|
+
},
|
|
110
|
+
database: {
|
|
111
|
+
absolute: databasePath,
|
|
112
|
+
display: toDisplayPath(databasePath),
|
|
113
|
+
exists: existsSync(databasePath),
|
|
114
|
+
},
|
|
115
|
+
smartScanResults: {
|
|
116
|
+
absolute: scanResultsDir,
|
|
117
|
+
display: toDisplayPath(scanResultsDir),
|
|
118
|
+
exists: existsSync(scanResultsDir),
|
|
119
|
+
},
|
|
120
|
+
smartScanToken: {
|
|
121
|
+
absolute: tokenPath,
|
|
122
|
+
display: toDisplayPath(tokenPath),
|
|
123
|
+
exists: tokenMetadata.exists,
|
|
124
|
+
},
|
|
125
|
+
backupDirectories: {
|
|
126
|
+
cursor: {
|
|
127
|
+
absolute: cursorBackupDir,
|
|
128
|
+
display: toDisplayPath(cursorBackupDir),
|
|
129
|
+
exists: existsSync(cursorBackupDir),
|
|
130
|
+
},
|
|
131
|
+
windsurf: {
|
|
132
|
+
absolute: windsurfBackupDir,
|
|
133
|
+
display: toDisplayPath(windsurfBackupDir),
|
|
134
|
+
exists: existsSync(windsurfBackupDir),
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
configFiles: {
|
|
138
|
+
cursor: {
|
|
139
|
+
absolute: cursorConfigPath,
|
|
140
|
+
display: toDisplayPath(cursorConfigPath),
|
|
141
|
+
exists: existsSync(cursorConfigPath),
|
|
142
|
+
},
|
|
143
|
+
windsurf: {
|
|
144
|
+
absolute: windsurfConfigPath,
|
|
145
|
+
display: toDisplayPath(windsurfConfigPath),
|
|
146
|
+
exists: existsSync(windsurfConfigPath),
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
smartScan: {
|
|
151
|
+
token: tokenMetadata.token,
|
|
152
|
+
tokenPath: {
|
|
153
|
+
absolute: tokenMetadata.path,
|
|
154
|
+
display: toDisplayPath(tokenMetadata.path),
|
|
155
|
+
},
|
|
156
|
+
tokenUpdatedAt: tokenMetadata.updatedAt,
|
|
157
|
+
tokenExists: tokenMetadata.exists,
|
|
158
|
+
},
|
|
159
|
+
database: {
|
|
160
|
+
path: {
|
|
161
|
+
absolute: databasePath,
|
|
162
|
+
display: toDisplayPath(databasePath),
|
|
163
|
+
},
|
|
164
|
+
exists: existsSync(databasePath),
|
|
165
|
+
},
|
|
166
|
+
system: {
|
|
167
|
+
platform: process.platform,
|
|
168
|
+
homeDirectory: {
|
|
169
|
+
absolute: homeDir,
|
|
170
|
+
display: '~',
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
backups: {
|
|
174
|
+
directories: [
|
|
175
|
+
{
|
|
176
|
+
absolute: cursorBackupDir,
|
|
177
|
+
display: toDisplayPath(cursorBackupDir),
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
absolute: windsurfBackupDir,
|
|
181
|
+
display: toDisplayPath(windsurfBackupDir),
|
|
182
|
+
},
|
|
183
|
+
],
|
|
184
|
+
count: getBackupCount(),
|
|
185
|
+
},
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
res.json(settings);
|
|
189
|
+
} catch (error) {
|
|
190
|
+
logger.error({ error: error.message }, 'Error getting settings');
|
|
191
|
+
res.status(500).json({
|
|
192
|
+
error: 'Failed to get settings',
|
|
193
|
+
details: error.message,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
return router;
|
|
199
|
+
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { readFileSync, existsSync } from 'node:fs';
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
3
2
|
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
3
|
+
import { getMcpConfigPath } from 'mcp-shark-common/configs/index.js';
|
|
4
4
|
import { convertMcpServersToServers } from '../../utils/config.js';
|
|
5
|
+
import logger from '../../utils/logger.js';
|
|
5
6
|
import { createTransport } from './transport.js';
|
|
6
7
|
|
|
7
8
|
/**
|
|
@@ -51,7 +52,7 @@ async function discoverServer(serverName, serverConfig) {
|
|
|
51
52
|
if (transport.close) {
|
|
52
53
|
await transport.close();
|
|
53
54
|
}
|
|
54
|
-
} catch (
|
|
55
|
+
} catch (_closeError) {
|
|
55
56
|
// Ignore close errors
|
|
56
57
|
}
|
|
57
58
|
throw error;
|
|
@@ -62,7 +63,7 @@ async function discoverServer(serverName, serverConfig) {
|
|
|
62
63
|
* Discover all MCP servers from config
|
|
63
64
|
* GET /api/smartscan/discover
|
|
64
65
|
*/
|
|
65
|
-
export async function discoverServers(
|
|
66
|
+
export async function discoverServers(_req, res) {
|
|
66
67
|
try {
|
|
67
68
|
const configPath = getMcpConfigPath();
|
|
68
69
|
|
|
@@ -90,7 +91,7 @@ export async function discoverServers(req, res) {
|
|
|
90
91
|
try {
|
|
91
92
|
return await discoverServer(serverName, serverConfig);
|
|
92
93
|
} catch (error) {
|
|
93
|
-
|
|
94
|
+
logger.error({ serverName, error: error.message }, 'Error discovering server');
|
|
94
95
|
return {
|
|
95
96
|
name: serverName,
|
|
96
97
|
tools: [],
|
|
@@ -108,7 +109,7 @@ export async function discoverServers(req, res) {
|
|
|
108
109
|
servers: discoveredServers,
|
|
109
110
|
});
|
|
110
111
|
} catch (error) {
|
|
111
|
-
|
|
112
|
+
logger.error({ error: error.message }, 'Error discovering servers');
|
|
112
113
|
return res.status(500).json({
|
|
113
114
|
error: 'Failed to discover servers',
|
|
114
115
|
message: error.message,
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
import logger from '../../../utils/logger.js';
|
|
1
2
|
import { clearAllScanResults } from '../../../utils/scan-cache.js';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Clear all cached scan results
|
|
5
6
|
* POST /api/smartscan/cache/clear
|
|
6
7
|
*/
|
|
7
|
-
export function clearCache(
|
|
8
|
+
export function clearCache(_req, res) {
|
|
8
9
|
try {
|
|
9
10
|
const deletedCount = clearAllScanResults();
|
|
10
11
|
return res.json({
|
|
@@ -13,7 +14,7 @@ export function clearCache(req, res) {
|
|
|
13
14
|
deletedCount,
|
|
14
15
|
});
|
|
15
16
|
} catch (error) {
|
|
16
|
-
|
|
17
|
+
logger.error({ error: error.message }, 'Error clearing cache');
|
|
17
18
|
return res.status(500).json({
|
|
18
19
|
error: 'Failed to clear cache',
|
|
19
20
|
message: error.message,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const API_BASE_URL = 'https://smart.mcpshark.sh';
|
|
2
|
+
import logger from '../../../utils/logger.js';
|
|
2
3
|
import { computeMcpHash, getCachedScanResult, storeScanResult } from '../../../utils/scan-cache.js';
|
|
3
4
|
|
|
4
5
|
/**
|
|
@@ -25,7 +26,7 @@ export async function createBatchScans(req, res) {
|
|
|
25
26
|
const hash = computeMcpHash(serverData);
|
|
26
27
|
const cachedResult = getCachedScanResult(hash);
|
|
27
28
|
if (cachedResult) {
|
|
28
|
-
|
|
29
|
+
logger.info({ serverName: serverData.name }, 'Using cached scan result for server');
|
|
29
30
|
return {
|
|
30
31
|
serverName: serverData.name,
|
|
31
32
|
success: true,
|
|
@@ -91,7 +92,7 @@ export async function createBatchScans(req, res) {
|
|
|
91
92
|
|
|
92
93
|
if (response.ok && data) {
|
|
93
94
|
storeScanResult(serverData.name, hash, data);
|
|
94
|
-
|
|
95
|
+
logger.info({ serverName: serverData.name }, 'Stored scan result in cache for server');
|
|
95
96
|
}
|
|
96
97
|
|
|
97
98
|
return result;
|
|
@@ -114,7 +115,7 @@ export async function createBatchScans(req, res) {
|
|
|
114
115
|
results,
|
|
115
116
|
});
|
|
116
117
|
} catch (error) {
|
|
117
|
-
|
|
118
|
+
logger.error({ error: error.message }, 'Smart Scan batch API error');
|
|
118
119
|
return res.status(500).json({
|
|
119
120
|
error: 'Failed to create batch scans',
|
|
120
121
|
message: error.message,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const API_BASE_URL = 'https://smart.mcpshark.sh';
|
|
2
|
+
import logger from '../../../utils/logger.js';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Proxy POST request to create a scan
|
|
@@ -33,7 +34,7 @@ export async function createScan(req, res) {
|
|
|
33
34
|
const data = await response.json();
|
|
34
35
|
return res.status(response.status).json(data);
|
|
35
36
|
} catch (error) {
|
|
36
|
-
|
|
37
|
+
logger.error({ error: error.message }, 'Smart Scan API error');
|
|
37
38
|
return res.status(500).json({
|
|
38
39
|
error: 'Failed to create scan',
|
|
39
40
|
message: error.message,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import logger from '../../../utils/logger.js';
|
|
1
2
|
import { computeMcpHash, getCachedScanResult } from '../../../utils/scan-cache.js';
|
|
2
3
|
|
|
3
4
|
/**
|
|
@@ -42,7 +43,7 @@ export function getCachedResults(req, res) {
|
|
|
42
43
|
results: cachedResults,
|
|
43
44
|
});
|
|
44
45
|
} catch (error) {
|
|
45
|
-
|
|
46
|
+
logger.error({ error: error.message }, 'Error getting cached results');
|
|
46
47
|
return res.status(500).json({
|
|
47
48
|
error: 'Failed to get cached results',
|
|
48
49
|
message: error.message,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const API_BASE_URL = 'https://smart.mcpshark.sh';
|
|
2
|
+
import logger from '../../../utils/logger.js';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Proxy GET request to get a scan by ID
|
|
@@ -32,7 +33,7 @@ export async function getScan(req, res) {
|
|
|
32
33
|
const data = await response.json();
|
|
33
34
|
return res.status(response.status).json(data);
|
|
34
35
|
} catch (error) {
|
|
35
|
-
|
|
36
|
+
logger.error({ error: error.message }, 'Smart Scan API error');
|
|
36
37
|
return res.status(500).json({
|
|
37
38
|
error: 'Failed to get scan',
|
|
38
39
|
message: error.message,
|
|
@@ -1,21 +1,22 @@
|
|
|
1
|
+
import logger from '../../../utils/logger.js';
|
|
1
2
|
import { getAllCachedScanResults } from '../../../utils/scan-cache.js';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* List all scans from local cache only
|
|
5
6
|
* GET /api/smartscan/scans?cache=true
|
|
6
7
|
*/
|
|
7
|
-
export async function listScans(
|
|
8
|
+
export async function listScans(_req, res) {
|
|
8
9
|
try {
|
|
9
|
-
|
|
10
|
+
logger.info('Loading cached scans from local storage');
|
|
10
11
|
const cachedScans = getAllCachedScanResults();
|
|
11
|
-
|
|
12
|
+
logger.info({ count: cachedScans.length }, 'Returning cached scans');
|
|
12
13
|
return res.json({
|
|
13
14
|
scans: cachedScans,
|
|
14
15
|
cached: true,
|
|
15
16
|
count: cachedScans.length,
|
|
16
17
|
});
|
|
17
18
|
} catch (error) {
|
|
18
|
-
|
|
19
|
+
logger.error({ error: error.message }, 'Error loading cached scans');
|
|
19
20
|
return res.status(500).json({
|
|
20
21
|
error: 'Failed to load cached scans',
|
|
21
22
|
message: error.message,
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import * as clearCacheRoute from './scans/clearCache.js';
|
|
2
|
+
import * as createBatchScansRoute from './scans/createBatchScans.js';
|
|
1
3
|
import * as createScanRoute from './scans/createScan.js';
|
|
2
|
-
import * as getScanRoute from './scans/getScan.js';
|
|
3
4
|
import * as getCachedResultsRoute from './scans/getCachedResults.js';
|
|
4
|
-
import * as
|
|
5
|
+
import * as getScanRoute from './scans/getScan.js';
|
|
5
6
|
import * as listScansRoute from './scans/listScans.js';
|
|
6
|
-
import * as clearCacheRoute from './scans/clearCache.js';
|
|
7
7
|
|
|
8
8
|
export const createScan = createScanRoute.createScan;
|
|
9
9
|
export const getScan = getScanRoute.getScan;
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
import logger from '../../utils/logger.js';
|
|
1
2
|
import { readSmartScanToken, writeSmartScanToken } from '../../utils/smartscan-token.js';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Get stored Smart Scan token
|
|
5
6
|
* GET /api/smartscan/token
|
|
6
7
|
*/
|
|
7
|
-
export function getToken(
|
|
8
|
+
export function getToken(_req, res) {
|
|
8
9
|
try {
|
|
9
10
|
const token = readSmartScanToken();
|
|
10
11
|
return res.json({
|
|
@@ -12,7 +13,7 @@ export function getToken(req, res) {
|
|
|
12
13
|
token: token || null,
|
|
13
14
|
});
|
|
14
15
|
} catch (error) {
|
|
15
|
-
|
|
16
|
+
logger.error({ error: error.message }, 'Error reading Smart Scan token');
|
|
16
17
|
return res.status(500).json({
|
|
17
18
|
error: 'Failed to read token',
|
|
18
19
|
message: error.message,
|
|
@@ -47,7 +48,7 @@ export function saveToken(req, res) {
|
|
|
47
48
|
message: 'Token saved successfully',
|
|
48
49
|
});
|
|
49
50
|
} catch (error) {
|
|
50
|
-
|
|
51
|
+
logger.error({ error: error.message }, 'Error saving Smart Scan token');
|
|
51
52
|
return res.status(500).json({
|
|
52
53
|
error: 'Failed to save token',
|
|
53
54
|
message: error.message,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
|
|
2
|
-
import { WebSocketClientTransport } from '@modelcontextprotocol/sdk/client/websocket.js';
|
|
3
2
|
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
|
3
|
+
import { WebSocketClientTransport } from '@modelcontextprotocol/sdk/client/websocket.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Create transport for MCP server based on config
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
* Proxies requests to the Smart Scan API to avoid CORS issues
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import * as tokenRoutes from './smartscan/token.js';
|
|
7
6
|
import * as discoverRoutes from './smartscan/discover.js';
|
|
8
7
|
import * as scanRoutes from './smartscan/scans.js';
|
|
8
|
+
import * as tokenRoutes from './smartscan/token.js';
|
|
9
9
|
|
|
10
10
|
export function createSmartScanRoutes() {
|
|
11
11
|
const router = {};
|
|
@@ -1,5 +1,14 @@
|
|
|
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 createStatisticsRoutes(db) {
|
|
5
14
|
const router = {};
|
|
@@ -7,13 +16,7 @@ export function createStatisticsRoutes(db) {
|
|
|
7
16
|
router.getStatistics = (req, res) => {
|
|
8
17
|
try {
|
|
9
18
|
// Sanitize search parameter - convert empty strings to null
|
|
10
|
-
|
|
11
|
-
if (search !== undefined && search !== null) {
|
|
12
|
-
search = String(search).trim();
|
|
13
|
-
search = search.length > 0 ? search : null;
|
|
14
|
-
} else {
|
|
15
|
-
search = null;
|
|
16
|
-
}
|
|
19
|
+
const search = sanitizeSearch(req.query.search);
|
|
17
20
|
|
|
18
21
|
// Build filters object matching the requests route
|
|
19
22
|
const filters = {
|
|
@@ -21,7 +24,7 @@ export function createStatisticsRoutes(db) {
|
|
|
21
24
|
direction: (req.query.direction && String(req.query.direction).trim()) || null,
|
|
22
25
|
method: (req.query.method && String(req.query.method).trim()) || null,
|
|
23
26
|
jsonrpcMethod: (req.query.jsonrpcMethod && String(req.query.jsonrpcMethod).trim()) || null,
|
|
24
|
-
statusCode: req.query.statusCode ? parseInt(req.query.statusCode) : null,
|
|
27
|
+
statusCode: req.query.statusCode ? Number.parseInt(req.query.statusCode) : null,
|
|
25
28
|
jsonrpcId: (req.query.jsonrpcId && String(req.query.jsonrpcId).trim()) || null,
|
|
26
29
|
search: search,
|
|
27
30
|
serverName: (req.query.serverName && String(req.query.serverName).trim()) || null,
|
|
@@ -74,7 +77,7 @@ export function createStatisticsRoutes(db) {
|
|
|
74
77
|
|
|
75
78
|
res.json(serializeBigInt(stats));
|
|
76
79
|
} catch (error) {
|
|
77
|
-
|
|
80
|
+
logger.error({ error: error.message }, 'Error in getStatistics');
|
|
78
81
|
res.status(500).json({ error: 'Failed to get statistics', details: error.message });
|
|
79
82
|
}
|
|
80
83
|
};
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import * as fs from 'node:fs';
|
|
2
|
-
import * as path from 'node:path';
|
|
3
2
|
import { homedir } from 'node:os';
|
|
3
|
+
import * as path from 'node:path';
|
|
4
4
|
import { storeOriginalConfig } from './config.js';
|
|
5
|
+
import logger from './logger.js';
|
|
5
6
|
|
|
6
7
|
function findLatestBackup(filePath) {
|
|
7
8
|
const dir = path.dirname(filePath);
|
|
@@ -51,7 +52,7 @@ function findLatestBackup(filePath) {
|
|
|
51
52
|
backups.sort((a, b) => b.modifiedAt - a.modifiedAt);
|
|
52
53
|
return backups[0].backupPath;
|
|
53
54
|
} catch (error) {
|
|
54
|
-
|
|
55
|
+
logger.error({ error: error.message }, 'Error finding latest backup');
|
|
55
56
|
return null;
|
|
56
57
|
}
|
|
57
58
|
}
|
|
@@ -101,7 +102,7 @@ function shouldCreateBackup(
|
|
|
101
102
|
}
|
|
102
103
|
return true;
|
|
103
104
|
} catch (error) {
|
|
104
|
-
|
|
105
|
+
logger.error({ error: error.message }, 'Error comparing with latest backup');
|
|
105
106
|
// If comparison fails, create backup to be safe
|
|
106
107
|
return true;
|
|
107
108
|
}
|
|
@@ -157,7 +158,7 @@ function computeBackupPath(resolvedFilePath, content, mcpSharkLogs, broadcastLog
|
|
|
157
158
|
|
|
158
159
|
export function updateConfigFile(
|
|
159
160
|
originalConfig,
|
|
160
|
-
|
|
161
|
+
_selectedServiceNames,
|
|
161
162
|
resolvedFilePath,
|
|
162
163
|
content,
|
|
163
164
|
mcpSharkLogs,
|
|
@@ -170,7 +171,7 @@ export function updateConfigFile(
|
|
|
170
171
|
const updatedServers = {};
|
|
171
172
|
// Transform all original servers to HTTP URLs pointing to MCP shark server
|
|
172
173
|
// Each server gets its own endpoint to avoid tool name prefixing issues
|
|
173
|
-
Object.entries(serverObject).forEach(([name,
|
|
174
|
+
Object.entries(serverObject).forEach(([name, _cfg]) => {
|
|
174
175
|
updatedServers[name] = {
|
|
175
176
|
type: 'http',
|
|
176
177
|
url: `http://localhost:9851/mcp/${encodeURIComponent(name)}`,
|
|
@@ -188,7 +189,7 @@ export function updateConfigFile(
|
|
|
188
189
|
|
|
189
190
|
if (resolvedFilePath && fs.existsSync(resolvedFilePath)) {
|
|
190
191
|
fs.writeFileSync(resolvedFilePath, JSON.stringify(updatedConfig, null, 2));
|
|
191
|
-
|
|
192
|
+
logger.info({ path: resolvedFilePath }, 'Updated config file');
|
|
192
193
|
}
|
|
193
194
|
|
|
194
195
|
return { updatedConfig, backupPath: createdBackupPath };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as fs from 'node:fs';
|
|
2
|
-
import
|
|
2
|
+
import logger from './logger.js';
|
|
3
3
|
|
|
4
4
|
const state = { originalConfigData: null };
|
|
5
5
|
|
|
@@ -8,21 +8,21 @@ export function storeOriginalConfig(filePath, originalContent, backupPath) {
|
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
export function restoreOriginalConfig(mcpSharkLogs, broadcastLogUpdate) {
|
|
11
|
-
if (state.originalConfigData
|
|
11
|
+
if (state.originalConfigData?.filePath) {
|
|
12
12
|
try {
|
|
13
13
|
if (fs.existsSync(state.originalConfigData.filePath)) {
|
|
14
14
|
fs.writeFileSync(
|
|
15
15
|
state.originalConfigData.filePath,
|
|
16
16
|
state.originalConfigData.originalContent
|
|
17
17
|
);
|
|
18
|
-
|
|
18
|
+
logger.info({ path: state.originalConfigData.filePath }, 'Restored original config');
|
|
19
19
|
state.originalConfigData = null;
|
|
20
20
|
return true;
|
|
21
21
|
}
|
|
22
22
|
state.originalConfigData = null;
|
|
23
23
|
return false;
|
|
24
24
|
} catch (error) {
|
|
25
|
-
|
|
25
|
+
logger.error({ error: error.message }, 'Failed to restore original config');
|
|
26
26
|
const timestamp = new Date().toISOString();
|
|
27
27
|
const errorLog = {
|
|
28
28
|
timestamp,
|