diff --git a/src/handlers/shttp.integration.test.ts b/src/handlers/shttp.integration.test.ts index a7c6ec3..8ffea82 100644 --- a/src/handlers/shttp.integration.test.ts +++ b/src/handlers/shttp.integration.test.ts @@ -1,10 +1,10 @@ import { jest } from '@jest/globals'; import { Request, Response } from 'express'; -import { JSONRPCMessage } from '@modelcontextprotocol/sdk/types.js'; +import { JSONRPCMessage, JSONRPCResponse } from '@modelcontextprotocol/sdk/types.js'; import { MockRedisClient, setRedisClient } from '../redis.js'; import { handleStreamableHTTP } from './shttp.js'; import { AuthInfo } from '@modelcontextprotocol/sdk/server/auth/types.js'; -import { randomUUID } from 'crypto'; +// import { randomUUID } from 'crypto'; // Currently unused but may be needed for future tests import { shutdownSession } from '../services/redisTransport.js'; describe('Streamable HTTP Handler Integration Tests', () => { @@ -147,7 +147,6 @@ describe('Streamable HTTP Handler Integration Tests', () => { }); it('should handle cleanup errors gracefully', async () => { - const sessionId = randomUUID(); const initRequest: JSONRPCMessage = { jsonrpc: '2.0', id: 'init-1', @@ -176,7 +175,6 @@ describe('Streamable HTTP Handler Integration Tests', () => { )?.[1] as (() => Promise) | undefined; // Simulate error during cleanup - const originalSimulateError = mockRedis.simulateError.bind(mockRedis); // Cleanup should not throw error even if Redis operations fail if (finishHandler) { @@ -240,7 +238,7 @@ describe('Streamable HTTP Handler Integration Tests', () => { let sessionId: string | undefined; if (jsonCalls.length > 0) { - const response = jsonCalls[0][0] as any; + const response = jsonCalls[0][0] as JSONRPCResponse; if (response?.result?._meta?.sessionId) { sessionId = response.result._meta.sessionId; } @@ -254,11 +252,11 @@ describe('Streamable HTTP Handler Integration Tests', () => { try { // SSE data format: "data: {...}\n\n" const jsonStr = data.replace(/^data: /, '').trim(); - const parsed = JSON.parse(jsonStr) as any; + const parsed = JSON.parse(jsonStr) as JSONRPCResponse; if (parsed?.result?._meta?.sessionId) { sessionId = parsed.result._meta.sessionId; } - } catch (e) { + } catch { // Not valid JSON, continue } } @@ -344,7 +342,7 @@ describe('Streamable HTTP Handler Integration Tests', () => { // Check JSON responses const jsonCalls = (mockRes.json as jest.Mock).mock.calls; if (jsonCalls.length > 0) { - const response = jsonCalls[0][0] as any; + const response = jsonCalls[0][0] as JSONRPCResponse; if (response?.result?._meta?.sessionId) { sessionId = response.result._meta.sessionId; } @@ -357,11 +355,13 @@ describe('Streamable HTTP Handler Integration Tests', () => { if (typeof data === 'string' && data.includes('sessionId')) { try { const jsonStr = data.replace(/^data: /, '').trim(); - const parsed = JSON.parse(jsonStr) as any; + const parsed = JSON.parse(jsonStr) as JSONRPCResponse; if (parsed?.result?._meta?.sessionId) { sessionId = parsed.result._meta.sessionId; } - } catch (e) {} + } catch { + // Ignore JSON parse errors + } } } } @@ -442,7 +442,7 @@ describe('Streamable HTTP Handler Integration Tests', () => { // Check JSON responses const jsonCalls = (mockRes.json as jest.Mock).mock.calls; if (jsonCalls.length > 0) { - const response = jsonCalls[0][0] as any; + const response = jsonCalls[0][0] as JSONRPCResponse; if (response?.result?._meta?.sessionId) { actualSessionId = response.result._meta.sessionId; } @@ -459,7 +459,9 @@ describe('Streamable HTTP Handler Integration Tests', () => { if (parsed?.result?._meta?.sessionId) { actualSessionId = parsed.result._meta.sessionId; } - } catch (e) {} + } catch { + // Ignore JSON parse errors + } } } } @@ -658,8 +660,7 @@ describe('Streamable HTTP Handler Integration Tests', () => { await finishHandler2(); } - // Track session 2 ID - const session2Id = 'user2-session-id'; + // Track session 2 ID (placeholder for actual implementation) // User 1 deletes their session jest.clearAllMocks(); diff --git a/src/services/redisTransport.integration.test.ts b/src/services/redisTransport.integration.test.ts index a3e1672..3fcc778 100644 --- a/src/services/redisTransport.integration.test.ts +++ b/src/services/redisTransport.integration.test.ts @@ -4,8 +4,7 @@ import { MockRedisClient, setRedisClient } from '../redis.js'; import { ServerRedisTransport, redisRelayToMcpServer, - shutdownSession, - setSessionOwner + shutdownSession } from './redisTransport.js'; import { createMcpServer } from './mcp.js'; import { Transport } from '@modelcontextprotocol/sdk/shared/transport.js'; diff --git a/src/services/redisTransport.test.ts b/src/services/redisTransport.test.ts index 0cfd3a5..60e7e33 100644 --- a/src/services/redisTransport.test.ts +++ b/src/services/redisTransport.test.ts @@ -12,7 +12,6 @@ import { isSessionOwnedBy } from './redisTransport.js'; import { Transport } from '@modelcontextprotocol/sdk/shared/transport.js'; -import { createMcpServer } from './mcp.js'; describe('Redis Transport', () => { let mockRedis: MockRedisClient; diff --git a/src/services/redisTransport.ts b/src/services/redisTransport.ts index d48f78e..e35eb9e 100644 --- a/src/services/redisTransport.ts +++ b/src/services/redisTransport.ts @@ -28,8 +28,8 @@ function sendToMcpServer(sessionId: string, message: JSONRPCMessage, extra?: { a logger.debug('Sending message to MCP server via Redis', { sessionId, channel: toServerChannel, - method: (message as any).method, - id: (message as any).id + method: ('method' in message ? message.method : undefined), + id: ('id' in message ? message.id : undefined) }); const redisMessage: RedisMessage = { type: 'mcp', message, extra, options }; @@ -125,7 +125,7 @@ export async function redisRelayToMcpServer(sessionId: string, transport: Transp logger.debug('Relaying message from Redis to client', { sessionId, requestId, - method: (redisMessage.message as any).method + method: ('method' in redisMessage.message ? redisMessage.message.method : undefined) }); await transport.send(redisMessage.message, redisMessage.options); } @@ -148,7 +148,7 @@ export async function redisRelayToMcpServer(sessionId: string, transport: Transp logger.debug('Setting up response subscription', { sessionId, messageId: message.id, - method: (message as any).method + method: ('method' in message ? message.method : undefined) }); await subscribe(message.id.toString()); } @@ -235,8 +235,8 @@ export class ServerRedisTransport implements Transport { logger.debug('Received MCP message from client', { sessionId: this._sessionId, - method: (redisMessage.message as any).method, - id: (redisMessage.message as any).id + method: ('method' in redisMessage.message ? redisMessage.message.method : undefined), + id: ('id' in redisMessage.message ? redisMessage.message.id : undefined) }); this.onmessage?.(redisMessage.message, redisMessage.extra); @@ -295,8 +295,8 @@ export class ServerRedisTransport implements Transport { logger.debug('Sending message to client', { sessionId: this._sessionId, channel, - method: (message as any).method, - id: (message as any).id, + method: ('method' in message ? message.method : undefined), + id: ('id' in message ? message.id : undefined), relatedRequestId }); diff --git a/src/utils/logger.ts b/src/utils/logger.ts index 828ca92..f9879db 100644 --- a/src/utils/logger.ts +++ b/src/utils/logger.ts @@ -1,5 +1,5 @@ import { AsyncLocalStorage } from 'async_hooks'; -import { Request } from 'express'; +import { Request, Response, NextFunction } from 'express'; // Severity levels as per Google Cloud Logging export enum LogSeverity { @@ -17,7 +17,11 @@ export enum LogSeverity { interface LogContext { trace?: string; spanId?: string; - [key: string]: any; + requestId?: string; + userAgent?: string; + method?: string; + path?: string; + [key: string]: string | undefined; } interface StructuredLogEntry { @@ -26,7 +30,7 @@ interface StructuredLogEntry { timestamp: string; 'logging.googleapis.com/trace'?: string; 'logging.googleapis.com/spanId'?: string; - [key: string]: any; + [key: string]: unknown; } class StructuredLogger { @@ -73,7 +77,7 @@ class StructuredLogger { * Create Express middleware for request context */ middleware() { - return (req: Request, res: any, next: any) => { + return (req: Request, res: Response, next: NextFunction) => { const context = this.extractTraceContext(req); this.runWithContext(context, () => { next(); @@ -84,7 +88,7 @@ class StructuredLogger { /** * Log a structured message */ - private log(severity: LogSeverity, message: string, metadata?: Record) { + private log(severity: LogSeverity, message: string, metadata?: Record) { const context = this.asyncLocalStorage.getStore() || {}; const entry: StructuredLogEntry = { @@ -114,23 +118,23 @@ class StructuredLogger { } // Convenience methods for different severity levels - debug(message: string, metadata?: Record) { + debug(message: string, metadata?: Record) { this.log(LogSeverity.DEBUG, message, metadata); } - info(message: string, metadata?: Record) { + info(message: string, metadata?: Record) { this.log(LogSeverity.INFO, message, metadata); } - notice(message: string, metadata?: Record) { + notice(message: string, metadata?: Record) { this.log(LogSeverity.NOTICE, message, metadata); } - warning(message: string, metadata?: Record) { + warning(message: string, metadata?: Record) { this.log(LogSeverity.WARNING, message, metadata); } - error(message: string, error?: Error, metadata?: Record) { + error(message: string, error?: Error, metadata?: Record) { const errorMetadata = { ...metadata, error: error ? { @@ -142,15 +146,15 @@ class StructuredLogger { this.log(LogSeverity.ERROR, message, errorMetadata); } - critical(message: string, metadata?: Record) { + critical(message: string, metadata?: Record) { this.log(LogSeverity.CRITICAL, message, metadata); } - alert(message: string, metadata?: Record) { + alert(message: string, metadata?: Record) { this.log(LogSeverity.ALERT, message, metadata); } - emergency(message: string, metadata?: Record) { + emergency(message: string, metadata?: Record) { this.log(LogSeverity.EMERGENCY, message, metadata); }