@mcp-shark/mcp-shark 1.5.3 → 1.5.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (188) hide show
  1. package/README.md +32 -96
  2. package/bin/mcp-shark.js +1 -1
  3. package/core/configs/codex.js +68 -0
  4. package/core/configs/environment.js +51 -0
  5. package/{lib/common → core}/configs/index.js +16 -1
  6. package/core/constants/Defaults.js +15 -0
  7. package/core/constants/HttpStatus.js +14 -0
  8. package/core/constants/Server.js +20 -0
  9. package/core/constants/StatusCodes.js +25 -0
  10. package/core/constants/index.js +7 -0
  11. package/core/container/DependencyContainer.js +179 -0
  12. package/core/db/init.js +33 -0
  13. package/core/index.js +10 -0
  14. package/{mcp-server/lib/common/error.js → core/libraries/ErrorLibrary.js} +4 -0
  15. package/core/libraries/LoggerLibrary.js +91 -0
  16. package/core/libraries/SerializationLibrary.js +32 -0
  17. package/core/libraries/bootstrap-logger.js +19 -0
  18. package/core/libraries/errors/ApplicationError.js +97 -0
  19. package/core/libraries/index.js +17 -0
  20. package/{mcp-server/lib → core/mcp-server}/auditor/audit.js +77 -53
  21. package/core/mcp-server/index.js +192 -0
  22. package/{mcp-server/lib → core/mcp-server}/server/external/all.js +1 -1
  23. package/core/mcp-server/server/external/config.js +75 -0
  24. package/{mcp-server/lib → core/mcp-server}/server/external/single/client.js +1 -1
  25. package/{mcp-server/lib → core/mcp-server}/server/external/single/request.js +1 -1
  26. package/{mcp-server/lib → core/mcp-server}/server/external/single/run.js +20 -11
  27. package/{mcp-server/lib → core/mcp-server}/server/external/single/transport.js +1 -1
  28. package/{mcp-server/lib → core/mcp-server}/server/internal/handlers/error.js +1 -1
  29. package/core/mcp-server/server/internal/handlers/prompts-get.js +28 -0
  30. package/core/mcp-server/server/internal/handlers/prompts-list.js +21 -0
  31. package/core/mcp-server/server/internal/handlers/resources-list.js +21 -0
  32. package/core/mcp-server/server/internal/handlers/resources-read.js +28 -0
  33. package/core/mcp-server/server/internal/handlers/tools-call.js +44 -0
  34. package/core/mcp-server/server/internal/handlers/tools-list.js +23 -0
  35. package/core/mcp-server/server/internal/run.js +53 -0
  36. package/{mcp-server/lib → core/mcp-server}/server/internal/server.js +11 -1
  37. package/core/models/ConversationFilters.js +31 -0
  38. package/core/models/ExportFormat.js +8 -0
  39. package/core/models/RequestFilters.js +43 -0
  40. package/core/models/SessionFilters.js +23 -0
  41. package/core/models/index.js +8 -0
  42. package/core/repositories/AuditRepository.js +233 -0
  43. package/core/repositories/ConversationRepository.js +182 -0
  44. package/core/repositories/PacketRepository.js +237 -0
  45. package/core/repositories/SchemaRepository.js +107 -0
  46. package/core/repositories/SessionRepository.js +59 -0
  47. package/core/repositories/StatisticsRepository.js +54 -0
  48. package/core/repositories/index.js +10 -0
  49. package/core/services/AuditService.js +144 -0
  50. package/core/services/BackupService.js +222 -0
  51. package/core/services/ConfigDetectionService.js +89 -0
  52. package/core/services/ConfigFileService.js +210 -0
  53. package/core/services/ConfigPatchingService.js +137 -0
  54. package/core/services/ConfigService.js +250 -0
  55. package/core/services/ConfigTransformService.js +178 -0
  56. package/core/services/ConversationService.js +19 -0
  57. package/core/services/ExportService.js +117 -0
  58. package/core/services/LogService.js +64 -0
  59. package/core/services/McpClientService.js +235 -0
  60. package/core/services/McpDiscoveryService.js +107 -0
  61. package/core/services/RequestService.js +56 -0
  62. package/core/services/ScanCacheService.js +242 -0
  63. package/core/services/ScanService.js +167 -0
  64. package/core/services/ServerManagementService.js +206 -0
  65. package/core/services/SessionService.js +34 -0
  66. package/core/services/SettingsService.js +163 -0
  67. package/core/services/StatisticsService.js +64 -0
  68. package/core/services/TokenService.js +94 -0
  69. package/core/services/index.js +25 -0
  70. package/core/services/parsers/ConfigParserFactory.js +113 -0
  71. package/core/services/parsers/JsonConfigParser.js +66 -0
  72. package/core/services/parsers/LegacyJsonConfigParser.js +71 -0
  73. package/core/services/parsers/TomlConfigParser.js +87 -0
  74. package/core/services/parsers/index.js +4 -0
  75. package/{ui/server → core}/utils/scan-cache/directory.js +1 -1
  76. package/core/utils/validation.js +77 -0
  77. package/package.json +14 -11
  78. package/ui/dist/assets/index-CArYxKxS.js +35 -0
  79. package/ui/dist/index.html +1 -1
  80. package/ui/server/controllers/BackupController.js +129 -0
  81. package/ui/server/controllers/ConfigController.js +92 -0
  82. package/ui/server/controllers/ConversationController.js +41 -0
  83. package/ui/server/controllers/LogController.js +44 -0
  84. package/ui/server/controllers/McpClientController.js +60 -0
  85. package/ui/server/controllers/McpDiscoveryController.js +44 -0
  86. package/ui/server/controllers/RequestController.js +129 -0
  87. package/ui/server/controllers/ScanController.js +122 -0
  88. package/ui/server/controllers/ServerManagementController.js +134 -0
  89. package/ui/server/controllers/SessionController.js +57 -0
  90. package/ui/server/controllers/SettingsController.js +24 -0
  91. package/ui/server/controllers/StatisticsController.js +54 -0
  92. package/ui/server/controllers/TokenController.js +58 -0
  93. package/ui/server/controllers/index.js +17 -0
  94. package/ui/server/routes/backups/index.js +15 -9
  95. package/ui/server/routes/composite/index.js +62 -32
  96. package/ui/server/routes/composite/servers.js +20 -15
  97. package/ui/server/routes/config.js +13 -172
  98. package/ui/server/routes/conversations.js +9 -19
  99. package/ui/server/routes/help.js +4 -3
  100. package/ui/server/routes/logs.js +14 -26
  101. package/ui/server/routes/playground.js +11 -174
  102. package/ui/server/routes/requests.js +12 -232
  103. package/ui/server/routes/sessions.js +10 -21
  104. package/ui/server/routes/settings.js +10 -192
  105. package/ui/server/routes/smartscan.js +26 -15
  106. package/ui/server/routes/statistics.js +8 -79
  107. package/ui/server/setup.js +162 -0
  108. package/ui/server/swagger/paths/backups.js +151 -0
  109. package/ui/server/swagger/paths/components.js +76 -0
  110. package/ui/server/swagger/paths/config.js +117 -0
  111. package/ui/server/swagger/paths/conversations.js +29 -0
  112. package/ui/server/swagger/paths/help.js +82 -0
  113. package/ui/server/swagger/paths/logs.js +87 -0
  114. package/ui/server/swagger/paths/playground.js +49 -0
  115. package/ui/server/swagger/paths/requests.js +178 -0
  116. package/ui/server/swagger/paths/serverManagement.js +169 -0
  117. package/ui/server/swagger/paths/sessions.js +61 -0
  118. package/ui/server/swagger/paths/settings.js +31 -0
  119. package/ui/server/swagger/paths/smartScan/discovery.js +97 -0
  120. package/ui/server/swagger/paths/smartScan/index.js +13 -0
  121. package/ui/server/swagger/paths/smartScan/scans.js +151 -0
  122. package/ui/server/swagger/paths/smartScan/token.js +71 -0
  123. package/ui/server/swagger/paths/statistics.js +40 -0
  124. package/ui/server/swagger/paths.js +38 -0
  125. package/ui/server/swagger/swagger.js +37 -0
  126. package/ui/server/utils/cleanup.js +99 -0
  127. package/ui/server/utils/config.js +18 -96
  128. package/ui/server/utils/errorHandler.js +43 -0
  129. package/ui/server/utils/logger.js +2 -2
  130. package/ui/server/utils/paths.js +27 -30
  131. package/ui/server/utils/port.js +21 -21
  132. package/ui/server/utils/process.js +18 -10
  133. package/ui/server/utils/processState.js +17 -0
  134. package/ui/server/utils/signals.js +34 -0
  135. package/ui/server/websocket/broadcast.js +33 -0
  136. package/ui/server/websocket/handler.js +52 -0
  137. package/ui/server.js +51 -230
  138. package/ui/src/App.jsx +2 -0
  139. package/ui/src/CompositeSetup.jsx +23 -9
  140. package/ui/src/PacketFilters.jsx +17 -3
  141. package/ui/src/components/AlertModal.jsx +116 -0
  142. package/ui/src/components/App/ApiDocsButton.jsx +57 -0
  143. package/ui/src/components/App/useAppState.js +43 -1
  144. package/ui/src/components/BackupList.jsx +27 -3
  145. package/ui/src/utils/requestPairing.js +35 -36
  146. package/ui/src/utils/requestUtils.js +1 -0
  147. package/lib/common/db/init.js +0 -132
  148. package/lib/common/db/logger.js +0 -349
  149. package/lib/common/db/query.js +0 -403
  150. package/lib/common/logger.js +0 -90
  151. package/mcp-server/index.js +0 -111
  152. package/mcp-server/lib/server/external/config.js +0 -57
  153. package/mcp-server/lib/server/internal/handlers/prompts-get.js +0 -20
  154. package/mcp-server/lib/server/internal/handlers/prompts-list.js +0 -13
  155. package/mcp-server/lib/server/internal/handlers/resources-list.js +0 -13
  156. package/mcp-server/lib/server/internal/handlers/resources-read.js +0 -20
  157. package/mcp-server/lib/server/internal/handlers/tools-call.js +0 -35
  158. package/mcp-server/lib/server/internal/handlers/tools-list.js +0 -15
  159. package/mcp-server/lib/server/internal/run.js +0 -37
  160. package/mcp-server/mcp-shark.js +0 -22
  161. package/ui/dist/assets/index-CFHeMNwd.js +0 -35
  162. package/ui/server/routes/backups/deleteBackup.js +0 -54
  163. package/ui/server/routes/backups/listBackups.js +0 -75
  164. package/ui/server/routes/backups/restoreBackup.js +0 -83
  165. package/ui/server/routes/backups/viewBackup.js +0 -47
  166. package/ui/server/routes/composite/setup.js +0 -129
  167. package/ui/server/routes/composite/status.js +0 -7
  168. package/ui/server/routes/composite/stop.js +0 -39
  169. package/ui/server/routes/composite/utils.js +0 -45
  170. package/ui/server/routes/smartscan/discover.js +0 -118
  171. package/ui/server/routes/smartscan/scans/clearCache.js +0 -23
  172. package/ui/server/routes/smartscan/scans/createBatchScans.js +0 -124
  173. package/ui/server/routes/smartscan/scans/createScan.js +0 -43
  174. package/ui/server/routes/smartscan/scans/getCachedResults.js +0 -52
  175. package/ui/server/routes/smartscan/scans/getScan.js +0 -42
  176. package/ui/server/routes/smartscan/scans/listScans.js +0 -25
  177. package/ui/server/routes/smartscan/scans.js +0 -13
  178. package/ui/server/routes/smartscan/token.js +0 -57
  179. package/ui/server/utils/config-update.js +0 -240
  180. package/ui/server/utils/scan-cache/all-results.js +0 -197
  181. package/ui/server/utils/scan-cache/file-operations.js +0 -107
  182. package/ui/server/utils/scan-cache/hash.js +0 -47
  183. package/ui/server/utils/scan-cache/server-operations.js +0 -85
  184. package/ui/server/utils/scan-cache.js +0 -12
  185. package/ui/server/utils/smartscan-token.js +0 -43
  186. /package/{mcp-server/lib → core/mcp-server}/server/external/kv.js +0 -0
  187. /package/{mcp-server/lib → core/mcp-server}/server/internal/handlers/common.js +0 -0
  188. /package/{mcp-server/lib → core/mcp-server}/server/internal/session.js +0 -0
