import express, { Request, Response, Application } from 'express';
import { Server as HttpServer } from 'http';
import cors from 'cors';
import { LogManager, LogEntry } from './log-handler.js';
import { portManager } from './port-manager.js';
export function createHttpServer(logManager: LogManager): Application {
const app = express();
// Use port 0 for dynamic allocation, or use environment variable if specified
const requestedPort = process.env.DEBUG_PORT || process.env.MCP_DEBUG_PORT;
const PORT = requestedPort ? parseInt(requestedPort) : 0;
// Set host from environment or use default
const HOST = process.env.DEBUG_HOST || process.env.MCP_DEBUG_HOST || 'localhost';
portManager.setHost(HOST);
// Middleware
app.use(cors());
app.use(express.json());
// GET /api/info - Server information
app.get('/api/info', (req: Request, res: Response) => {
try {
const actualPort = portManager.getPort() || PORT;
const host = portManager.getHost();
res.json({
service: 'debug-mcp-server',
version: '2.0.0',
port: actualPort,
host: host,
endpoints: {
log: `http://${host}:${actualPort}/api/log`,
info: `http://${host}:${actualPort}/api/info`,
stats: `http://${host}:${actualPort}/api/stats`,
health: `http://${host}:${actualPort}/health`
},
usage: {
logEndpoint: `POST http://${host}:${actualPort}/api/log`,
bodyExample: {
projectPath: '/path/to/your/project',
timestamp: '2025-01-03T10:30:00Z',
level: 'info',
message: 'Your debug message',
data: { key: 'value' }
},
note: 'projectPath is optional - if not provided, logs go to MCP directory'
},
timestamp: new Date().toISOString()
});
} catch (error) {
res.status(500).json({
success: false,
error: error instanceof Error ? error.message : 'Unknown error'
});
}
});
// POST /api/log - Append log entry
app.post('/api/log', (req: Request, res: Response) => {
try {
const { projectPath, timestamp, level, message, data } = req.body;
// Validate required fields
if (!message) {
return res.status(400).json({
success: false,
error: 'Message is required'
});
}
const entry: LogEntry = {
timestamp: timestamp || new Date().toISOString(),
level: level || 'info',
message,
data: data || undefined,
projectPath: projectPath || undefined
};
// Use projectPath from request body to determine where to store the log
const success = logManager.appendLog(entry, projectPath);
const stats = logManager.getLogStats(projectPath);
res.json({
success,
entries: stats.totalLines,
logFilePath: stats.logFilePath,
message: success ? 'Log entry added' : 'Failed to add log entry'
});
} catch (error) {
res.status(500).json({
success: false,
error: error instanceof Error ? error.message : 'Unknown error'
});
}
});
// GET /api/log - Read logs
app.get('/api/log', (req: Request, res: Response) => {
try {
const lastLines = req.query.last ? parseInt(req.query.last as string) : 100;
const projectPath = req.query.projectPath as string | undefined;
const logs = logManager.readLogs(lastLines, projectPath);
const stats = logManager.getLogStats(projectPath);
res.json({
logs,
lineCount: logs.length,
logFilePath: stats.logFilePath,
timestamp: new Date().toISOString()
});
} catch (error) {
res.status(500).json({
success: false,
error: error instanceof Error ? error.message : 'Unknown error'
});
}
});
// DELETE /api/log - Clear logs
app.delete('/api/log', (req: Request, res: Response) => {
try {
const projectPath = req.query.projectPath as string | undefined;
const result = logManager.clearLogs(projectPath);
res.json({
...result,
message: result.success
? `Cleared ${result.deletedCount} log entries`
: 'Failed to clear logs'
});
} catch (error) {
res.status(500).json({
success: false,
error: error instanceof Error ? error.message : 'Unknown error'
});
}
});
// GET /api/stats - Get log statistics
app.get('/api/stats', (req: Request, res: Response) => {
try {
const projectPath = req.query.projectPath as string | undefined;
const stats = logManager.getLogStats(projectPath);
res.json({
...stats,
timestamp: new Date().toISOString()
});
} catch (error) {
res.status(500).json({
success: false,
error: error instanceof Error ? error.message : 'Unknown error'
});
}
});
// Health check
app.get('/health', (req: Request, res: Response) => {
const actualPort = portManager.getPort() || PORT;
res.json({
status: 'ok',
service: 'debug-mcp-server',
version: '2.0.0',
port: actualPort,
host: portManager.getHost(),
timestamp: new Date().toISOString()
});
});
// Start server with dynamic port
const server: HttpServer = app.listen(PORT, HOST, () => {
const address = server.address();
let actualPort: number;
let actualHost: string;
if (typeof address === 'string') {
// Unix socket
actualPort = 0;
actualHost = address;
} else if (address) {
// TCP socket
actualPort = address.port;
actualHost = address.address;
} else {
// Fallback
actualPort = PORT || 0;
actualHost = HOST;
}
// Store port in port manager
portManager.setPort(actualPort);
if (actualHost) {
portManager.setHost(actualHost);
}
console.error(`[Debug MCP] HTTP API server listening on ${actualHost}:${actualPort}`);
console.error(`[Debug MCP] Server info: http://${actualHost}:${actualPort}/api/info`);
console.error(`[Debug MCP] Log endpoint: http://${actualHost}:${actualPort}/api/log`);
});
return app;
}