@mcp-shark/mcp-shark 1.5.4 → 1.5.6

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 +154 -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 +63 -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 +163 -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 +205 -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 -138
  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
@@ -1,403 +0,0 @@
1
- /**
2
- * Query functions for forensic analysis of HTTP packets
3
- * Can be used independently in other projects
4
- */
5
-
6
- /**
7
- * Query packets with forensic filters
8
- */
9
- export function queryPackets(db, filters = {}) {
10
- const {
11
- sessionId = null,
12
- direction = null,
13
- method = null,
14
- jsonrpcMethod = null,
15
- statusCode = null,
16
- startTime = null,
17
- endTime = null,
18
- jsonrpcId = null,
19
- limit = 1000,
20
- offset = 0,
21
- } = filters;
22
-
23
- const queryParts = ['SELECT * FROM packets WHERE 1=1'];
24
- const params = [];
25
-
26
- if (sessionId) {
27
- queryParts.push('AND session_id = ?');
28
- params.push(sessionId);
29
- }
30
-
31
- if (direction) {
32
- queryParts.push('AND direction = ?');
33
- params.push(direction);
34
- }
35
-
36
- if (method) {
37
- queryParts.push('AND method = ?');
38
- params.push(method);
39
- }
40
-
41
- if (jsonrpcMethod) {
42
- queryParts.push('AND jsonrpc_method = ?');
43
- params.push(jsonrpcMethod);
44
- }
45
-
46
- if (statusCode !== null && statusCode !== undefined) {
47
- queryParts.push('AND status_code = ?');
48
- params.push(statusCode);
49
- }
50
-
51
- if (startTime) {
52
- queryParts.push('AND timestamp_ns >= ?');
53
- params.push(startTime);
54
- }
55
-
56
- if (endTime) {
57
- queryParts.push('AND timestamp_ns <= ?');
58
- params.push(endTime);
59
- }
60
-
61
- if (jsonrpcId) {
62
- queryParts.push('AND jsonrpc_id = ?');
63
- params.push(jsonrpcId);
64
- }
65
-
66
- queryParts.push('ORDER BY timestamp_ns ASC LIMIT ? OFFSET ?');
67
- params.push(limit, offset);
68
-
69
- const query = queryParts.join(' ');
70
- const stmt = db.prepare(query);
71
- return stmt.all(...params);
72
- }
73
-
74
- /**
75
- * Enhanced query function for requests/responses with search and server name filtering
76
- * Supports partial matching and general search across multiple fields
77
- */
78
- export function queryRequests(db, filters = {}) {
79
- const {
80
- sessionId = null,
81
- direction = null,
82
- method = null,
83
- jsonrpcMethod = null,
84
- statusCode = null,
85
- startTime = null,
86
- endTime = null,
87
- jsonrpcId = null,
88
- search = null, // General search across multiple fields
89
- serverName = null, // MCP server name filter
90
- limit = 1000,
91
- offset = 0,
92
- } = filters;
93
-
94
- const queryParts = ['SELECT * FROM packets WHERE 1=1'];
95
- const params = [];
96
-
97
- // General search - searches across multiple fields with partial matching
98
- // Also searches for server names in JSON-RPC params (e.g., "params":{"name":"server-name.tool")
99
- if (search) {
100
- const searchPattern = `%${search}%`;
101
- // Also create a pattern to search for server name in params (e.g., "name":"server-name")
102
- const serverNamePattern = `%"name":"${search}%`;
103
- queryParts.push(`AND (
104
- session_id LIKE ? ESCAPE '\\' OR
105
- method LIKE ? ESCAPE '\\' OR
106
- url LIKE ? ESCAPE '\\' OR
107
- jsonrpc_method LIKE ? ESCAPE '\\' OR
108
- jsonrpc_id LIKE ? ESCAPE '\\' OR
109
- info LIKE ? ESCAPE '\\' OR
110
- body_raw LIKE ? ESCAPE '\\' OR
111
- body_json LIKE ? ESCAPE '\\' OR
112
- headers_json LIKE ? ESCAPE '\\' OR
113
- host LIKE ? ESCAPE '\\' OR
114
- remote_address LIKE ? ESCAPE '\\' OR
115
- -- Search for server name in JSON-RPC params (e.g., "params":{"name":"server-name.tool")
116
- body_json LIKE ? ESCAPE '\\' OR
117
- body_raw LIKE ? ESCAPE '\\'
118
- )`);
119
- // Add the pattern for each field (11 regular fields + 2 server name fields = 13 total)
120
- for (let i = 0; i < 11; i++) {
121
- params.push(searchPattern);
122
- }
123
- // Add server name specific patterns
124
- params.push(serverNamePattern);
125
- params.push(serverNamePattern);
126
- }
127
-
128
- // Specific field filters with partial matching
129
- if (sessionId) {
130
- queryParts.push("AND session_id LIKE ? ESCAPE '\\'");
131
- params.push(`%${sessionId}%`);
132
- }
133
- if (direction) {
134
- queryParts.push('AND direction = ?');
135
- params.push(direction);
136
- }
137
- if (method) {
138
- queryParts.push("AND method LIKE ? ESCAPE '\\'");
139
- params.push(`%${method}%`);
140
- }
141
- if (jsonrpcMethod) {
142
- queryParts.push("AND jsonrpc_method LIKE ? ESCAPE '\\'");
143
- params.push(`%${jsonrpcMethod}%`);
144
- }
145
- if (statusCode !== null && statusCode !== undefined) {
146
- queryParts.push('AND status_code = ?');
147
- params.push(statusCode);
148
- }
149
- if (startTime) {
150
- queryParts.push('AND timestamp_ns >= ?');
151
- params.push(startTime);
152
- }
153
- if (endTime) {
154
- queryParts.push('AND timestamp_ns <= ?');
155
- params.push(endTime);
156
- }
157
- if (jsonrpcId) {
158
- queryParts.push("AND jsonrpc_id LIKE ? ESCAPE '\\'");
159
- params.push(`%${jsonrpcId}%`);
160
- }
161
-
162
- // Filter by MCP server name - search in JSON-RPC params
163
- // Server names appear as "params":{"name":"server-name.tool-name" or "name":"server-name.tool-name"
164
- if (serverName) {
165
- const serverPattern = `%"name":"${serverName}.%`;
166
- const serverPattern2 = `%"name":"${serverName}"%`;
167
- queryParts.push(`AND (
168
- body_json LIKE ? ESCAPE '\\' OR
169
- body_raw LIKE ? ESCAPE '\\' OR
170
- body_json LIKE ? ESCAPE '\\' OR
171
- body_raw LIKE ? ESCAPE '\\'
172
- )`);
173
- params.push(serverPattern);
174
- params.push(serverPattern);
175
- params.push(serverPattern2);
176
- params.push(serverPattern2);
177
- }
178
-
179
- queryParts.push('ORDER BY timestamp_ns DESC LIMIT ? OFFSET ?');
180
- params.push(limit, offset);
181
-
182
- const query = queryParts.join(' ');
183
- const stmt = db.prepare(query);
184
- return stmt.all(...params);
185
- }
186
-
187
- /**
188
- * Get conversation flow (request/response pairs)
189
- */
190
- export function queryConversations(db, filters = {}) {
191
- const {
192
- sessionId = null,
193
- method = null,
194
- status = null,
195
- startTime = null,
196
- endTime = null,
197
- jsonrpcId = null,
198
- limit = 1000,
199
- offset = 0,
200
- } = filters;
201
-
202
- const queryParts = [
203
- 'SELECT',
204
- ' c.*,',
205
- ' req.frame_number as req_frame,',
206
- ' req.timestamp_iso as req_timestamp_iso,',
207
- ' req.method as req_method,',
208
- ' req.url as req_url,',
209
- ' req.jsonrpc_method as req_jsonrpc_method,',
210
- ' req.body_json as req_body_json,',
211
- ' req.headers_json as req_headers_json,',
212
- ' resp.frame_number as resp_frame,',
213
- ' resp.timestamp_iso as resp_timestamp_iso,',
214
- ' resp.status_code as resp_status_code,',
215
- ' resp.jsonrpc_method as resp_jsonrpc_method,',
216
- ' resp.body_json as resp_body_json,',
217
- ' resp.headers_json as resp_headers_json,',
218
- ' resp.jsonrpc_result as resp_jsonrpc_result,',
219
- ' resp.jsonrpc_error as resp_jsonrpc_error',
220
- 'FROM conversations c',
221
- 'LEFT JOIN packets req ON c.request_frame_number = req.frame_number',
222
- 'LEFT JOIN packets resp ON c.response_frame_number = resp.frame_number',
223
- 'WHERE 1=1',
224
- ];
225
-
226
- const params = [];
227
-
228
- if (sessionId) {
229
- queryParts.push('AND c.session_id = ?');
230
- params.push(sessionId);
231
- }
232
-
233
- if (method) {
234
- queryParts.push('AND c.method = ?');
235
- params.push(method);
236
- }
237
-
238
- if (status) {
239
- queryParts.push('AND c.status = ?');
240
- params.push(status);
241
- }
242
-
243
- if (startTime) {
244
- queryParts.push('AND c.request_timestamp_ns >= ?');
245
- params.push(startTime);
246
- }
247
-
248
- if (endTime) {
249
- queryParts.push('AND c.request_timestamp_ns <= ?');
250
- params.push(endTime);
251
- }
252
-
253
- if (jsonrpcId) {
254
- queryParts.push('AND c.jsonrpc_id = ?');
255
- params.push(jsonrpcId);
256
- }
257
-
258
- queryParts.push('ORDER BY c.request_timestamp_ns ASC LIMIT ? OFFSET ?');
259
- params.push(limit, offset);
260
-
261
- const query = queryParts.join(' ');
262
- const stmt = db.prepare(query);
263
- return stmt.all(...params);
264
- }
265
-
266
- /**
267
- * Get all packets for a specific session (for forensic analysis)
268
- */
269
- export function getSessionPackets(db, sessionId, limit = 10000) {
270
- const stmt = db.prepare(`
271
- SELECT * FROM packets
272
- WHERE session_id = ?
273
- ORDER BY timestamp_ns ASC
274
- LIMIT ?
275
- `);
276
- return stmt.all(sessionId, limit);
277
- }
278
-
279
- /**
280
- * Get all requests for a specific session (ordered by most recent first)
281
- */
282
- export function getSessionRequests(db, sessionId, limit = 10000) {
283
- const stmt = db.prepare(`
284
- SELECT * FROM packets
285
- WHERE session_id = ?
286
- ORDER BY timestamp_ns DESC
287
- LIMIT ?
288
- `);
289
- return stmt.all(sessionId, limit);
290
- }
291
-
292
- /**
293
- * Get session metadata
294
- */
295
- export function getSessions(db, filters = {}) {
296
- const { startTime = null, endTime = null, limit = 1000, offset = 0 } = filters;
297
-
298
- const queryParts = ['SELECT * FROM sessions WHERE 1=1'];
299
- const params = [];
300
-
301
- if (startTime) {
302
- queryParts.push('AND first_seen_ns >= ?');
303
- params.push(startTime);
304
- }
305
-
306
- if (endTime) {
307
- queryParts.push('AND last_seen_ns <= ?');
308
- params.push(endTime);
309
- }
310
-
311
- queryParts.push('ORDER BY first_seen_ns DESC LIMIT ? OFFSET ?');
312
- params.push(limit, offset);
313
-
314
- const query = queryParts.join(' ');
315
- const stmt = db.prepare(query);
316
- return stmt.all(...params);
317
- }
318
-
319
- /**
320
- * Get statistics for forensic analysis
321
- */
322
- export function getStatistics(db, filters = {}) {
323
- const { sessionId = null, startTime = null, endTime = null } = filters;
324
-
325
- const whereParts = ['WHERE 1=1'];
326
- const params = [];
327
-
328
- if (sessionId) {
329
- whereParts.push('AND session_id = ?');
330
- params.push(sessionId);
331
- }
332
-
333
- if (startTime) {
334
- whereParts.push('AND timestamp_ns >= ?');
335
- params.push(startTime);
336
- }
337
-
338
- if (endTime) {
339
- whereParts.push('AND timestamp_ns <= ?');
340
- params.push(endTime);
341
- }
342
-
343
- const whereClause = whereParts.join(' ');
344
- const statsQuery = `
345
- SELECT
346
- COUNT(*) as total_packets,
347
- COUNT(CASE WHEN direction = 'request' THEN 1 END) as total_requests,
348
- COUNT(CASE WHEN direction = 'response' THEN 1 END) as total_responses,
349
- COUNT(CASE WHEN status_code >= 400 THEN 1 END) as total_errors,
350
- COUNT(DISTINCT session_id) as unique_sessions,
351
- AVG(length) as avg_packet_size,
352
- SUM(length) as total_bytes,
353
- MIN(timestamp_ns) as first_packet_ns,
354
- MAX(timestamp_ns) as last_packet_ns
355
- FROM packets
356
- ${whereClause}
357
- `;
358
-
359
- const stmt = db.prepare(statsQuery);
360
- return stmt.get(...params);
361
- }
362
-
363
- /**
364
- * Get conversation statistics
365
- */
366
- export function getConversationStatistics(db, filters = {}) {
367
- const { sessionId = null, startTime = null, endTime = null } = filters;
368
-
369
- const whereParts = ['WHERE 1=1'];
370
- const params = [];
371
-
372
- if (sessionId) {
373
- whereParts.push('AND session_id = ?');
374
- params.push(sessionId);
375
- }
376
-
377
- if (startTime) {
378
- whereParts.push('AND request_timestamp_ns >= ?');
379
- params.push(startTime);
380
- }
381
-
382
- if (endTime) {
383
- whereParts.push('AND request_timestamp_ns <= ?');
384
- params.push(endTime);
385
- }
386
-
387
- const whereClause = whereParts.join(' ');
388
- const statsQuery = `
389
- SELECT
390
- COUNT(*) as total_conversations,
391
- COUNT(CASE WHEN status = 'completed' THEN 1 END) as completed,
392
- COUNT(CASE WHEN status = 'pending' THEN 1 END) as pending,
393
- COUNT(CASE WHEN status = 'error' THEN 1 END) as errors,
394
- AVG(duration_ms) as avg_duration_ms,
395
- MIN(duration_ms) as min_duration_ms,
396
- MAX(duration_ms) as max_duration_ms
397
- FROM conversations
398
- ${whereClause}
399
- `;
400
-
401
- const stmt = db.prepare(statsQuery);
402
- return stmt.get(...params);
403
- }
@@ -1,90 +0,0 @@
1
- import { consola } from 'consola';
2
-
3
- /**
4
- * Unified logger for the entire codebase
5
- * Uses consola (already installed) with a consistent API
6
- * Supports both Pino-style API (object, message) and consola-style (multiple args)
7
- */
8
- const logger = {
9
- info: (...args) => {
10
- if (args.length === 0) {
11
- return;
12
- }
13
- // If first arg is an object and second is a string, use Pino-style
14
- if (
15
- args.length === 2 &&
16
- typeof args[0] === 'object' &&
17
- args[0] !== null &&
18
- typeof args[1] === 'string'
19
- ) {
20
- consola.info(args[1] || '', args[0]);
21
- } else {
22
- // Otherwise, pass all args to consola (supports multiple strings/values)
23
- consola.info(...args);
24
- }
25
- },
26
- error: (...args) => {
27
- if (args.length === 0) {
28
- return;
29
- }
30
- if (
31
- args.length === 2 &&
32
- typeof args[0] === 'object' &&
33
- args[0] !== null &&
34
- typeof args[1] === 'string'
35
- ) {
36
- consola.error(args[1] || '', args[0]);
37
- } else {
38
- consola.error(...args);
39
- }
40
- },
41
- warn: (...args) => {
42
- if (args.length === 0) {
43
- return;
44
- }
45
- if (
46
- args.length === 2 &&
47
- typeof args[0] === 'object' &&
48
- args[0] !== null &&
49
- typeof args[1] === 'string'
50
- ) {
51
- consola.warn(args[1] || '', args[0]);
52
- } else {
53
- consola.warn(...args);
54
- }
55
- },
56
- debug: (...args) => {
57
- if (args.length === 0) {
58
- return;
59
- }
60
- if (
61
- args.length === 2 &&
62
- typeof args[0] === 'object' &&
63
- args[0] !== null &&
64
- typeof args[1] === 'string'
65
- ) {
66
- consola.debug(args[1] || '', args[0]);
67
- } else {
68
- consola.debug(...args);
69
- }
70
- },
71
- log: (...args) => {
72
- if (args.length === 0) {
73
- return;
74
- }
75
- if (
76
- args.length === 2 &&
77
- typeof args[0] === 'object' &&
78
- args[0] !== null &&
79
- typeof args[1] === 'string'
80
- ) {
81
- consola.log(args[1] || '', args[0]);
82
- } else {
83
- consola.log(...args);
84
- }
85
- },
86
- // Expose consola directly for advanced usage
87
- consola,
88
- };
89
-
90
- export default logger;
@@ -1,138 +0,0 @@
1
- import { createServer } from 'node:http';
2
-
3
- import serverLogger from '#common/logger';
4
- import { isError } from './lib/common/error.js';
5
- import { runAllExternalServers } from './lib/server/external/all.js';
6
-
7
- import { getDatabaseFile, getMcpConfigPath, prepareAppDataSpaces } from '#common/configs';
8
- import { initDb } from '#common/db/init';
9
- import { getLogger } from '#common/db/logger';
10
- import { withAuditRequestResponseHandler } from './lib/auditor/audit.js';
11
- import { getInternalServer } from './lib/server/internal/run.js';
12
- import { createInternalServerFactory } from './lib/server/internal/server.js';
13
-
14
- function initAuditLogger(serverLogger) {
15
- serverLogger.info({ path: getDatabaseFile() }, 'Initializing audit logger at');
16
- return getLogger(initDb(getDatabaseFile()));
17
- }
18
-
19
- /**
20
- * Start the MCP Shark server
21
- * @param {Object} options - Configuration options
22
- * @param {string} [options.configPath] - Path to MCP config file (defaults to getMcpConfigPath())
23
- * @param {number} [options.port=9851] - Port to listen on
24
- * @param {Function} [options.onError] - Error callback
25
- * @param {Function} [options.onReady] - Ready callback
26
- * @returns {Promise<{app: Express, server: http.Server, stop: Function}>} Server instance
27
- */
28
- export async function startMcpSharkServer(options = {}) {
29
- const { configPath = getMcpConfigPath(), port = 9851, onError, onReady } = options;
30
-
31
- prepareAppDataSpaces();
32
-
33
- serverLogger.info('[MCP-Shark] Starting MCP server...');
34
- serverLogger.info(`[MCP-Shark] Config path: ${configPath}`);
35
- serverLogger.info(`[MCP-Shark] Database path: ${getDatabaseFile()}`);
36
- serverLogger.info(`[MCP-Shark] Working directory: ${process.cwd()}`);
37
- serverLogger.info(`[MCP-Shark] PATH: ${process.env.PATH}`);
38
-
39
- try {
40
- const auditLogger = initAuditLogger(serverLogger);
41
- const externalServersResult = await runAllExternalServers(serverLogger, configPath);
42
-
43
- if (isError(externalServersResult)) {
44
- serverLogger.error(
45
- {
46
- error: externalServersResult,
47
- message: externalServersResult.message,
48
- stack: externalServersResult.stack,
49
- },
50
- `[MCP-Shark] ${externalServersResult.message}`
51
- );
52
- const error = new Error(JSON.stringify(externalServersResult));
53
- if (onError) {
54
- onError(error);
55
- }
56
- throw error;
57
- }
58
-
59
- const { kv, servers: externalServers } = externalServersResult;
60
-
61
- const internalServerFactory = createInternalServerFactory(serverLogger, kv);
62
- const app = getInternalServer(
63
- internalServerFactory,
64
- auditLogger,
65
- withAuditRequestResponseHandler
66
- );
67
-
68
- const httpServer = createServer(app);
69
-
70
- const server = await new Promise((resolve, reject) => {
71
- httpServer.on('error', (error) => {
72
- serverLogger.error(
73
- { error, message: error.message, stack: error.stack },
74
- `[MCP-Shark] ${error.message}`
75
- );
76
- if (onError) {
77
- onError(error);
78
- }
79
-
80
- reject(error);
81
- });
82
-
83
- httpServer.listen(port, '0.0.0.0', () => {
84
- serverLogger.info(`MCP proxy HTTP server listening on http://localhost:${port}/mcp`);
85
- if (onReady) {
86
- onReady();
87
- }
88
- resolve(httpServer);
89
- });
90
- });
91
-
92
- const stop = async () => {
93
- // Close all external server clients
94
- for (const serverInfo of externalServers) {
95
- if (serverInfo?.client && !isError(serverInfo)) {
96
- try {
97
- await serverInfo.client.close();
98
- } catch (error) {
99
- serverLogger.warn(
100
- { error, message: error.message },
101
- 'Error closing external server client'
102
- );
103
- }
104
- }
105
- }
106
-
107
- // Close all connections forcefully
108
- if (server.closeAllConnections) {
109
- server.closeAllConnections();
110
- }
111
-
112
- // Close the server
113
- return new Promise((resolve) => {
114
- server.close(() => {
115
- serverLogger.info('MCP Shark server stopped');
116
- resolve();
117
- });
118
- });
119
- };
120
-
121
- return {
122
- app,
123
- server,
124
- stop,
125
- auditLogger,
126
- };
127
- } catch (error) {
128
- const errorMsg = 'Error starting MCP server';
129
- serverLogger.error(
130
- { error, message: error.message, stack: error.stack },
131
- `[MCP-Shark] ${errorMsg}`
132
- );
133
- if (onError) {
134
- onError(error);
135
- }
136
- throw error;
137
- }
138
- }
@@ -1,57 +0,0 @@
1
- import { readFileSync } from 'node:fs';
2
- import { CompositeError, isError } from '../../common/error.js';
3
-
4
- const DEFAULT_TYPE = 'stdio';
5
- export class ConfigError extends CompositeError {
6
- constructor(message, error) {
7
- super('ConfigError', message, error);
8
- }
9
- }
10
-
11
- function parseConfig(configPath) {
12
- try {
13
- const conf = JSON.parse(readFileSync(configPath, 'utf-8'));
14
- if (conf && typeof conf === 'object') {
15
- return conf;
16
- }
17
- return new ConfigError(
18
- 'Invalid config file',
19
- new Error(`Invalid config file: ${configPath} - ${JSON.stringify(conf, null, 2)}`)
20
- );
21
- } catch (error) {
22
- return new ConfigError(
23
- 'Error parsing config',
24
- new Error(`Error parsing config: ${configPath} - ${error.message}`)
25
- );
26
- }
27
- }
28
-
29
- export function normalizeConfig(configPath) {
30
- const parsedConfigResult = parseConfig(configPath);
31
- if (isError(parsedConfigResult)) {
32
- return parsedConfigResult;
33
- }
34
- const out = new Map();
35
- const { servers, mcpServers } = parsedConfigResult;
36
- // Servers are the old format
37
- if (servers) {
38
- Object.entries(servers).forEach(([name, cfg]) => {
39
- const type = cfg.type ?? DEFAULT_TYPE;
40
- out.set(name, { type, ...cfg });
41
- });
42
- }
43
- // MCP Servers are the new format
44
- if (mcpServers) {
45
- Object.entries(mcpServers).forEach(([name, cfg]) => {
46
- // Cursor/Claude usually omit type; assume stdio if command is given
47
- const type = cfg.type ?? DEFAULT_TYPE;
48
- out.set(name, { type, ...cfg });
49
- });
50
- }
51
-
52
- if (out.size === 0) {
53
- return new ConfigError('No servers found in config');
54
- }
55
-
56
- return Object.fromEntries(out);
57
- }
@@ -1,20 +0,0 @@
1
- import { getBy } from '../../external/kv.js';
2
- import { InternalServerError } from './error.js';
3
-
4
- export function createPromptsGetHandler(logger, mcpServers, requestedMcpServer) {
5
- return async (req) => {
6
- const name = req.params.name;
7
- const promptArgs = req?.params?.arguments || {};
8
- logger.debug('Prompt get', name, promptArgs);
9
-
10
- const getPrompt = getBy(mcpServers, requestedMcpServer, name, 'getPrompt', promptArgs);
11
- if (!getPrompt) {
12
- throw new InternalServerError(`Prompt not found: ${name}`);
13
- }
14
-
15
- const result = await getPrompt(name, promptArgs);
16
- logger.debug('Prompt get result', result);
17
-
18
- return result;
19
- };
20
- }