Add production-ready HTTP server for remote access to yt-dlp-mcp tools using the official MCP Streamable HTTP transport protocol. Features: - MCP spec-compliant Streamable HTTP transport with SSE - API key authentication with timing-safe comparison - CORS configuration with credential handling - Rate limiting per session (60 req/min default) - Session management with automatic cleanup - Request size limits (4MB) and input validation - Health check endpoint - Graceful shutdown handling - Memory leak protection (1000 events/session max) New binary: - yt-dlp-mcp-http - HTTP server (separate from stdio server) Configuration via environment variables: - YTDLP_HTTP_PORT (default: 3000) - YTDLP_HTTP_HOST (default: 0.0.0.0) - YTDLP_API_KEY (recommended for production) - YTDLP_CORS_ORIGIN (default: *) - YTDLP_RATE_LIMIT (default: 60) - YTDLP_SESSION_TIMEOUT (default: 1 hour) Endpoints: - POST/GET/DELETE /mcp - MCP protocol endpoint - GET /health - Health check endpoint Documentation: - Comprehensive guide in docs/remote-server.md - Quick start, deployment examples, security best practices - Client configuration for Claude Desktop and Cline
72 lines
1.8 KiB
TypeScript
72 lines
1.8 KiB
TypeScript
/**
|
|
* Express middleware for authentication and rate limiting
|
|
*/
|
|
|
|
import type { Request, Response, NextFunction } from "express";
|
|
import rateLimit from "express-rate-limit";
|
|
import { timingSafeEqual } from "crypto";
|
|
import { API_KEY, RATE_LIMIT } from "./config.mjs";
|
|
|
|
/**
|
|
* Validate API key using constant-time comparison to prevent timing attacks
|
|
*/
|
|
function validateApiKey(req: Request): boolean {
|
|
if (!API_KEY) return true;
|
|
|
|
const authHeader = req.headers.authorization;
|
|
if (!authHeader) return false;
|
|
|
|
const token = authHeader.replace(/^Bearer\s+/i, '');
|
|
|
|
// Constant-time comparison to prevent timing attacks
|
|
if (token.length !== API_KEY.length) return false;
|
|
|
|
try {
|
|
return timingSafeEqual(
|
|
Buffer.from(token),
|
|
Buffer.from(API_KEY)
|
|
);
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* API key authentication middleware
|
|
*/
|
|
export function apiKeyMiddleware(req: Request, res: Response, next: NextFunction): void {
|
|
if (req.path === '/health') {
|
|
return next();
|
|
}
|
|
|
|
if (!validateApiKey(req)) {
|
|
res.status(401).json({ error: 'Unauthorized' });
|
|
return;
|
|
}
|
|
|
|
next();
|
|
}
|
|
|
|
/**
|
|
* Rate limiting middleware using express-rate-limit
|
|
*/
|
|
export const rateLimitMiddleware = rateLimit({
|
|
windowMs: 60 * 1000, // 1 minute
|
|
max: RATE_LIMIT,
|
|
keyGenerator: (req: Request) => {
|
|
// Use session ID for per-session rate limiting
|
|
const sessionId = Array.isArray(req.headers['mcp-session-id'])
|
|
? req.headers['mcp-session-id'][0]
|
|
: req.headers['mcp-session-id'];
|
|
return sessionId || req.ip || 'anonymous';
|
|
},
|
|
standardHeaders: true,
|
|
legacyHeaders: false,
|
|
handler: (_req: Request, res: Response) => {
|
|
res.status(429).json({
|
|
jsonrpc: '2.0',
|
|
error: { code: -32000, message: 'Rate limit exceeded' },
|
|
});
|
|
},
|
|
});
|