@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: resBodyJson || resBodyStr,
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.CLIENT_ERROR_START} THEN 1 END) as total_errors,
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.9",
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": ["bin", "ui", "core", "README.md", "LICENSE", "package.json"],
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';