import { createServer, IncomingMessage, Server, ServerResponse } from 'node:http';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
import { logger } from './utils/logger.js';
/**
* 传输方式类型
*/
export type TransportType = 'stdio' | 'http';
/**
* HTTP Transport 配置选项
*/
export interface HTTPTransportOptions {
host?: string;
port?: number;
path?: string;
}
/**
* 创建 Stdio Transport
*/
export function createStdioTransport(): Transport {
logger.info('Creating stdio transport');
const transport = new StdioServerTransport();
logger.debug('Stdio transport created successfully');
return transport;
}
/**
* 创建 HTTP Transport 并启动 HTTP 服务器
*/
export async function createHTTPTransport(
options: HTTPTransportOptions = {}
): Promise<{ transport: Transport; httpServer: Server }> {
const host = options.host || '0.0.0.0';
const port = options.port || 3000;
const path = options.path || '/mcp';
// 创建 StreamableHTTPServerTransport 实例(使用无状态模式)
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined, // 无状态模式
});
// 创建 HTTP 服务器
const httpServer = createServer(async (req: IncomingMessage, res: ServerResponse) => {
const startTime = Date.now();
const requestId = `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
logger.debug(`[${requestId}] Incoming HTTP request: ${req.method} ${req.url}`);
// 设置 CORS 头
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, DELETE, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, MCP-Protocol-Version, Accept');
// 处理 OPTIONS 预检请求
if (req.method === 'OPTIONS') {
logger.debug(`[${requestId}] Handling OPTIONS preflight request`);
res.writeHead(200);
res.end();
return;
}
// 只处理指定路径的请求
const urlPath = req.url?.split('?')[0]; // 移除查询参数
if (!urlPath || (urlPath !== path && !urlPath.startsWith(`${path}/`))) {
logger.warn(`[${requestId}] Path not found: ${urlPath}`);
res.writeHead(404, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Not Found' }));
return;
}
try {
// 解析请求体(如果是 POST 请求)
let parsedBody: unknown = undefined;
if (req.method === 'POST') {
logger.debug(`[${requestId}] Parsing POST request body`);
const chunks: Buffer[] = [];
for await (const chunk of req) {
chunks.push(chunk);
}
const body = Buffer.concat(chunks).toString('utf-8');
if (body) {
try {
parsedBody = JSON.parse(body);
logger.debug(`[${requestId}] Request body parsed successfully`);
} catch (e) {
logger.error(`[${requestId}] Failed to parse JSON body`, e);
res.writeHead(400, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Invalid JSON' }));
return;
}
}
}
// 处理请求
logger.info(`[${requestId}] Processing ${req.method} request to ${urlPath}`);
await transport.handleRequest(req, res, parsedBody);
const duration = Date.now() - startTime;
logger.info(`[${requestId}] Request completed in ${duration}ms`);
} catch (error) {
const duration = Date.now() - startTime;
logger.error(`[${requestId}] Error handling HTTP request (duration: ${duration}ms)`, error);
if (!res.headersSent) {
res.writeHead(500, { 'Content-Type': 'application/json' });
res.end(
JSON.stringify({
error: 'Internal Server Error',
message: error instanceof Error ? error.message : 'Unknown error',
})
);
}
}
});
// 启动 HTTP 服务器
return new Promise((resolve, reject) => {
logger.info(`Starting HTTP server on ${host}:${port}${path}`);
httpServer.listen(port, host, () => {
logger.info(`Time MCP server running on http://${host}:${port}${path}`);
resolve({ transport, httpServer });
});
httpServer.on('error', (error: Error) => {
logger.error('HTTP server error', error);
reject(error);
});
});
}
/**
* 根据传输类型创建 Transport
*/
export async function createTransport(
type: TransportType,
options?: HTTPTransportOptions
): Promise<Transport | { transport: Transport; httpServer?: Server }> {
logger.info(`Creating transport: ${type}`);
switch (type) {
case 'stdio':
return createStdioTransport();
case 'http':
return createHTTPTransport(options);
default:
logger.error(`Unsupported transport type: ${type}`);
throw new Error(`Unsupported transport type: ${type}`);
}
}