@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
@@ -0,0 +1,91 @@
1
+ import { consola } from 'consola';
2
+
3
+ /**
4
+ * Library for logging utilities
5
+ * Pure utility - no dependencies on services or repositories
6
+ * Uses consola with a consistent API supporting both Pino-style and consola-style
7
+ */
8
+ export class LoggerLibrary {
9
+ constructor() {
10
+ this._consola = consola;
11
+ }
12
+
13
+ _formatArgs(args) {
14
+ if (args.length === 0) {
15
+ return null;
16
+ }
17
+ if (
18
+ args.length === 2 &&
19
+ typeof args[0] === 'object' &&
20
+ args[0] !== null &&
21
+ typeof args[1] === 'string'
22
+ ) {
23
+ return { style: 'pino', message: args[1] || '', metadata: args[0] };
24
+ }
25
+ return { style: 'consola', args };
26
+ }
27
+
28
+ info(...args) {
29
+ const formatted = this._formatArgs(args);
30
+ if (!formatted) {
31
+ return;
32
+ }
33
+ if (formatted.style === 'pino') {
34
+ consola.info(formatted.message, formatted.metadata);
35
+ } else {
36
+ consola.info(...formatted.args);
37
+ }
38
+ }
39
+
40
+ error(...args) {
41
+ const formatted = this._formatArgs(args);
42
+ if (!formatted) {
43
+ return;
44
+ }
45
+ if (formatted.style === 'pino') {
46
+ consola.error(formatted.message, formatted.metadata);
47
+ } else {
48
+ consola.error(...formatted.args);
49
+ }
50
+ }
51
+
52
+ warn(...args) {
53
+ const formatted = this._formatArgs(args);
54
+ if (!formatted) {
55
+ return;
56
+ }
57
+ if (formatted.style === 'pino') {
58
+ consola.warn(formatted.message, formatted.metadata);
59
+ } else {
60
+ consola.warn(...formatted.args);
61
+ }
62
+ }
63
+
64
+ debug(...args) {
65
+ const formatted = this._formatArgs(args);
66
+ if (!formatted) {
67
+ return;
68
+ }
69
+ if (formatted.style === 'pino') {
70
+ consola.debug(formatted.message, formatted.metadata);
71
+ } else {
72
+ consola.debug(...formatted.args);
73
+ }
74
+ }
75
+
76
+ log(...args) {
77
+ const formatted = this._formatArgs(args);
78
+ if (!formatted) {
79
+ return;
80
+ }
81
+ if (formatted.style === 'pino') {
82
+ consola.log(formatted.message, formatted.metadata);
83
+ } else {
84
+ consola.log(...formatted.args);
85
+ }
86
+ }
87
+
88
+ get consola() {
89
+ return this._consola;
90
+ }
91
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Library for serialization utilities
3
+ * Pure utility functions - no dependencies on services or repositories
4
+ */
5
+ export class SerializationLibrary {
6
+ /**
7
+ * Serialize BigInt values to strings for JSON compatibility
8
+ */
9
+ serializeBigInt(data) {
10
+ if (data === null || data === undefined) {
11
+ return data;
12
+ }
13
+
14
+ if (typeof data === 'bigint') {
15
+ return data.toString();
16
+ }
17
+
18
+ if (Array.isArray(data)) {
19
+ return data.map((item) => this.serializeBigInt(item));
20
+ }
21
+
22
+ if (typeof data === 'object') {
23
+ const serialized = {};
24
+ for (const [key, value] of Object.entries(data)) {
25
+ serialized[key] = this.serializeBigInt(value);
26
+ }
27
+ return serialized;
28
+ }
29
+
30
+ return data;
31
+ }
32
+ }
@@ -0,0 +1,19 @@
1
+ import { LoggerLibrary } from './LoggerLibrary.js';
2
+
3
+ /**
4
+ * Bootstrap logger instance for use before DI container is available
5
+ * Re-exports LoggerLibrary instance for backward compatibility
6
+ * In core architecture, use LoggerLibrary via DependencyContainer
7
+ */
8
+ const loggerInstance = new LoggerLibrary();
9
+
10
+ const logger = {
11
+ info: (...args) => loggerInstance.info(...args),
12
+ error: (...args) => loggerInstance.error(...args),
13
+ warn: (...args) => loggerInstance.warn(...args),
14
+ debug: (...args) => loggerInstance.debug(...args),
15
+ log: (...args) => loggerInstance.log(...args),
16
+ consola: loggerInstance.consola,
17
+ };
18
+
19
+ export default logger;
@@ -0,0 +1,97 @@
1
+ import { CompositeError } from '../ErrorLibrary.js';
2
+
3
+ /**
4
+ * Base application error class
5
+ * All custom errors should extend this
6
+ */
7
+ export class ApplicationError extends CompositeError {
8
+ constructor(name, message, statusCode = 500, error = null) {
9
+ super(name, message, error);
10
+ this.statusCode = statusCode;
11
+ this.name = name;
12
+ }
13
+
14
+ /**
15
+ * Convert error to HTTP response format
16
+ * @returns {{error: string, message: string, details?: string}}
17
+ */
18
+ toResponse() {
19
+ return {
20
+ error: this.name,
21
+ message: this.message,
22
+ ...(this.error && { details: this.error.message || String(this.error) }),
23
+ };
24
+ }
25
+ }
26
+
27
+ /**
28
+ * Validation error (400 Bad Request)
29
+ */
30
+ export class ValidationError extends ApplicationError {
31
+ constructor(message, error = null) {
32
+ super('ValidationError', message, 400, error);
33
+ }
34
+ }
35
+
36
+ /**
37
+ * Not found error (404 Not Found)
38
+ */
39
+ export class NotFoundError extends ApplicationError {
40
+ constructor(message, error = null) {
41
+ super('NotFoundError', message, 404, error);
42
+ }
43
+ }
44
+
45
+ /**
46
+ * Service unavailable error (503 Service Unavailable)
47
+ */
48
+ export class ServiceUnavailableError extends ApplicationError {
49
+ constructor(message, error = null) {
50
+ super('ServiceUnavailableError', message, 503, error);
51
+ }
52
+ }
53
+
54
+ /**
55
+ * Internal server error (500 Internal Server Error)
56
+ */
57
+ export class InternalServerError extends ApplicationError {
58
+ constructor(message, error = null) {
59
+ super('InternalServerError', message, 500, error);
60
+ }
61
+ }
62
+
63
+ /**
64
+ * Check if error is an ApplicationError
65
+ * @param {*} error
66
+ * @returns {boolean}
67
+ */
68
+ export function isApplicationError(error) {
69
+ return error instanceof ApplicationError;
70
+ }
71
+
72
+ /**
73
+ * Convert any error to ApplicationError
74
+ * @param {Error|ApplicationError} error
75
+ * @param {string} defaultMessage
76
+ * @returns {ApplicationError}
77
+ */
78
+ export function toApplicationError(error, defaultMessage = 'An unexpected error occurred') {
79
+ if (isApplicationError(error)) {
80
+ return error;
81
+ }
82
+
83
+ // Check for common error patterns
84
+ if (error?.message?.includes('ECONNREFUSED') || error?.message?.includes('connect')) {
85
+ return new ServiceUnavailableError('Service unavailable', error);
86
+ }
87
+
88
+ if (error?.message?.includes('required')) {
89
+ return new ValidationError(error.message, error);
90
+ }
91
+
92
+ if (error?.message?.includes('not found') || error?.code === 'ENOENT') {
93
+ return new NotFoundError(error.message || 'Resource not found', error);
94
+ }
95
+
96
+ return new InternalServerError(defaultMessage, error);
97
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Library layer exports
3
+ * Libraries are pure utilities with no dependencies on services or repositories
4
+ */
5
+ export { SerializationLibrary } from './SerializationLibrary.js';
6
+ export { LoggerLibrary } from './LoggerLibrary.js';
7
+ export { CompositeError, isError, getErrors } from './ErrorLibrary.js';
8
+ export {
9
+ ApplicationError,
10
+ ValidationError,
11
+ NotFoundError,
12
+ ServiceUnavailableError,
13
+ InternalServerError,
14
+ isApplicationError,
15
+ toApplicationError,
16
+ } from './errors/ApplicationError.js';
17
+ export { default as bootstrapLogger } from './bootstrap-logger.js';
@@ -2,11 +2,6 @@ import { Readable } from 'node:stream';
2
2
  import { parse as parseJsonRpc } from 'jsonrpc-lite';
3
3
  import { getSessionFromRequest } from '../server/internal/handlers/common.js';
4
4
 
5
- /* ---------- helpers ---------- */
6
-
7
- /**
8
- * Safely parse JSON string, returning null if parsing fails
9
- */
10
5
  function parseJsonSafely(jsonString) {
11
6
  try {
12
7
  return JSON.parse(jsonString);
@@ -53,40 +48,66 @@ async function readBody(req) {
53
48
  return Buffer.concat(chunks);
54
49
  }
55
50
 
51
+ function writeHandlerFunction(chunks, target, chunk, ...args) {
52
+ if (chunk) {
53
+ const buf = toBuffer(chunk);
54
+ chunks.push(buf);
55
+ }
56
+ return target.write(chunk, ...args);
57
+ }
58
+
59
+ function createWriteHandler(chunks, target) {
60
+ return (chunk, ...args) => {
61
+ return writeHandlerFunction(chunks, target, chunk, ...args);
62
+ };
63
+ }
64
+
65
+ function endHandlerFunction(chunks, target, chunk, ...args) {
66
+ if (chunk) {
67
+ const buf = toBuffer(chunk);
68
+ chunks.push(buf);
69
+ }
70
+ return target.end(chunk, ...args);
71
+ }
72
+
73
+ function createEndHandler(chunks, target) {
74
+ return (chunk, ...args) => {
75
+ return endHandlerFunction(chunks, target, chunk, ...args);
76
+ };
77
+ }
78
+
79
+ function proxyGetHandlerFunction(chunks, target, prop, receiver) {
80
+ if (prop === 'write') {
81
+ return createWriteHandler(chunks, target);
82
+ }
83
+
84
+ if (prop === 'end') {
85
+ return createEndHandler(chunks, target);
86
+ }
87
+
88
+ return Reflect.get(target, prop, receiver);
89
+ }
90
+
91
+ function createProxyGetHandler(chunks) {
92
+ return (target, prop, receiver) => {
93
+ return proxyGetHandlerFunction(chunks, target, prop, receiver);
94
+ };
95
+ }
96
+
97
+ function getResponseBody(chunks) {
98
+ return Buffer.concat(chunks);
99
+ }
100
+
56
101
  function captureResponse(res) {
57
102
  const chunks = [];
58
103
 
59
104
  const wrapped = new Proxy(res, {
60
- get(target, prop, receiver) {
61
- if (prop === 'write') {
62
- return (chunk, ...args) => {
63
- if (chunk) {
64
- const buf = toBuffer(chunk);
65
- chunks.push(buf);
66
- }
67
- return target.write(chunk, ...args);
68
- };
69
- }
70
-
71
- if (prop === 'end') {
72
- return (chunk, ...args) => {
73
- if (chunk) {
74
- const buf = toBuffer(chunk);
75
- chunks.push(buf);
76
- }
77
- return target.end(chunk, ...args);
78
- };
79
- }
80
-
81
- return Reflect.get(target, prop, receiver);
82
- },
105
+ get: createProxyGetHandler(chunks),
83
106
  });
84
107
 
85
108
  return {
86
109
  res: wrapped,
87
- getBody: () => {
88
- return Buffer.concat(chunks);
89
- },
110
+ getBody: () => getResponseBody(chunks),
90
111
  };
91
112
  }
92
113
 
@@ -105,12 +126,16 @@ function tryParseJsonRpc(buf) {
105
126
  }
106
127
  }
107
128
 
129
+ function createReadableRead(buf) {
130
+ return function read() {
131
+ this.push(buf);
132
+ this.push(null);
133
+ };
134
+ }
135
+
108
136
  function rebuildReq(req, buf) {
109
137
  const r = new Readable({
110
- read() {
111
- this.push(buf);
112
- this.push(null);
113
- },
138
+ read: createReadableRead(buf),
114
139
  });
115
140
 
116
141
  r.headers = req.headers;
@@ -120,24 +145,30 @@ function rebuildReq(req, buf) {
120
145
  return r;
121
146
  }
122
147
 
148
+ function finishOnce(state, resolve) {
149
+ if (!state.done) {
150
+ state.done = true;
151
+ resolve();
152
+ }
153
+ }
154
+
155
+ function handleResponseFinish(state, resolve) {
156
+ finishOnce(state, resolve);
157
+ }
158
+
159
+ function handleResponseClose(state, resolve) {
160
+ finishOnce(state, resolve);
161
+ }
162
+
123
163
  function waitForResponseFinish(res) {
124
164
  return new Promise((resolve) => {
125
165
  const state = { done: false };
126
166
 
127
- function finishOnce() {
128
- if (!state.done) {
129
- state.done = true;
130
- resolve();
131
- }
132
- }
133
-
134
- res.on('finish', finishOnce);
135
- res.on('close', finishOnce);
167
+ res.on('finish', () => handleResponseFinish(state, resolve));
168
+ res.on('close', () => handleResponseClose(state, resolve));
136
169
  });
137
170
  }
138
171
 
139
- /* ---------- main handler ---------- */
140
-
141
172
  export async function withAuditRequestResponseHandler(
142
173
  transport,
143
174
  req,
@@ -149,8 +180,6 @@ export async function withAuditRequestResponseHandler(
149
180
  const reqBuf = await readBody(req);
150
181
  const reqJsonRpc = tryParseJsonRpc(reqBuf);
151
182
 
152
- // Extract session ID from request
153
- // If no session ID exists, it's an initiation request
154
183
  const sessionIdFromRequest = getSessionFromRequest(req);
155
184
  const sessionId =
156
185
  sessionIdFromRequest === null ||
@@ -180,14 +209,12 @@ export async function withAuditRequestResponseHandler(
180
209
 
181
210
  const parsedForTransport = reqJsonRpc ? parseJsonSafely(reqBuf.toString('utf8')) : undefined;
182
211
 
183
- // hand over to transport
184
212
  if (!transport || typeof transport.handleRequest !== 'function') {
185
213
  res.status(500).json({ error: 'Transport not available' });
186
214
  return;
187
215
  }
188
216
  await transport.handleRequest(replayReq, wrappedRes, parsedForTransport);
189
217
 
190
- // wait until response fully finished (important for SSE / streaming)
191
218
  await waitForResponseFinish(wrappedRes);
192
219
 
193
220
  const resBuf = getBody();
@@ -201,11 +228,8 @@ export async function withAuditRequestResponseHandler(
201
228
  const resBodyStr = resBuf.toString('utf8');
202
229
  const resBodyJson = parseJsonSafely(resBodyStr);
203
230
 
204
- // Extract JSON-RPC ID from request for correlation
205
231
  const jsonrpcId = reqJsonRpc?.payload?.id !== undefined ? String(reqJsonRpc.payload.id) : null;
206
232
 
207
- // Log response packet to database
208
- // Use the same session ID from the request
209
233
  auditLogger.logResponsePacket({
210
234
  statusCode: wrappedRes.statusCode || 200,
211
235
  headers: resHeaders,
@@ -0,0 +1,192 @@
1
+ import { createServer } from 'node:http';
2
+
3
+ import { isError } from '#core/libraries/ErrorLibrary.js';
4
+ import { bootstrapLogger as serverLogger } from '#core/libraries/index.js';
5
+ import { runAllExternalServers } from './server/external/all.js';
6
+
7
+ import { DependencyContainer } from '#core';
8
+ import {
9
+ Environment,
10
+ getDatabaseFile,
11
+ getMcpConfigPath,
12
+ prepareAppDataSpaces,
13
+ } from '#core/configs/index.js';
14
+ import { initDb } from '#core/db/init.js';
15
+ import { withAuditRequestResponseHandler } from './auditor/audit.js';
16
+ import { getInternalServer } from './server/internal/run.js';
17
+ import { createInternalServerFactory } from './server/internal/server.js';
18
+
19
+ /**
20
+ * Initialize audit logger
21
+ * @param {Object} serverLogger - Logger instance
22
+ * @returns {Object} Audit logger instance
23
+ */
24
+ export function initAuditLogger(serverLogger) {
25
+ serverLogger.info({ path: getDatabaseFile() }, 'Initializing audit logger at');
26
+ const db = initDb(getDatabaseFile());
27
+ const container = new DependencyContainer(db);
28
+ return container.getAuditLogger();
29
+ }
30
+
31
+ function createCloseCallback(serverLogger, resolve) {
32
+ return () => {
33
+ serverLogger.info('MCP Shark server stopped');
34
+ resolve();
35
+ };
36
+ }
37
+
38
+ async function performStop(server, externalServers, serverLogger) {
39
+ // Close all external server clients
40
+ for (const serverInfo of externalServers) {
41
+ if (serverInfo?.client && !isError(serverInfo)) {
42
+ try {
43
+ await serverInfo.client.close();
44
+ } catch (error) {
45
+ serverLogger.warn(
46
+ { error, message: error.message },
47
+ 'Error closing external server client'
48
+ );
49
+ }
50
+ }
51
+ }
52
+
53
+ // Close all connections forcefully
54
+ if (server.closeAllConnections) {
55
+ server.closeAllConnections();
56
+ }
57
+
58
+ // Close the server
59
+ return new Promise((resolve) => {
60
+ const closeCallback = createCloseCallback(serverLogger, resolve);
61
+ server.close(closeCallback);
62
+ });
63
+ }
64
+
65
+ function stopFunctionWrapper(server, externalServers, serverLogger) {
66
+ return performStop(server, externalServers, serverLogger);
67
+ }
68
+
69
+ async function stopServerWrapper(server, externalServers, serverLogger) {
70
+ return stopFunctionWrapper(server, externalServers, serverLogger);
71
+ }
72
+
73
+ function createStopFunction(server, externalServers, serverLogger) {
74
+ return async () => {
75
+ return stopServerWrapper(server, externalServers, serverLogger);
76
+ };
77
+ }
78
+
79
+ function handleServerError(error, serverLogger, onError, reject) {
80
+ serverLogger.error(
81
+ { error, message: error.message, stack: error.stack },
82
+ `[MCP-Shark] ${error.message}`
83
+ );
84
+ if (onError) {
85
+ onError(error);
86
+ }
87
+ reject(error);
88
+ }
89
+
90
+ function handleServerReady(httpServer, port, serverLogger, onReady, resolve) {
91
+ serverLogger.info(`MCP proxy HTTP server listening on http://localhost:${port}/mcp`);
92
+ if (onReady) {
93
+ onReady();
94
+ }
95
+ resolve(httpServer);
96
+ }
97
+
98
+ function createServerPromise(httpServer, port, serverLogger, onError, onReady) {
99
+ return new Promise((resolve, reject) => {
100
+ httpServer.on('error', (error) => {
101
+ handleServerError(error, serverLogger, onError, reject);
102
+ });
103
+
104
+ httpServer.listen(port, '0.0.0.0', () => {
105
+ handleServerReady(httpServer, port, serverLogger, onReady, resolve);
106
+ });
107
+ });
108
+ }
109
+
110
+ /**
111
+ * Start the MCP Shark server
112
+ * @param {Object} options - Configuration options
113
+ * @param {string} [options.configPath] - Path to MCP config file (defaults to getMcpConfigPath())
114
+ * @param {number} [options.port=9851] - Port to listen on
115
+ * @param {Function} [options.onError] - Error callback
116
+ * @param {Function} [options.onReady] - Ready callback
117
+ * @param {Object} options.auditLogger - Required audit logger instance (use initAuditLogger() to create)
118
+ * @returns {Promise<{app: Express, server: http.Server, stop: Function}>} Server instance
119
+ */
120
+ export async function startMcpSharkServer(options = {}) {
121
+ const {
122
+ configPath = getMcpConfigPath(),
123
+ port = 9851,
124
+ onError,
125
+ onReady,
126
+ auditLogger: providedAuditLogger,
127
+ } = options;
128
+
129
+ prepareAppDataSpaces();
130
+
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');
136
+
137
+ try {
138
+ if (!providedAuditLogger) {
139
+ throw new Error('auditLogger is required. Call initAuditLogger() and pass it in options.');
140
+ }
141
+ const auditLogger = providedAuditLogger;
142
+ const externalServersResult = await runAllExternalServers(serverLogger, configPath);
143
+
144
+ if (isError(externalServersResult)) {
145
+ serverLogger.error(
146
+ {
147
+ error: externalServersResult,
148
+ message: externalServersResult.message,
149
+ stack: externalServersResult.stack,
150
+ },
151
+ `[MCP-Shark] ${externalServersResult.message}`
152
+ );
153
+ const error = new Error(JSON.stringify(externalServersResult));
154
+ if (onError) {
155
+ onError(error);
156
+ }
157
+ throw error;
158
+ }
159
+
160
+ const { kv, servers: externalServers } = externalServersResult;
161
+
162
+ const internalServerFactory = createInternalServerFactory(serverLogger, kv);
163
+ const app = getInternalServer(
164
+ internalServerFactory,
165
+ auditLogger,
166
+ withAuditRequestResponseHandler
167
+ );
168
+
169
+ const httpServer = createServer(app);
170
+
171
+ const server = await createServerPromise(httpServer, port, serverLogger, onError, onReady);
172
+
173
+ const stop = createStopFunction(server, externalServers, serverLogger);
174
+
175
+ return {
176
+ app,
177
+ server,
178
+ stop,
179
+ auditLogger,
180
+ };
181
+ } catch (error) {
182
+ const errorMsg = 'Error starting MCP server';
183
+ serverLogger.error(
184
+ { error, message: error.message, stack: error.stack },
185
+ `[MCP-Shark] ${errorMsg}`
186
+ );
187
+ if (onError) {
188
+ onError(error);
189
+ }
190
+ throw error;
191
+ }
192
+ }
@@ -1,4 +1,4 @@
1
- import { CompositeError, getErrors } from '../../common/error.js';
1
+ import { CompositeError, getErrors } from '#core/libraries/ErrorLibrary.js';
2
2
  import { normalizeConfig } from './config.js';
3
3
  import { buildKv } from './kv.js';
4
4
  import { runExternalServer } from './single/run.js';