cprime-supergateway 3.4.4 → 3.4.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.
@@ -10,15 +10,21 @@ import { serializeCorsOrigin } from '../lib/serializeCorsOrigin.js';
10
10
  import { EncryptionService } from '../services/encryptionService.js';
11
11
  import { initMongoClient } from '../lib/initMongoClient.js';
12
12
  import { McpServerLogRepository } from '../lib/mcpServerLogRepository.js';
13
+ import { LogService } from '../services/logService.js';
13
14
  const encryptionService = new EncryptionService('env');
14
- const plaintext = await encryptionService.decryptText(process.env.ENCRYPTED_ENV ?? '', process.env.AAD_JSON ? JSON.parse(process.env.AAD_JSON) : {});
15
+ const mongoClient = initMongoClient();
16
+ const mcpServerLogRepository = new McpServerLogRepository(mongoClient, process.env.MONGO_DB ?? 'localhost', process.env.LOGS_COLLECTION ?? 'mcp_server_logs');
17
+ const logService = new LogService(mcpServerLogRepository);
15
18
  let decryptedEnvs = {};
16
- try {
17
- const asObj = JSON.parse(plaintext);
18
- decryptedEnvs = asObj;
19
- }
20
- catch {
21
- console.error('Failed to parse decrypted envs', plaintext);
19
+ if (process.env.ENCRYPTED_ENV) {
20
+ const plaintext = await encryptionService.decryptText(process.env.ENCRYPTED_ENV, process.env.AAD_JSON ? JSON.parse(process.env.AAD_JSON) : {});
21
+ try {
22
+ const asObj = JSON.parse(plaintext);
23
+ decryptedEnvs = asObj;
24
+ }
25
+ catch {
26
+ console.error('Failed to parse decrypted envs', plaintext);
27
+ }
22
28
  }
23
29
  const setResponseHeaders = ({ res, headers, }) => Object.entries(headers).forEach(([key, value]) => {
24
30
  res.setHeader(key, value);
@@ -36,8 +42,6 @@ export async function stdioToSse(args) {
36
42
  logger.info(` - CORS: ${corsOrigin ? `enabled (${serializeCorsOrigin({ corsOrigin })})` : 'disabled'}`);
37
43
  logger.info(` - Health endpoints: ${healthEndpoints.length ? healthEndpoints.join(', ') : '(none)'}`);
38
44
  onSignals({ logger });
39
- const mongoClient = initMongoClient();
40
- const mcpServerLogRepository = new McpServerLogRepository(mongoClient, process.env.MONGO_DB ?? 'localhost', process.env.LOGS_COLLECTION ?? 'mcp_server_logs');
41
45
  const child = spawn(stdioCmd, {
42
46
  shell: true,
43
47
  env: { ...process.env, ...decryptedEnvs },
@@ -85,18 +89,9 @@ export async function stdioToSse(args) {
85
89
  }
86
90
  sseTransport.onmessage = (msg) => {
87
91
  logger.info(`SSE → Child (session ${sessionId}): ${JSON.stringify(msg)}`);
88
- mcpServerLogRepository
89
- .insert({
90
- ip: req.ip ?? '',
91
- userId: req.query.userId ?? '',
92
+ logService.log(msg, 'request-rpc', logger, {
93
+ ...sessions[sessionId],
92
94
  sessionId,
93
- type: 'rpc',
94
- data: msg,
95
- createdAt: new Date(),
96
- updatedAt: new Date(),
97
- })
98
- .catch((err) => {
99
- logger.error(`Failed to insert log:`, JSON.stringify(err));
100
95
  });
101
96
  child.stdin.write(JSON.stringify(msg) + '\n');
102
97
  };
@@ -151,18 +146,9 @@ export async function stdioToSse(args) {
151
146
  for (const [sid, session] of Object.entries(sessions)) {
152
147
  try {
153
148
  session.transport.send(jsonMsg);
154
- mcpServerLogRepository
155
- .insert({
156
- ip: session.ip,
157
- userId: session.userId,
149
+ logService.log(jsonMsg, 'response-rpc', logger, {
150
+ ...session,
158
151
  sessionId: sid,
159
- type: 'system',
160
- data: line,
161
- createdAt: new Date(),
162
- updatedAt: new Date(),
163
- })
164
- .catch((err) => {
165
- logger.error(`Failed to insert log:`, JSON.stringify(err));
166
152
  });
167
153
  }
168
154
  catch (err) {
@@ -173,10 +159,22 @@ export async function stdioToSse(args) {
173
159
  }
174
160
  catch {
175
161
  logger.error(`Child non-JSON: ${line}`);
162
+ for (const [sid, session] of Object.entries(sessions)) {
163
+ logService.log(line.toString(), 'system', logger, {
164
+ ...session,
165
+ sessionId: sid,
166
+ });
167
+ }
176
168
  }
177
169
  });
178
170
  });
179
171
  child.stderr.on('data', (chunk) => {
180
172
  logger.error(`Child stderr: ${chunk.toString('utf8')}`);
173
+ for (const [sid, session] of Object.entries(sessions)) {
174
+ logService.log(chunk.toString('utf8'), 'system', logger, {
175
+ ...session,
176
+ sessionId: sid,
177
+ });
178
+ }
181
179
  });
182
180
  }
@@ -0,0 +1,24 @@
1
+ export class LogService {
2
+ repository;
3
+ constructor(repository) {
4
+ this.repository = repository;
5
+ }
6
+ async log(data, type, logger, session) {
7
+ const logDto = {
8
+ ip: session.ip,
9
+ userId: session.userId,
10
+ sessionId: session.sessionId,
11
+ type: type,
12
+ data: data,
13
+ createdAt: new Date(),
14
+ updatedAt: new Date(),
15
+ };
16
+ // Insert to MongoDB with error handling
17
+ try {
18
+ await this.repository.insert(logDto);
19
+ }
20
+ catch (err) {
21
+ logger.error(`Failed to insert log:`, JSON.stringify(err));
22
+ }
23
+ }
24
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cprime-supergateway",
3
- "version": "3.4.4",
3
+ "version": "3.4.6",
4
4
  "description": "Run MCP stdio servers over SSE, Streamable HTTP or visa versa",
5
5
  "repository": {
6
6
  "type": "git",
@@ -12,6 +12,7 @@ import { serializeCorsOrigin } from '../lib/serializeCorsOrigin.js'
12
12
  import { EncryptionService } from '../services/encryptionService.js'
13
13
  import { initMongoClient } from '../lib/initMongoClient.js'
14
14
  import { McpServerLogRepository } from '../lib/mcpServerLogRepository.js'
15
+ import { ILogService, LogService } from '../services/logService.js'
15
16
 
16
17
  export interface StdioToSseArgs {
17
18
  stdioCmd: string
@@ -26,16 +27,25 @@ export interface StdioToSseArgs {
26
27
  }
27
28
 
28
29
  const encryptionService = new EncryptionService('env')
29
- const plaintext = await encryptionService.decryptText(
30
- process.env.ENCRYPTED_ENV ?? '',
31
- process.env.AAD_JSON ? JSON.parse(process.env.AAD_JSON) : {},
30
+ const mongoClient = initMongoClient()
31
+ const mcpServerLogRepository = new McpServerLogRepository(
32
+ mongoClient,
33
+ process.env.MONGO_DB ?? 'localhost',
34
+ process.env.LOGS_COLLECTION ?? 'mcp_server_logs',
32
35
  )
36
+ const logService: ILogService = new LogService(mcpServerLogRepository)
33
37
  let decryptedEnvs = {}
34
- try {
35
- const asObj = JSON.parse(plaintext)
36
- decryptedEnvs = asObj
37
- } catch {
38
- console.error('Failed to parse decrypted envs', plaintext)
38
+ if (process.env.ENCRYPTED_ENV) {
39
+ const plaintext = await encryptionService.decryptText(
40
+ process.env.ENCRYPTED_ENV,
41
+ process.env.AAD_JSON ? JSON.parse(process.env.AAD_JSON) : {},
42
+ )
43
+ try {
44
+ const asObj = JSON.parse(plaintext)
45
+ decryptedEnvs = asObj
46
+ } catch {
47
+ console.error('Failed to parse decrypted envs', plaintext)
48
+ }
39
49
  }
40
50
 
41
51
  const setResponseHeaders = ({
@@ -82,13 +92,6 @@ export async function stdioToSse(args: StdioToSseArgs) {
82
92
 
83
93
  onSignals({ logger })
84
94
 
85
- const mongoClient = initMongoClient()
86
- const mcpServerLogRepository = new McpServerLogRepository(
87
- mongoClient,
88
- process.env.MONGO_DB ?? 'localhost',
89
- process.env.LOGS_COLLECTION ?? 'mcp_server_logs',
90
- )
91
-
92
95
  const child: ChildProcessWithoutNullStreams = spawn(stdioCmd, {
93
96
  shell: true,
94
97
  env: { ...process.env, ...decryptedEnvs },
@@ -157,19 +160,10 @@ export async function stdioToSse(args: StdioToSseArgs) {
157
160
 
158
161
  sseTransport.onmessage = (msg: JSONRPCMessage) => {
159
162
  logger.info(`SSE → Child (session ${sessionId}): ${JSON.stringify(msg)}`)
160
- mcpServerLogRepository
161
- .insert({
162
- ip: req.ip ?? '',
163
- userId: (req.query.userId as string) ?? '',
164
- sessionId,
165
- type: 'rpc',
166
- data: msg,
167
- createdAt: new Date(),
168
- updatedAt: new Date(),
169
- })
170
- .catch((err) => {
171
- logger.error(`Failed to insert log:`, JSON.stringify(err))
172
- })
163
+ logService.log(msg, 'request-rpc', logger, {
164
+ ...sessions[sessionId],
165
+ sessionId,
166
+ })
173
167
  child.stdin.write(JSON.stringify(msg) + '\n')
174
168
  }
175
169
 
@@ -230,19 +224,10 @@ export async function stdioToSse(args: StdioToSseArgs) {
230
224
  for (const [sid, session] of Object.entries(sessions)) {
231
225
  try {
232
226
  session.transport.send(jsonMsg)
233
- mcpServerLogRepository
234
- .insert({
235
- ip: session.ip,
236
- userId: session.userId,
237
- sessionId: sid,
238
- type: 'system',
239
- data: line,
240
- createdAt: new Date(),
241
- updatedAt: new Date(),
242
- })
243
- .catch((err) => {
244
- logger.error(`Failed to insert log:`, JSON.stringify(err))
245
- })
227
+ logService.log(jsonMsg, 'response-rpc', logger, {
228
+ ...session,
229
+ sessionId: sid,
230
+ })
246
231
  } catch (err) {
247
232
  logger.error(`Failed to send to session ${sid}:`, err)
248
233
  delete sessions[sid]
@@ -250,11 +235,23 @@ export async function stdioToSse(args: StdioToSseArgs) {
250
235
  }
251
236
  } catch {
252
237
  logger.error(`Child non-JSON: ${line}`)
238
+ for (const [sid, session] of Object.entries(sessions)) {
239
+ logService.log(line.toString(), 'system', logger, {
240
+ ...session,
241
+ sessionId: sid,
242
+ })
243
+ }
253
244
  }
254
245
  })
255
246
  })
256
247
 
257
248
  child.stderr.on('data', (chunk: Buffer) => {
258
249
  logger.error(`Child stderr: ${chunk.toString('utf8')}`)
250
+ for (const [sid, session] of Object.entries(sessions)) {
251
+ logService.log(chunk.toString('utf8'), 'system', logger, {
252
+ ...session,
253
+ sessionId: sid,
254
+ })
255
+ }
259
256
  })
260
257
  }
@@ -39,7 +39,7 @@ export class McpServerLogRepository {
39
39
 
40
40
  export type McpServerLogDto = {
41
41
  ip: string
42
- type: 'rpc' | 'error' | 'system'
42
+ type: 'request-rpc' | 'response-rpc' | 'system'
43
43
  userId: string
44
44
  sessionId: string
45
45
  data: JSONRPCMessage | string
@@ -0,0 +1,43 @@
1
+ import { Logger } from '../types.js'
2
+ import {
3
+ McpServerLogRepository,
4
+ type McpServerLogDto,
5
+ } from '../lib/mcpServerLogRepository.js'
6
+ import { JSONRPCMessage } from '@modelcontextprotocol/sdk/types.js'
7
+
8
+ export interface ILogService {
9
+ log(
10
+ data: string | object,
11
+ type: string,
12
+ logger: Logger,
13
+ session: { ip: string; userId: string; sessionId: string },
14
+ ): Promise<void>
15
+ }
16
+
17
+ export class LogService implements ILogService {
18
+ constructor(private readonly repository: McpServerLogRepository) {}
19
+
20
+ async log(
21
+ data: string | object,
22
+ type: string,
23
+ logger: Logger,
24
+ session: { ip: string; userId: string; sessionId: string },
25
+ ): Promise<void> {
26
+ const logDto: McpServerLogDto = {
27
+ ip: session.ip,
28
+ userId: session.userId,
29
+ sessionId: session.sessionId,
30
+ type: type as McpServerLogDto['type'],
31
+ data: data as JSONRPCMessage | string,
32
+ createdAt: new Date(),
33
+ updatedAt: new Date(),
34
+ }
35
+
36
+ // Insert to MongoDB with error handling
37
+ try {
38
+ await this.repository.insert(logDto)
39
+ } catch (err) {
40
+ logger.error(`Failed to insert log:`, JSON.stringify(err))
41
+ }
42
+ }
43
+ }