@douyinfe/semi-mcp 1.0.13 → 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 +94 -73
  2. package/package.json +2 -2
package/dist/http.js CHANGED
@@ -1387,28 +1387,37 @@ Endpoints:
1387
1387
  }
1388
1388
  function cleanupSessions() {
1389
1389
  const now = Date.now();
1390
- for (const [sessionId, info] of sessions)if (now - info.lastActivity > SESSION_TIMEOUT) {
1391
- sessions.delete(sessionId);
1392
- console.log(`[${new Date().toISOString()}] 会话 ${sessionId} 已过期清理`);
1393
- }
1390
+ for (const [sessionId, info] of sessions)if (now - info.lastActivity > SESSION_TIMEOUT) sessions.delete(sessionId);
1394
1391
  }
1395
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
+ }
1396
1411
  async function main() {
1397
1412
  const { port, hosts, stateless, timeout } = parseArgs();
1398
1413
  const version = getPackageVersion();
1399
1414
  let processedHosts = hosts;
1400
1415
  const hasIPv4All = hosts.includes('0.0.0.0');
1401
1416
  const hasIPv6All = hosts.includes('::');
1402
- if (hasIPv4All && hasIPv6All) {
1403
- processedHosts = hosts.filter((h)=>'0.0.0.0' !== h);
1404
- console.log(`[${new Date().toISOString()}] 检测到同时监听 IPv4 和 IPv6,使用 :: (IPv6) 统一监听`);
1405
- }
1406
- if (0 === processedHosts.length) {
1407
- processedHosts = [
1408
- '::'
1409
- ];
1410
- console.log(`[${new Date().toISOString()}] 使用默认配置: 监听 IPv6 (::)`);
1411
- }
1417
+ if (hasIPv4All && hasIPv6All) processedHosts = hosts.filter((h)=>'0.0.0.0' !== h);
1418
+ if (0 === processedHosts.length) processedHosts = [
1419
+ '::'
1420
+ ];
1412
1421
  const server = createMCPServer();
1413
1422
  console.log(`[${new Date().toISOString()}] MCP 服务器已启动`);
1414
1423
  console.log(`[${new Date().toISOString()}] 模式: ${stateless ? '无状态 (Stateless)' : '有状态 (Stateful)'}`);
@@ -1440,45 +1449,69 @@ async function main() {
1440
1449
  }
1441
1450
  if ('/mcp' === url.pathname) {
1442
1451
  const sessionId = req.headers['mcp-session-id'];
1443
- try {
1444
- let transport;
1445
- if (stateless) {
1446
- transport = new StreamableHTTPServerTransport({
1447
- sessionIdGenerator: void 0
1448
- });
1449
- await server.connect(transport);
1450
- } else if (sessionId && sessions.has(sessionId)) {
1451
- transport = sessions.get(sessionId).transport;
1452
- sessions.get(sessionId).lastActivity = Date.now();
1453
- } else {
1454
- const newSessionId = crypto.randomUUID();
1455
- transport = new StreamableHTTPServerTransport({
1456
- sessionIdGenerator: ()=>newSessionId
1457
- });
1458
- await server.connect(transport);
1459
- sessions.set(newSessionId, {
1460
- transport,
1461
- lastActivity: Date.now()
1462
- });
1463
- }
1464
- await transport.handleRequest(req, res);
1465
- } catch (error) {
1466
- const errorMessage = error instanceof Error ? error.message : String(error);
1467
- console.error(`[${new Date().toISOString()}] 请求处理错误:`, errorMessage);
1468
- if (!res.headersSent) {
1469
- res.writeHead(500, {
1470
- 'Content-Type': 'application/json'
1471
- });
1472
- res.end(JSON.stringify({
1473
- jsonrpc: '2.0',
1474
- error: {
1475
- code: -32000,
1476
- message: errorMessage
1477
- },
1478
- id: null
1479
- }));
1480
- }
1481
- }
1452
+ let body = '';
1453
+ req.on('data', (chunk)=>{
1454
+ body += chunk.toString();
1455
+ });
1456
+ await new Promise((resolve)=>{
1457
+ req.on('end', async ()=>{
1458
+ try {
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);
1496
+ } catch (error) {
1497
+ const errorMessage = error instanceof Error ? error.message : String(error);
1498
+ if (!res.headersSent) {
1499
+ res.writeHead(500, {
1500
+ 'Content-Type': 'application/json'
1501
+ });
1502
+ res.end(JSON.stringify({
1503
+ jsonrpc: '2.0',
1504
+ error: {
1505
+ code: -32000,
1506
+ message: errorMessage
1507
+ },
1508
+ id: null
1509
+ }));
1510
+ }
1511
+ }
1512
+ resolve();
1513
+ });
1514
+ });
1482
1515
  return;
1483
1516
  }
