Add comprehensive structured logging to streamable HTTP

- Remove sensitive data (auth tokens, cookies) from request logging
- Add detailed logging throughout streamable HTTP request lifecycle:
  - Session creation/reuse
  - Session ownership validation
  - Transport initialization
  - Message flow through Redis channels
  - Session shutdown and cleanup
- Add logging for inactivity timeouts and control messages
- Use appropriate log levels (info for lifecycle, debug for details, error for failures)

All logs now include contextual information like sessionId, userId, and channel names
for easier debugging and monitoring.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Jerome 2025-07-10 11:05:50 +01:00
commit d67ef14ad6
No known key found for this signature in database

View file

@ -29,6 +29,10 @@ export async function handleStreamableHTTP(req: Request, res: Response) {
});
const onsessionclosed = async (sessionId: string) => {
logger.info('Session closed callback triggered', {
sessionId,
userId: getUserIdFromAuth(req.auth)
});
await shutdownSession(sessionId);
}
@ -39,6 +43,10 @@ export async function handleStreamableHTTP(req: Request, res: Response) {
// if no userid, return 401, we shouldn't get here ideally
if (!userId) {
logger.warning('Request without user ID', {
sessionId,
hasAuth: !!req.auth
});
res.status(401)
return;
}
@ -48,14 +56,29 @@ export async function handleStreamableHTTP(req: Request, res: Response) {
// incorrect session for the authed user, return 401
if (sessionId) {
if (!(await isSessionOwnedBy(sessionId, userId))) {
logger.warning('Session ownership mismatch', {
sessionId,
userId,
requestMethod: req.method
});
res.status(401)
return;
}
// Reuse existing transport for owned session
logger.info('Reusing existing session', {
sessionId,
userId,
isGetRequest
});
shttpTransport = await getShttpTransport(sessionId, onsessionclosed, isGetRequest);
} else if (isInitializeRequest(req.body)) {
// New initialization request - use JSON response mode
const onsessioninitialized = async (sessionId: string) => {
logger.info('Initializing new session', {
sessionId,
userId
});
const { server, cleanup: mcpCleanup } = createMcpServer();
const serverRedisTransport = new ServerRedisTransport(sessionId);
@ -64,6 +87,11 @@ export async function handleStreamableHTTP(req: Request, res: Response) {
// Set session ownership
await setSessionOwner(sessionId, userId);
logger.info('Session initialized successfully', {
sessionId,
userId
});
}
const sessionId = randomUUID();
@ -75,6 +103,12 @@ export async function handleStreamableHTTP(req: Request, res: Response) {
shttpTransport.onclose = await redisRelayToMcpServer(sessionId, shttpTransport);
} else {
// Invalid request - no session ID and not initialization request
logger.warning('Invalid request: no session ID and not initialization', {
hasSessionId: !!sessionId,
isInitRequest: false,
userId,
method: req.method
});
res.status(400)
return;
}