import express, { Request, Response } from 'express';
import cors from 'cors';
import { randomUUID } from 'crypto';
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
import { Server as HttpServer } from 'http';
import { logger } from '../utils/logger';
export interface HttpServerConfig {
port: number;
host?: string;
corsOrigins?: string[];
}
export interface HttpServerInstance {
app: express.Application;
server: HttpServer | null;
stop: () => Promise<void>;
}
function setupMiddleware(app: express.Application) {
// 简化的 CORS 配置,允许所有本地请求
app.use(cors({
origin: true, // 允许所有源(开发环境)
credentials: true,
methods: ['GET', 'POST', 'OPTIONS', 'DELETE'],
allowedHeaders: ['Content-Type', 'Accept', 'Origin', 'Mcp-Session-Id', 'Authorization']
}));
app.use(express.json({ limit: '10mb' }));
app.use(express.text());
app.use(express.urlencoded({ extended: true }));
app.use((req, res, next) => {
logger.info('Received request', req.method, req.url);
next();
});
}
export async function startHttpServer(mcpServer: McpServer, config: HttpServerConfig): Promise<HttpServerInstance> {
const app = express();
setupMiddleware(app);
let httpServer: HttpServer | null = null;
// 创建 HTTP Streamable Transport
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: () => randomUUID(),
});
// MCP Server 连接 Transport
await mcpServer.connect(transport);
// MCP 端点
app.all('/mcp', async (req: Request, res: Response) => {
try {
await transport.handleRequest(req, res, req.body);
} catch (error) {
logger.error('Error handling MCP request:', error);
if (!res.headersSent) {
res.status(500).json({
jsonrpc: '2.0',
error: {
code: -32603,
message: 'Internal server error',
},
id: null,
});
}
}
});
// 健康检查端点
app.get('/health', (req: Request, res: Response) => {
res.json({
status: 'healthy',
timestamp: new Date().toISOString(),
mode: 'http',
tools: ['trigger_pipeline', 'get_pipeline_status', 'list_pipelines', 'get_pipeline_jobs', 'cancel_pipeline']
});
});
// 启动 HTTP 服务器
const host = config.host || '127.0.0.1';
return new Promise((resolve, reject) => {
httpServer = app.listen(config.port, host, () => {
console.error('='.repeat(50));
console.error('🚀 GitLab MCP 服务器已启动');
console.error('📋 模式: Streamable HTTP');
console.error(`🌐 服务地址: http://localhost:${config.port}`);
console.error(`🌐 服务地址: http://127.0.0.1:${config.port}`);
if (host !== '127.0.0.1' && host !== 'localhost') {
console.error(`🌐 服务地址: http://${host}:${config.port}`);
}
console.error(`🔗 MCP 端点: http://localhost:${config.port}/mcp`);
console.error(`💚 健康检查: http://localhost:${config.port}/health`);
console.error('🛠️ 可用工具: trigger_pipeline, get_pipeline_status, list_pipelines, get_pipeline_jobs, cancel_pipeline');
console.error('='.repeat(50));
const instance: HttpServerInstance = {
app,
server: httpServer,
stop: async () => {
return new Promise((resolve) => {
if (httpServer) {
httpServer.close(() => {
console.error('🛑 GitLab MCP 服务器已停止');
resolve();
});
} else {
resolve();
}
});
}
};
resolve(instance);
});
httpServer.on('error', (error: Error) => {
reject(error);
});
});
}