@douyinfe/semi-mcp 1.0.14 → 1.0.16

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.
package/dist/http.js CHANGED
@@ -8,6 +8,7 @@ import { fileURLToPath } from "url";
8
8
  import { dirname, join as external_path_join } from "path";
9
9
  import { homedir } from "os";
10
10
  import { mkdir, readFile, writeFile } from "fs/promises";
11
+ import { lt } from "semver";
11
12
  import { parseSync } from "oxc-parser";
12
13
  function getCacheDir() {
13
14
  return external_path_join(homedir(), '.semi-mcp', 'cache');
@@ -45,6 +46,7 @@ async function writeCache(cacheDir, key, content) {
45
46
  await writeFile(filePath, content, 'utf-8');
46
47
  } catch {}
47
48
  }
49
+ const MIN_SUPPORTED_VERSION = '2.90.2';
48
50
  function getVersionCacheDir() {
49
51
  return external_path_join(getCacheDir(), 'version');
50
52
  }
@@ -80,7 +82,8 @@ async function fetchVersionFromRegistry(packageName, tag) {
80
82
  throw new Error(`无法获取 ${packageName}@${tag} 的版本号: ${lastError?.message}`);
81
83
  }
82
84
  async function resolveVersion(packageName, version) {
83
- if (/^\d+\.\d+\.\d+/.test(version)) return version;
85
+ if (/^\d+\.\d+\.\d+/.test(version)) if (!lt(version, MIN_SUPPORTED_VERSION)) return version;
86
+ else version = 'latest';
84
87
  const cacheDir = getVersionCacheDir();
85
88
  const cacheKey = getVersionCacheKey(packageName, version);
86
89
  const today = getCurrentDate();
@@ -380,13 +383,13 @@ function replaceCodeBlocksWithPlaceholders(content, componentName) {
380
383
  }
381
384
  const getSemiDocumentTool = {
382
385
  name: 'get_semi_document',
383
- description: '获取 Semi Design 组件文档或组件列表。对于大型文档,代码块会被替换为占位符,需要使用 get_semi_code_block 工具获取具体代码',
386
+ description: '获取 Semi Design 组件文档、额外文档或组件列表。支持获取:1) 组件文档(如 Button、Input 等组件);2) 额外文档,包括:advanced 分类下的文档(customize-theme、dark-mode、design-source、design-to-code)、ecosystem 分类下的文档(changelog、faq、react19、tailwind、update-to-v2、web-components)、start 分类下的文档(getting-started、introduction、overview)。注意:changelog 文档较大,需要使用分页格式获取,传入 changelog-1(第1页,最新内容)、changelog-2(第2页)等格式,每页300行。对于大型文档,代码块会被替换为占位符,需要使用 get_semi_code_block 工具获取具体代码',
384
387
  inputSchema: {
385
388
  type: 'object',
386
389
  properties: {
387
390
  componentName: {
388
391
  type: 'string',
389
- description: '组件名称,例如 Button、Input 等。如果不提供,则返回组件列表'
392
+ description: '组件名称或文档名称。组件名称例如:Button、Input、Table 等;额外文档名称例如:customize-theme、dark-mode、changelog-1(changelog第1页,最新)、changelog-2(changelog第2页)等、faq、react19、tailwind、update-to-v2、web-components、getting-started、introduction、overview 等。注意:changelog 必须使用分页格式(changelog-1、changelog-2等),不能直接传入 changelog。如果不提供,则返回组件列表'
390
393
  },
391
394
  version: {
392
395
  type: 'string',
@@ -397,12 +400,59 @@ const getSemiDocumentTool = {
397
400
  }
398
401
  };
399
402
  const LARGE_DOCUMENT_THRESHOLD = 888;
403
+ const CHANGELOG_PAGE_SIZE = 300;
404
+ function parseChangelogPage(componentName) {
405
+ const changelogMatch = componentName.match(/^(changelog)(?:-(\d+))?$/);
406
+ if (changelogMatch) {
407
+ const baseName = changelogMatch[1];
408
+ const pageStr = changelogMatch[2];
409
+ if (!pageStr) return {
410
+ isChangelog: true,
411
+ baseName
412
+ };
413
+ {
414
+ const page = parseInt(pageStr, 10);
415
+ return {
416
+ isChangelog: true,
417
+ page,
418
+ baseName
419
+ };
420
+ }
421
+ }
422
+ return {
423
+ isChangelog: false
424
+ };
425
+ }
426
+ function paginateChangelog(content, page) {
427
+ const lines = content.split('\n');
428
+ const totalLines = lines.length;
429
+ const totalPages = Math.ceil(totalLines / CHANGELOG_PAGE_SIZE);
430
+ const startIndex = (page - 1) * CHANGELOG_PAGE_SIZE;
431
+ const endIndex = Math.min(startIndex + CHANGELOG_PAGE_SIZE, totalLines);
432
+ const pageLines = lines.slice(startIndex, endIndex);
433
+ const pageContent = pageLines.join('\n');
434
+ return {
435
+ content: pageContent,
436
+ totalPages,
437
+ currentPage: page
438
+ };
439
+ }
400
440
  async function handleGetSemiDocument(args) {
401
441
  const componentName = args?.componentName;
402
442
  const version = args?.version || 'latest';
403
443
  try {
404
444
  if (componentName) {
405
- const result = await getComponentDocuments(componentName, version);
445
+ const changelogInfo = parseChangelogPage(componentName);
446
+ if (changelogInfo.isChangelog && !changelogInfo.page) return {
447
+ content: [
448
+ {
449
+ type: 'text',
450
+ text: `changelog 文档较大,需要使用分页方式获取。\n\n请使用以下格式获取:\n- changelog-1(第1页,最新内容)\n- changelog-2(第2页)\n- changelog-3(第3页)\n- ...\n\n页码从1开始,1为最新内容。`
451
+ }
452
+ ]
453
+ };
454
+ const actualComponentName = changelogInfo.baseName || componentName;
455
+ const result = await getComponentDocuments(actualComponentName, version);
406
456
  const allComponents = await getComponentList(version);
407
457
  if (!result) return {
408
458
  content: [
@@ -412,6 +462,45 @@ async function handleGetSemiDocument(args) {
412
462
  }
413
463
  ]
414
464
  };
465
+ if (changelogInfo.isChangelog && changelogInfo.page) {
466
+ const page = changelogInfo.page;
467
+ if (0 === result.documents.length) return {
468
+ content: [
469
+ {
470
+ type: 'text',
471
+ text: `未找到 changelog 文档 (版本 ${version})`
472
+ }
473
+ ],
474
+ isError: true
475
+ };
476
+ const doc = result.documents[0];
477
+ const paginated = paginateChangelog(doc.content, page);
478
+ if (page < 1 || page > paginated.totalPages) return {
479
+ content: [
480
+ {
481
+ type: 'text',
482
+ text: `页码 ${page} 超出范围。changelog 文档共有 ${paginated.totalPages} 页,请使用 changelog-1 到 changelog-${paginated.totalPages}。`
483
+ }
484
+ ],
485
+ isError: true
486
+ };
487
+ const nextPageHint = page < paginated.totalPages ? `使用 changelog-${page + 1} 获取下一页` : '';
488
+ const prevPageHint = page > 1 ? `使用 changelog-${page - 1} 获取上一页` : '';
489
+ const pageHints = [
490
+ nextPageHint,
491
+ prevPageHint
492
+ ].filter(Boolean).join(',');
493
+ const header = `===== ${doc.name} (第 ${page}/${paginated.totalPages} 页) =====${pageHints ? `\n[提示: ${pageHints}]` : ''}`;
494
+ const footer = `\n\n[当前页: ${page}/${paginated.totalPages} | 总行数: ${doc.content.split('\n').length} | 每页: ${CHANGELOG_PAGE_SIZE} 行]`;
495
+ return {
496
+ content: [
497
+ {
498
+ type: 'text',
499
+ text: `${header}\n\n${paginated.content}${footer}`
500
+ }
501
+ ]
502
+ };
503
+ }
415
504
  const documentsWithLines = result.documents.map((doc)=>({
416
505
  ...doc,
417
506
  lines: doc.content.split('\n').length
package/dist/index.js CHANGED
@@ -7,6 +7,7 @@ import { fileURLToPath } from "url";
7
7
  import { dirname, join as external_path_join } from "path";
8
8
  import { homedir } from "os";
9
9
  import { mkdir, readFile, writeFile } from "fs/promises";
10
+ import { lt } from "semver";
10
11
  import { parseSync } from "oxc-parser";
11
12
  function getCacheDir() {
12
13
  return external_path_join(homedir(), '.semi-mcp', 'cache');
@@ -44,6 +45,7 @@ async function writeCache(cacheDir, key, content) {
44
45
  await writeFile(filePath, content, 'utf-8');
45
46
  } catch {}
46
47
  }
48
+ const MIN_SUPPORTED_VERSION = '2.90.2';
47
49
  function getVersionCacheDir() {
48
50
  return external_path_join(getCacheDir(), 'version');
49
51
  }
@@ -79,7 +81,8 @@ async function fetchVersionFromRegistry(packageName, tag) {
79
81
  throw new Error(`无法获取 ${packageName}@${tag} 的版本号: ${lastError?.message}`);
80
82
  }
81
83
  async function resolveVersion(packageName, version) {
82
- if (/^\d+\.\d+\.\d+/.test(version)) return version;
84
+ if (/^\d+\.\d+\.\d+/.test(version)) if (!lt(version, MIN_SUPPORTED_VERSION)) return version;
85
+ else version = 'latest';
83
86
  const cacheDir = getVersionCacheDir();
84
87
  const cacheKey = getVersionCacheKey(packageName, version);
85
88
  const today = getCurrentDate();
@@ -379,13 +382,13 @@ function replaceCodeBlocksWithPlaceholders(content, componentName) {
379
382
  }
380
383
  const getSemiDocumentTool = {
381
384
  name: 'get_semi_document',
382
- description: '获取 Semi Design 组件文档或组件列表。对于大型文档,代码块会被替换为占位符,需要使用 get_semi_code_block 工具获取具体代码',
385
+ description: '获取 Semi Design 组件文档、额外文档或组件列表。支持获取:1) 组件文档(如 Button、Input 等组件);2) 额外文档,包括:advanced 分类下的文档(customize-theme、dark-mode、design-source、design-to-code)、ecosystem 分类下的文档(changelog、faq、react19、tailwind、update-to-v2、web-components)、start 分类下的文档(getting-started、introduction、overview)。注意:changelog 文档较大,需要使用分页格式获取,传入 changelog-1(第1页,最新内容)、changelog-2(第2页)等格式,每页300行。对于大型文档,代码块会被替换为占位符,需要使用 get_semi_code_block 工具获取具体代码',
383
386
  inputSchema: {
384
387
  type: 'object',
385
388
  properties: {
386
389
  componentName: {
387
390
  type: 'string',
388
- description: '组件名称,例如 Button、Input 等。如果不提供,则返回组件列表'
391
+ description: '组件名称或文档名称。组件名称例如:Button、Input、Table 等;额外文档名称例如:customize-theme、dark-mode、changelog-1(changelog第1页,最新)、changelog-2(changelog第2页)等、faq、react19、tailwind、update-to-v2、web-components、getting-started、introduction、overview 等。注意:changelog 必须使用分页格式(changelog-1、changelog-2等),不能直接传入 changelog。如果不提供,则返回组件列表'
389
392
  },
390
393
  version: {
391
394
  type: 'string',
@@ -396,12 +399,59 @@ const getSemiDocumentTool = {
396
399
  }
397
400
  };
398
401
  const LARGE_DOCUMENT_THRESHOLD = 888;
402
+ const CHANGELOG_PAGE_SIZE = 300;
403
+ function parseChangelogPage(componentName) {
404
+ const changelogMatch = componentName.match(/^(changelog)(?:-(\d+))?$/);
405
+ if (changelogMatch) {
406
+ const baseName = changelogMatch[1];
407
+ const pageStr = changelogMatch[2];
408
+ if (!pageStr) return {
409
+ isChangelog: true,
410
+ baseName
411
+ };
412
+ {
413
+ const page = parseInt(pageStr, 10);
414
+ return {
415
+ isChangelog: true,
416
+ page,
417
+ baseName
418
+ };
419
+ }
420
+ }
421
+ return {
422
+ isChangelog: false
423
+ };
424
+ }
425
+ function paginateChangelog(content, page) {
426
+ const lines = content.split('\n');
427
+ const totalLines = lines.length;
428
+ const totalPages = Math.ceil(totalLines / CHANGELOG_PAGE_SIZE);
429
+ const startIndex = (page - 1) * CHANGELOG_PAGE_SIZE;
430
+ const endIndex = Math.min(startIndex + CHANGELOG_PAGE_SIZE, totalLines);
431
+ const pageLines = lines.slice(startIndex, endIndex);
432
+ const pageContent = pageLines.join('\n');
433
+ return {
434
+ content: pageContent,
435
+ totalPages,
436
+ currentPage: page
437
+ };
438
+ }
399
439
  async function handleGetSemiDocument(args) {
400
440
  const componentName = args?.componentName;
401
441
  const version = args?.version || 'latest';
402
442
  try {
403
443
  if (componentName) {
404
- const result = await getComponentDocuments(componentName, version);
444
+ const changelogInfo = parseChangelogPage(componentName);
445
+ if (changelogInfo.isChangelog && !changelogInfo.page) return {
446
+ content: [
447
+ {
448
+ type: 'text',
449
+ text: `changelog 文档较大,需要使用分页方式获取。\n\n请使用以下格式获取:\n- changelog-1(第1页,最新内容)\n- changelog-2(第2页)\n- changelog-3(第3页)\n- ...\n\n页码从1开始,1为最新内容。`
450
+ }
451
+ ]
452
+ };
453
+ const actualComponentName = changelogInfo.baseName || componentName;
454
+ const result = await getComponentDocuments(actualComponentName, version);
405
455
  const allComponents = await getComponentList(version);
406
456
  if (!result) return {
407
457
  content: [
@@ -411,6 +461,45 @@ async function handleGetSemiDocument(args) {
411
461
  }
412
462
  ]
413
463
  };
464
+ if (changelogInfo.isChangelog && changelogInfo.page) {
465
+ const page = changelogInfo.page;
466
+ if (0 === result.documents.length) return {
467
+ content: [
468
+ {
469
+ type: 'text',
470
+ text: `未找到 changelog 文档 (版本 ${version})`
471
+ }
472
+ ],
473
+ isError: true
474
+ };
475
+ const doc = result.documents[0];
476
+ const paginated = paginateChangelog(doc.content, page);
477
+ if (page < 1 || page > paginated.totalPages) return {
478
+ content: [
479
+ {
480
+ type: 'text',
481
+ text: `页码 ${page} 超出范围。changelog 文档共有 ${paginated.totalPages} 页,请使用 changelog-1 到 changelog-${paginated.totalPages}。`
482
+ }
483
+ ],
484
+ isError: true
485
+ };
486
+ const nextPageHint = page < paginated.totalPages ? `使用 changelog-${page + 1} 获取下一页` : '';
487
+ const prevPageHint = page > 1 ? `使用 changelog-${page - 1} 获取上一页` : '';
488
+ const pageHints = [
489
+ nextPageHint,
490
+ prevPageHint
491
+ ].filter(Boolean).join(',');
492
+ const header = `===== ${doc.name} (第 ${page}/${paginated.totalPages} 页) =====${pageHints ? `\n[提示: ${pageHints}]` : ''}`;
493
+ const footer = `\n\n[当前页: ${page}/${paginated.totalPages} | 总行数: ${doc.content.split('\n').length} | 每页: ${CHANGELOG_PAGE_SIZE} 行]`;
494
+ return {
495
+ content: [
496
+ {
497
+ type: 'text',
498
+ text: `${header}\n\n${paginated.content}${footer}`
499
+ }
500
+ ]
501
+ };
502
+ }
414
503
  const documentsWithLines = result.documents.map((doc)=>({
415
504
  ...doc,
416
505
  lines: doc.content.split('\n').length
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@douyinfe/semi-mcp",
3
- "version": "1.0.14",
3
+ "version": "1.0.16",
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",
@@ -51,7 +51,8 @@
51
51
  "dependencies": {
52
52
  "@modelcontextprotocol/sdk": "^1.25.1",
53
53
  "@swc/core": "^1.15.8",
54
- "oxc-parser": "^0.106.0"
54
+ "oxc-parser": "^0.106.0",
55
+ "semver": "^7.7.3"
55
56
  },
56
57
  "devDependencies": {
57
58
  "@rslib/core": "^0.18.5",