@mcp-shark/mcp-shark 1.5.4 → 1.5.5
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 +32 -96
- package/bin/mcp-shark.js +1 -1
- package/core/configs/codex.js +68 -0
- package/core/configs/environment.js +51 -0
- package/{lib/common → core}/configs/index.js +16 -1
- package/core/constants/Defaults.js +15 -0
- package/core/constants/HttpStatus.js +14 -0
- package/core/constants/Server.js +20 -0
- package/core/constants/StatusCodes.js +25 -0
- package/core/constants/index.js +7 -0
- package/core/container/DependencyContainer.js +179 -0
- package/core/db/init.js +33 -0
- package/core/index.js +10 -0
- package/{mcp-server/lib/common/error.js → core/libraries/ErrorLibrary.js} +4 -0
- package/core/libraries/LoggerLibrary.js +91 -0
- package/core/libraries/SerializationLibrary.js +32 -0
- package/core/libraries/bootstrap-logger.js +19 -0
- package/core/libraries/errors/ApplicationError.js +97 -0
- package/core/libraries/index.js +17 -0
- package/{mcp-server/lib → core/mcp-server}/auditor/audit.js +77 -53
- package/core/mcp-server/index.js +192 -0
- package/{mcp-server/lib → core/mcp-server}/server/external/all.js +1 -1
- package/core/mcp-server/server/external/config.js +75 -0
- package/{mcp-server/lib → core/mcp-server}/server/external/single/client.js +1 -1
- package/{mcp-server/lib → core/mcp-server}/server/external/single/request.js +1 -1
- package/{mcp-server/lib → core/mcp-server}/server/external/single/run.js +20 -11
- package/{mcp-server/lib → core/mcp-server}/server/external/single/transport.js +1 -1
- package/{mcp-server/lib → core/mcp-server}/server/internal/handlers/error.js +1 -1
- package/core/mcp-server/server/internal/handlers/prompts-get.js +28 -0
- package/core/mcp-server/server/internal/handlers/prompts-list.js +21 -0
- package/core/mcp-server/server/internal/handlers/resources-list.js +21 -0
- package/core/mcp-server/server/internal/handlers/resources-read.js +28 -0
- package/core/mcp-server/server/internal/handlers/tools-call.js +44 -0
- package/core/mcp-server/server/internal/handlers/tools-list.js +23 -0
- package/core/mcp-server/server/internal/run.js +53 -0
- package/{mcp-server/lib → core/mcp-server}/server/internal/server.js +11 -1
- package/core/models/ConversationFilters.js +31 -0
- package/core/models/ExportFormat.js +8 -0
- package/core/models/RequestFilters.js +43 -0
- package/core/models/SessionFilters.js +23 -0
- package/core/models/index.js +8 -0
- package/core/repositories/AuditRepository.js +233 -0
- package/core/repositories/ConversationRepository.js +182 -0
- package/core/repositories/PacketRepository.js +237 -0
- package/core/repositories/SchemaRepository.js +107 -0
- package/core/repositories/SessionRepository.js +59 -0
- package/core/repositories/StatisticsRepository.js +54 -0
- package/core/repositories/index.js +10 -0
- package/core/services/AuditService.js +144 -0
- package/core/services/BackupService.js +222 -0
- package/core/services/ConfigDetectionService.js +89 -0
- package/core/services/ConfigFileService.js +210 -0
- package/core/services/ConfigPatchingService.js +137 -0
- package/core/services/ConfigService.js +250 -0
- package/core/services/ConfigTransformService.js +178 -0
- package/core/services/ConversationService.js +19 -0
- package/core/services/ExportService.js +117 -0
- package/core/services/LogService.js +64 -0
- package/core/services/McpClientService.js +235 -0
- package/core/services/McpDiscoveryService.js +107 -0
- package/core/services/RequestService.js +56 -0
- package/core/services/ScanCacheService.js +242 -0
- package/core/services/ScanService.js +167 -0
- package/core/services/ServerManagementService.js +206 -0
- package/core/services/SessionService.js +34 -0
- package/core/services/SettingsService.js +163 -0
- package/core/services/StatisticsService.js +64 -0
- package/core/services/TokenService.js +94 -0
- package/core/services/index.js +25 -0
- package/core/services/parsers/ConfigParserFactory.js +113 -0
- package/core/services/parsers/JsonConfigParser.js +66 -0
- package/core/services/parsers/LegacyJsonConfigParser.js +71 -0
- package/core/services/parsers/TomlConfigParser.js +87 -0
- package/core/services/parsers/index.js +4 -0
- package/{ui/server → core}/utils/scan-cache/directory.js +1 -1
- package/core/utils/validation.js +77 -0
- package/package.json +14 -11
- package/ui/dist/assets/index-CArYxKxS.js +35 -0
- package/ui/dist/index.html +1 -1
- package/ui/server/controllers/BackupController.js +129 -0
- package/ui/server/controllers/ConfigController.js +92 -0
- package/ui/server/controllers/ConversationController.js +41 -0
- package/ui/server/controllers/LogController.js +44 -0
- package/ui/server/controllers/McpClientController.js +60 -0
- package/ui/server/controllers/McpDiscoveryController.js +44 -0
- package/ui/server/controllers/RequestController.js +129 -0
- package/ui/server/controllers/ScanController.js +122 -0
- package/ui/server/controllers/ServerManagementController.js +134 -0
- package/ui/server/controllers/SessionController.js +57 -0
- package/ui/server/controllers/SettingsController.js +24 -0
- package/ui/server/controllers/StatisticsController.js +54 -0
- package/ui/server/controllers/TokenController.js +58 -0
- package/ui/server/controllers/index.js +17 -0
- package/ui/server/routes/backups/index.js +15 -9
- package/ui/server/routes/composite/index.js +62 -32
- package/ui/server/routes/composite/servers.js +20 -15
- package/ui/server/routes/config.js +13 -172
- package/ui/server/routes/conversations.js +9 -19
- package/ui/server/routes/help.js +4 -3
- package/ui/server/routes/logs.js +14 -26
- package/ui/server/routes/playground.js +11 -174
- package/ui/server/routes/requests.js +12 -232
- package/ui/server/routes/sessions.js +10 -21
- package/ui/server/routes/settings.js +10 -192
- package/ui/server/routes/smartscan.js +26 -15
- package/ui/server/routes/statistics.js +8 -79
- package/ui/server/setup.js +162 -0
- package/ui/server/swagger/paths/backups.js +151 -0
- package/ui/server/swagger/paths/components.js +76 -0
- package/ui/server/swagger/paths/config.js +117 -0
- package/ui/server/swagger/paths/conversations.js +29 -0
- package/ui/server/swagger/paths/help.js +82 -0
- package/ui/server/swagger/paths/logs.js +87 -0
- package/ui/server/swagger/paths/playground.js +49 -0
- package/ui/server/swagger/paths/requests.js +178 -0
- package/ui/server/swagger/paths/serverManagement.js +169 -0
- package/ui/server/swagger/paths/sessions.js +61 -0
- package/ui/server/swagger/paths/settings.js +31 -0
- package/ui/server/swagger/paths/smartScan/discovery.js +97 -0
- package/ui/server/swagger/paths/smartScan/index.js +13 -0
- package/ui/server/swagger/paths/smartScan/scans.js +151 -0
- package/ui/server/swagger/paths/smartScan/token.js +71 -0
- package/ui/server/swagger/paths/statistics.js +40 -0
- package/ui/server/swagger/paths.js +38 -0
- package/ui/server/swagger/swagger.js +37 -0
- package/ui/server/utils/cleanup.js +99 -0
- package/ui/server/utils/config.js +18 -96
- package/ui/server/utils/errorHandler.js +43 -0
- package/ui/server/utils/logger.js +2 -2
- package/ui/server/utils/paths.js +27 -30
- package/ui/server/utils/port.js +21 -21
- package/ui/server/utils/process.js +18 -10
- package/ui/server/utils/processState.js +17 -0
- package/ui/server/utils/signals.js +34 -0
- package/ui/server/websocket/broadcast.js +33 -0
- package/ui/server/websocket/handler.js +52 -0
- package/ui/server.js +51 -230
- package/ui/src/App.jsx +2 -0
- package/ui/src/CompositeSetup.jsx +23 -9
- package/ui/src/PacketFilters.jsx +17 -3
- package/ui/src/components/AlertModal.jsx +116 -0
- package/ui/src/components/App/ApiDocsButton.jsx +57 -0
- package/ui/src/components/App/useAppState.js +43 -1
- package/ui/src/components/BackupList.jsx +27 -3
- package/ui/src/utils/requestPairing.js +35 -36
- package/ui/src/utils/requestUtils.js +1 -0
- package/lib/common/db/init.js +0 -132
- package/lib/common/db/logger.js +0 -349
- package/lib/common/db/query.js +0 -403
- package/lib/common/logger.js +0 -90
- package/mcp-server/index.js +0 -138
- package/mcp-server/lib/server/external/config.js +0 -57
- package/mcp-server/lib/server/internal/handlers/prompts-get.js +0 -20
- package/mcp-server/lib/server/internal/handlers/prompts-list.js +0 -13
- package/mcp-server/lib/server/internal/handlers/resources-list.js +0 -13
- package/mcp-server/lib/server/internal/handlers/resources-read.js +0 -20
- package/mcp-server/lib/server/internal/handlers/tools-call.js +0 -35
- package/mcp-server/lib/server/internal/handlers/tools-list.js +0 -15
- package/mcp-server/lib/server/internal/run.js +0 -37
- package/mcp-server/mcp-shark.js +0 -22
- package/ui/dist/assets/index-CFHeMNwd.js +0 -35
- package/ui/server/routes/backups/deleteBackup.js +0 -54
- package/ui/server/routes/backups/listBackups.js +0 -75
- package/ui/server/routes/backups/restoreBackup.js +0 -83
- package/ui/server/routes/backups/viewBackup.js +0 -47
- package/ui/server/routes/composite/setup.js +0 -129
- package/ui/server/routes/composite/status.js +0 -7
- package/ui/server/routes/composite/stop.js +0 -39
- package/ui/server/routes/composite/utils.js +0 -45
- package/ui/server/routes/smartscan/discover.js +0 -118
- package/ui/server/routes/smartscan/scans/clearCache.js +0 -23
- package/ui/server/routes/smartscan/scans/createBatchScans.js +0 -124
- package/ui/server/routes/smartscan/scans/createScan.js +0 -43
- package/ui/server/routes/smartscan/scans/getCachedResults.js +0 -52
- package/ui/server/routes/smartscan/scans/getScan.js +0 -42
- package/ui/server/routes/smartscan/scans/listScans.js +0 -25
- package/ui/server/routes/smartscan/scans.js +0 -13
- package/ui/server/routes/smartscan/token.js +0 -57
- package/ui/server/utils/config-update.js +0 -240
- package/ui/server/utils/scan-cache/all-results.js +0 -197
- package/ui/server/utils/scan-cache/file-operations.js +0 -107
- package/ui/server/utils/scan-cache/hash.js +0 -47
- package/ui/server/utils/scan-cache/server-operations.js +0 -85
- package/ui/server/utils/scan-cache.js +0 -12
- package/ui/server/utils/smartscan-token.js +0 -43
- /package/{mcp-server/lib → core/mcp-server}/server/external/kv.js +0 -0
- /package/{mcp-server/lib → core/mcp-server}/server/internal/handlers/common.js +0 -0
- /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(
|
|
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
|
-
|
|
128
|
-
|
|
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 '
|
|
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';
|