@fjyueke/bify-mcp 1.0.3 → 1.0.6

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 (3) hide show
  1. package/README.md +68 -5
  2. package/dist/index.js +133 -37
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -9,26 +9,89 @@
9
9
 
10
10
  Bify 是基于 Taroify 的移动端组件库,基于 Vant 视觉规范,提供一致的 API 接口,助力开发者快速搭建小程序应用。
11
11
 
12
- Bify MCP 是一项独立的 MCP(模型上下文协议)服务,旨在将 Bify 与大模型连接起来。它使大模型能够直接从文档中检索组件、API数据。
12
+ Bify MCP 是一项独立的 MCP(模型上下文协议)服务,旨在将 Bify 与大模型连接起来。它使大模型能够直接从文档中检索组件、API 数据,并支持 Arco Mobile → Bify 代码转换。
13
13
 
14
14
  - 直接使用 npx 运行
15
15
  - 无需外部依赖,只需要 Node 环境
16
+ - 支持 68+ 个组件文档查询
17
+ - 支持 Arco Mobile 代码转换
16
18
 
17
- ## Cursor 用法
19
+ ## 配置方式
18
20
 
19
- Cursor Mcp 使用指南参考:<https://docs.cursor.com/context/model-context-protocol#using-mcp-tools-in-agent>
21
+ ### Cursor
20
22
 
21
- ``` json
23
+ ```json
22
24
  {
23
25
  "mcpServers": {
24
26
  "@fjyueke/bify-mcp": {
25
27
  "command": "npx",
26
28
  "args": [
27
29
  "-y",
28
- "@fjyueke/bify-mcp",
30
+ "@fjyueke/bify-mcp"
29
31
  ],
30
32
  "env": {}
31
33
  }
32
34
  }
33
35
  }