1484
1517
  if ('/' === url.pathname && 'GET' === req.method) {
@@ -1494,18 +1527,10 @@ async function main() {
1494
1527
  sessionTimeout: `${timeout} minutes`,
1495
1528
  endpoints: {
1496
1529
  mcp: {
1497
- POST: '/mcp - 发送 MCP 请求',
1498
- GET: '/mcp - SSE 流 (需要 Mcp-Session-Id 头)'
1530
+ POST: '/mcp',
1531
+ GET: '/mcp (SSE)'
1499
1532
  },
1500
- health: '/health - 健康检查'
1501
- },
1502
- headers: {
1503
- 'Mcp-Session-Id': '会话 ID (初始化响应后获取,后续请求需携带)'
1504
- },
1505
- usage: {
1506
- step1: '客户端发送 initialize 请求',
1507
- step2: '服务器返回响应并包含 Mcp-Session-Id header',
1508
- step3: '后续请求携带 Mcp-Session-Id header'
1533
+ health: '/health'
1509
1534
  }
1510
1535
  }, null, 2));
1511
1536
  return;
@@ -1514,15 +1539,15 @@ async function main() {
1514
1539
  'Content-Type': 'application/json'
1515
1540
  });
1516
1541
  res.end(JSON.stringify({
1517
- error: '未知的端点'
1542
+ error: 'Unknown endpoint'
1518
1543
  }));
1519
1544
  });
1520
1545
  const servers = [];
1521
1546
  let startedCount = 0;
1522
1547
  console.log(`
1523
- ╔══════════════════════════════════════════════════════════════╗
1548
+ ╔══════════════════════════════════════════════════════════════════════╗
1524
1549
  ║ Semi MCP Server (Streamable HTTP) v${version.padEnd(10)} ║
1525
- ╠══════════════════════════════════════════════════════════════╣
1550
+ ╠══════════════════════════════════════════════════════════════════════╣
1526
1551
  ║ 模式: ${stateless ? '无状态 (Stateless)' : '有状态 (Stateful) '} ║
1527
1552
  ║ 会话超时: ${String(timeout).padEnd(3)} 分钟 ║
1528
1553
  ║ ║`);
@@ -1549,7 +1574,7 @@ async function main() {
1549
1574
  ║ POST /mcp 发送 MCP 请求 ║
1550
1575
  ║ GET /mcp SSE 流 (服务器推送) ║
1551
1576
  ║ GET /health 健康检查 ║
1552
- ╚══════════════════════════════════════════════════════════════╝
1577
+ ╚══════════════════════════════════════════════════════════════════════╝
1553
1578
  `);
1554
1579
  console.log(`[${new Date().toISOString()}] 所有服务器已启动,监听 ${processedHosts.length} 个地址`);
1555
1580
  console.log(`[${new Date().toISOString()}] 总计监听: ${processedHosts.join(', ')}`);
@@ -1561,16 +1586,12 @@ async function main() {
1561
1586
  console.log('\n正在关闭服务器...');
1562
1587
  for (const [sessionId, info] of sessions)try {
1563
1588
  await info.transport.close();
1564
- console.log(`[${new Date().toISOString()}] 会话 ${sessionId} 已关闭`);
1565
- } catch (error) {
1566
- console.error(`关闭会话 ${sessionId} 时出错:`, error);
1567
- }
1589
+ } catch {}
1568
1590
  sessions.clear();
1569
1591
  let closedCount = 0;
1570
1592
  servers.forEach((server, index)=>{
1571
1593
  server.close(()=>{
1572
1594
  closedCount++;
1573
- console.log(`[${new Date().toISOString()}] 服务器 ${index + 1}/${servers.length} 已关闭`);
1574
1595
  if (closedCount === servers.length) {
1575
1596
  console.log('所有服务器已关闭');
1576
1597
  process.exit(0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@douyinfe/semi-mcp",
3
- "version": "1.0.13",
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
  },