@douyinfe/semi-mcp 1.0.12 → 1.0.14

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/http.js +80 -42
  2. package/package.json +2 -2
package/dist/http.js CHANGED
@@ -1326,6 +1326,8 @@ function createMCPServer() {
1326
1326
  });
1327
1327
  return server;
1328
1328
  }
1329
+ const sessions = new Map();
1330
+ const SESSION_TIMEOUT = 1800000;
1329
1331
  function parseArgs() {
1330
1332
  const args = process.argv.slice(2);
1331
1333
  let port = 3000;
@@ -1383,27 +1385,40 @@ Endpoints:
1383
1385
  timeout
1384
1386
  };
1385
1387
  }
1388
+ function cleanupSessions() {
1389
+ const now = Date.now();
1390
+ for (const [sessionId, info] of sessions)if (now - info.lastActivity > SESSION_TIMEOUT) sessions.delete(sessionId);
1391
+ }
1392
+ setInterval(cleanupSessions, 60000);
1393
+ async function processSessionRequest(sessionId, handler) {
1394
+ const session = sessions.get(sessionId);
1395
+ if (!session) throw new Error(`Session not found: ${sessionId}`);
1396
+ const requestPromise = handler();
1397
+ session.requestQueue.push(()=>requestPromise);
1398
+ if (!session.isProcessing) {
1399
+ session.isProcessing = true;
1400
+ try {
1401
+ while(session.requestQueue.length > 0){
1402
+ const nextHandler = session.requestQueue.shift();
1403
+ await nextHandler();
1404
+ }
1405
+ } finally{
1406
+ session.isProcessing = false;
1407
+ }
1408
+ }
1409
+ return requestPromise;
1410
+ }
1386
1411
  async function main() {
1387
1412
  const { port, hosts, stateless, timeout } = parseArgs();
1388
1413
  const version = getPackageVersion();
1389
1414
  let processedHosts = hosts;
1390
1415
  const hasIPv4All = hosts.includes('0.0.0.0');
1391
1416
  const hasIPv6All = hosts.includes('::');
1392
- if (hasIPv4All && hasIPv6All) {
1393
- processedHosts = hosts.filter((h)=>'0.0.0.0' !== h);
1394
- console.log(`[${new Date().toISOString()}] 检测到同时监听 IPv4 和 IPv6,使用 :: (IPv6) 统一监听`);
1395
- }
1396
- if (0 === processedHosts.length) {
1397
- processedHosts = [
1398
- '::'
1399
- ];
1400
- console.log(`[${new Date().toISOString()}] 使用默认配置: 监听 IPv6 (::)`);
1401
- }
1417
+ if (hasIPv4All && hasIPv6All) processedHosts = hosts.filter((h)=>'0.0.0.0' !== h);
1418
+ if (0 === processedHosts.length) processedHosts = [
1419
+ '::'
1420
+ ];
1402
1421
  const server = createMCPServer();
1403
- const transport = new StreamableHTTPServerTransport({
1404
- sessionIdGenerator: stateless ? void 0 : ()=>crypto.randomUUID()
1405
- });
1406
- await server.connect(transport);
1407
1422
  console.log(`[${new Date().toISOString()}] MCP 服务器已启动`);
1408
1423
  console.log(`[${new Date().toISOString()}] 模式: ${stateless ? '无状态 (Stateless)' : '有状态 (Stateful)'}`);
1409
1424
  const httpServer = createServer(async (req, res)=>{
@@ -1427,11 +1442,13 @@ async function main() {
1427
1442
  version,
1428
1443
  transport: 'streamable-http',
1429
1444
  stateless,
1430
- sessionTimeout: `${timeout} minutes`
1445
+ sessionTimeout: `${timeout} minutes`,
1446
+ activeSessions: sessions.size
1431
1447
  }));
1432
1448
  return;
1433
1449
  }
