cq-mcp-server 0.3.0 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +56 -23
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2,6 +2,7 @@
2
2
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
3
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
4
  import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
5
+ import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
5
6
  import { createServer } from "node:http";
6
7
  import { randomUUID } from "node:crypto";
7
8
  import { z } from "zod";
@@ -330,42 +331,74 @@ log.info(`CloudQuery MCP Server 启动 transport=${MCP_TRANSPORT}`);
330
331
  if (MCP_TRANSPORT === "http") {
331
332
  // session_id → transport 映射,支持多用户并发
332
333
  const sessions = new Map();
334
+ // SSE transport 映射(sessionId → transport),用于兼容 Dify 等旧版客户端
335
+ const sseSessions = new Map();
333
336
  const httpServer = createServer(async (req, res) => {
337
+ // 允许跨域(Dify 可能从不同域访问)
338
+ res.setHeader("Access-Control-Allow-Origin", "*");
339
+ res.setHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, OPTIONS");
340
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type, mcp-session-id");
341
+ res.setHeader("Access-Control-Expose-Headers", "mcp-session-id");
342
+ if (req.method === "OPTIONS") {
343
+ res.writeHead(204).end();
344
+ return;
345
+ }
334
346
  if (!req.url?.startsWith("/mcp")) {
335
347
  res.writeHead(404).end("Not Found");
336
348
  return;
337
349
  }
338
- // 每个新 session(首次 initialize 请求)创建独立的 server + transport
339
- const sessionId = req.headers["mcp-session-id"];
340
- if (!sessionId) {
341
- // session:为该用户创建独立实例
342
- const transport = new StreamableHTTPServerTransport({
343
- sessionIdGenerator: () => randomUUID(),
350
+ // ── SSE transport(兼容 Dify)──
351
+ // GET /mcp → 建立 SSE 连接
352
+ if (req.method === "GET") {
353
+ const sseTransport = new SSEServerTransport("/mcp", res);
354
+ sseSessions.set(sseTransport.sessionId, sseTransport);
355
+ log.info(`SSE 连接建立: ${sseTransport.sessionId}`);
356
+ const mcpServer = createMcpServer();
357
+ await mcpServer.connect(sseTransport);
358
+ req.on("close", () => {
359
+ sseSessions.delete(sseTransport.sessionId);
360
+ log.info(`SSE 连接关闭: ${sseTransport.sessionId},当前活跃: ${sseSessions.size}`);
344
361
  });
345
- const server = createMcpServer();
346
- await server.connect(transport);
347
- transport.onclose = () => {
348
- const sid = transport.sessionId;
349
- if (sid) {
350
- sessions.delete(sid);
351
- log.info(`Session 关闭: ${sid},当前活跃: ${sessions.size}`);
352
- }
353
- };
354
- await transport.handleRequest(req, res);
355
- // handleRequest 后 sessionId 已生成,存入 map
356
- if (transport.sessionId) {
357
- sessions.set(transport.sessionId, transport);
358
- log.info(`新 Session: ${transport.sessionId},当前活跃: ${sessions.size}`);
359
- }
362
+ return;
360
363
  }
361
- else {
362
- // 已有 session:路由到对应 transport
364
+ // POST /mcp?sessionId=xxx → SSE 消息(Dify 旧版协议)
365
+ const urlObj = new URL(req.url, `http://${req.headers.host}`);
366
+ const sseSessionId = urlObj.searchParams.get("sessionId");
367
+ if (sseSessionId && sseSessions.has(sseSessionId)) {
368
+ await sseSessions.get(sseSessionId).handlePostMessage(req, res);
369
+ return;
370
+ }
371
+ // ── Streamable HTTP transport(新版协议)──
372
+ const sessionId = req.headers["mcp-session-id"];
373
+ if (sessionId) {
363
374
  const transport = sessions.get(sessionId);
364
375
  if (!transport) {
365
376
  res.writeHead(404).end("Session not found");
366
377
  return;
367
378
  }
368
379
  await transport.handleRequest(req, res);
380
+ return;
381
+ }
382
+ if (req.method !== "POST") {
383
+ res.writeHead(400).end("Missing mcp-session-id");
384
+ return;
385
+ }
386
+ const transport = new StreamableHTTPServerTransport({
387
+ sessionIdGenerator: () => randomUUID(),
388
+ });
389
+ const mcpServer = createMcpServer();
390
+ await mcpServer.connect(transport);
391
+ transport.onclose = () => {
392
+ const sid = transport.sessionId;
393
+ if (sid) {
394
+ sessions.delete(sid);
395
+ log.info(`Session 关闭: ${sid},当前活跃: ${sessions.size}`);
396
+ }
397
+ };
398
+ await transport.handleRequest(req, res);
399
+ if (transport.sessionId) {
400
+ sessions.set(transport.sessionId, transport);
401
+ log.info(`新 Session: ${transport.sessionId},当前活跃: ${sessions.size}`);
369
402
  }
370
403
  });
371
404
  httpServer.listen(MCP_PORT, () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cq-mcp-server",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "MCP Server for CloudQuery platform — list databases, list tables, inspect columns, and execute SQL via MCP tools.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",