/** * MCP HTTP 传输层 * 以 Streamable HTTP 协议在 /mcp 端点暴露 MCP 服务。 * 鉴权:基于 data/mcp.json 中配置的 token,要求请求头 Authorization: Bearer 。 */ const crypto = require("crypto"); const express = require("express"); const { randomUUID } = require("node:crypto"); const { StreamableHTTPServerTransport, } = require("@modelcontextprotocol/sdk/server/streamableHttp.js"); const { isInitializeRequest } = require("@modelcontextprotocol/sdk/types.js"); const { createMcpServer } = require("./tools"); const { loadMcpConfig } = require("../persistence/jsonAdapter"); const router = express.Router(); // sessionId -> transport,用于会话复用 const transports = new Map(); /** * 校验请求携带的 Bearer token 是否与配置中的 token 匹配 * @param {string} [header] - Authorization 请求头 * @returns {boolean} 是否鉴权通过 */ function isAuthorized(header) { const mcp = loadMcpConfig(); // 未配置 token 时视为未启用鉴权 if (!mcp.token) { return true; } const expected = mcp.token; const provided = header && header.startsWith("Bearer ") ? header.slice(7).trim() : ""; if (!provided || provided.length !== expected.length) { return false; } // 使用常量时间比较,避免时序侧信道 const a = Buffer.from(provided); const b = Buffer.from(expected); return crypto.timingSafeEqual(a, b); } /** * 鉴权中间件:token 校验失败返回 401 */ router.use((req, res, next) => { if (!isAuthorized(req.headers.authorization)) { return res .status(401) .json({ jsonrpc: "2.0", error: { code: -32001, message: "Unauthorized: invalid or missing token" }, id: null }); } next(); }); /** * 根据会话 ID 或其下的 transport 处理请求 * 若带 mcp-session-id 且存在则复用;否则仅初始化请求可新建会话 */ async function handleRequest(req, res) { const sessionId = req.headers["mcp-session-id"]; const jsonBody = req.body; try { let transport; if (sessionId && transports.get(sessionId)) { transport = transports.get(sessionId); } else if (!sessionId && isInitializeRequest(jsonBody)) { transport = new StreamableHTTPServerTransport({ sessionIdGenerator: () => randomUUID(), onsessioninitialized: (sid) => { transports.set(sid, transport); }, }); transport.onclose = () => { if (transport.sessionId) { transports.delete(transport.sessionId); } }; const server = createMcpServer(); await server.connect(transport); await transport.handleRequest(req, res, req.body); return; } else { res.status(400).json({ jsonrpc: "2.0", error: { code: -32000, message: "Bad Request: missing or invalid mcp-session-id" }, id: null, }); return; } await transport.handleRequest(req, res, req.body); } catch (err) { console.error("MCP 请求处理失败:", err); if (!res.headersSent) { res.status(500).json({ jsonrpc: "2.0", error: { code: -32603, message: "Internal server error" }, id: null, }); } } } // POST /mcp:初始化与工具调用 router.post("/", (req, res) => handleRequest(req, res)); // GET /mcp:建立 SSE 流(会话恢复/服务器通知) router.get("/", (req, res) => handleRequest(req, res)); // DELETE /mcp:会话终止 router.delete("/", (req, res) => handleRequest(req, res)); module.exports = router;