34
36
  ```
37
+
38
+ ### Trae
39
+
40
+ 在 `.trae/skills/` 目录下创建配置文件,或在 Trae 设置中添加 MCP 服务。
41
+
42
+ ### 其他支持 MCP 的 IDE
43
+
44
+ 参考对应 IDE 的 MCP 配置文档,使用上述配置格式。
45
+
46
+ ## 可用工具
47
+
48
+ | 工具名 | 功能 |
49
+ |--------|------|
50
+ | `list-components` | 列出所有可用的 Bify 组件 |
51
+ | `get-component-docs` | 获取指定组件的详细文档 |
52
+ | `get-component-props` | 获取指定组件的属性和 API 文档 |
53
+ | `list-component-example` | 获取指定组件的所有示例代码 |
54
+ | `convert-arco-to-bify` | 将 Arco Mobile React 代码转换为 Bify 代码 |
55
+ | `get-arco-conversion-help` | 获取 Arco Mobile → Bify 转换帮助信息 |
56
+
57
+ ## 使用示例
58
+
59
+ ### 查询组件文档
60
+
61
+ ```bash
62
+ # 列出所有组件
63
+ list-components
64
+
65
+ # 获取 Cell 组件文档
66
+ get-component-docs Cell
67
+
68
+ # 获取 Cell 组件属性
69
+ get-component-props Cell
70
+
71
+ # 获取 Cell 组件示例
72
+ list-component-example Cell
73
+ ```
74
+
75
+ ### Arco Mobile 代码转换
76
+
77
+ ```bash
78
+ # 获取转换帮助
79
+ get-arco-conversion-help
80
+
81
+ # 转换代码
82
+ convert-arco-to-bify "<Cell label='标题' desc='描述' showArrow>内容</Cell>"
83
+ ```
84
+
85
+ ## 版本历史
86
+
87
+ | 版本 | 日期 | 说明 |
88
+ |------|------|------|
89
+ | 1.0.3 | 2026-07-13 | 优化代码转换工具,增加 40+ 组件映射、值映射、布尔属性转换 |
90
+ | 1.0.2 | 2026-07-13 | 添加 Arco Mobile → Bify 代码转换功能 |
91
+ | 1.0.1 | 2026-07-13 | 更正包名为 bify-mcp |
92
+ | 1.0.0 | 2026-07-13 | 初始版本,支持 68+ 组件文档查询 |
93
+
94
+ ## 相关链接
95
+
96
+ - npm 包地址:https://www.npmjs.com/package/@fjyueke/bify-mcp
97
+ - Bify 组件库:https://github.com/fjyueke/bify
package/dist/index.js CHANGED
@@ -195,20 +195,22 @@ var componentMappings = [
195
195
  arcoImport: "@arco-design/mobile-react",
196
196
  bifyImport: "@fjyueke/bify-core",
197
197
  propMappings: [
198
- { arcoProp: "type", bifyProp: "type" },
198
+ { arcoProp: "type", bifyProp: "color" },
199
+ { arcoProp: "ghost", bifyProp: "variant", transform: () => "outlined" },
199
200
  { arcoProp: "size", bifyProp: "size" },
200
201
  { arcoProp: "disabled", bifyProp: "disabled" },
201
202
  { arcoProp: "loading", bifyProp: "loading" },
202
203
  { arcoProp: "icon", bifyProp: "icon" },
203
204
  { arcoProp: "onClick", bifyProp: "onClick" },
204
205
  { arcoProp: "className", bifyProp: "className" },
205
- { arcoProp: "style", bifyProp: "style" }
206
+ { arcoProp: "style", bifyProp: "style" },
207
+ { arcoProp: "block", bifyProp: "block" }
206
208
  ],
207
209
  valueMappings: {
208
- type: {
209
- ghost: "outlined",
210
+ color: {
210
211
  default: "default",
211
- primary: "primary"
212
+ primary: "primary",
213
+ ghost: "default"
212
214
  },
213
215
  size: {
214
216
  mini: "mini",
@@ -238,7 +240,9 @@ var componentMappings = [
238
240
  propMappings: [
239
241
  { arcoProp: "dot", bifyProp: "dot" },
240
242
  { arcoProp: "count", bifyProp: "content" },
241
- { arcoProp: "maxCount", bifyProp: "max" }
243
+ { arcoProp: "maxCount", bifyProp: "max" },
244
+ { arcoProp: "position", bifyProp: "position" },
245
+ { arcoProp: "fixed", bifyProp: "fixed" }
242
246
  ]
243
247
  },
244
248
  {
@@ -247,18 +251,24 @@ var componentMappings = [
247
251
  arcoImport: "@arco-design/mobile-react",
248
252
  bifyImport: "@fjyueke/bify-core",
249
253
  propMappings: [
250
- { arcoProp: "type", bifyProp: "type" },
254
+ { arcoProp: "type", bifyProp: "color" },
251
255
  { arcoProp: "size", bifyProp: "size" },
252
- { arcoProp: "closable", bifyProp: "closable" },
253
- { arcoProp: "onClose", bifyProp: "onClose" }
256
+ { arcoProp: "closable", bifyProp: "closeable" },
257
+ { arcoProp: "onClose", bifyProp: "onClose" },
258
+ { arcoProp: "shape", bifyProp: "shape" }
254
259
  ],
255
260
  valueMappings: {
256
- type: {
261
+ color: {
257
262
  default: "default",
258
263
  primary: "primary",
259
264
  success: "success",
260
265
  warning: "warning",
261
266
  danger: "danger"
267
+ },
268
+ size: {
269
+ small: "small",
270
+ medium: "medium",
271
+ large: "large"
262
272
  }
263
273
  }
264
274
  },
@@ -325,8 +335,12 @@ var componentMappings = [
325
335
  propMappings: [
326
336
  { arcoProp: "visible", bifyProp: "open" },
327
337
  { arcoProp: "title", bifyProp: "title" },
328
- { arcoProp: "content", bifyProp: "children" },
329
- { arcoProp: "onClose", bifyProp: "onClose" }
338
+ { arcoProp: "content", bifyProp: "message" },
339
+ { arcoProp: "onClose", bifyProp: "onClose" },
340
+ { arcoProp: "confirmText", bifyProp: "confirm" },
341
+ { arcoProp: "cancelText", bifyProp: "cancel" },
342
+ { arcoProp: "onConfirm", bifyProp: "onConfirm" },
343
+ { arcoProp: "onCancel", bifyProp: "onCancel" }
330
344
  ]
331
345
  },
332
346
  {
@@ -616,20 +630,36 @@ function findAllArcoComponents() {
616
630
 
617
631
  // src/services/arco-converter.ts
618
632
  async function convertArcoToBify(arcoCode) {
633
+ const warnings = [];
634
+ const suggestions = [];
635
+ const usedMappings = [];
619
636
  let result = arcoCode;
620
- result = convertImports(result);
621
- result = convertComponents(result);
637
+ const importsResult = convertImports(result, warnings);
638
+ result = importsResult.code;
639
+ usedMappings.push(...importsResult.mappings);
640
+ const componentsResult = convertComponents(result, warnings, suggestions);
641
+ result = componentsResult.code;
642
+ usedMappings.push(...componentsResult.mappings);
622
643
  result = convertIconImports(result);
623
644
  result = fixCodeFormat(result);
624
- return result;
645
+ const uniqueMappings = usedMappings.filter(
646
+ (m, index, self) => self.findIndex((t) => t.arcoName === m.arcoName) === index
647
+ );
648
+ return {
649
+ code: result,
650
+ mappings: uniqueMappings,
651
+ warnings,
652
+ suggestions
653
+ };
625
654
  }
626
- function convertImports(code) {
655
+ function convertImports(code, warnings) {
627
656
  let result = code;
657
+ const usedMappings = [];
628
658
  const arcoImportRegex = /import\s+([\s\S]*?)\s+from\s+['"]@arco-design\/mobile-react['"]/g;
629
659
  const matches = [...code.matchAll(arcoImportRegex)];
630
660
  const bifyCoreComponents = [];
631
661
  const bifyIconComponents = [];
632
- const remainingComponents = [];
662
+ const unsupportedComponents = [];
633
663
  for (const match of matches) {
634
664
  const importContent = match[1].trim();
635
665
  const namedImportRegex = /\{([\s\S]*?)\}/;
@@ -639,17 +669,16 @@ function convertImports(code) {
639
669
  for (const component of components) {
640
670
  const mapping = findMappingByArcoName(component);
641
671
  if (mapping) {
672
+ usedMappings.push(mapping);
642
673
  if (mapping.bifyImport === "@fjyueke/bify-icons") {
643
674
  bifyIconComponents.push(component);
644
675
  } else {
645
676
  bifyCoreComponents.push(mapping.bifyName || component);
646
677
  }
647
678
  } else {
648
- remainingComponents.push(component);
679
+ unsupportedComponents.push(component);
649
680
  }
650
681
  }
651
- } else {
652
- remainingComponents.push(importContent);
653
682
  }
654
683
  result = result.replace(match[0], "");
655
684
  }
@@ -666,22 +695,29 @@ function convertImports(code) {
666
695
  `;
