@mcp-shark/mcp-shark 1.5.13 → 1.7.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 +482 -56
- package/bin/mcp-shark.js +146 -52
- package/core/cli/AutoFixEngine.js +93 -0
- package/core/cli/ConfigScanner.js +193 -0
- package/core/cli/DataLoader.js +200 -0
- package/core/cli/DeclarativeRuleEngine.js +363 -0
- package/core/cli/DoctorCommand.js +218 -0
- package/core/cli/FixHandlers.js +222 -0
- package/core/cli/HtmlReportGenerator.js +203 -0
- package/core/cli/IdeConfigPaths.js +175 -0
- package/core/cli/ListCommand.js +255 -0
- package/core/cli/LockCommand.js +164 -0
- package/core/cli/LockDiffEngine.js +152 -0
- package/core/cli/RuleRegistryConfig.js +131 -0
- package/core/cli/ScanCommand.js +244 -0
- package/core/cli/ScanService.js +200 -0
- package/core/cli/SecretDetector.js +92 -0
- package/core/cli/SharkScoreCalculator.js +109 -0
- package/core/cli/ToolClassifications.js +51 -0
- package/core/cli/ToxicFlowAnalyzer.js +212 -0
- package/core/cli/UpdateCommand.js +188 -0
- package/core/cli/WalkthroughGenerator.js +195 -0
- package/core/cli/WatchCommand.js +129 -0
- package/core/cli/YamlRuleEngine.js +197 -0
- package/core/cli/data/rule-packs/aauth-visibility.json +117 -0
- package/core/cli/data/rule-packs/agentic-security-2026.json +180 -0
- package/core/cli/data/rule-packs/general-security.json +173 -0
- package/core/cli/data/rule-packs/owasp-mcp-2026.json +244 -0
- package/core/cli/data/rule-packs/toxic-flow-heuristics.json +21 -0
- package/core/cli/data/rule-sources.json +5 -0
- package/core/cli/data/secret-patterns.json +18 -0
- package/core/cli/data/tool-classifications.json +111 -0
- package/core/cli/data/toxic-flow-rules.json +47 -0
- package/core/cli/index.js +23 -0
- package/core/cli/output/Banner.js +52 -0
- package/core/cli/output/Formatter.js +183 -0
- package/core/cli/output/JsonFormatter.js +106 -0
- package/core/cli/output/index.js +16 -0
- package/core/cli/secureRegistryFetch.js +157 -0
- package/core/cli/symbols.js +16 -0
- package/core/configs/environment.js +3 -1
- package/core/configs/index.js +3 -64
- package/core/container/DependencyContainer.js +4 -1
- package/core/mcp-server/index.js +4 -1
- package/core/mcp-server/server/external/all.js +10 -3
- package/core/mcp-server/server/external/config.js +62 -5
- package/core/models/RequestFilters.js +3 -0
- package/core/repositories/PacketRepository.js +16 -0
- package/core/services/AuditService.js +2 -0
- package/core/services/ConfigService.js +9 -1
- package/core/services/ConfigTransformService.js +34 -2
- package/core/services/RequestService.js +58 -5
- package/core/services/ServerManagementService.js +59 -4
- package/core/services/security/StaticRulesService.js +69 -13
- package/core/services/security/TrafficAnalysisService.js +19 -1
- package/core/services/security/TrafficToxicFlowService.js +154 -0
- package/core/services/security/aauthGraph.js +199 -0
- package/core/services/security/aauthParser.js +274 -0
- package/core/services/security/aauthSelfTest.js +346 -0
- package/core/services/security/index.js +2 -1
- package/core/services/security/rules/index.js +25 -59
- package/core/services/security/rules/scans/configPermissions.js +91 -0
- package/core/services/security/rules/scans/duplicateToolNames.js +85 -0
- package/core/services/security/rules/scans/insecureTransport.js +148 -0
- package/core/services/security/rules/scans/missingContainment.js +123 -0
- package/core/services/security/rules/scans/shellEnvInjection.js +101 -0
- package/core/services/security/rules/scans/unsafeDefaults.js +99 -0
- package/core/services/security/toolsListFromTrafficParser.js +70 -0
- package/core/tui/App.js +144 -0
- package/core/tui/FindingsPanel.js +115 -0
- package/core/tui/FixPanel.js +132 -0
- package/core/tui/Header.js +51 -0
- package/core/tui/HelpBar.js +42 -0
- package/core/tui/ServersPanel.js +109 -0
- package/core/tui/ToxicFlowsPanel.js +100 -0
- package/core/tui/h.js +8 -0
- package/core/tui/index.js +11 -0
- package/core/tui/render.js +22 -0
- package/package.json +24 -16
- package/ui/dist/assets/index-D6zDrtMV.js +81 -0
- package/ui/dist/index.html +1 -1
- package/ui/server/controllers/AauthController.js +279 -0
- package/ui/server/controllers/RequestController.js +12 -1
- package/ui/server/controllers/SecurityFindingsController.js +46 -1
- package/ui/server/routes/aauth.js +18 -0
- package/ui/server/routes/requests.js +8 -1
- package/ui/server/routes/security.js +5 -1
- package/ui/server/setup.js +224 -6
- package/ui/server/swagger/paths/components.js +55 -0
- package/ui/server/swagger/paths/securityTrafficFlows.js +59 -0
- package/ui/server/swagger/paths.js +2 -2
- package/ui/server/swagger/swagger.js +5 -2
- package/ui/server.js +1 -1
- package/ui/src/App.jsx +26 -52
- package/ui/src/PacketFilters.jsx +31 -1
- package/ui/src/PacketList.jsx +2 -2
- package/ui/src/Security.jsx +10 -0
- package/ui/src/TabNavigation.jsx +8 -0
- package/ui/src/components/AAuthBadge.jsx +92 -0
- package/ui/src/components/AauthExplorer/AauthExplorerGraph.jsx +231 -0
- package/ui/src/components/AauthExplorer/AauthExplorerView.jsx +387 -0
- package/ui/src/components/AauthExplorer/NodeDetailPanel.jsx +272 -0
- package/ui/src/components/App/ActionMenu.jsx +4 -31
- package/ui/src/components/App/ApiDocsButton.jsx +0 -1
- package/ui/src/components/App/ShutdownButton.jsx +0 -1
- package/ui/src/components/App/useAppState.js +19 -26
- package/ui/src/components/DetailsTab/AAuthIdentitySection.jsx +119 -0
- package/ui/src/components/DetailsTab/RequestDetailsSection.jsx +2 -0
- package/ui/src/components/DetailsTab/ResponseDetailsSection.jsx +2 -0
- package/ui/src/components/DetectedPathsList.jsx +1 -5
- package/ui/src/components/FileInput.jsx +0 -1
- package/ui/src/components/PacketFilters/AAuthPostureFilter.jsx +81 -0
- package/ui/src/components/RequestRow/RequestRowMain.jsx +7 -1
- package/ui/src/components/Security/AAuthPosturePanel.jsx +360 -0
- package/ui/src/components/Security/ScannerContent.jsx +33 -1
- package/ui/src/components/Security/TrafficToxicFlowsPanel.jsx +253 -0
- package/ui/src/components/Security/securityApi.js +15 -0
- package/ui/src/components/Security/useSecurity.js +60 -3
- package/ui/src/components/ServerControl.jsx +0 -1
- package/ui/src/components/TabNavigation/DesktopTabs.jsx +0 -11
- package/ui/src/components/TabNavigationIcons.jsx +5 -0
- package/ui/src/components/ViewModeTabs.jsx +0 -1
- package/ui/src/utils/animations.js +26 -9
- package/core/services/security/rules/scans/agentic01GoalHijack.js +0 -130
- package/core/services/security/rules/scans/agentic02ToolMisuse.js +0 -129
- package/core/services/security/rules/scans/agentic03IdentityAbuse.js +0 -130
- package/core/services/security/rules/scans/agentic04SupplyChain.js +0 -130
- package/core/services/security/rules/scans/agentic06MemoryPoisoning.js +0 -130
- package/core/services/security/rules/scans/agentic07InsecureCommunication.js +0 -135
- package/core/services/security/rules/scans/agentic08CascadingFailures.js +0 -135
- package/core/services/security/rules/scans/agentic09TrustExploitation.js +0 -135
- package/core/services/security/rules/scans/agentic10RogueAgent.js +0 -130
- package/core/services/security/rules/scans/hardcodedSecrets.js +0 -130
- package/core/services/security/rules/scans/mcp01TokenMismanagement.js +0 -127
- package/core/services/security/rules/scans/mcp02ScopeCreep.js +0 -130
- package/core/services/security/rules/scans/mcp03ToolPoisoning.js +0 -132
- package/core/services/security/rules/scans/mcp04SupplyChain.js +0 -131
- package/core/services/security/rules/scans/mcp06PromptInjection.js +0 -200
- package/core/services/security/rules/scans/mcp07InsufficientAuth.js +0 -130
- package/core/services/security/rules/scans/mcp08LackAudit.js +0 -129
- package/core/services/security/rules/scans/mcp09ShadowServers.js +0 -129
- package/core/services/security/rules/scans/mcp10ContextInjection.js +0 -130
- package/ui/dist/assets/index-CiCSDYf-.js +0 -97
- package/ui/server/routes/help.js +0 -44
- package/ui/server/swagger/paths/help.js +0 -82
- package/ui/src/HelpGuide/HelpGuideContent.jsx +0 -118
- package/ui/src/HelpGuide/HelpGuideFooter.jsx +0 -59
- package/ui/src/HelpGuide/HelpGuideHeader.jsx +0 -57
- package/ui/src/HelpGuide.jsx +0 -78
- package/ui/src/IntroTour.jsx +0 -154
- package/ui/src/components/App/HelpButton.jsx +0 -90
- package/ui/src/components/TourOverlay.jsx +0 -117
- package/ui/src/components/TourTooltip/TourTooltipButtons.jsx +0 -120
- package/ui/src/components/TourTooltip/TourTooltipHeader.jsx +0 -71
- package/ui/src/components/TourTooltip/TourTooltipIcons.jsx +0 -54
- package/ui/src/components/TourTooltip/useTooltipPosition.js +0 -135
- package/ui/src/components/TourTooltip.jsx +0 -91
- package/ui/src/config/tourSteps.jsx +0 -140
package/ui/server/routes/help.js
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { readHelpState, writeHelpState } from '#core/configs/index.js';
|
|
2
|
-
import { StatusCodes } from '#core/constants/index.js';
|
|
3
|
-
|
|
4
|
-
export function createHelpRoutes() {
|
|
5
|
-
const router = {};
|
|
6
|
-
|
|
7
|
-
router.getState = (_req, res) => {
|
|
8
|
-
const state = readHelpState();
|
|
9
|
-
res.json({
|
|
10
|
-
dismissed: state.dismissed || false,
|
|
11
|
-
tourCompleted: state.tourCompleted || false,
|
|
12
|
-
});
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
router.dismiss = (req, res) => {
|
|
16
|
-
const { tourCompleted } = req.body || {};
|
|
17
|
-
const state = {
|
|
18
|
-
dismissed: true,
|
|
19
|
-
tourCompleted: tourCompleted || false,
|
|
20
|
-
};
|
|
21
|
-
const success = writeHelpState(state);
|
|
22
|
-
if (success) {
|
|
23
|
-
res.json({ success: true });
|
|
24
|
-
} else {
|
|
25
|
-
res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({ error: 'Failed to save help state' });
|
|
26
|
-
}
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
router.reset = (_req, res) => {
|
|
30
|
-
const state = {
|
|
31
|
-
dismissed: false,
|
|
32
|
-
tourCompleted: false,
|
|
33
|
-
dismissedAt: null,
|
|
34
|
-
};
|
|
35
|
-
const success = writeHelpState(state);
|
|
36
|
-
if (success) {
|
|
37
|
-
res.json({ success: true });
|
|
38
|
-
} else {
|
|
39
|
-
res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({ error: 'Failed to reset help state' });
|
|
40
|
-
}
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
return router;
|
|
44
|
-
}
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Help endpoints - Help and tour management
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
export const helpPaths = {
|
|
6
|
-
'/api/help/state': {
|
|
7
|
-
get: {
|
|
8
|
-
tags: ['Help'],
|
|
9
|
-
summary: 'Get help tour state',
|
|
10
|
-
description: 'Get the current state of the help tour (dismissed/completed)',
|
|
11
|
-
responses: {
|
|
12
|
-
200: {
|
|
13
|
-
description: 'Tour state',
|
|
14
|
-
content: {
|
|
15
|
-
'application/json': {
|
|
16
|
-
schema: {
|
|
17
|
-
type: 'object',
|
|
18
|
-
properties: {
|
|
19
|
-
dismissed: { type: 'boolean' },
|
|
20
|
-
tourCompleted: { type: 'boolean' },
|
|
21
|
-
},
|
|
22
|
-
},
|
|
23
|
-
},
|
|
24
|
-
},
|
|
25
|
-
},
|
|
26
|
-
500: {
|
|
27
|
-
description: 'Internal server error',
|
|
28
|
-
},
|
|
29
|
-
},
|
|
30
|
-
},
|
|
31
|
-
},
|
|
32
|
-
'/api/help/dismiss': {
|
|
33
|
-
post: {
|
|
34
|
-
tags: ['Help'],
|
|
35
|
-
summary: 'Dismiss help tour',
|
|
36
|
-
description: 'Mark the help tour as dismissed',
|
|
37
|
-
responses: {
|
|
38
|
-
200: {
|
|
39
|
-
description: 'Tour dismissed',
|
|
40
|
-
content: {
|
|
41
|
-
'application/json': {
|
|
42
|
-
schema: {
|
|
43
|
-
type: 'object',
|
|
44
|
-
properties: {
|
|
45
|
-
success: { type: 'boolean' },
|
|
46
|
-
},
|
|
47
|
-
},
|
|
48
|
-
},
|
|
49
|
-
},
|
|
50
|
-
},
|
|
51
|
-
500: {
|
|
52
|
-
description: 'Internal server error',
|
|
53
|
-
},
|
|
54
|
-
},
|
|
55
|
-
},
|
|
56
|
-
},
|
|
57
|
-
'/api/help/reset': {
|
|
58
|
-
post: {
|
|
59
|
-
tags: ['Help'],
|
|
60
|
-
summary: 'Reset help tour',
|
|
61
|
-
description: 'Reset the help tour state to show it again',
|
|
62
|
-
responses: {
|
|
63
|
-
200: {
|
|
64
|
-
description: 'Tour reset',
|
|
65
|
-
content: {
|
|
66
|
-
'application/json': {
|
|
67
|
-
schema: {
|
|
68
|
-
type: 'object',
|
|
69
|
-
properties: {
|
|
70
|
-
success: { type: 'boolean' },
|
|
71
|
-
},
|
|
72
|
-
},
|
|
73
|
-
},
|
|
74
|
-
},
|
|
75
|
-
},
|
|
76
|
-
500: {
|
|
77
|
-
description: 'Internal server error',
|
|
78
|
-
},
|
|
79
|
-
},
|
|
80
|
-
},
|
|
81
|
-
},
|
|
82
|
-
};
|
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
import { colors, fonts } from '../theme';
|
|
2
|
-
|
|
3
|
-
export default function HelpGuideContent() {
|
|
4
|
-
return (
|
|
5
|
-
<div
|
|
6
|
-
style={{
|
|
7
|
-
color: colors.textPrimary,
|
|
8
|
-
lineHeight: '1.6',
|
|
9
|
-
fontSize: '14px',
|
|
10
|
-
fontFamily: fonts.body,
|
|
11
|
-
}}
|
|
12
|
-
>
|
|
13
|
-
<section style={{ marginBottom: '24px' }}>
|
|
14
|
-
<h3
|
|
15
|
-
style={{
|
|
16
|
-
color: colors.accentBlue,
|
|
17
|
-
marginTop: 0,
|
|
18
|
-
marginBottom: '12px',
|
|
19
|
-
fontSize: '16px',
|
|
20
|
-
fontFamily: fonts.body,
|
|
21
|
-
}}
|
|
22
|
-
>
|
|
23
|
-
What is MCP Shark?
|
|
24
|
-
</h3>
|
|
25
|
-
<p style={{ margin: 0, color: colors.textSecondary }}>
|
|
26
|
-
MCP Shark is a powerful tool for monitoring, debugging, and analyzing Model Context
|
|
27
|
-
Protocol (MCP) traffic. It captures all communication between MCP clients and servers,
|
|
28
|
-
allowing you to inspect requests, responses, and understand the flow of data.
|
|
29
|
-
</p>
|
|
30
|
-
</section>
|
|
31
|
-
|
|
32
|
-
<section style={{ marginBottom: '24px' }}>
|
|
33
|
-
<h3
|
|
34
|
-
style={{
|
|
35
|
-
color: colors.accentBlue,
|
|
36
|
-
marginTop: 0,
|
|
37
|
-
marginBottom: '12px',
|
|
38
|
-
fontSize: '16px',
|
|
39
|
-
fontFamily: fonts.body,
|
|
40
|
-
}}
|
|
41
|
-
>
|
|
42
|
-
Getting Started
|
|
43
|
-
</h3>
|
|
44
|
-
<ol style={{ margin: 0, paddingLeft: '20px', color: colors.textSecondary }}>
|
|
45
|
-
<li style={{ marginBottom: '8px' }}>
|
|
46
|
-
Go to the <strong>Setup</strong> tab to configure your MCP servers
|
|
47
|
-
</li>
|
|
48
|
-
<li style={{ marginBottom: '8px' }}>
|
|
49
|
-
Start the MCP Shark server to begin capturing traffic
|
|
50
|
-
</li>
|
|
51
|
-
<li style={{ marginBottom: '8px' }}>
|
|
52
|
-
Use your MCP client (Cursor, Claude Desktop, etc.) as normal
|
|
53
|
-
</li>
|
|
54
|
-
<li style={{ marginBottom: '8px' }}>
|
|
55
|
-
View captured traffic in the <strong>Traffic Capture</strong> tab
|
|
56
|
-
</li>
|
|
57
|
-
</ol>
|
|
58
|
-
</section>
|
|
59
|
-
|
|
60
|
-
<section style={{ marginBottom: '24px' }}>
|
|
61
|
-
<h3
|
|
62
|
-
style={{
|
|
63
|
-
color: colors.accentBlue,
|
|
64
|
-
marginTop: 0,
|
|
65
|
-
marginBottom: '12px',
|
|
66
|
-
fontSize: '16px',
|
|
67
|
-
fontFamily: fonts.body,
|
|
68
|
-
}}
|
|
69
|
-
>
|
|
70
|
-
Traffic Capture Features
|
|
71
|
-
</h3>
|
|
72
|
-
<ul style={{ margin: 0, paddingLeft: '20px', color: colors.textSecondary }}>
|
|
73
|
-
<li style={{ marginBottom: '8px' }}>
|
|
74
|
-
<strong>General List:</strong> View all requests in a flat chronological list
|
|
75
|
-
</li>
|
|
76
|
-
<li style={{ marginBottom: '8px' }}>
|
|
77
|
-
<strong>MCP Protocol View:</strong> Organize traffic by MCP protocol categories
|
|
78
|
-
</li>
|
|
79
|
-
<li style={{ marginBottom: '8px' }}>
|
|
80
|
-
<strong>Filters:</strong> Use the search bar and filters to find specific requests,
|
|
81
|
-
sessions, or servers
|
|
82
|
-
</li>
|
|
83
|
-
<li style={{ marginBottom: '8px' }}>
|
|
84
|
-
<strong>Details:</strong> Click any request to view full headers, body, and metadata
|
|
85
|
-
</li>
|
|
86
|
-
</ul>
|
|
87
|
-
</section>
|
|
88
|
-
|
|
89
|
-
<section style={{ marginBottom: '24px' }}>
|
|
90
|
-
<h3
|
|
91
|
-
style={{
|
|
92
|
-
color: colors.accentBlue,
|
|
93
|
-
marginTop: 0,
|
|
94
|
-
marginBottom: '12px',
|
|
95
|
-
fontSize: '16px',
|
|
96
|
-
fontFamily: fonts.body,
|
|
97
|
-
}}
|
|
98
|
-
>
|
|
99
|
-
Tips
|
|
100
|
-
</h3>
|
|
101
|
-
<ul style={{ margin: 0, paddingLeft: '20px', color: colors.textSecondary }}>
|
|
102
|
-
<li style={{ marginBottom: '8px' }}>
|
|
103
|
-
Use the search bar to find requests by method, URL, JSON-RPC method, or any text content
|
|
104
|
-
</li>
|
|
105
|
-
<li style={{ marginBottom: '8px' }}>
|
|
106
|
-
Filter by session ID to track a specific conversation
|
|
107
|
-
</li>
|
|
108
|
-
<li style={{ marginBottom: '8px' }}>
|
|
109
|
-
Filter by server name to see all traffic for a specific MCP server
|
|
110
|
-
</li>
|
|
111
|
-
<li style={{ marginBottom: '8px' }}>
|
|
112
|
-
Click on request/response rows to see detailed packet information
|
|
113
|
-
</li>
|
|
114
|
-
</ul>
|
|
115
|
-
</section>
|
|
116
|
-
</div>
|
|
117
|
-
);
|
|
118
|
-
}
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import { colors, fonts } from '../theme';
|
|
2
|
-
|
|
3
|
-
export default function HelpGuideFooter({ dontShowAgain, setDontShowAgain, onClose }) {
|
|
4
|
-
return (
|
|
5
|
-
<div
|
|
6
|
-
style={{
|
|
7
|
-
marginTop: '24px',
|
|
8
|
-
paddingTop: '20px',
|
|
9
|
-
borderTop: `1px solid ${colors.borderLight}`,
|
|
10
|
-
display: 'flex',
|
|
11
|
-
alignItems: 'center',
|
|
12
|
-
justifyContent: 'space-between',
|
|
13
|
-
}}
|
|
14
|
-
>
|
|
15
|
-
<label
|
|
16
|
-
style={{
|
|
17
|
-
display: 'flex',
|
|
18
|
-
alignItems: 'center',
|
|
19
|
-
gap: '8px',
|
|
20
|
-
color: colors.textTertiary,
|
|
21
|
-
cursor: 'pointer',
|
|
22
|
-
fontSize: '13px',
|
|
23
|
-
fontFamily: fonts.body,
|
|
24
|
-
}}
|
|
25
|
-
>
|
|
26
|
-
<input
|
|
27
|
-
type="checkbox"
|
|
28
|
-
checked={dontShowAgain}
|
|
29
|
-
onChange={(e) => setDontShowAgain(e.target.checked)}
|
|
30
|
-
style={{ cursor: 'pointer' }}
|
|
31
|
-
/>
|
|
32
|
-
Don't show this again
|
|
33
|
-
</label>
|
|
34
|
-
<button
|
|
35
|
-
type="button"
|
|
36
|
-
onClick={onClose}
|
|
37
|
-
style={{
|
|
38
|
-
background: colors.buttonPrimary,
|
|
39
|
-
border: 'none',
|
|
40
|
-
color: colors.textInverse,
|
|
41
|
-
padding: '8px 20px',
|
|
42
|
-
borderRadius: '4px',
|
|
43
|
-
cursor: 'pointer',
|
|
44
|
-
fontSize: '14px',
|
|
45
|
-
fontWeight: '500',
|
|
46
|
-
fontFamily: fonts.body,
|
|
47
|
-
}}
|
|
48
|
-
onMouseEnter={(e) => {
|
|
49
|
-
e.currentTarget.style.background = colors.buttonPrimaryHover;
|
|
50
|
-
}}
|
|
51
|
-
onMouseLeave={(e) => {
|
|
52
|
-
e.currentTarget.style.background = colors.buttonPrimary;
|
|
53
|
-
}}
|
|
54
|
-
>
|
|
55
|
-
Got it!
|
|
56
|
-
</button>
|
|
57
|
-
</div>
|
|
58
|
-
);
|
|
59
|
-
}
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { IconHelp, IconX } from '@tabler/icons-react';
|
|
2
|
-
import { colors, fonts } from '../theme';
|
|
3
|
-
|
|
4
|
-
export default function HelpGuideHeader({ onClose }) {
|
|
5
|
-
return (
|
|
6
|
-
<div
|
|
7
|
-
style={{
|
|
8
|
-
padding: '20px 24px',
|
|
9
|
-
borderBottom: `1px solid ${colors.borderLight}`,
|
|
10
|
-
display: 'flex',
|
|
11
|
-
alignItems: 'center',
|
|
12
|
-
justifyContent: 'space-between',
|
|
13
|
-
}}
|
|
14
|
-
>
|
|
15
|
-
<div style={{ display: 'flex', alignItems: 'center', gap: '12px' }}>
|
|
16
|
-
<div style={{ color: colors.accentBlue }}>
|
|
17
|
-
<IconHelp size={24} stroke={1.5} />
|
|
18
|
-
</div>
|
|
19
|
-
<h2
|
|
20
|
-
style={{
|
|
21
|
-
margin: 0,
|
|
22
|
-
color: colors.textPrimary,
|
|
23
|
-
fontSize: '20px',
|
|
24
|
-
fontWeight: '600',
|
|
25
|
-
fontFamily: fonts.body,
|
|
26
|
-
}}
|
|
27
|
-
>
|
|
28
|
-
Welcome to MCP Shark
|
|
29
|
-
</h2>
|
|
30
|
-
</div>
|
|
31
|
-
<button
|
|
32
|
-
type="button"
|
|
33
|
-
onClick={onClose}
|
|
34
|
-
style={{
|
|
35
|
-
background: 'transparent',
|
|
36
|
-
border: 'none',
|
|
37
|
-
color: colors.textTertiary,
|
|
38
|
-
cursor: 'pointer',
|
|
39
|
-
padding: '4px',
|
|
40
|
-
display: 'flex',
|
|
41
|
-
alignItems: 'center',
|
|
42
|
-
borderRadius: '8px',
|
|
43
|
-
}}
|
|
44
|
-
onMouseEnter={(e) => {
|
|
45
|
-
e.currentTarget.style.background = colors.bgHover;
|
|
46
|
-
e.currentTarget.style.color = colors.textPrimary;
|
|
47
|
-
}}
|
|
48
|
-
onMouseLeave={(e) => {
|
|
49
|
-
e.currentTarget.style.background = 'transparent';
|
|
50
|
-
e.currentTarget.style.color = colors.textTertiary;
|
|
51
|
-
}}
|
|
52
|
-
>
|
|
53
|
-
<IconX size={20} stroke={1.5} />
|
|
54
|
-
</button>
|
|
55
|
-
</div>
|
|
56
|
-
);
|
|
57
|
-
}
|
package/ui/src/HelpGuide.jsx
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import { useState } from 'react';
|
|
2
|
-
import HelpGuideContent from './HelpGuide/HelpGuideContent';
|
|
3
|
-
import HelpGuideFooter from './HelpGuide/HelpGuideFooter';
|
|
4
|
-
import HelpGuideHeader from './HelpGuide/HelpGuideHeader';
|
|
5
|
-
import { colors } from './theme';
|
|
6
|
-
|
|
7
|
-
function HelpGuide({ onClose }) {
|
|
8
|
-
const [dontShowAgain, setDontShowAgain] = useState(false);
|
|
9
|
-
|
|
10
|
-
const handleClose = async () => {
|
|
11
|
-
if (dontShowAgain) {
|
|
12
|
-
try {
|
|
13
|
-
await fetch('/api/help/dismiss', { method: 'POST' });
|
|
14
|
-
} catch (error) {
|
|
15
|
-
console.error('Failed to save help state:', error);
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
onClose();
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
return (
|
|
22
|
-
<dialog
|
|
23
|
-
open
|
|
24
|
-
aria-modal="true"
|
|
25
|
-
style={{
|
|
26
|
-
position: 'fixed',
|
|
27
|
-
top: 0,
|
|
28
|
-
left: 0,
|
|
29
|
-
right: 0,
|
|
30
|
-
bottom: 0,
|
|
31
|
-
background: 'rgba(0, 0, 0, 0.7)',
|
|
32
|
-
zIndex: 1000,
|
|
33
|
-
display: 'flex',
|
|
34
|
-
alignItems: 'center',
|
|
35
|
-
justifyContent: 'center',
|
|
36
|
-
padding: '20px',
|
|
37
|
-
border: 'none',
|
|
38
|
-
margin: 0,
|
|
39
|
-
width: '100%',
|
|
40
|
-
height: '100%',
|
|
41
|
-
}}
|
|
42
|
-
onClick={handleClose}
|
|
43
|
-
onKeyDown={(e) => {
|
|
44
|
-
if (e.key === 'Escape') {
|
|
45
|
-
handleClose();
|
|
46
|
-
}
|
|
47
|
-
}}
|
|
48
|
-
>
|
|
49
|
-
<div
|
|
50
|
-
role="document"
|
|
51
|
-
style={{
|
|
52
|
-
background: colors.bgCard,
|
|
53
|
-
border: `1px solid ${colors.borderLight}`,
|
|
54
|
-
borderRadius: '12px',
|
|
55
|
-
maxWidth: '700px',
|
|
56
|
-
width: '100%',
|
|
57
|
-
maxHeight: '90vh',
|
|
58
|
-
overflow: 'auto',
|
|
59
|
-
boxShadow: '0 8px 32px rgba(0, 0, 0, 0.5)',
|
|
60
|
-
}}
|
|
61
|
-
onClick={(e) => e.stopPropagation()}
|
|
62
|
-
onKeyDown={(e) => e.stopPropagation()}
|
|
63
|
-
>
|
|
64
|
-
<HelpGuideHeader onClose={handleClose} />
|
|
65
|
-
<div style={{ padding: '24px' }}>
|
|
66
|
-
<HelpGuideContent />
|
|
67
|
-
<HelpGuideFooter
|
|
68
|
-
dontShowAgain={dontShowAgain}
|
|
69
|
-
setDontShowAgain={setDontShowAgain}
|
|
70
|
-
onClose={handleClose}
|
|
71
|
-
/>
|
|
72
|
-
</div>
|
|
73
|
-
</div>
|
|
74
|
-
</dialog>
|
|
75
|
-
);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
export default HelpGuide;
|
package/ui/src/IntroTour.jsx
DELETED
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
import { useEffect, useRef, useState } from 'react';
|
|
2
|
-
import TourOverlay from './components/TourOverlay';
|
|
3
|
-
import TourTooltip from './components/TourTooltip';
|
|
4
|
-
|
|
5
|
-
function IntroTour({ steps, onComplete, onSkip, onStepChange }) {
|
|
6
|
-
const [currentStep, setCurrentStep] = useState(0);
|
|
7
|
-
const [highlightedElement, setHighlightedElement] = useState(null);
|
|
8
|
-
const [elementRect, setElementRect] = useState(null);
|
|
9
|
-
const overlayRef = useRef(null);
|
|
10
|
-
|
|
11
|
-
useEffect(() => {
|
|
12
|
-
if (!highlightedElement) {
|
|
13
|
-
return;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const updatePosition = () => {
|
|
17
|
-
if (highlightedElement) {
|
|
18
|
-
const rect = highlightedElement.getBoundingClientRect();
|
|
19
|
-
setElementRect(rect);
|
|
20
|
-
}
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
updatePosition();
|
|
24
|
-
|
|
25
|
-
const options = { passive: true, capture: true };
|
|
26
|
-
window.addEventListener('scroll', updatePosition, options);
|
|
27
|
-
window.addEventListener('resize', updatePosition, { passive: true });
|
|
28
|
-
|
|
29
|
-
return () => {
|
|
30
|
-
window.removeEventListener('scroll', updatePosition, options);
|
|
31
|
-
window.removeEventListener('resize', updatePosition, { passive: true });
|
|
32
|
-
};
|
|
33
|
-
}, [highlightedElement]);
|
|
34
|
-
|
|
35
|
-
useEffect(() => {
|
|
36
|
-
if (steps.length === 0) {
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const step = steps[currentStep];
|
|
41
|
-
if (!step) {
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
if (onStepChange) {
|
|
46
|
-
onStepChange(currentStep);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const timer = setTimeout(() => {
|
|
50
|
-
const element = document.querySelector(step.target);
|
|
51
|
-
if (element) {
|
|
52
|
-
setHighlightedElement(element);
|
|
53
|
-
element.scrollIntoView({ behavior: 'smooth', block: 'center', inline: 'nearest' });
|
|
54
|
-
setTimeout(() => {
|
|
55
|
-
const rect = element.getBoundingClientRect();
|
|
56
|
-
setElementRect(rect);
|
|
57
|
-
}, 300);
|
|
58
|
-
} else {
|
|
59
|
-
setHighlightedElement(null);
|
|
60
|
-
setElementRect(null);
|
|
61
|
-
}
|
|
62
|
-
}, 200);
|
|
63
|
-
|
|
64
|
-
return () => clearTimeout(timer);
|
|
65
|
-
}, [currentStep, steps, onStepChange]);
|
|
66
|
-
|
|
67
|
-
const handleNext = () => {
|
|
68
|
-
if (currentStep < steps.length - 1) {
|
|
69
|
-
const nextStep = currentStep + 1;
|
|
70
|
-
setCurrentStep(nextStep);
|
|
71
|
-
if (onStepChange) {
|
|
72
|
-
onStepChange(nextStep);
|
|
73
|
-
}
|
|
74
|
-
} else {
|
|
75
|
-
handleComplete();
|
|
76
|
-
}
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
const handlePrevious = () => {
|
|
80
|
-
if (currentStep > 0) {
|
|
81
|
-
const prevStep = currentStep - 1;
|
|
82
|
-
setCurrentStep(prevStep);
|
|
83
|
-
if (onStepChange) {
|
|
84
|
-
onStepChange(prevStep);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
const handleComplete = async () => {
|
|
90
|
-
try {
|
|
91
|
-
await fetch('/api/help/dismiss', {
|
|
92
|
-
method: 'POST',
|
|
93
|
-
headers: { 'Content-Type': 'application/json' },
|
|
94
|
-
body: JSON.stringify({ tourCompleted: true }),
|
|
95
|
-
});
|
|
96
|
-
} catch (error) {
|
|
97
|
-
console.error('Failed to save tour state:', error);
|
|
98
|
-
}
|
|
99
|
-
onComplete();
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
const handleSkip = () => {
|
|
103
|
-
handleComplete();
|
|
104
|
-
if (onSkip) {
|
|
105
|
-
onSkip();
|
|
106
|
-
}
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
if (steps.length === 0 || currentStep >= steps.length) {
|
|
110
|
-
return null;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
const step = steps[currentStep];
|
|
114
|
-
|
|
115
|
-
return (
|
|
116
|
-
<>
|
|
117
|
-
<div
|
|
118
|
-
ref={overlayRef}
|
|
119
|
-
role="presentation"
|
|
120
|
-
style={{
|
|
121
|
-
position: 'fixed',
|
|
122
|
-
top: 0,
|
|
123
|
-
left: 0,
|
|
124
|
-
right: 0,
|
|
125
|
-
bottom: 0,
|
|
126
|
-
zIndex: 9998,
|
|
127
|
-
pointerEvents: 'auto',
|
|
128
|
-
}}
|
|
129
|
-
onClick={handleSkip}
|
|
130
|
-
onKeyDown={(e) => {
|
|
131
|
-
if (e.key === 'Escape') {
|
|
132
|
-
handleSkip();
|
|
133
|
-
}
|
|
134
|
-
}}
|
|
135
|
-
>
|
|
136
|
-
<TourOverlay elementRect={elementRect} />
|
|
137
|
-
</div>
|
|
138
|
-
|
|
139
|
-
{elementRect && (
|
|
140
|
-
<TourTooltip
|
|
141
|
-
elementRect={elementRect}
|
|
142
|
-
step={step}
|
|
143
|
-
currentStep={currentStep}
|
|
144
|
-
totalSteps={steps.length}
|
|
145
|
-
onNext={handleNext}
|
|
146
|
-
onPrevious={handlePrevious}
|
|
147
|
-
onSkip={handleSkip}
|
|
148
|
-
/>
|
|
149
|
-
)}
|
|
150
|
-
</>
|
|
151
|
-
);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
export default IntroTour;
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
import { colors, fonts } from '../../theme';
|
|
2
|
-
|
|
3
|
-
const TourIcon = ({ size = 16, color = 'currentColor' }) => (
|
|
4
|
-
<svg
|
|
5
|
-
width={size}
|
|
6
|
-
height={size}
|
|
7
|
-
viewBox="0 0 24 24"
|
|
8
|
-
fill="none"
|
|
9
|
-
stroke={color}
|
|
10
|
-
strokeWidth="2"
|
|
11
|
-
strokeLinecap="round"
|
|
12
|
-
strokeLinejoin="round"
|
|
13
|
-
role="img"
|
|
14
|
-
aria-label="Tour icon"
|
|
15
|
-
>
|
|
16
|
-
<title>Tour icon</title>
|
|
17
|
-
<path d="M12 2L2 7l10 5 10-5-10-5z" />
|
|
18
|
-
<path d="M2 17l10 5 10-5" />
|
|
19
|
-
<path d="M2 12l10 5 10-5" />
|
|
20
|
-
</svg>
|
|
21
|
-
);
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Help button component
|
|
25
|
-
* Starts the application tour
|
|
26
|
-
* @param {Object} props
|
|
27
|
-
* @param {Function} props.onClick - Callback when button is clicked
|
|
28
|
-
* @param {Object} props.style - Custom styles for the button
|
|
29
|
-
* @param {Function} props.onMouseEnter - Optional mouse enter handler
|
|
30
|
-
* @param {Function} props.onMouseLeave - Optional mouse leave handler
|
|
31
|
-
*/
|
|
32
|
-
export default function HelpButton({ onClick, style, onMouseEnter, onMouseLeave }) {
|
|
33
|
-
const defaultStyle = {
|
|
34
|
-
position: 'fixed',
|
|
35
|
-
bottom: '20px',
|
|
36
|
-
right: '20px',
|
|
37
|
-
background: colors.bgCard,
|
|
38
|
-
border: `1px solid ${colors.borderLight}`,
|
|
39
|
-
borderRadius: '50%',
|
|
40
|
-
width: '48px',
|
|
41
|
-
height: '48px',
|
|
42
|
-
padding: 0,
|
|
43
|
-
color: colors.textSecondary,
|
|
44
|
-
cursor: 'pointer',
|
|
45
|
-
fontFamily: fonts.body,
|
|
46
|
-
boxShadow: `0 4px 12px ${colors.shadowMd}`,
|
|
47
|
-
display: 'flex',
|
|
48
|
-
alignItems: 'center',
|
|
49
|
-
justifyContent: 'center',
|
|
50
|
-
zIndex: 9999,
|
|
51
|
-
transition: 'all 0.2s ease',
|
|
52
|
-
...style,
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
const handleMouseEnter = (e) => {
|
|
56
|
-
e.currentTarget.style.background = colors.bgHover;
|
|
57
|
-
e.currentTarget.style.color = colors.textPrimary;
|
|
58
|
-
e.currentTarget.style.borderColor = colors.borderMedium;
|
|
59
|
-
e.currentTarget.style.transform = 'scale(1.1)';
|
|
60
|
-
e.currentTarget.style.boxShadow = `0 6px 16px ${colors.shadowLg}`;
|
|
61
|
-
if (onMouseEnter) {
|
|
62
|
-
onMouseEnter(e);
|
|
63
|
-
}
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
const handleMouseLeave = (e) => {
|
|
67
|
-
e.currentTarget.style.background = colors.bgCard;
|
|
68
|
-
e.currentTarget.style.color = colors.textSecondary;
|
|
69
|
-
e.currentTarget.style.borderColor = colors.borderLight;
|
|
70
|
-
e.currentTarget.style.transform = 'scale(1)';
|
|
71
|
-
e.currentTarget.style.boxShadow = `0 4px 12px ${colors.shadowMd}`;
|
|
72
|
-
if (onMouseLeave) {
|
|
73
|
-
onMouseLeave(e);
|
|
74
|
-
}
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
return (
|
|
78
|
-
<button
|
|
79
|
-
type="button"
|
|
80
|
-
onClick={onClick}
|
|
81
|
-
data-tour="help-button"
|
|
82
|
-
style={defaultStyle}
|
|
83
|
-
onMouseEnter={handleMouseEnter}
|
|
84
|
-
onMouseLeave={handleMouseLeave}
|
|
85
|
-
title="Start tour"
|
|
86
|
-
>
|
|
87
|
-
<TourIcon size={20} />
|
|
88
|
-
</button>
|
|
89
|
-
);
|
|
90
|
-
}
|