@mcp-shark/mcp-shark 1.5.8 → 1.5.9

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/README.md CHANGED
@@ -54,6 +54,7 @@ MCP Shark is a monitoring and aggregation solution for Model Context Protocol (M
54
54
  - **Security analysis**: AI-powered scanning for security risks and vulnerabilities
55
55
  - **IDE integration**: Automatic configuration detection for Cursor, Windsurf, Codex, and other IDEs
56
56
  - **API documentation**: Comprehensive Swagger/OpenAPI documentation for all endpoints with interactive testing
57
+ - **Action menu**: Expandable menu providing quick access to API docs, help tour, and server shutdown
57
58
 
58
59
  ## Documentation
59
60
 
@@ -72,7 +73,7 @@ MCP Shark is a monitoring and aggregation solution for Model Context Protocol (M
72
73
  - **[Architecture](docs/architecture.md)** - System architecture and design
73
74
  - **[Database Architecture](docs/database-architecture.md)** - Database architecture and repository pattern
74
75
  - **[API Reference](docs/api-reference.md)** - API endpoints and WebSocket protocol
75
- - **API Documentation** - Interactive Swagger/OpenAPI documentation available at `/api-docs` when server is running (or click the 📡 button in the UI)
76
+ - **API Documentation** - Interactive Swagger/OpenAPI documentation available at `/api-docs` when server is running (or click the menu button in the bottom-right corner, then select the API docs button 📡)
76
77
 
77
78
  ### Architecture & Coding Rules
78
79
  - **[Architecture Rules](rules/ARCHITECTURE_RULES.md)** - Architecture principles and guidelines
@@ -111,4 +112,4 @@ See the [LICENSE](LICENSE) file for full terms and conditions.
111
112
 
112
113
  ---
113
114
 
114
- **Version**: 1.5.4 | **Homepage**: [https://mcpshark.sh](https://mcpshark.sh)
115
+ **Version**: 1.5.9 | **Homepage**: [https://mcpshark.sh](https://mcpshark.sh)
@@ -7,14 +7,9 @@ import { Server } from '#core/constants/Server.js';
7
7
  * Provides validated access to environment variables with defaults
8
8
  */
9
9
  export const Environment = {
10
- /**
11
- * Get Codex home directory
12
- * @returns {string} Codex home path
13
- */
14
- getCodexHome() {
15
- return process.env.CODEX_HOME || join(homedir(), '.codex');
10
+ getEnv() {
11
+ return process.env;
16
12
  },
17
-
18
13
  /**
19
14
  * Get UI server port
20
15
  * @returns {number} UI server port (default: 9853)
@@ -48,4 +43,12 @@ export const Environment = {
48
43
  getMcpSharkHome() {
49
44
  return process.env.MCP_SHARK_HOME || join(homedir(), '.mcp-shark');
50
45
  },
46
+
47
+ getUserProfile() {
48
+ return process.env.USERPROFILE || homedir();
49
+ },
50
+
51
+ getCodexHome() {
52
+ return process.env.CODEX_HOME || join(homedir(), '.codex');
53
+ },
51
54
  };
@@ -2,7 +2,6 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
2
  import { homedir } from 'node:os';
3
3
  import { dirname, join } from 'node:path';
4
4
 
5
- export * from './codex.js';
6
5
  export { Environment } from './environment.js';
7
6
 
8
7
  const WORKING_DIRECTORY_NAME = '.mcp-shark';
@@ -12,4 +12,6 @@ export const Defaults = {
12
12
 
13
13
  // Statistics defaults
14
14
  STATISTICS_LIMIT: 1000000,
15
+
16
+ DEFAULT_MCP_SERVER_PORT: 9851,
15
17
  };
@@ -2,6 +2,8 @@ import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js'
2
2
  import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
3
3
  import { WebSocketClientTransport } from '@modelcontextprotocol/sdk/client/websocket.js';
4
4
 
5
+ import { Environment } from '#core/configs/environment.js';
6
+
5
7
  /**
6
8
  * Library for MCP transport creation utilities
7
9
  * Pure utility - no dependencies on services or repositories
@@ -31,7 +33,7 @@ export function createTransport(serverConfig, serverName = null) {
31
33
  } = serverConfig;
32
34
 
33
35
  const env = {
34
- ...process.env,
36
+ ...Environment.getEnv(),
35
37
  ...configEnv,
36
38
  };
37
39
 
@@ -11,6 +11,7 @@ import {
11
11
  getMcpConfigPath,
12
12
  prepareAppDataSpaces,
13
13
  } from '#core/configs/index.js';
14
+ import { Defaults } from '#core/constants/Defaults.js';
14
15
  import { initDb } from '#core/db/init.js';
15
16
  import { withAuditRequestResponseHandler } from './auditor/audit.js';
16
17
  import { getInternalServer } from './server/internal/run.js';
@@ -114,25 +115,27 @@ function createServerPromise(httpServer, port, serverLogger, onError, onReady) {
114
115
  * @param {number} [options.port=9851] - Port to listen on
115
116
  * @param {Function} [options.onError] - Error callback
116
117
  * @param {Function} [options.onReady] - Ready callback
118
+ * @param {Function} [options.onLog] - Log callback: (type: string, message: string) => void
117
119
  * @param {Object} options.auditLogger - Required audit logger instance (use initAuditLogger() to create)
118
120
  * @returns {Promise<{app: Express, server: http.Server, stop: Function}>} Server instance
119
121
  */
120
122
  export async function startMcpSharkServer(options = {}) {
121
123
  const {
122
124
  configPath = getMcpConfigPath(),
123
- port = 9851,
125
+ port = Defaults.DEFAULT_MCP_SERVER_PORT,
124
126
  onError,
125
127
  onReady,
128
+ onLog,
126
129
  auditLogger: providedAuditLogger,
127
130
  } = options;
128
131
 
129
132
  prepareAppDataSpaces();
130
133
 
131
- serverLogger.info('[MCP-Shark] Starting MCP server...');
132
- serverLogger.info(`[MCP-Shark] Config path: ${configPath}`);
133
- serverLogger.info(`[MCP-Shark] Database path: ${getDatabaseFile()}`);
134
- serverLogger.info(`[MCP-Shark] Working directory: ${process.cwd()}`);
135
- serverLogger.info({ path: Environment.getPath() }, '[MCP-Shark] PATH');
134
+ logServerInfo(serverLogger, onLog, 'Starting MCP server...', { port });
135
+ logServerInfo(serverLogger, onLog, 'Config path', { path: configPath });
136
+ logServerInfo(serverLogger, onLog, 'Database path', { path: getDatabaseFile() });
137
+ logServerInfo(serverLogger, onLog, 'Working directory', { path: process.cwd() });
138
+ logServerInfo(serverLogger, onLog, 'PATH', { path: Environment.getPath() });
136
139
 
137
140
  try {
138
141
  if (!providedAuditLogger) {
@@ -190,3 +193,16 @@ export async function startMcpSharkServer(options = {}) {
190
193
  throw error;
191
194
  }
192
195
  }
196
+
197
+ function logServerInfo(serverLogger, onLog, message, metadata) {
198
+ const finalMessage = `[MCP Server] ${message}`;
199
+ serverLogger.info({ ...metadata, message: finalMessage }, finalMessage);
200
+ if (onLog) {
201
+ onLog(
202
+ 'stdout',
203
+ `${finalMessage} ${Object.entries(metadata)
204
+ .map(([key, value]) => `${key}=${value}`)
205
+ .join(' ')}`
206
+ );
207
+ }
208
+ }
@@ -2,8 +2,8 @@ import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js'
2
2
  import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
3
3
  import { WebSocketClientTransport } from '@modelcontextprotocol/sdk/client/websocket.js';
4
4
 
5
+ import { Environment } from '#core/configs/environment.js';
5
6
  import { CompositeError } from '#core/libraries/ErrorLibrary.js';
6
-
7
7
  export class TransportError extends CompositeError {
8
8
  constructor(message, error) {
9
9
  super('TransportError', message, error);
@@ -20,7 +20,7 @@ export function makeTransport({
20
20
  }) {
21
21
  // Start with enhanced PATH
22
22
  const env = {
23
- ...process.env,
23
+ ...Environment.getEnv(),
24
24
  ...configEnv,
25
25
  };
26
26
 
@@ -1,7 +1,11 @@
1
1
  import * as fs from 'node:fs';
2
2
  import { homedir } from 'node:os';
3
3
  import * as path from 'node:path';
4
- import { getCodexConfigPath } from '#core/configs/index.js';
4
+ import { Environment } from '#core/configs/environment.js';
5
+
6
+ const cursorDefaultPath = path.join(homedir(), '.cursor', 'mcp.json');
7
+ const windsurfDefaultPath = path.join(homedir(), '.codeium', 'windsurf', 'mcp_config.json');
8
+ const codexDefaultPath = path.join(homedir(), '.codex', 'config.toml');
5
9
 
6
10
  /**
7
11
  * Service for detecting configuration files on the system
@@ -16,16 +20,24 @@ export class ConfigDetectionService {
16
20
  const platform = process.platform;
17
21
 
18
22
  const cursorPaths = [
19
- path.join(homeDir, '.cursor', 'mcp.json'),
23
+ cursorDefaultPath,
20
24
  ...(platform === 'win32'
21
- ? [path.join(process.env.USERPROFILE || '', '.cursor', 'mcp.json')]
25
+ ? [path.join(Environment.getUserProfile(), '.cursor', 'mcp.json')]
22
26
  : []),
23
27
  ];
24
28
 
25
29
  const windsurfPaths = [
26
- path.join(homeDir, '.codeium', 'windsurf', 'mcp_config.json'),
30
+ windsurfDefaultPath,
31
+ ...(platform === 'win32'
32
+ ? [path.join(Environment.getUserProfile(), '.codeium', 'windsurf', 'mcp_config.json')]
33
+ : []),
34
+ ];
35
+
36
+ const codexPaths = [
37
+ codexDefaultPath,
38
+ path.join(Environment.getCodexHome(), 'config.toml'),
27
39
  ...(platform === 'win32'
28
- ? [path.join(process.env.USERPROFILE || '', '.codeium', 'windsurf', 'mcp_config.json')]
40
+ ? [path.join(Environment.getUserProfile(), '.codex', 'config.toml')]
29
41
  : []),
30
42
  ];
31
43
 
@@ -53,34 +65,35 @@ export class ConfigDetectionService {
53
65
  }
54
66
  }
55
67
 
56
- const codexPath = getCodexConfigPath();
57
- if (fs.existsSync(codexPath)) {
58
- detected.push({
59
- editor: 'Codex',
60
- path: codexPath,
61
- displayPath: codexPath.replace(homeDir, '~'),
62
- exists: true,
63
- });
68
+ for (const codexPath of codexPaths) {
69
+ if (fs.existsSync(codexPath)) {
70
+ detected.push({
71
+ editor: 'Codex',
72
+ path: codexPath,
73
+ displayPath: codexPath.replace(homeDir, '~'),
74
+ exists: true,
75
+ });
76
+ }
64
77
  }
65
78
 
66
79
  const defaultPaths = [
67
80
  {
68
81
  editor: 'Cursor',
69
- path: path.join(homeDir, '.cursor', 'mcp.json'),
70
- displayPath: '~/.cursor/mcp.json',
71
- exists: fs.existsSync(path.join(homeDir, '.cursor', 'mcp.json')),
82
+ path: cursorDefaultPath.replace(homeDir, '~'),
83
+ displayPath: cursorDefaultPath.replace(homeDir, '~'),
84
+ exists: fs.existsSync(cursorDefaultPath),
72
85
  },
73
86
  {
74
87
  editor: 'Windsurf',
75
- path: path.join(homeDir, '.codeium', 'windsurf', 'mcp_config.json'),
76
- displayPath: '~/.codeium/windsurf/mcp_config.json',
77
- exists: fs.existsSync(path.join(homeDir, '.codeium', 'windsurf', 'mcp_config.json')),
88
+ path: windsurfDefaultPath.replace(homeDir, '~'),
89
+ displayPath: windsurfDefaultPath.replace(homeDir, '~'),
90
+ exists: fs.existsSync(windsurfDefaultPath),
78
91
  },
79
92
  {
80
93
  editor: 'Codex',
81
- path: codexPath,
82
- displayPath: codexPath.replace(homeDir, '~'),
83
- exists: fs.existsSync(codexPath),
94
+ path: codexDefaultPath.replace(homeDir, '~'),
95
+ displayPath: codexDefaultPath.replace(homeDir, '~'),
96
+ exists: fs.existsSync(codexDefaultPath),
84
97
  },
85
98
  ];
86
99
 
@@ -1,3 +1,4 @@
1
+ import { Defaults } from '#core/constants/Defaults.js';
1
2
  import { initAuditLogger, startMcpSharkServer } from '#core/mcp-server/index.js';
2
3
 
3
4
  /**
@@ -19,13 +20,21 @@ export class ServerManagementService {
19
20
  * @param {string} [options.filePath] - Path to config file
20
21
  * @param {string} [options.fileContent] - Config file content
21
22
  * @param {Array} [options.selectedServices] - Selected services to include
22
- * @param {number} [options.port=9851] - Server port
23
+ * @param {number} [options.port=Defaults.DEFAULT_MCP_SERVER_PORT] - Server port
23
24
  * @param {Function} [options.onError] - Error callback
24
25
  * @param {Function} [options.onReady] - Ready callback
25
26
  * @returns {Promise<Object>} Setup result with convertedConfig, updatedConfig, filePath
26
27
  */
27
28
  async setup(options = {}) {
28
- const { filePath, fileContent, selectedServices, port = 9851, onError, onReady } = options;
29
+ const {
30
+ filePath,
31
+ fileContent,
32
+ selectedServices,
33
+ port = Defaults.DEFAULT_MCP_SERVER_PORT,
34
+ onError,
35
+ onReady,
36
+ onLog,
37
+ } = options;
29
38
 
30
39
  if (!filePath && !fileContent) {
31
40
  return {
@@ -63,6 +72,7 @@ export class ServerManagementService {
63
72
  await this.startServer({
64
73
  configPath: mcpsJsonPath,
65
74
  port,
75
+ onLog,
66
76
  onError: (err) => {
67
77
  if (onError) {
68
78
  onError(err);
@@ -98,7 +108,13 @@ export class ServerManagementService {
98
108
  * Start MCP Shark server
99
109
  */
100
110
  async startServer(options = {}) {
101
- const { configPath, port = 9851, onError, onReady } = options;
111
+ const {
112
+ configPath,
113
+ port = Defaults.DEFAULT_MCP_SERVER_PORT,
114
+ onError,
115
+ onReady,
116
+ onLog,
117
+ } = options;
102
118
 
103
119
  const mcpsJsonPath = configPath || this.configService.getMcpConfigPath();
104
120
 
@@ -114,8 +130,13 @@ export class ServerManagementService {
114
130
  configPath: mcpsJsonPath,
115
131
  port,
116
132
  auditLogger,
133
+ logger: this.logger,
134
+ onLog,
117
135
  onError: (err) => {
118
- this.logger?.error({ error: err.message }, 'Failed to start mcp-shark server');
136
+ this.logger?.error(
137
+ { message: err.message, stack: err.stack, error: err },
138
+ 'Failed to start mcp-shark server'
139
+ );
119
140
  this.serverInstance = null;
120
141
  if (onError) {
121
142
  onError(err);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcp-shark/mcp-shark",
3
- "version": "1.5.8",
3
+ "version": "1.5.9",
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",
@@ -46,7 +46,7 @@
46
46
  "publish:dry-run": "npm publish --dry-run"
47
47
  },
48
48
  "lint-staged": {
49
- "*": ["biome check --write"]
49
+ "*.{js,jsx,ts,tsx,json}": ["biome check --write"]
50
50
  },
51
51
  "keywords": [
52
52
  "mcp",