@mcp-shark/mcp-shark 1.5.9 → 1.5.10
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.
|
@@ -226,14 +226,13 @@ export async function withAuditRequestResponseHandler(
|
|
|
226
226
|
|
|
227
227
|
// Extract response body as string
|
|
228
228
|
const resBodyStr = resBuf.toString('utf8');
|
|
229
|
-
const resBodyJson = parseJsonSafely(resBodyStr);
|
|
230
229
|
|
|
231
230
|
const jsonrpcId = reqJsonRpc?.payload?.id !== undefined ? String(reqJsonRpc.payload.id) : null;
|
|
232
231
|
|
|
233
232
|
auditLogger.logResponsePacket({
|
|
234
233
|
statusCode: wrappedRes.statusCode || 200,
|
|
235
234
|
headers: resHeaders,
|
|
236
|
-
body:
|
|
235
|
+
body: resBodyStr,
|
|
237
236
|
requestFrameNumber: requestResult?.frameNumber || null,
|
|
238
237
|
requestTimestampNs: requestResult?.timestampNs || null,
|
|
239
238
|
jsonrpcId,
|
|
@@ -69,17 +69,68 @@ export class AuditRepository {
|
|
|
69
69
|
return `${statusCode}${rpcInfo}`;
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
+
_isCharacterCodeObject(obj) {
|
|
73
|
+
if (!obj || typeof obj !== 'object' || Array.isArray(obj)) {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const keys = Object.keys(obj);
|
|
78
|
+
if (keys.length === 0) {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Check if all keys are numeric strings starting from "0"
|
|
83
|
+
for (let i = 0; i < keys.length; i++) {
|
|
84
|
+
if (keys[i] !== String(i)) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
// Check if values are numbers (character codes)
|
|
88
|
+
if (typeof obj[keys[i]] !== 'number') {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
_convertCharacterCodeObjectToString(obj) {
|
|
97
|
+
const chars = [];
|
|
98
|
+
const keys = Object.keys(obj).sort((a, b) => Number(a) - Number(b));
|
|
99
|
+
for (const key of keys) {
|
|
100
|
+
chars.push(String.fromCharCode(obj[key]));
|
|
101
|
+
}
|
|
102
|
+
return chars.join('');
|
|
103
|
+
}
|
|
104
|
+
|
|
72
105
|
_normalizeBody(body) {
|
|
73
106
|
if (!body) {
|
|
74
107
|
return { bodyRaw: '', bodyJson: null };
|
|
75
108
|
}
|
|
109
|
+
|
|
110
|
+
// Handle String objects (created with new String())
|
|
111
|
+
if (Object.prototype.toString.call(body) === '[object String]') {
|
|
112
|
+
const str = String(body);
|
|
113
|
+
return { bodyRaw: str, bodyJson: str };
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Handle primitive strings
|
|
76
117
|
if (typeof body === 'string') {
|
|
77
118
|
return { bodyRaw: body, bodyJson: body };
|
|
78
119
|
}
|
|
120
|
+
|
|
121
|
+
// Handle objects
|
|
79
122
|
if (typeof body === 'object') {
|
|
123
|
+
// Check if it's a character code object and convert it back to string
|
|
124
|
+
if (this._isCharacterCodeObject(body)) {
|
|
125
|
+
const str = this._convertCharacterCodeObjectToString(body);
|
|
126
|
+
return { bodyRaw: str, bodyJson: str };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Otherwise, stringify normally
|
|
80
130
|
const raw = JSON.stringify(body);
|
|
81
131
|
return { bodyRaw: raw, bodyJson: raw };
|
|
82
132
|
}
|
|
133
|
+
|
|
83
134
|
return { bodyRaw: '', bodyJson: null };
|
|
84
135
|
}
|
|
85
136
|
|
|
@@ -38,7 +38,7 @@ export class StatisticsRepository {
|
|
|
38
38
|
COUNT(*) as total_packets,
|
|
39
39
|
COUNT(CASE WHEN direction = 'request' THEN 1 END) as total_requests,
|
|
40
40
|
COUNT(CASE WHEN direction = 'response' THEN 1 END) as total_responses,
|
|
41
|
-
COUNT(CASE WHEN status_code >= ${StatusCodeRanges.
|
|
41
|
+
COUNT(CASE WHEN status_code >= ${StatusCodeRanges.CLIENT_ERROR_MIN} THEN 1 END) as total_errors,
|
|
42
42
|
COUNT(DISTINCT session_id) as unique_sessions,
|
|
43
43
|
AVG(length) as avg_packet_size,
|
|
44
44
|
SUM(length) as total_bytes,
|
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.10",
|
|
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",
|
|
@@ -25,13 +25,26 @@
|
|
|
25
25
|
"bugs": {
|
|
26
26
|
"url": "https://github.com/mcp-shark/mcp-shark/issues"
|
|
27
27
|
},
|
|
28
|
-
"files": [
|
|
28
|
+
"files": [
|
|
29
|
+
"bin",
|
|
30
|
+
"ui",
|
|
31
|
+
"core",
|
|
32
|
+
"!**/__tests__",
|
|
33
|
+
"!**/*.test.js",
|
|
34
|
+
"!**/*.spec.js",
|
|
35
|
+
"README.md",
|
|
36
|
+
"LICENSE",
|
|
37
|
+
"package.json"
|
|
38
|
+
],
|
|
29
39
|
"scripts": {
|
|
30
40
|
"start": "node scripts/start-ui.js",
|
|
31
41
|
"predev": "npm run build:ui",
|
|
32
42
|
"dev": "node scripts/start-ui.js",
|
|
33
43
|
"build": "bash -c 'cd ui && vite build'",
|
|
34
44
|
"build:ui": "bash -c 'cd ui && vite build'",
|
|
45
|
+
"test": "c8 node --test --test-name-pattern='.*' --test-reporter spec $(find core ui/server -path '*/__tests__/*.test.js' -type f)",
|
|
46
|
+
"test:ui": "vitest run --coverage",
|
|
47
|
+
"test:all": "npm run test:coverage && npm run test:ui",
|
|
35
48
|
"lint": "biome lint .",
|
|
36
49
|
"lint:fix": "biome lint --write .",
|
|
37
50
|
"format": "biome format --write .",
|
|
@@ -93,9 +106,21 @@
|
|
|
93
106
|
"@biomejs/biome": "^1.9.4",
|
|
94
107
|
"@commitlint/cli": "^19.5.0",
|
|
95
108
|
"@commitlint/config-conventional": "^19.5.0",
|
|
109
|
+
"@testing-library/jest-dom": "^6.4.0",
|
|
110
|
+
"@testing-library/react": "^16.0.0",
|
|
96
111
|
"@vitejs/plugin-react": "^4.2.1",
|
|
112
|
+
"@vitest/coverage-v8": "^4.0.18",
|
|
113
|
+
"c8": "^10.1.2",
|
|
97
114
|
"husky": "^9.1.6",
|
|
115
|
+
"jsdom": "^25.0.0",
|
|
98
116
|
"lint-staged": "^15.2.10",
|
|
99
|
-
"vite": "^7.3.0"
|
|
117
|
+
"vite": "^7.3.0",
|
|
118
|
+
"vitest": "^4.0.18"
|
|
119
|
+
},
|
|
120
|
+
"c8": {
|
|
121
|
+
"include": ["core/**/*.js", "ui/server/**/*.js"],
|
|
122
|
+
"exclude": ["**/__tests__/**", "**/*.test.js", "**/index.js"],
|
|
123
|
+
"reporter": ["text", "html"],
|
|
124
|
+
"check-coverage": false
|
|
100
125
|
}
|
|
101
126
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import '@testing-library/jest-dom';
|