/**
* Request Validator
*
* Validates MCP requests and provides type-safe request detection.
* Separates validation logic from business logic.
*/
import { InitializeRequestSchema } from '@modelcontextprotocol/sdk/types.js';
import { ILogger } from '../logging/logger.js';
/**
* Request validator for MCP protocol
*/
export class RequestValidator {
private readonly logger: ILogger;
constructor(logger: ILogger) {
this.logger = logger;
}
/**
* Check if request body contains an initialize request
*
* @param body - Request body (single object or array)
* @returns true if body contains initialize request
*/
isInitializeRequest(body: unknown): boolean {
if (!body) {
return false;
}
// Handle array of requests
if (Array.isArray(body)) {
return body.some((request) => this.isInitialRequest(request));
}
// Handle single request
return this.isInitialRequest(body);
}
/**
* Validate session ID from header
*
* @param sessionId - Session ID string
* @returns Validation result
*/
validateSessionId(
sessionId: string | undefined
): { valid: boolean; error?: string } {
if (!sessionId) {
return {
valid: false,
error: 'Missing session ID',
};
}
if (typeof sessionId !== 'string') {
return {
valid: false,
error: 'Session ID must be a string',
};
}
if (sessionId.trim().length === 0) {
return {
valid: false,
error: 'Session ID cannot be empty',
};
}
return { valid: true };
}
/**
* Check if a single request is an initialize request
*
* @param data - Request data
* @returns true if initialize request
*/
private isInitialRequest(data: unknown): boolean {
try {
const result = InitializeRequestSchema.safeParse(data);
return result.success;
} catch (error) {
this.logger.debug('Error parsing initialize request', {
error: error instanceof Error ? error.message : String(error),
});
return false;
}
}
}
/**
* Factory function to create request validator
*/
export function createRequestValidator(logger: ILogger): RequestValidator {
return new RequestValidator(logger);
}