@@ -0,0 +1,151 @@
1
+ /**
2
+ * Smart Scan - Scan endpoints
3
+ * Create, list, and retrieve scan results
4
+ */
5
+
6
+ export const scansPaths = {
7
+ '/api/smartscan/scans': {
8
+ get: {
9
+ tags: ['Smart Scan'],
10
+ summary: 'List scans',
11
+ description: 'Get a list of all cached Smart Scan results',
12
+ responses: {
13
+ 200: {
14
+ description: 'List of scans',
15
+ content: {
16
+ 'application/json': {
17
+ schema: {
18
+ type: 'array',
19
+ items: { $ref: '#/components/schemas/Scan' },
20
+ },
21
+ },
22
+ },
23
+ },
24
+ 500: {
25
+ description: 'Internal server error',
26
+ },
27
+ },
28
+ },
29
+ post: {
30
+ tags: ['Smart Scan'],
31
+ summary: 'Create scan',
32
+ description: 'Create a new Smart Scan for an MCP server',
33
+ requestBody: {
34
+ required: true,
35
+ content: {
36
+ 'application/json': {
37
+ schema: {
38
+ type: 'object',
39
+ required: ['apiToken', 'scanData'],
40
+ properties: {
41
+ apiToken: { type: 'string', description: 'Smart Scan API token' },
42
+ scanData: {
43
+ type: 'object',
44
+ description: 'Scan configuration data',
45
+ },
46
+ },
47
+ },
48
+ },
49
+ },
50
+ },
51
+ responses: {
52
+ 200: {
53
+ description: 'Scan created',
54
+ content: {
55
+ 'application/json': {
56
+ schema: { $ref: '#/components/schemas/Scan' },
57
+ },
58
+ },
59
+ },
60
+ 400: {
61
+ description: 'Bad request',
62
+ },
63
+ 500: {
64
+ description: 'Internal server error',
65
+ },
66
+ },
67
+ },
68
+ },
69
+ '/api/smartscan/scans/{scanId}': {
70
+ get: {
71
+ tags: ['Smart Scan'],
72
+ summary: 'Get scan',
73
+ description: 'Get details of a specific scan',
74
+ parameters: [
75
+ {
76
+ name: 'scanId',
77
+ in: 'path',
78
+ required: true,
79
+ description: 'Scan ID',
80
+ schema: { type: 'string' },
81
+ },
82
+ ],
83
+ responses: {
84
+ 200: {
85
+ description: 'Scan details',
86
+ content: {
87
+ 'application/json': {
88
+ schema: { $ref: '#/components/schemas/Scan' },
89
+ },
90
+ },
91
+ },
92
+ 404: {
93
+ description: 'Scan not found',
94
+ },
95
+ 500: {
96
+ description: 'Internal server error',
97
+ },
98
+ },
99
+ },
100
+ },
101
+ '/api/smartscan/scans/batch': {
102
+ post: {
103
+ tags: ['Smart Scan'],
104
+ summary: 'Create batch scans',
105
+ description: 'Create multiple scans for multiple servers',
106
+ requestBody: {
107
+ required: true,
108
+ content: {
109
+ 'application/json': {
110
+ schema: {
111
+ type: 'object',
112
+ required: ['apiToken', 'servers'],
113
+ properties: {
114
+ apiToken: { type: 'string', description: 'Smart Scan API token' },
115
+ servers: {
116
+ type: 'array',
117
+ items: { type: 'object' },
118
+ description: 'List of server configurations to scan',
119
+ },
120
+ },
121
+ },
122
+ },
123
+ },
124
+ },
125
+ responses: {
126
+ 200: {
127
+ description: 'Batch scans created',
128
+ content: {
129
+ 'application/json': {
130
+ schema: {
131
+ type: 'object',
132
+ properties: {
133
+ scans: {
134
+ type: 'array',
135
+ items: { $ref: '#/components/schemas/Scan' },
136
+ },
137
+ },
138
+ },
139
+ },
140
+ },
141
+ },
142
+ 400: {
143
+ description: 'Bad request',
144
+ },
145
+ 500: {
146
+ description: 'Internal server error',
147
+ },
148
+ },
149
+ },
150
+ },
151
+ };
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Smart Scan - Token management endpoints
3
+ */
4
+
5
+ export const tokenPaths = {
6
+ '/api/smartscan/token': {
7
+ get: {
8
+ tags: ['Smart Scan'],
9
+ summary: 'Get API token',
10
+ description: 'Get the stored Smart Scan API token',
11
+ responses: {
12
+ 200: {
13
+ description: 'API token',
14
+ content: {
15
+ 'application/json': {
16
+ schema: {
17
+ type: 'object',
18
+ properties: {
19
+ token: { type: 'string', nullable: true },
20
+ },
21
+ },
22
+ },
23
+ },
24
+ },
25
+ 500: {
26
+ description: 'Internal server error',
27
+ },
28
+ },
29
+ },
30
+ post: {
31
+ tags: ['Smart Scan'],
32
+ summary: 'Save API token',
33
+ description: 'Save the Smart Scan API token',
34
+ requestBody: {
35
+ required: true,
36
+ content: {
37
+ 'application/json': {
38
+ schema: {
39
+ type: 'object',
40
+ required: ['token'],
41
+ properties: {
42
+ token: { type: 'string', description: 'Smart Scan API token' },
43
+ },
44
+ },
45
+ },
46
+ },
47
+ },
48
+ responses: {
49
+ 200: {
50
+ description: 'Token saved',
51
+ content: {
52
+ 'application/json': {
53
+ schema: {
54
+ type: 'object',
55
+ properties: {
56
+ success: { type: 'boolean' },
57
+ },
58
+ },
59
+ },
60
+ },
61
+ },
62
+ 400: {
63
+ description: 'Bad request',
64
+ },
65
+ 500: {
66
+ description: 'Internal server error',
67
+ },
68
+ },
69
+ },
70
+ },
71
+ };
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Statistics endpoints - Traffic statistics and analytics
3
+ */
4
+
5
+ export const statisticsPaths = {
6
+ '/api/statistics': {
7
+ get: {
8
+ tags: ['Statistics'],
9
+ summary: 'Get traffic statistics',
10
+ description: 'Retrieve aggregated statistics about captured traffic',
11
+ parameters: [
12
+ {
13
+ name: 'sessionId',
14
+ in: 'query',
15
+ description: 'Filter by session ID',
16
+ schema: { type: 'string' },
17
+ },
18
+ {
19
+ name: 'serverName',
20
+ in: 'query',
21
+ description: 'Filter by server name',
22
+ schema: { type: 'string' },
23
+ },
24
+ ],
25
+ responses: {
26
+ 200: {
27
+ description: 'Traffic statistics',
28
+ content: {
29
+ 'application/json': {
30
+ schema: { $ref: '#/components/schemas/Statistics' },
31
+ },
32
+ },
33
+ },
34
+ 500: {
35
+ description: 'Internal server error',
36
+ },
37
+ },
38
+ },
39
+ },
40
+ };
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Swagger/OpenAPI path definitions for MCP Shark API
3
+ * Aggregates all endpoint documentation from individual files
4
+ */
5
+
6
+ import { backupsPaths } from './paths/backups.js';
7
+ import { components } from './paths/components.js';
8
+ import { configPaths } from './paths/config.js';
9
+ import { conversationsPaths } from './paths/conversations.js';
10
+ import { helpPaths } from './paths/help.js';
11
+ import { logsPaths } from './paths/logs.js';
12
+ import { playgroundPaths } from './paths/playground.js';
13
+ import { requestsPaths } from './paths/requests.js';
14
+ import { serverManagementPaths } from './paths/serverManagement.js';
15
+ import { sessionsPaths } from './paths/sessions.js';
16
+ import { settingsPaths } from './paths/settings.js';
17
+ import { smartScanPaths } from './paths/smartScan/index.js';
18
+ import { statisticsPaths } from './paths/statistics.js';
19
+
20
+ /**
21
+ * Combine all path definitions into a single object
22
+ */
23
+ export const paths = {
24
+ ...requestsPaths,
25
+ ...sessionsPaths,
26
+ ...conversationsPaths,
27
+ ...statisticsPaths,
28
+ ...logsPaths,
29
+ ...configPaths,
30
+ ...backupsPaths,
31
+ ...serverManagementPaths,
32
+ ...helpPaths,
33
+ ...playgroundPaths,
34
+ ...smartScanPaths,
35
+ ...settingsPaths,
36
+ };
37
+
38
+ export { components };
@@ -0,0 +1,37 @@
1
+ import { components, paths } from './paths.js';
2
+
3
+ export const swaggerSpec = {
4
+ openapi: '3.0.0',
5
+ info: {
6
+ title: 'MCP Shark API',
7
+ version: '1.5.4',
8
+ description:
9
+ 'API documentation for MCP Shark - A powerful monitoring and debugging tool for Model Context Protocol (MCP) servers. Provides deep visibility into every request and response.',
10
+ contact: {
11
+ name: 'MCP Shark',
12
+ url: 'https://mcpshark.sh',
13
+ },
14
+ },
15
+ servers: [
16
+ {
17
+ url: 'http://localhost:9853',
18
+ description: 'Development server',
19
+ },
20
+ ],
21
+ tags: [
22
+ { name: 'Requests', description: 'Traffic capture and request/response analysis' },
23
+ { name: 'Sessions', description: 'Session management and tracking' },
24
+ { name: 'Conversations', description: 'Conversation tracking and analysis' },
25
+ { name: 'Statistics', description: 'Traffic statistics and analytics' },
26
+ { name: 'Logs', description: 'MCP Shark server logs' },
27
+ { name: 'Config', description: 'Configuration file management' },
28
+ { name: 'Backups', description: 'Configuration backup management' },
29
+ { name: 'Server Management', description: 'MCP Shark server lifecycle management' },
30
+ { name: 'Help', description: 'Help and tour management' },
31
+ { name: 'Playground', description: 'MCP playground for testing tools and resources' },
32
+ { name: 'Smart Scan', description: 'AI-powered security analysis for MCP servers' },
33
+ { name: 'Settings', description: 'Application settings' },
34
+ ],
35
+ paths,
36
+ components,
37
+ };
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Perform cleanup operations on shutdown
3
+ * @param {number} intervalId - Interval ID to clear
4
+ * @param {object} processState - Process state object
5
+ * @param {Set} clients - Set of WebSocket clients
6
+ * @param {WebSocketServer} wss - WebSocket server instance
7
+ * @param {object} server - HTTP server instance
8
+ * @param {object} container - Dependency container
9
+ * @param {object} logger - Logger instance
10
+ * @returns {Promise} Promise that resolves when cleanup is complete
11
+ */
12
+ export async function performCleanup(
13
+ intervalId,
14
+ processState,
15
+ clients,
16
+ wss,
17
+ server,
18
+ container,
19
+ logger
20
+ ) {
21
+ logger?.info('Shutting down UI server...');
22
+
23
+ // Clear interval
24
+ clearInterval(intervalId);
25
+
26
+ // Stop MCP Shark server if running and restore config
27
+ // Use ServerManagementService to ensure proper cleanup and config restoration
28
+ try {
29
+ const serverManagementService = container.getService('serverManagement');
30
+ const serverStatus = serverManagementService.getServerStatus();
31
+
32
+ if (serverStatus.running) {
33
+ logger?.info('Stopping MCP Shark server...');
34
+ try {
35
+ await serverManagementService.stopServer();
36
+ logger?.info('MCP Shark server stopped');
37
+ } catch (stopErr) {
38
+ logger?.warn(
39
+ { error: stopErr.message },
40
+ 'Error stopping MCP Shark server, continuing with cleanup'
41
+ );
42
+ }
43
+ }
44
+ } catch (err) {
45
+ logger?.warn(
46
+ { error: err.message },
47
+ 'Error accessing server management service during shutdown'
48
+ );
49
+ // Continue with cleanup even if server stop fails
50
+ }
51
+
52
+ // Restore config (always attempt, even if server stop failed)
53
+ // This ensures patched configs are restored on exit
54
+ try {
55
+ const configService = container.getService('config');
56
+ const restored = configService.restoreOriginalConfig();
57
+ if (restored) {
58
+ logger?.info('Config restored successfully');
59
+ }
60
+ } catch (configErr) {
61
+ logger?.warn({ error: configErr.message }, 'Failed to restore config during shutdown');
62
+ // Continue anyway - config restoration failure shouldn't prevent exit
63
+ }
64
+
65
+ // Clear process state
66
+ processState.mcpSharkServer = null;
67
+
68
+ // Close WebSocket connections
69
+ try {
70
+ for (const client of clients) {
71
+ if (client.readyState === 1) {
72
+ client.close();
73
+ }
74
+ }
75
+ clients.clear();
76
+ } catch (err) {
77
+ logger?.warn({ error: err.message }, 'Error closing WebSocket connections');
78
+ }
79
+
80
+ // Close WebSocket server
81
+ try {
82
+ wss.close();
83
+ } catch (err) {
84
+ logger?.warn({ error: err.message }, 'Error closing WebSocket server');
85
+ }
86
+
87
+ // Close HTTP server
88
+ return new Promise((resolve) => {
89
+ try {
90
+ server.close(() => {
91
+ logger?.info('UI server stopped');
92
+ resolve();
93
+ });
94
+ } catch (err) {
95
+ logger?.warn({ error: err.message }, 'Error closing HTTP server');
96
+ resolve(); // Resolve anyway to allow exit
97
+ }
98
+ });
99
+ }
@@ -1,98 +1,20 @@
1
- import * as fs from 'node:fs';
2
- import logger from './logger.js';
3
-
4
- const state = { originalConfigData: null };
5
-
6
- export function storeOriginalConfig(filePath, originalContent, backupPath) {
7
- state.originalConfigData = { filePath, originalContent, backupPath };
8
- }
9
-
10
- export function restoreOriginalConfig(mcpSharkLogs, broadcastLogUpdate) {
11
- if (state.originalConfigData?.filePath) {
12
- try {
13
- if (fs.existsSync(state.originalConfigData.filePath)) {
14
- fs.writeFileSync(
15
- state.originalConfigData.filePath,
16
- state.originalConfigData.originalContent
17
- );
18
- logger.info({ path: state.originalConfigData.filePath }, 'Restored original config');
19
- state.originalConfigData = null;
20
- return true;
21
- }
22
- state.originalConfigData = null;
23
- return false;
24
- } catch (error) {
25
- logger.error({ error: error.message }, 'Failed to restore original config');
26
- const timestamp = new Date().toISOString();
27
- const errorLog = {
28
- timestamp,
29
- type: 'error',
30
- line: `[RESTORE ERROR] Failed to restore: ${error.message}`,
31
- };
32
- mcpSharkLogs.push(errorLog);
33
- if (mcpSharkLogs.length > 10000) {
34
- mcpSharkLogs.shift();
35
- }
36
- broadcastLogUpdate(errorLog);
37
- return false;
38
- }
39
- }
40
- return false;
41
- }
42
-
43
- export function clearOriginalConfig() {
44
- state.originalConfigData = null;
45
- }
46
-
47
- export function convertMcpServersToServers(config) {
48
- const { mcpServers, servers } = config;
49
- const converted = { servers: {} };
50
-
51
- if (servers) {
52
- converted.servers = servers;
53
- }
54
-
55
- if (mcpServers) {
56
- Object.entries(mcpServers).forEach(([name, cfg]) => {
57
- const type = cfg.type || (cfg.url ? 'http' : cfg.command ? 'stdio' : 'stdio');
58
- converted.servers[name] = { type, ...cfg };
59
- });
60
- }
61
-
62
- return converted;
63
- }
64
-
65
- export function extractServices(config) {
66
- const { mcpServers, servers } = config;
67
- const servicesMap = new Map();
68
-
69
- if (servers) {
70
- Object.entries(servers).forEach(([name, cfg]) => {
71
- const type = cfg.type || (cfg.url ? 'http' : cfg.command ? 'stdio' : 'stdio');
72
- servicesMap.set(name, {
73
- name,
74
- type,
75
- url: cfg.url || null,
76
- command: cfg.command || null,
77
- args: cfg.args || null,
78
- });
79
- });
80
- }
81
-
82
- if (mcpServers) {
83
- Object.entries(mcpServers).forEach(([name, cfg]) => {
84
- if (!servicesMap.has(name)) {
85
- const type = cfg.type || (cfg.url ? 'http' : cfg.command ? 'stdio' : 'stdio');
86
- servicesMap.set(name, {
87
- name,
88
- type,
89
- url: cfg.url || null,
90
- command: cfg.command || null,
91
- args: cfg.args || null,
92
- });
93
- }
94
- });
1
+ /**
2
+ * Restore original config from backup
3
+ * @param {object} container - Dependency container
4
+ * @returns {boolean} True if config was restored
5
+ */
6
+ export function restoreConfig(container) {
7
+ const configService = container.getService('config');
8
+ const logService = container.getService('log');
9
+ const restored = configService.restoreOriginalConfig();
10
+ if (restored) {
11
+ const timestamp = new Date().toISOString();
12
+ const restoreLog = {
13
+ timestamp,
14
+ type: 'stdout',
15
+ line: '[RESTORE] Restored original config',
16
+ };
17
+ logService.addLog(restoreLog);
95
18
  }
96
-
97
- return Array.from(servicesMap.values());
19
+ return restored;
98
20
  }
@@ -0,0 +1,43 @@
1
+ import { StatusCodes } from '#core/constants/index.js';
2
+ import { toApplicationError } from '#core/libraries/errors/ApplicationError.js';
3
+
4
+ /**
5
+ * Standardized error handler for controllers
6
+ * Converts errors to consistent HTTP responses
7
+ *
8
+ * @param {Error|ApplicationError} error - The error to handle
9
+ * @param {Object} res - Express response object
10
+ * @param {Object} logger - Logger instance
11
+ * @param {string} context - Context for logging (e.g., 'Error in endpoint name')
12
+ */
13
+ export function handleError(error, res, logger, context) {
14
+ const appError = toApplicationError(error, 'An unexpected error occurred');
15
+
16
+ // Log error with full context
17
+ logger?.error(
18
+ {
19
+ error: appError.message,
20
+ statusCode: appError.statusCode,
21
+ stack: appError.stack,
22
+ originalError: appError.error?.message,
23
+ },
24
+ context || 'Error handling request'
25
+ );
26
+
27
+ // Send standardized error response
28
+ res.status(appError.statusCode || StatusCodes.INTERNAL_SERVER_ERROR).json(appError.toResponse());
29
+ }
30
+
31
+ /**
32
+ * Handle validation errors specifically
33
+ * @param {string} message - Validation error message
34
+ * @param {Object} res - Express response object
35
+ * @param {Object} logger - Logger instance
36
+ */
37
+ export function handleValidationError(message, res, logger) {
38
+ logger?.warn({ message }, 'Validation error');
39
+ res.status(StatusCodes.BAD_REQUEST).json({
40
+ error: 'ValidationError',
41
+ message,
42
+ });
43
+ }
@@ -1,2 +1,2 @@
1
- // Re-export common logger
2
- export { default } from '#common/logger';
1
+ // Re-export bootstrap logger for UI server utilities
2
+ export { bootstrapLogger as default } from '#core/libraries/index.js';
@@ -23,10 +23,10 @@ function getNvmNodeBinPaths(homeDir) {
23
23
 
24
24
  export function findMcpServerPath() {
25
25
  const pathsToCheck = [
26
- path.join(process.cwd(), '../mcp-server'),
27
- path.join(__dirname, '../../mcp-server'),
28
- path.join(process.cwd(), 'mcp-server'),
29
- path.join(__dirname, '../../mcp-server'),
26
+ path.join(process.cwd(), 'core/mcp-server'),
27
+ path.join(__dirname, '../../../core/mcp-server'),
28
+ path.join(process.cwd(), '../core/mcp-server'),
29
+ path.join(__dirname, '../../../../core/mcp-server'),
30
30
  ];
31
31
 
32
32
  for (const possiblePath of pathsToCheck) {
@@ -35,7 +35,29 @@ export function findMcpServerPath() {
35
35
  }
36
36
  }
37
37
 
38
- return path.join(process.cwd(), '../mcp-server');
38
+ return path.join(process.cwd(), 'core/mcp-server');
39
+ }
40
+
41
+ function getPathOutput(shell, shellName) {
42
+ const execOptions = {
43
+ encoding: 'utf8',
44
+ timeout: 2000,
45
+ stdio: ['ignore', 'pipe', 'ignore'],
46
+ maxBuffer: 1024 * 1024,
47
+ env: {
48
+ ...Object.fromEntries(Object.entries(process.env).filter(([key]) => key !== 'PATH')),
49
+ },
50
+ };
51
+
52
+ if (shellName === 'zsh') {
53
+ try {
54
+ return execSync(`${shell} -i -c 'echo $PATH'`, execOptions);
55
+ } catch (_e) {
56
+ return execSync(`${shell} -l -c 'echo $PATH'`, execOptions);
57
+ }
58
+ }
59
+
60
+ return execSync(`${shell} -l -c 'echo $PATH'`, execOptions);
39
61
  }
40
62
 
41
63
  /**
@@ -60,31 +82,6 @@ function getSystemPath() {
60
82
  if (fs.existsSync(shell)) {
61
83
  try {
62
84
  const shellName = path.basename(shell);
63
-
64
- const getPathOutput = (shell, shellName) => {
65
- const execOptions = {
66
- encoding: 'utf8',
67
- timeout: 2000,
68
- stdio: ['ignore', 'pipe', 'ignore'],
69
- maxBuffer: 1024 * 1024,
70
- env: {
71
- ...Object.fromEntries(
72
- Object.entries(process.env).filter(([key]) => key !== 'PATH')
73
- ),
74
- },
75
- };
76
-
77
- if (shellName === 'zsh') {
78
- try {
79
- return execSync(`${shell} -i -c 'echo $PATH'`, execOptions);
80
- } catch (_e) {
81
- return execSync(`${shell} -l -c 'echo $PATH'`, execOptions);
82
- }
83
- }
84
-
85
- return execSync(`${shell} -l -c 'echo $PATH'`, execOptions);
86
- };
87
-
88
85
  const pathOutput = getPathOutput(shell, shellName);
89
86
  const systemPath = pathOutput.trim();
90
87
  if (systemPath) {