@mcp-shark/mcp-shark 1.5.0 → 1.5.3
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/bin/mcp-shark.js +1 -53
- package/lib/common/configs/index.js +108 -0
- package/lib/common/db/init.js +132 -0
- package/lib/common/db/logger.js +349 -0
- package/lib/common/db/query.js +403 -0
- package/lib/common/logger.js +90 -0
- package/mcp-server/index.js +4 -8
- package/mcp-server/mcp-shark.js +1 -1
- package/package.json +12 -4
- package/ui/dist/assets/{index-srLDlk97.js → index-CFHeMNwd.js} +7 -7
- package/ui/dist/index.html +1 -1
- package/ui/server/routes/composite/servers.js +1 -1
- package/ui/server/routes/composite/setup.js +1 -1
- package/ui/server/routes/conversations.js +1 -1
- package/ui/server/routes/help.js +1 -1
- package/ui/server/routes/requests.js +2 -2
- package/ui/server/routes/sessions.js +1 -1
- package/ui/server/routes/settings.js +1 -1
- package/ui/server/routes/smartscan/discover.js +1 -1
- package/ui/server/routes/statistics.js +1 -1
- package/ui/server/utils/logger.js +2 -2
- package/ui/server/utils/scan-cache/directory.js +1 -1
- package/ui/server/utils/smartscan-token.js +1 -1
- package/ui/server.js +3 -3
- package/ui/src/components/SmartScan/SmartScanControls.jsx +1 -0
|
@@ -0,0 +1,403 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
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;
|
package/mcp-server/index.js
CHANGED
|
@@ -1,16 +1,12 @@
|
|
|
1
1
|
import { createServer } from 'node:http';
|
|
2
2
|
|
|
3
|
-
import serverLogger from '
|
|
3
|
+
import serverLogger from '#common/logger';
|
|
4
4
|
import { isError } from './lib/common/error.js';
|
|
5
5
|
import { runAllExternalServers } from './lib/server/external/all.js';
|
|
6
6
|
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
prepareAppDataSpaces,
|
|
11
|
-
} from 'mcp-shark-common/configs/index.js';
|
|
12
|
-
import { initDb } from 'mcp-shark-common/db/init.js';
|
|
13
|
-
import { getLogger } from 'mcp-shark-common/db/logger.js';
|
|
7
|
+
import { getDatabaseFile, getMcpConfigPath, prepareAppDataSpaces } from '#common/configs';
|
|
8
|
+
import { initDb } from '#common/db/init';
|
|
9
|
+
import { getLogger } from '#common/db/logger';
|
|
14
10
|
import { withAuditRequestResponseHandler } from './lib/auditor/audit.js';
|
|
15
11
|
import { getInternalServer } from './lib/server/internal/run.js';
|
|
16
12
|
import { createInternalServerFactory } from './lib/server/internal/server.js';
|
package/mcp-server/mcp-shark.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mcp-shark/mcp-shark",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.3",
|
|
4
4
|
"description": "Aggregate multiple Model Context Protocol (MCP) servers into a single unified interface with a powerful monitoring UI. Prov deep visibility into every request and response.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./bin/mcp-shark.js",
|
|
@@ -8,6 +8,14 @@
|
|
|
8
8
|
".": "./bin/mcp-shark.js",
|
|
9
9
|
"./mcp-server": "./mcp-server/index.js"
|
|
10
10
|
},
|
|
11
|
+
"imports": {
|
|
12
|
+
"#common/configs": "./lib/common/configs/index.js",
|
|
13
|
+
"#common/db/query": "./lib/common/db/query.js",
|
|
14
|
+
"#common/db/init": "./lib/common/db/init.js",
|
|
15
|
+
"#common/db/logger": "./lib/common/db/logger.js",
|
|
16
|
+
"#common/logger": "./lib/common/logger.js",
|
|
17
|
+
"#common/*": "./lib/common/*"
|
|
18
|
+
},
|
|
11
19
|
"bin": {
|
|
12
20
|
"mcp-shark": "./bin/mcp-shark.js",
|
|
13
21
|
"@mcp-shark/mcp-shark": "./bin/mcp-shark.js"
|
|
@@ -20,7 +28,7 @@
|
|
|
20
28
|
"bugs": {
|
|
21
29
|
"url": "https://github.com/mcp-shark/mcp-shark/issues"
|
|
22
30
|
},
|
|
23
|
-
"files": ["bin", "ui", "mcp-server", "README.md", "LICENSE", "package.json"],
|
|
31
|
+
"files": ["bin", "ui", "mcp-server", "lib", "README.md", "LICENSE", "package.json"],
|
|
24
32
|
"scripts": {
|
|
25
33
|
"start": "node scripts/start-ui.js",
|
|
26
34
|
"predev": "npm run build:ui",
|
|
@@ -61,7 +69,7 @@
|
|
|
61
69
|
"author": "",
|
|
62
70
|
"license": "SEE LICENSE IN LICENSE",
|
|
63
71
|
"engines": {
|
|
64
|
-
"node": ">=
|
|
72
|
+
"node": ">=20.0.0"
|
|
65
73
|
},
|
|
66
74
|
"dependencies": {
|
|
67
75
|
"@modelcontextprotocol/sdk": "^1.20.2",
|
|
@@ -72,7 +80,7 @@
|
|
|
72
80
|
"cors": "^2.8.5",
|
|
73
81
|
"express": "^4.18.2",
|
|
74
82
|
"jsonrpc-lite": "^2.2.0",
|
|
75
|
-
"
|
|
83
|
+
"better-sqlite3": "^12.4.1",
|
|
76
84
|
"open": "^11.0.0",
|
|
77
85
|
"react": "^18.2.0",
|
|
78
86
|
"react-dom": "^18.2.0",
|