1434
1450
  if ('/mcp' === url.pathname) {
1451
+ const sessionId = req.headers['mcp-session-id'];
1435
1452
  let body = '';
1436
1453
  req.on('data', (chunk)=>{
1437
1454
  body += chunk.toString();
@@ -1439,12 +1456,45 @@ async function main() {
1439
1456
  await new Promise((resolve)=>{
1440
1457
  req.on('end', async ()=>{
1441
1458
  try {
1442
- const parsedBody = body ? JSON.parse(body) : void 0;
1443
- await transport.handleRequest(req, res, parsedBody);
1444
- console.log(`[${new Date().toISOString()}] ${req.method} ${url.pathname} - ${res.statusCode}`);
1459
+ let transport;
1460
+ if (stateless) {
1461
+ transport = new StreamableHTTPServerTransport({
1462
+ sessionIdGenerator: void 0
1463
+ });
1464
+ await server.connect(transport);
1465
+ } else if (sessionId && sessions.has(sessionId)) {
1466
+ transport = sessions.get(sessionId).transport;
1467
+ sessions.get(sessionId).lastActivity = Date.now();
1468
+ } else {
1469
+ const newSessionId = sessionId || crypto.randomUUID();
1470
+ transport = new StreamableHTTPServerTransport({
1471
+ sessionIdGenerator: ()=>newSessionId
1472
+ });
1473
+ await server.connect(transport);
1474
+ sessions.set(newSessionId, {
1475
+ transport,
1476
+ lastActivity: Date.now(),
1477
+ requestQueue: [],
1478
+ isProcessing: false
1479
+ });
1480
+ }
1481
+ if ('GET' === req.method) {
1482
+ transport.handleRequest(req, res).catch(()=>{});
1483
+ resolve();
1484
+ return;
1485
+ }
1486
+ let parsedBody;
1487
+ if (body.trim()) try {
1488
+ parsedBody = JSON.parse(body);
1489
+ } catch {
1490
+ parsedBody = void 0;
1491
+ }
1492
+ if (sessionId && sessions.has(sessionId)) await processSessionRequest(sessionId, async ()=>{
1493
+ await transport.handleRequest(req, res, parsedBody);
1494
+ });
1495
+ else await transport.handleRequest(req, res, parsedBody);
1445
1496
  } catch (error) {
1446
1497
  const errorMessage = error instanceof Error ? error.message : String(error);
1447
- console.error(`[${new Date().toISOString()}] 请求处理错误:`, errorMessage);
1448
1498
  if (!res.headersSent) {
1449
1499
  res.writeHead(500, {
1450
1500
  'Content-Type': 'application/json'
@@ -1477,18 +1527,10 @@ async function main() {
1477
1527
  sessionTimeout: `${timeout} minutes`,
1478
1528
  endpoints: {
1479
1529
  mcp: {
1480
- POST: '/mcp - 发送 MCP 请求',
1481
- GET: '/mcp - SSE 流 (需要 Mcp-Session-Id 头)'
1530
+ POST: '/mcp',
1531
+ GET: '/mcp (SSE)'
1482
1532
  },
1483
- health: '/health - 健康检查'
1484
- },
1485
- headers: {
1486
- 'Mcp-Session-Id': '会话 ID (初始化响应后获取,后续请求需携带)'
1487
- },
1488
- usage: {
1489
- step1: '客户端发送 initialize 请求',
1490
- step2: '服务器返回响应并包含 Mcp-Session-Id header',
1491
- step3: '后续请求携带 Mcp-Session-Id header'
1533
+ health: '/health'
1492
1534
  }
1493
1535
  }, null, 2));
1494
1536
  return;
@@ -1497,15 +1539,15 @@ async function main() {
1497
1539
  'Content-Type': 'application/json'
1498
1540
  });
1499
1541
  res.end(JSON.stringify({
1500
- error: '未知的端点'
1542
+ error: 'Unknown endpoint'
1501
1543
  }));
1502
1544
  });
1503
1545
  const servers = [];
1504
1546
  let startedCount = 0;
1505
1547
  console.log(`
1506
- ╔══════════════════════════════════════════════════════════════╗
1548
+ ╔══════════════════════════════════════════════════════════════════════╗
1507
1549
  ║ Semi MCP Server (Streamable HTTP) v${version.padEnd(10)} ║
1508
- ╠══════════════════════════════════════════════════════════════╣
1550
+ ╠══════════════════════════════════════════════════════════════════════╣
1509
1551
  ║ 模式: ${stateless ? '无状态 (Stateless)' : '有状态 (Stateful) '} ║
1510
1552
  ║ 会话超时: ${String(timeout).padEnd(3)} 分钟 ║
1511
1553
  ║ ║`);
@@ -1532,7 +1574,7 @@ async function main() {
1532
1574
  ║ POST /mcp 发送 MCP 请求 ║
1533
1575
  ║ GET /mcp SSE 流 (服务器推送) ║
1534
1576
  ║ GET /health 健康检查 ║
1535
- ╚══════════════════════════════════════════════════════════════╝
1577
+ ╚══════════════════════════════════════════════════════════════════════╝
1536
1578
  `);
1537
1579
  console.log(`[${new Date().toISOString()}] 所有服务器已启动,监听 ${processedHosts.length} 个地址`);
1538
1580
  console.log(`[${new Date().toISOString()}] 总计监听: ${processedHosts.join(', ')}`);
@@ -1540,20 +1582,16 @@ async function main() {
1540
1582
  });
1541
1583
  servers.push(httpServer);
1542
1584
  });
1543
- processedHosts.length;
1544
1585
  const shutdown = async ()=>{
1545
1586
  console.log('\n正在关闭服务器...');
1546
- try {
1547
- await transport.close();
1548
- console.log('Transport 已关闭');
1549
- } catch (error) {
1550
- console.error('关闭 transport 时出错:', error);
1551
- }
1587
+ for (const [sessionId, info] of sessions)try {
1588
+ await info.transport.close();
1589
+ } catch {}
1590
+ sessions.clear();
1552
1591
  let closedCount = 0;
1553
1592
  servers.forEach((server, index)=>{
1554
1593
  server.close(()=>{
1555
1594
  closedCount++;
1556
- console.log(`[${new Date().toISOString()}] 服务器 ${index + 1}/${servers.length} 已关闭`);
1557
1595
  if (closedCount === servers.length) {
1558
1596
  console.log('所有服务器已关闭');
1559
1597
  process.exit(0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@douyinfe/semi-mcp",
3
- "version": "1.0.12",
3
+ "version": "1.0.14",
4
4
  "description": "Semi Design MCP Server - Model Context Protocol server for Semi Design components and documentation",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -49,7 +49,7 @@
49
49
  "prepublishOnly": "npm run build && npm test"
50
50
  },
51
51
  "dependencies": {
52
- "@modelcontextprotocol/sdk": "^1.0.4",
52
+ "@modelcontextprotocol/sdk": "^1.25.1",
53
53
  "@swc/core": "^1.15.8",
54
54
  "oxc-parser": "^0.106.0"
55
55
  },