667
696
  result = iconImport + result;
668
697
  }
669
- return result;
698
+ if (unsupportedComponents.length > 0) {
699
+ warnings.push(`\u4EE5\u4E0B\u7EC4\u4EF6\u6682\u4E0D\u652F\u6301\u81EA\u52A8\u8F6C\u6362\uFF1A${unsupportedComponents.join(", ")}`);
700
+ }
701
+ return { code: result, mappings: usedMappings };
670
702
  }
671
- function convertComponents(code) {
703
+ function convertComponents(code, warnings, suggestions) {
672
704
  let result = code;
705
+ const usedMappings = [];
673
706
  const arcoComponents = findAllArcoComponents();
674
707
  for (const arcoComponent of arcoComponents) {
675
708
  const mapping = findMappingByArcoName(arcoComponent);
676
709
  if (!mapping || !mapping.bifyName) continue;
710
+ const componentPattern = new RegExp(`<${arcoComponent}`, "g");
711
+ if (!componentPattern.test(code)) continue;
712
+ usedMappings.push(mapping);
677
713
  const bifyComponent = mapping.bifyName;
678
- const componentRegex = new RegExp(
714
+ const selfClosingRegex = new RegExp(
679
715
  `<${arcoComponent}([^>]*)\\/?>`,
680
716
  "g"
681
717
  );
682
- result = result.replace(componentRegex, (match, props) => {
718
+ result = result.replace(selfClosingRegex, (match, props) => {
683
719
  let newProps = props;
684
- newProps = applyPropMappings(newProps, mapping);
720
+ newProps = applyPropMappings(newProps, mapping, warnings);
685
721
  newProps = applyValueMappings(newProps, mapping);
686
722
  return `<${bifyComponent}${newProps}/>`;
687
723
  });
@@ -691,15 +727,23 @@ function convertComponents(code) {
691
727
  );
692
728
  result = result.replace(closingComponentRegex, (match, props, children) => {
693
729
  let newProps = props;
694
- newProps = applyPropMappings(newProps, mapping);
730
+ newProps = applyPropMappings(newProps, mapping, warnings);
695
731
  newProps = applyValueMappings(newProps, mapping);
696
732
  return `<${bifyComponent}${newProps}>${children}</${bifyComponent}>`;
697
733
  });
734
+ addSuggestions(mapping, suggestions);
698
735
  }
699
- return result;
736
+ return { code: result, mappings: usedMappings };
700
737
  }
701
- function applyPropMappings(props, mapping) {
738
+ function applyPropMappings(props, mapping, warnings) {
702
739
  let result = props;
740
+ const arcoProps = props.match(/\b(\w+)=/g)?.map((p) => p.slice(0, -1)) || [];
741
+ const mappedProps = mapping.propMappings.map((m) => m.arcoProp);
742
+ for (const arcoProp of arcoProps) {
743
+ if (!mappedProps.includes(arcoProp) && !["className", "style", "children"].includes(arcoProp)) {
744
+ warnings.push(`\u7EC4\u4EF6 ${mapping.arcoName} \u7684\u5C5E\u6027 ${arcoProp} \u672A\u627E\u5230\u6620\u5C04\uFF0C\u5DF2\u4FDD\u7559\u539F\u6837`);
745
+ }
746
+ }
703
747
  for (const propMapping of mapping.propMappings) {
704
748
  const propRegex = new RegExp(
705
749
  `${propMapping.arcoProp}=(["'])(.*?)\\1`,
@@ -731,7 +775,10 @@ function applyValueMappings(props, mapping) {
731
775
  );
732
776
  result = result.replace(propRegex, (match, quote, value) => {
733
777
  const mappedValue = valueMap[value] || value;
734
- return `${propName}=${quote}${mappedValue}${quote}`;
778
+ if (mappedValue !== value) {
779
+ return `${propName}=${quote}${mappedValue}${quote}`;
780
+ }
781
+ return match;
735
782
  });
736
783
  }
737
784
  return result;
@@ -753,6 +800,18 @@ function fixCodeFormat(code) {
753
800
  result = nonEmptyLines.join("\n");
754
801
  return result.trim();
755
802
  }
803
+ function addSuggestions(mapping, suggestions) {
804
+ const suggestionMap = {
805
+ Cell: "Bify \u7684 Cell \u7EC4\u4EF6\u652F\u6301\u66F4\u591A\u5E03\u5C40\u5C5E\u6027\uFF0C\u5EFA\u8BAE\u67E5\u770B\u6587\u6863\u4E86\u89E3 align\u3001size \u7B49\u5C5E\u6027",
806
+ Button: "Bify \u7684 Button \u7EC4\u4EF6\u652F\u6301\u66F4\u591A\u6837\u5F0F\u53D8\u4F53\uFF0C\u5EFA\u8BAE\u67E5\u770B variant \u5C5E\u6027",
807
+ Dialog: "Bify \u7684 Dialog \u7EC4\u4EF6\u652F\u6301\u66F4\u591A\u4EA4\u4E92\u9009\u9879\uFF0C\u5EFA\u8BAE\u67E5\u770B closeOnClickOverlay \u7B49\u5C5E\u6027",
808
+ Tabs: "Bify \u7684 Tabs \u7EC4\u4EF6\u652F\u6301\u6ED1\u52A8\u5207\u6362\uFF0C\u5EFA\u8BAE\u67E5\u770B swipeable \u5C5E\u6027",
809
+ Field: "Bify \u7684 Field \u7EC4\u4EF6\u652F\u6301\u66F4\u591A\u9A8C\u8BC1\u5C5E\u6027\uFF0C\u5EFA\u8BAE\u67E5\u770B rules \u5C5E\u6027"
810
+ };
811
+ if (suggestionMap[mapping.arcoName]) {
812
+ suggestions.push(suggestionMap[mapping.arcoName]);
813
+ }
814
+ }
756
815
  async function getArcoConversionHelp() {
757
816
  const availableComponents = componentMappings.filter((m) => m.bifyName).map((m) => `- ${m.arcoName} \u2192 ${m.bifyName}`).join("\n");
758
817
  const valueMappingExamples = componentMappings.filter((m) => m.valueMappings).map((m) => {
@@ -806,9 +865,15 @@ import { Cell, CellGroup, Button } from '@fjyueke/bify-core';
806
865
  | \`max\` | \`count\` | Rate \u6700\u5927\u6570\u91CF |
807
866
  | \`percent\` | \`percentage\` | Progress \u767E\u5206\u6BD4 |
808
867
 
868
+ ### \u6700\u4F73\u5B9E\u8DF5
869
+
870
+ 1. **\u4F7F\u7528 AI \u8F85\u52A9\u8F6C\u6362**\uFF1A\u540C\u65F6\u914D\u7F6E Arco Mobile MCP \u548C Bify MCP\uFF0C\u8BA9 AI \u67E5\u8BE2\u53CC\u65B9\u6587\u6863\u540E\u518D\u8FDB\u884C\u8F6C\u6362
871
+ 2. **\u624B\u52A8\u68C0\u67E5**\uFF1A\u8F6C\u6362\u540E\u8BF7\u68C0\u67E5\u8B66\u544A\u4FE1\u606F\uFF0C\u5904\u7406\u672A\u6620\u5C04\u7684\u5C5E\u6027
872
+ 3. **\u53C2\u8003\u6587\u6863**\uFF1A\u4F7F\u7528 \`get-component-docs\` \u548C \`get-component-props\` \u67E5\u770B Bify \u7EC4\u4EF6\u7684\u8BE6\u7EC6\u7528\u6CD5
873
+
809
874
  ### \u8C03\u7528\u65B9\u5F0F
810
875
 
811
- \u4F7F\u7528 \`convert-arco-to-bify\` \u5DE5\u5177\uFF0C\u4F20\u5165 Arco Mobile \u4EE3\u7801\u5373\u53EF\u83B7\u5F97\u8F6C\u6362\u540E\u7684 Bify \u4EE3\u7801\u3002`;
876
+ \u4F7F\u7528 \`convert-arco-to-bify\` \u5DE5\u5177\uFF0C\u4F20\u5165 Arco Mobile \u4EE3\u7801\u5373\u53EF\u83B7\u5F97\u8F6C\u6362\u540E\u7684 Bify \u4EE3\u7801\u53CA\u8BE6\u7EC6\u62A5\u544A\u3002`;
812
877
  }
813
878
 
814
879
  // src/tools/components.ts
@@ -880,19 +945,50 @@ function registerTifyTools(server) {
880
945
  );
881
946
  server.tool(
882
947
  "convert-arco-to-bify",
883
- "Converts Arco Mobile React code to Bify code",
948
+ "Converts Arco Mobile React code to Bify code with detailed report including warnings and suggestions",
884
949
  { arcoCode: z.string() },
885
950
  async (args) => {
886
- const convertedCode = await convertArcoToBify(args.arcoCode);
951
+ const result = await convertArcoToBify(args.arcoCode);
952
+ let report = `## \u8F6C\u6362\u7ED3\u679C
953
+
954
+ ### \u8F6C\u6362\u540E\u7684\u4EE3\u7801
955
+
956
+ \`\`\`tsx
957
+ ${result.code}
958
+ \`\`\``;
959
+ if (result.mappings.length > 0) {
960
+ report += `
961
+
962
+ ### \u4F7F\u7528\u7684\u6620\u5C04
963
+
964
+ | Arco \u7EC4\u4EF6 | Bify \u7EC4\u4EF6 |
965
+ |-----------|-----------|
966
+ ${result.mappings.map((m) => `| ${m.arcoName} | ${m.bifyName} |`).join("\n")}`;
967
+ }
968
+ if (result.warnings.length > 0) {
969
+ report += `
970
+
971
+ ### \u26A0\uFE0F \u8B66\u544A
972
+
973
+ - ${result.warnings.join("\n- ")}`;
974
+ }
975
+ if (result.suggestions.length > 0) {
976
+ report += `
977
+
978
+ ### \u{1F4A1} \u5EFA\u8BAE
979
+
980
+ - ${result.suggestions.join("\n- ")}`;
981
+ }
982
+ report += `
983
+
984
+ ### \u8F6C\u6362\u8BF4\u660E
985
+
986
+ \u5982\u9700\u66F4\u7CBE\u786E\u7684\u8F6C\u6362\uFF0C\u5EFA\u8BAE\u540C\u65F6\u4F7F\u7528 Arco Mobile MCP \u548C Bify MCP \u67E5\u8BE2\u53CC\u65B9\u7EC4\u4EF6\u6587\u6863\u540E\u8FDB\u884C\u624B\u52A8\u8C03\u6574\u3002`;
887
987
  return {
888
988
  content: [
889
989
  {
890
990
  type: "text",
891
- text: `## \u8F6C\u6362\u7ED3\u679C
892
-
893
- \`\`\`tsx
894
- ${convertedCode}
895
- \`\`\``
991
+ text: report
896
992
  }
897
993
  ]
898
994
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fjyueke/bify-mcp",
3
- "version": "1.0.3",
3
+ "version": "1.0.6",
4
4
  "type": "module",
5
5
  "description": "MCP UI builder by Bify",
6
6
  "license": "MIT",