@mcp-shark/mcp-shark 1.4.1 → 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 +34 -42
- 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 +21 -40
- 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 +5 -7
- package/mcp-server/lib/server/internal/handlers/prompts-list.js +5 -4
- package/mcp-server/lib/server/internal/handlers/resources-list.js +5 -4
- package/mcp-server/lib/server/internal/handlers/resources-read.js +5 -4
- package/mcp-server/lib/server/internal/handlers/tools-call.js +8 -10
- package/mcp-server/lib/server/internal/handlers/tools-list.js +6 -5
- package/mcp-server/lib/server/internal/run.js +5 -17
- package/mcp-server/lib/server/internal/server.js +14 -15
- package/mcp-server/lib/server/internal/session.js +8 -13
- 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 +91 -62
- 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 +140 -112
- 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 +87 -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 -10
- 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 +52 -23
- package/ui/src/components/McpPlayground/PromptsSection/PromptsList.jsx +13 -11
- 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 +66 -34
- package/ui/src/components/McpPlayground/ResourcesSection/ResourcesList.jsx +13 -11
- 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 +52 -23
- package/ui/src/components/McpPlayground/ToolsSection/ToolsList.jsx +13 -11
- package/ui/src/components/McpPlayground/ToolsSection.jsx +2 -1
- package/ui/src/components/McpPlayground/hooks/useMcpDataLoader.js +107 -0
- package/ui/src/components/McpPlayground/hooks/useMcpRequest.js +70 -0
- package/ui/src/components/McpPlayground/hooks/useMcpServerStatus.js +90 -0
- package/ui/src/components/McpPlayground/useMcpPlayground.js +118 -159
- package/ui/src/components/McpPlayground.jsx +105 -23
- 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 +37 -105
- 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 -244
|
@@ -17,7 +17,10 @@ const ChevronDown = ({ size = 14, color = 'currentColor', rotated = false }) =>
|
|
|
17
17
|
transform: rotated ? 'rotate(-90deg)' : 'rotate(0deg)',
|
|
18
18
|
transition: 'transform 0.2s ease',
|
|
19
19
|
}}
|
|
20
|
+
role="img"
|
|
21
|
+
aria-label="Chevron down icon"
|
|
20
22
|
>
|
|
23
|
+
<title>Chevron down icon</title>
|
|
21
24
|
<polyline points="6 9 12 15 18 9" />
|
|
22
25
|
</svg>
|
|
23
26
|
);
|
|
@@ -35,8 +38,15 @@ function CollapsibleRequestResponse({ title, titleColor, children, defaultExpand
|
|
|
35
38
|
marginBottom: '20px',
|
|
36
39
|
}}
|
|
37
40
|
>
|
|
38
|
-
<
|
|
41
|
+
<button
|
|
42
|
+
type="button"
|
|
39
43
|
onClick={() => setIsExpanded(!isExpanded)}
|
|
44
|
+
onKeyDown={(e) => {
|
|
45
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
46
|
+
e.preventDefault();
|
|
47
|
+
setIsExpanded(!isExpanded);
|
|
48
|
+
}
|
|
49
|
+
}}
|
|
40
50
|
style={{
|
|
41
51
|
padding: '16px 20px',
|
|
42
52
|
background: isExpanded ? colors.bgCard : colors.bgSecondary,
|
|
@@ -47,6 +57,9 @@ function CollapsibleRequestResponse({ title, titleColor, children, defaultExpand
|
|
|
47
57
|
alignItems: 'center',
|
|
48
58
|
justifyContent: 'space-between',
|
|
49
59
|
transition: 'background-color 0.15s ease',
|
|
60
|
+
width: '100%',
|
|
61
|
+
border: 'none',
|
|
62
|
+
textAlign: 'left',
|
|
50
63
|
}}
|
|
51
64
|
onMouseEnter={(e) => {
|
|
52
65
|
e.currentTarget.style.background = colors.bgHover;
|
|
@@ -71,7 +84,7 @@ function CollapsibleRequestResponse({ title, titleColor, children, defaultExpand
|
|
|
71
84
|
<ChevronDown size={14} color={titleColor} rotated={!isExpanded} />
|
|
72
85
|
{title}
|
|
73
86
|
</div>
|
|
74
|
-
</
|
|
87
|
+
</button>
|
|
75
88
|
{isExpanded && <div style={{ padding: '20px' }}>{children}</div>}
|
|
76
89
|
</div>
|
|
77
90
|
);
|
|
@@ -100,7 +113,7 @@ function HexTab({ requestHexLines, responseHexLines, hasRequest, hasResponse })
|
|
|
100
113
|
{requestHexLines.length > 0 ? (
|
|
101
114
|
requestHexLines.map((line, i) => (
|
|
102
115
|
<div
|
|
103
|
-
key={i}
|
|
116
|
+
key={`request-${line.offset}-${i}`}
|
|
104
117
|
style={{
|
|
105
118
|
display: 'flex',
|
|
106
119
|
gap: '16px',
|
|
@@ -150,7 +163,7 @@ function HexTab({ requestHexLines, responseHexLines, hasRequest, hasResponse })
|
|
|
150
163
|
{responseHexLines.length > 0 ? (
|
|
151
164
|
responseHexLines.map((line, i) => (
|
|
152
165
|
<div
|
|
153
|
-
key={i}
|
|
166
|
+
key={`response-${line.offset}-${i}`}
|
|
154
167
|
style={{
|
|
155
168
|
display: 'flex',
|
|
156
169
|
gap: '16px',
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { IconDownload, IconSearch, IconTrash } from '@tabler/icons-react';
|
|
1
2
|
import { colors, fonts } from '../theme';
|
|
2
|
-
import { IconTrash, IconDownload, IconSearch } from '@tabler/icons-react';
|
|
3
3
|
|
|
4
4
|
export default function LogsToolbar({
|
|
5
5
|
filter,
|
|
@@ -117,6 +117,7 @@ export default function LogsToolbar({
|
|
|
117
117
|
</label>
|
|
118
118
|
|
|
119
119
|
<button
|
|
120
|
+
type="button"
|
|
120
121
|
onClick={onClearLogs}
|
|
121
122
|
style={{
|
|
122
123
|
padding: '8px 14px',
|
|
@@ -148,6 +149,7 @@ export default function LogsToolbar({
|
|
|
148
149
|
</button>
|
|
149
150
|
|
|
150
151
|
<button
|
|
152
|
+
type="button"
|
|
151
153
|
onClick={onExportLogs}
|
|
152
154
|
style={{
|
|
153
155
|
padding: '8px 14px',
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import anime from 'animejs';
|
|
1
2
|
import { useEffect, useRef } from 'react';
|
|
2
3
|
import { colors, fonts } from '../../theme';
|
|
3
|
-
import anime from 'animejs';
|
|
4
4
|
|
|
5
5
|
export default function LoadingModal({ show }) {
|
|
6
6
|
const loadingModalRef = useRef(null);
|
|
@@ -42,7 +42,9 @@ export default function LoadingModal({ show }) {
|
|
|
42
42
|
}
|
|
43
43
|
}, [show]);
|
|
44
44
|
|
|
45
|
-
if (!show)
|
|
45
|
+
if (!show) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
46
48
|
|
|
47
49
|
return (
|
|
48
50
|
<div
|
|
@@ -96,7 +98,9 @@ export default function LoadingModal({ show }) {
|
|
|
96
98
|
<div
|
|
97
99
|
key={i}
|
|
98
100
|
ref={(el) => {
|
|
99
|
-
if (el)
|
|
101
|
+
if (el) {
|
|
102
|
+
dotsRef.current[i] = el;
|
|
103
|
+
}
|
|
100
104
|
}}
|
|
101
105
|
style={{
|
|
102
106
|
width: '12px',
|
|
@@ -43,6 +43,7 @@ export default function PromptCallPanel({
|
|
|
43
43
|
>
|
|
44
44
|
<div>
|
|
45
45
|
<label
|
|
46
|
+
htmlFor="prompt-args-textarea"
|
|
46
47
|
style={{
|
|
47
48
|
display: 'block',
|
|
48
49
|
fontSize: '12px',
|
|
@@ -54,6 +55,7 @@ export default function PromptCallPanel({
|
|
|
54
55
|
Arguments (JSON)
|
|
55
56
|
</label>
|
|
56
57
|
<textarea
|
|
58
|
+
id="prompt-args-textarea"
|
|
57
59
|
value={promptArgs}
|
|
58
60
|
onChange={(e) => onPromptArgsChange(e.target.value)}
|
|
59
61
|
style={{
|
|
@@ -71,6 +73,7 @@ export default function PromptCallPanel({
|
|
|
71
73
|
/>
|
|
72
74
|
</div>
|
|
73
75
|
<button
|
|
76
|
+
type="button"
|
|
74
77
|
onClick={onGetPrompt}
|
|
75
78
|
disabled={loading}
|
|
76
79
|
style={{
|
|
@@ -91,6 +94,7 @@ export default function PromptCallPanel({
|
|
|
91
94
|
{promptResult && (
|
|
92
95
|
<div>
|
|
93
96
|
<label
|
|
97
|
+
htmlFor="prompt-result-pre"
|
|
94
98
|
style={{
|
|
95
99
|
display: 'block',
|
|
96
100
|
fontSize: '12px',
|
|
@@ -102,6 +106,7 @@ export default function PromptCallPanel({
|
|
|
102
106
|
Result
|
|
103
107
|
</label>
|
|
104
108
|
<pre
|
|
109
|
+
id="prompt-result-pre"
|
|
105
110
|
style={{
|
|
106
111
|
padding: '12px',
|
|
107
112
|
background: colors.bgSecondary,
|
|
@@ -2,47 +2,76 @@ import { colors } from '../../../theme';
|
|
|
2
2
|
|
|
3
3
|
export default function PromptItem({ prompt, isSelected, onClick }) {
|
|
4
4
|
return (
|
|
5
|
-
<
|
|
5
|
+
<button
|
|
6
|
+
type="button"
|
|
6
7
|
onClick={onClick}
|
|
8
|
+
aria-label={`Select prompt ${prompt.name}`}
|
|
7
9
|
style={{
|
|
8
|
-
padding: '
|
|
9
|
-
|
|
10
|
+
padding: '16px 20px',
|
|
11
|
+
margin: '4px 8px',
|
|
12
|
+
borderRadius: '8px',
|
|
10
13
|
cursor: 'pointer',
|
|
11
|
-
background: isSelected ? colors.
|
|
12
|
-
|
|
14
|
+
background: isSelected ? colors.bgSelected : colors.bgCard,
|
|
15
|
+
border: isSelected ? `2px solid ${colors.accentBlue}` : `1px solid ${colors.borderLight}`,
|
|
16
|
+
boxShadow: isSelected ? `0 2px 4px ${colors.shadowSm}` : 'none',
|
|
17
|
+
transition: 'all 0.2s ease',
|
|
18
|
+
position: 'relative',
|
|
19
|
+
width: '100%',
|
|
20
|
+
textAlign: 'left',
|
|
13
21
|
}}
|
|
14
22
|
onMouseEnter={(e) => {
|
|
15
23
|
if (!isSelected) {
|
|
16
24
|
e.currentTarget.style.background = colors.bgHover;
|
|
25
|
+
e.currentTarget.style.borderColor = colors.borderMedium;
|
|
26
|
+
e.currentTarget.style.boxShadow = `0 2px 4px ${colors.shadowSm}`;
|
|
17
27
|
}
|
|
18
28
|
}}
|
|
19
29
|
onMouseLeave={(e) => {
|
|
20
30
|
if (!isSelected) {
|
|
21
31
|
e.currentTarget.style.background = colors.bgCard;
|
|
32
|
+
e.currentTarget.style.borderColor = colors.borderLight;
|
|
33
|
+
e.currentTarget.style.boxShadow = 'none';
|
|
22
34
|
}
|
|
23
35
|
}}
|
|
24
36
|
>
|
|
25
|
-
<div
|
|
26
|
-
style={{
|
|
27
|
-
fontWeight: '500',
|
|
28
|
-
fontSize: '13px',
|
|
29
|
-
color: colors.textPrimary,
|
|
30
|
-
marginBottom: '4px',
|
|
31
|
-
}}
|
|
32
|
-
>
|
|
33
|
-
{prompt.name}
|
|
34
|
-
</div>
|
|
35
|
-
{prompt.description && (
|
|
37
|
+
<div style={{ display: 'flex', alignItems: 'flex-start', gap: '12px' }}>
|
|
36
38
|
<div
|
|
37
39
|
style={{
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
40
|
+
width: '6px',
|
|
41
|
+
height: '6px',
|
|
42
|
+
borderRadius: '50%',
|
|
43
|
+
background: isSelected ? colors.accentBlue : colors.textTertiary,
|
|
44
|
+
marginTop: '6px',
|
|
45
|
+
flexShrink: 0,
|
|
46
|
+
transition: 'background 0.2s',
|
|
41
47
|
}}
|
|
42
|
-
|
|
43
|
-
|
|
48
|
+
/>
|
|
49
|
+
<div style={{ flex: 1, minWidth: 0 }}>
|
|
50
|
+
<div
|
|
51
|
+
style={{
|
|
52
|
+
fontWeight: isSelected ? '600' : '500',
|
|
53
|
+
fontSize: '14px',
|
|
54
|
+
color: colors.textPrimary,
|
|
55
|
+
marginBottom: prompt.description ? '6px' : '0',
|
|
56
|
+
lineHeight: '1.4',
|
|
57
|
+
}}
|
|
58
|
+
>
|
|
59
|
+
{prompt.name}
|
|
60
|
+
</div>
|
|
61
|
+
{prompt.description && (
|
|
62
|
+
<div
|
|
63
|
+
style={{
|
|
64
|
+
fontSize: '12px',
|
|
65
|
+
color: colors.textSecondary,
|
|
66
|
+
lineHeight: '1.5',
|
|
67
|
+
marginTop: '4px',
|
|
68
|
+
}}
|
|
69
|
+
>
|
|
70
|
+
{prompt.description}
|
|
71
|
+
</div>
|
|
72
|
+
)}
|
|
44
73
|
</div>
|
|
45
|
-
|
|
46
|
-
</
|
|
74
|
+
</div>
|
|
75
|
+
</button>
|
|
47
76
|
);
|
|
48
77
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { colors } from '../../../theme';
|
|
2
|
-
import LoadingState from '../common/LoadingState';
|
|
3
|
-
import ErrorState from '../common/ErrorState';
|
|
4
2
|
import EmptyState from '../common/EmptyState';
|
|
3
|
+
import ErrorState from '../common/ErrorState';
|
|
4
|
+
import LoadingState from '../common/LoadingState';
|
|
5
5
|
import PromptItem from './PromptItem';
|
|
6
6
|
|
|
7
7
|
export default function PromptsList({
|
|
@@ -26,19 +26,21 @@ export default function PromptsList({
|
|
|
26
26
|
<LoadingState message="Waiting for MCP server to start..." />
|
|
27
27
|
) : promptsLoading || !promptsLoaded ? (
|
|
28
28
|
<LoadingState message="Loading prompts..." />
|
|
29
|
-
) : error
|
|
29
|
+
) : error?.includes('prompts:') ? (
|
|
30
30
|
<ErrorState message={`Error loading prompts: ${error.replace('prompts: ', '')}`} />
|
|
31
31
|
) : prompts.length === 0 ? (
|
|
32
32
|
<EmptyState message="No prompts available." />
|
|
33
33
|
) : (
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
34
|
+
<div style={{ padding: '8px 0' }}>
|
|
35
|
+
{prompts.map((prompt, idx) => (
|
|
36
|
+
<PromptItem
|
|
37
|
+
key={prompt.name || `prompt-${idx}`}
|
|
38
|
+
prompt={prompt}
|
|
39
|
+
isSelected={selectedPrompt?.name === prompt.name}
|
|
40
|
+
onClick={() => onSelectPrompt(prompt)}
|
|
41
|
+
/>
|
|
42
|
+
))}
|
|
43
|
+
</div>
|
|
42
44
|
)}
|
|
43
45
|
</div>
|
|
44
46
|
);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { colors, fonts } from '../../theme';
|
|
2
|
-
import PromptsList from './PromptsSection/PromptsList';
|
|
3
2
|
import PromptCallPanel from './PromptsSection/PromptCallPanel';
|
|
3
|
+
import PromptsList from './PromptsSection/PromptsList';
|
|
4
4
|
|
|
5
5
|
export default function PromptsSection({
|
|
6
6
|
prompts,
|
|
@@ -32,6 +32,7 @@ export default function PromptsSection({
|
|
|
32
32
|
<div style={{ display: 'flex', flexDirection: 'column', gap: '16px', height: '100%' }}>
|
|
33
33
|
<div style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>
|
|
34
34
|
<button
|
|
35
|
+
type="button"
|
|
35
36
|
onClick={onRefresh}
|
|
36
37
|
disabled={loading || promptsLoading}
|
|
37
38
|
style={{
|
|
@@ -35,6 +35,7 @@ export default function ResourceCallPanel({ resource, resourceResult, onReadReso
|
|
|
35
35
|
}}
|
|
36
36
|
>
|
|
37
37
|
<button
|
|
38
|
+
type="button"
|
|
38
39
|
onClick={onReadResource}
|
|
39
40
|
disabled={loading}
|
|
40
41
|
style={{
|
|
@@ -55,6 +56,7 @@ export default function ResourceCallPanel({ resource, resourceResult, onReadReso
|
|
|
55
56
|
{resourceResult && (
|
|
56
57
|
<div>
|
|
57
58
|
<label
|
|
59
|
+
htmlFor="resource-result-pre"
|
|
58
60
|
style={{
|
|
59
61
|
display: 'block',
|
|
60
62
|
fontSize: '12px',
|
|
@@ -66,6 +68,7 @@ export default function ResourceCallPanel({ resource, resourceResult, onReadReso
|
|
|
66
68
|
Result
|
|
67
69
|
</label>
|
|
68
70
|
<pre
|
|
71
|
+
id="resource-result-pre"
|
|
69
72
|
style={{
|
|
70
73
|
padding: '12px',
|
|
71
74
|
background: colors.bgSecondary,
|
|
@@ -2,58 +2,90 @@ import { colors } from '../../../theme';
|
|
|
2
2
|
|
|
3
3
|
export default function ResourceItem({ resource, isSelected, onClick }) {
|
|
4
4
|
return (
|
|
5
|
-
<
|
|
5
|
+
<button
|
|
6
|
+
type="button"
|
|
6
7
|
onClick={onClick}
|
|
8
|
+
aria-label={`Select resource ${resource.uri}`}
|
|
7
9
|
style={{
|
|
8
|
-
padding: '
|
|
9
|
-
|
|
10
|
+
padding: '16px 20px',
|
|
11
|
+
margin: '4px 8px',
|
|
12
|
+
borderRadius: '8px',
|
|
10
13
|
cursor: 'pointer',
|
|
11
|
-
background: isSelected ? colors.
|
|
12
|
-
|
|
14
|
+
background: isSelected ? colors.bgSelected : colors.bgCard,
|
|
15
|
+
border: isSelected ? `2px solid ${colors.accentBlue}` : `1px solid ${colors.borderLight}`,
|
|
16
|
+
boxShadow: isSelected ? `0 2px 4px ${colors.shadowSm}` : 'none',
|
|
17
|
+
transition: 'all 0.2s ease',
|
|
18
|
+
position: 'relative',
|
|
19
|
+
width: '100%',
|
|
20
|
+
textAlign: 'left',
|
|
13
21
|
}}
|
|
14
22
|
onMouseEnter={(e) => {
|
|
15
23
|
if (!isSelected) {
|
|
16
24
|
e.currentTarget.style.background = colors.bgHover;
|
|
25
|
+
e.currentTarget.style.borderColor = colors.borderMedium;
|
|
26
|
+
e.currentTarget.style.boxShadow = `0 2px 4px ${colors.shadowSm}`;
|
|
17
27
|
}
|
|
18
28
|
}}
|
|
19
29
|
onMouseLeave={(e) => {
|
|
20
30
|
if (!isSelected) {
|
|
21
31
|
e.currentTarget.style.background = colors.bgCard;
|
|
32
|
+
e.currentTarget.style.borderColor = colors.borderLight;
|
|
33
|
+
e.currentTarget.style.boxShadow = 'none';
|
|
22
34
|
}
|
|
23
35
|
}}
|
|
24
36
|
>
|
|
25
|
-
<div
|
|
26
|
-
style={{
|
|
27
|
-
fontWeight: '500',
|
|
28
|
-
fontSize: '13px',
|
|
29
|
-
color: colors.textPrimary,
|
|
30
|
-
marginBottom: '4px',
|
|
31
|
-
}}
|
|
32
|
-
>
|
|
33
|
-
{resource.uri}
|
|
34
|
-
</div>
|
|
35
|
-
{resource.name && (
|
|
36
|
-
<div
|
|
37
|
-
style={{
|
|
38
|
-
fontSize: '12px',
|
|
39
|
-
color: colors.textSecondary,
|
|
40
|
-
marginTop: '4px',
|
|
41
|
-
}}
|
|
42
|
-
>
|
|
43
|
-
{resource.name}
|
|
44
|
-
</div>
|
|
45
|
-
)}
|
|
46
|
-
{resource.description && (
|
|
37
|
+
<div style={{ display: 'flex', alignItems: 'flex-start', gap: '12px' }}>
|
|
47
38
|
<div
|
|
48
39
|
style={{
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
40
|
+
width: '6px',
|
|
41
|
+
height: '6px',
|
|
42
|
+
borderRadius: '50%',
|
|
43
|
+
background: isSelected ? colors.accentBlue : colors.textTertiary,
|
|
44
|
+
marginTop: '6px',
|
|
45
|
+
flexShrink: 0,
|
|
46
|
+
transition: 'background 0.2s',
|
|
52
47
|
}}
|
|
53
|
-
|
|
54
|
-
|
|
48
|
+
/>
|
|
49
|
+
<div style={{ flex: 1, minWidth: 0 }}>
|
|
50
|
+
<div
|
|
51
|
+
style={{
|
|
52
|
+
fontWeight: isSelected ? '600' : '500',
|
|
53
|
+
fontSize: '14px',
|
|
54
|
+
color: colors.textPrimary,
|
|
55
|
+
marginBottom: resource.name || resource.description ? '6px' : '0',
|
|
56
|
+
lineHeight: '1.4',
|
|
57
|
+
wordBreak: 'break-word',
|
|
58
|
+
}}
|
|
59
|
+
>
|
|
60
|
+
{resource.uri}
|
|
61
|
+
</div>
|
|
62
|
+
{resource.name && (
|
|
63
|
+
<div
|
|
64
|
+
style={{
|
|
65
|
+
fontSize: '13px',
|
|
66
|
+
color: colors.textPrimary,
|
|
67
|
+
fontWeight: '500',
|
|
68
|
+
lineHeight: '1.5',
|
|
69
|
+
marginTop: '4px',
|
|
70
|
+
}}
|
|
71
|
+
>
|
|
72
|
+
{resource.name}
|
|
73
|
+
</div>
|
|
74
|
+
)}
|
|
75
|
+
{resource.description && (
|
|
76
|
+
<div
|
|
77
|
+
style={{
|
|
78
|
+
fontSize: '12px',
|
|
79
|
+
color: colors.textSecondary,
|
|
80
|
+
lineHeight: '1.5',
|
|
81
|
+
marginTop: resource.name ? '2px' : '4px',
|
|
82
|
+
}}
|
|
83
|
+
>
|
|
84
|
+
{resource.description}
|
|
85
|
+
</div>
|
|
86
|
+
)}
|
|
55
87
|
</div>
|
|
56
|
-
|
|
57
|
-
</
|
|
88
|
+
</div>
|
|
89
|
+
</button>
|
|
58
90
|
);
|
|
59
91
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { colors } from '../../../theme';
|
|
2
|
-
import LoadingState from '../common/LoadingState';
|
|
3
|
-
import ErrorState from '../common/ErrorState';
|
|
4
2
|
import EmptyState from '../common/EmptyState';
|
|
3
|
+
import ErrorState from '../common/ErrorState';
|
|
4
|
+
import LoadingState from '../common/LoadingState';
|
|
5
5
|
import ResourceItem from './ResourceItem';
|
|
6
6
|
|
|
7
7
|
export default function ResourcesList({
|
|
@@ -26,19 +26,21 @@ export default function ResourcesList({
|
|
|
26
26
|
<LoadingState message="Waiting for MCP server to start..." />
|
|
27
27
|
) : resourcesLoading || !resourcesLoaded ? (
|
|
28
28
|
<LoadingState message="Loading resources..." />
|
|
29
|
-
) : error
|
|
29
|
+
) : error?.includes('resources:') ? (
|
|
30
30
|
<ErrorState message={`Error loading resources: ${error.replace('resources: ', '')}`} />
|
|
31
31
|
) : resources.length === 0 ? (
|
|
32
32
|
<EmptyState message="No resources available." />
|
|
33
33
|
) : (
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
34
|
+
<div style={{ padding: '8px 0' }}>
|
|
35
|
+
{resources.map((resource, idx) => (
|
|
36
|
+
<ResourceItem
|
|
37
|
+
key={resource.uri || `resource-${idx}`}
|
|
38
|
+
resource={resource}
|
|
39
|
+
isSelected={selectedResource?.uri === resource.uri}
|
|
40
|
+
onClick={() => onSelectResource(resource)}
|
|
41
|
+
/>
|
|
42
|
+
))}
|
|
43
|
+
</div>
|
|
42
44
|
)}
|
|
43
45
|
</div>
|
|
44
46
|
);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { colors, fonts } from '../../theme';
|
|
2
|
-
import ResourcesList from './ResourcesSection/ResourcesList';
|
|
3
2
|
import ResourceCallPanel from './ResourcesSection/ResourceCallPanel';
|
|
3
|
+
import ResourcesList from './ResourcesSection/ResourcesList';
|
|
4
4
|
|
|
5
5
|
export default function ResourcesSection({
|
|
6
6
|
resources,
|
|
@@ -19,6 +19,7 @@ export default function ResourcesSection({
|
|
|
19
19
|
<div style={{ display: 'flex', flexDirection: 'column', gap: '16px', height: '100%' }}>
|
|
20
20
|
<div style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>
|
|
21
21
|
<button
|
|
22
|
+
type="button"
|
|
22
23
|
onClick={onRefresh}
|
|
23
24
|
disabled={loading || resourcesLoading}
|
|
24
25
|
style={{
|
|
@@ -43,6 +43,7 @@ export default function ToolCallPanel({
|
|
|
43
43
|
>
|
|
44
44
|
<div>
|
|
45
45
|
<label
|
|
46
|
+
htmlFor="tool-args-textarea"
|
|
46
47
|
style={{
|
|
47
48
|
display: 'block',
|
|
48
49
|
fontSize: '12px',
|
|
@@ -54,6 +55,7 @@ export default function ToolCallPanel({
|
|
|
54
55
|
Arguments (JSON)
|
|
55
56
|
</label>
|
|
56
57
|
<textarea
|
|
58
|
+
id="tool-args-textarea"
|
|
57
59
|
value={toolArgs}
|
|
58
60
|
onChange={(e) => onToolArgsChange(e.target.value)}
|
|
59
61
|
style={{
|
|
@@ -71,6 +73,7 @@ export default function ToolCallPanel({
|
|
|
71
73
|
/>
|
|
72
74
|
</div>
|
|
73
75
|
<button
|
|
76
|
+
type="button"
|
|
74
77
|
onClick={onCallTool}
|
|
75
78
|
disabled={loading}
|
|
76
79
|
style={{
|
|
@@ -91,6 +94,7 @@ export default function ToolCallPanel({
|
|
|
91
94
|
{toolResult && (
|
|
92
95
|
<div>
|
|
93
96
|
<label
|
|
97
|
+
htmlFor="tool-result-pre"
|
|
94
98
|
style={{
|
|
95
99
|
display: 'block',
|
|
96
100
|
fontSize: '12px',
|
|
@@ -102,6 +106,7 @@ export default function ToolCallPanel({
|
|
|
102
106
|
Result
|
|
103
107
|
</label>
|
|
104
108
|
<pre
|
|
109
|
+
id="tool-result-pre"
|
|
105
110
|
style={{
|
|
106
111
|
padding: '12px',
|
|
107
112
|
background: colors.bgSecondary,
|
|
@@ -2,47 +2,76 @@ import { colors } from '../../../theme';
|
|
|
2
2
|
|
|
3
3
|
export default function ToolItem({ tool, isSelected, onClick }) {
|
|
4
4
|
return (
|
|
5
|
-
<
|
|
5
|
+
<button
|
|
6
|
+
type="button"
|
|
6
7
|
onClick={onClick}
|
|
8
|
+
aria-label={`Select tool ${tool.name}`}
|
|
7
9
|
style={{
|
|
8
|
-
padding: '
|
|
9
|
-
|
|
10
|
+
padding: '16px 20px',
|
|
11
|
+
margin: '4px 8px',
|
|
12
|
+
borderRadius: '8px',
|
|
10
13
|
cursor: 'pointer',
|
|
11
|
-
background: isSelected ? colors.
|
|
12
|
-
|
|
14
|
+
background: isSelected ? colors.bgSelected : colors.bgCard,
|
|
15
|
+
border: isSelected ? `2px solid ${colors.accentBlue}` : `1px solid ${colors.borderLight}`,
|
|
16
|
+
boxShadow: isSelected ? `0 2px 4px ${colors.shadowSm}` : 'none',
|
|
17
|
+
transition: 'all 0.2s ease',
|
|
18
|
+
position: 'relative',
|
|
19
|
+
width: '100%',
|
|
20
|
+
textAlign: 'left',
|
|
13
21
|
}}
|
|
14
22
|
onMouseEnter={(e) => {
|
|
15
23
|
if (!isSelected) {
|
|
16
24
|
e.currentTarget.style.background = colors.bgHover;
|
|
25
|
+
e.currentTarget.style.borderColor = colors.borderMedium;
|
|
26
|
+
e.currentTarget.style.boxShadow = `0 2px 4px ${colors.shadowSm}`;
|
|
17
27
|
}
|
|
18
28
|
}}
|
|
19
29
|
onMouseLeave={(e) => {
|
|
20
30
|
if (!isSelected) {
|
|
21
31
|
e.currentTarget.style.background = colors.bgCard;
|
|
32
|
+
e.currentTarget.style.borderColor = colors.borderLight;
|
|
33
|
+
e.currentTarget.style.boxShadow = 'none';
|
|
22
34
|
}
|
|
23
35
|
}}
|
|
24
36
|
>
|
|
25
|
-
<div
|
|
26
|
-
style={{
|
|
27
|
-
fontWeight: '500',
|
|
28
|
-
fontSize: '13px',
|
|
29
|
-
color: colors.textPrimary,
|
|
30
|
-
marginBottom: '4px',
|
|
31
|
-
}}
|
|
32
|
-
>
|
|
33
|
-
{tool.name}
|
|
34
|
-
</div>
|
|
35
|
-
{tool.description && (
|
|
37
|
+
<div style={{ display: 'flex', alignItems: 'flex-start', gap: '12px' }}>
|
|
36
38
|
<div
|
|
37
39
|
style={{
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
40
|
+
width: '6px',
|
|
41
|
+
height: '6px',
|
|
42
|
+
borderRadius: '50%',
|
|
43
|
+
background: isSelected ? colors.accentBlue : colors.textTertiary,
|
|
44
|
+
marginTop: '6px',
|
|
45
|
+
flexShrink: 0,
|
|
46
|
+
transition: 'background 0.2s',
|
|
41
47
|
}}
|
|
42
|
-
|
|
43
|
-
|
|
48
|
+
/>
|
|
49
|
+
<div style={{ flex: 1, minWidth: 0 }}>
|
|
50
|
+
<div
|
|
51
|
+
style={{
|
|
52
|
+
fontWeight: isSelected ? '600' : '500',
|
|
53
|
+
fontSize: '14px',
|
|
54
|
+
color: colors.textPrimary,
|
|
55
|
+
marginBottom: tool.description ? '6px' : '0',
|
|
56
|
+
lineHeight: '1.4',
|
|
57
|
+
}}
|
|
58
|
+
>
|
|
59
|
+
{tool.name}
|
|
60
|
+
</div>
|
|
61
|
+
{tool.description && (
|
|
62
|
+
<div
|
|
63
|
+
style={{
|
|
64
|
+
fontSize: '12px',
|
|
65
|
+
color: colors.textSecondary,
|
|
66
|
+
lineHeight: '1.5',
|
|
67
|
+
marginTop: '4px',
|
|
68
|
+
}}
|
|
69
|
+
>
|
|
70
|
+
{tool.description}
|
|
71
|
+
</div>
|
|
72
|
+
)}
|
|
44
73
|
</div>
|
|
45
|
-
|
|
46
|
-
</
|
|
74
|
+
</div>
|
|
75
|
+
</button>
|
|
47
76
|
);
|
|
48
77
|
}
|