@codify-ai/mcp-client 1.0.28 → 1.0.30

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/README.md CHANGED
@@ -24,8 +24,6 @@
24
24
  - 🛠️ **全能工具箱**:包含代码获取、设计稿生成、双向同步等 10+ 个专业工具
25
25
  - 📦 **资源访问**:通过 URI `Codify://get_code/{content_id}` 访问代码
26
26
 
27
-
28
-
29
27
  ## 快速开始
30
28
 
31
29
  ### 1. 配置 MCP
@@ -58,8 +56,7 @@
58
56
 
59
57
  在IDE / CLI 中,您可以:
60
58
 
61
-
62
- ### 方式 1:将设计稿给其他人(研发)拉取代码到本地
59
+ #### 方式 1:将设计稿给其他人(研发)拉取代码到本地
63
60
 
64
61
  在 Codify 插件中选中一个元素,复制的指令然后给到其他人来获取代码。
65
62
 
@@ -69,8 +66,6 @@
69
66
  codify://getCode/{contentId}
70
67
  ```
71
68
 
72
-
73
-
74
69
  #### 方式 2:使用 AI 工具进行设计稿与代码的双向交互
75
70
 
76
71
  直接在对话框输入指令,AI 会自动识别并调用工具:
@@ -86,86 +81,101 @@ codify://getCode/{contentId}
86
81
 
87
82
  在对话中通过或直接输入 URI 引用:
88
83
 
89
- - `codify://generation-rules`
84
+ - `codify://generation-rules`
90
85
  - 逆向转译与代码规范:
91
- 你需要让 LLM 严格遵守转译代码规范才能完美的将它转译为 MasterGo 设计稿。当你需要将你已有的前端项目转换为设计稿的时候,必须让 LLM 读取此文档。
86
+ 你需要让 LLM 严格遵守转译代码规范才能完美的将它转译为 MasterGo 设计稿。当你需要将你已有的前端项目转换为设计稿的时候,必须让 LLM 读取此文档。
92
87
 
93
- - `codify://design-philosophy`
88
+ - `codify://design-philosophy`
94
89
  - 核心设计哲学:
95
- Codify为你总结了一套非常实用的UI设计哲学,它能够让LLM在生成页面的时候,保持优秀的设计审美。
96
-
97
-
98
-
99
-
90
+ Codify为你总结了一套非常实用的UI设计哲学,它能够让LLM在生成页面的时候,保持优秀的设计审美。
100
91
 
101
92
  ## 可用工具
102
93
 
103
94
  ### 🔍 获取与比对
104
95
 
105
96
  #### **get_selection_code**
97
+
106
98
  获取 MasterGo 中当前选中图层(或指定图层)的代码。支持根节点完整保存,以及子节点自动合并到本地基准 HTML 文件。
99
+
107
100
  - **参数**: `projectDir` (必填), `targetNodeId` (可选), `syncToBase` (可选, 默认 true)
108
101
  - **示例**: "获取当前选中图层的代码"
109
102
 
110
103
  #### **get_design_diff**
104
+
111
105
  获取最新设计稿代码与本地 `.codify` 目录中旧代码的差异 (Diff)。用于全量或局部同步。
106
+
112
107
  - **参数**: `projectDir` (必填), `targetNodeId` (可选), `filePath` (可选)
113
108
  - **示例**: "对比最新设计稿,看看有哪些改动"
114
109
 
115
110
  #### **get_code_list**
111
+
116
112
  列出所有可用的代码列表。
113
+
117
114
  - **参数**: 无
118
115
  - **示例**: "查看我可以拉取的代码列表"
119
116
 
120
117
  #### **get_code**
118
+
121
119
  通过 `contentId` 获取特定代码并保存到本地。
120
+
122
121
  - **参数**: `contentId` (必填), `projectDir` (必填), `outDir` (必填), `documentId` (可选)
123
122
  - **示例**: "从频道 demo-123 获取代码"
124
123
 
125
124
  ### 🎨 生成与设计
126
125
 
127
126
  #### **design**
127
+
128
128
  根据自然语言需求生成符合 Codify 规范的 HTML+CSS 代码。
129
+
129
130
  - **参数**: `requirement` (必填)
130
131
  - **示例**: "帮我设计一个深色模式的登录页面"
131
132
 
132
133
  #### **agent_create_page**
134
+
133
135
  将生成的 HTML 代码发送到 MasterGo 插件并创建新页面。
136
+
134
137
  - **参数**: `code` (可选), `filePath` (可选), `projectDir` (必填), `saveCodeToLocal` (可选)
135
138
  - **示例**: "在 MasterGo 中创建一个新页面,代码在 ./temp.html"
136
139
 
137
140
  #### **agent_create_component**
141
+
138
142
  创建一个 MasterGo 母版组件或组件集(变体)。
143
+
139
144
  - **参数**: `code` (必填)
140
145
  - **示例**: "将这段 HTML 创建为 MasterGo 组件"
141
146
 
142
147
  ### 🛠️ 操作与更新
143
148
 
144
149
  #### **agent_update_node**
150
+
145
151
  将修改后的 HTML 代码发回 MasterGo 进行局部更新。**注意:必须包含 `data-node-id`。**
152
+
146
153
  - **参数**: `code` (必填), `targetNodeId` (可选), `documentId` (可选)
147
154
  - **示例**: "更新选中的按钮颜色为蓝色"
148
155
 
149
156
  #### **agent_sync_design**
157
+
150
158
  将本地完整的静态 HTML 文件内容全量同步覆盖到 MasterGo 画布。
159
+
151
160
  - **参数**: `filePath` (必填), `targetNodeId` (必填, 根节点 ID), `documentId` (可选)
152
161
  - **示例**: "同步本地 index.html 到设计稿根节点"
153
162
 
154
163
  #### **agent_remove_node**
164
+
155
165
  在 MasterGo 中删除指定的或当前选中的图层。
166
+
156
167
  - **参数**: `targetNodeId` (可选), `documentId` (可选)
157
168
  - **示例**: "删除选中的图层"
158
169
 
159
-
160
-
161
170
  ### 👤 系统信息
162
171
 
163
172
  #### **get_user_info**
173
+
164
174
  查看当前登录用户信息、团队信息以及配额使用情况。
175
+
165
176
  - **参数**: 无
166
177
  - **示例**: "查看我的配额还剩多少"
167
178
 
168
-
169
179
  ## 许可证
170
180
 
171
181
  MIT
package/dist/index.js CHANGED
@@ -10,13 +10,110 @@ import fs from "fs/promises";
10
10
  import { parse } from "node-html-parser";
11
11
  const __dirname$1 = dirname(fileURLToPath(import.meta.url));
12
12
  const generationRules = readFileSync(
13
- resolve(__dirname$1, "rules.md"),
13
+ resolve(__dirname$1, "reversal-protocol.md"),
14
14
  "utf-8"
15
15
  );
16
16
  const designPhilosophy = readFileSync(
17
17
  resolve(__dirname$1, "design-philosophy.md"),
18
18
  "utf-8"
19
19
  );
20
+ const zh = {
21
+ getCodifyGuidelines: {
22
+ description: "【会话入口】当用户表示要使用 Codify / MasterGo 相关能力时(例如「使用 codify mcp」「用 Codify 做设计」),应首先调用本工具。返回 reversal-protocol.md 与 design-philosophy.md 全文,将基础规范载入上下文。内容与 MCP 资源 codify://generation-rules、codify://design-philosophy 一致;若宿主已自动读取资源可跳过。",
23
+ inputSchema: "无需参数"
24
+ },
25
+ createPage: {
26
+ description: `将代码发送到 Codify 插件转换为设计稿。
27
+ 【极致性能要求】若纯 HTML 已保存为本地文件,强烈建议通过 filePath 传入该文件的绝对路径以节省 Token。如果是直接生成的临时代码,可以通过 code 参数直接传入。`,
28
+ code: "【可选】要发送的 HTML 代码内容。仅当代码是临时生成且未保存为文件时使用。",
29
+ filePath: "【可选】本地 HTML 文件的绝对路径。若文件已存在本地,必须传此参数,工具会自动读取并执行落盘。",
30
+ projectDir: "【必填】用户当前工作区的根目录绝对路径",
31
+ saveCodeToLocal: "是否将插件返回的渲染结果保存到本地 .codify 目录(落盘机制)"
32
+ },
33
+ updateNode: {
34
+ description: "发回局部修改的代码。⚠️【工具选择规则】:所有不涉及布局和排版的修改(如修改文字内容、尺寸、颜色、边框、阴影、特效等),必须使用 agent_update_node。注意:如果是修改父容器的样式,传递的 HTML 代码中必须包含其原本所有的子元素结构以防丢失数据。",
35
+ documentId: "当前 MasterGo 文档 ID。",
36
+ documentPageId: "当前 MasterGo 页面 ID。",
37
+ targetNodeId: "【选填】目标图层 ID (例如 123:456)。如果不传,则默认更新 MasterGo 中当前选中的图层。",
38
+ code: "【必填】修改后的 HTML 代码片段。必须包含 data-node-id。"
39
+ },
40
+ replaceNode: {
41
+ description: "对指定节点进行【结构替换】。当你需要大规模改变一个组件的内部 HTML 结构(如增加、删除或重新排列子元素)时调用。即便结构变化,该工具也会自动尝试“借尸还魂”逻辑以保留根节点 ID,确保 Agent 上下文不丢失。",
42
+ documentId: "当前 MasterGo 文档 ID。",
43
+ documentPageId: "当前 MasterGo 页面 ID。",
44
+ targetNodeId: "【选填】目标图层 ID (例如 123:456)。如果不传,则默认替换 MasterGo 中当前选中的图层。",
45
+ code: "【必填】新的 HTML 代码内容。将完整替换目标节点内部。"
46
+ },
47
+ syncToDesign: {
48
+ description: "将本地完整的静态 HTML 文件内容同步覆盖到 MasterGo 画布进行【全量同步】(如保存整个页面、复杂的模块同步)。必须传入根节点 ID (rootId) 以确保层级正确。",
49
+ documentId: "当前 MasterGo 文档 ID。",
50
+ documentPageId: "当前 MasterGo 页面 ID。",
51
+ targetNodeId: "【必填】页面的根节点 ID (rootId)。",
52
+ filePath: "【必填】本地静态 HTML 文件的绝对路径(通常位于 .codify/... 目录下)。工具会自动读取内容。严禁传入 .vue/.tsx 业务代码路径!"
53
+ },
54
+ getSelectionCode: {
55
+ description: "获取 MasterGo 中当前选中图层(或指定图层)的代码。⚠️ 【严禁盲改】:用户的每一次修改操作(如改色、改文字等),你都必须首先调用本工具拉取最新的节点代码作为上下文!如果你获取的是子节点,工具只会将代码作为纯文本返回给你进行局部修改上下文,绝对不会在本地生成烦人的 HTML 碎片文件!若是根节点则会自动将完整页面代码同步保存到 .codify 目录。",
56
+ projectDir: "【必填】用户当前工作区的根目录绝对路径",
57
+ targetNodeId: "【选填】MasterGo图层ID (例如 123:456)。如果提供,将直接拉取该ID的代码;如果不提供,将拉取当前选中图层的代码。",
58
+ syncToBase: "【选填】是否将获取到的子图层代码同步回本地 .codify 目录下的基准 HTML 文件(合并更新)。默认为 true。"
59
+ },
60
+ createComponent: {
61
+ description: '创建一个 MasterGo 母版组件或组件集(变体)。应当使用 HTML 格式并包含 data-type="component" 属性。',
62
+ code: '组件的 HTML 结构。必须包含 data-type="component" 或 "component-set"。'
63
+ },
64
+ getDesignDiff: {
65
+ description: "获取本地基准文件与 MasterGo 画布设计现状的差异。调用后返回 JSON 形式的 Diff 结果,用于协助判断有哪些图层或样式发生了改变。",
66
+ projectDir: "【必填】用户当前工作区的根目录绝对路径",
67
+ targetNodeId: "【选填】MasterGo图层ID (例如 123:456)。如果不传,则默认获取当前选中图层的代码进行对比。",
68
+ filePath: "【选填】本地基准 HTML 文件的绝对路径。如果已通过 write_to_file 更新了基准文件,请直接传此路径。"
69
+ },
70
+ getCodeList: {
71
+ description: "获取所有可用的代码列表",
72
+ inputSchema: "无需参数获取代码列表"
73
+ },
74
+ design: {
75
+ description: "根据需求生成符合 Codify 规范的 HTML+CSS 代码。生成完成后,应调用 agent_create_page 将代码发送到画布。",
76
+ requirement: '界面需求描述,例如:"一个美观的登录页面"、"现代化的仪表盘界面"等。'
77
+ },
78
+ getUserInfo: {
79
+ description: "获取当前登录用户的信息,包括配额、团队等",
80
+ inputSchema: "获取当前用户信息"
81
+ },
82
+ getCode: {
83
+ description: '【特定场景】通过 codify://getCode/{contentId} 从 Codify 插件获取代码。常规的"获取选中代码"请优先使用 get_selection_code 工具。',
84
+ contentId: "从Codify插件复制图层的指令 (contentId)",
85
+ documentId: "当前 MasterGo 文档 ID。",
86
+ documentPageId: "当前 MasterGo 页面 ID。",
87
+ projectDir: "【必填】用户当前工作区的根目录绝对路径",
88
+ outDir: "【必填】保存代码和资源的绝对路径"
89
+ },
90
+ removeNode: {
91
+ description: "在 MasterGo 画布中执行删除节点操作。支持通过 targetNodeId 指定 ID,或在不传 ID 时默认删除当前选中图层。",
92
+ documentId: "当前 MasterGo 文档 ID。",
93
+ documentPageId: "当前 MasterGo 页面 ID。",
94
+ targetNodeId: "【选填】要删除的目标图层 ID (例如 123:456)。如果不传,则默认删除 MasterGo 中当前选中的图层。"
95
+ }
96
+ };
97
+ const i18n = zh;
98
+ const getCodifyGuidelinesTool = {
99
+ name: "get_codify_guidelines",
100
+ description: i18n.getCodifyGuidelines.description,
101
+ inputSchema: z.object({}).describe(i18n.getCodifyGuidelines.inputSchema),
102
+ handler: async () => {
103
+ const text = `# Codify 规范(reversal-protocol.md)
104
+
105
+ ${generationRules}
106
+
107
+ ---
108
+
109
+ # Codify 设计哲学(design-philosophy.md)
110
+
111
+ ${designPhilosophy}`;
112
+ return {
113
+ content: [{ type: "text", text }]
114
+ };
115
+ }
116
+ };
20
117
  const urlArg = process.argv.find((arg) => arg.startsWith("--url="));
21
118
  const SERVER_URL = urlArg ? urlArg.slice("--url=".length) : process.env.CODIFY_SERVER_URL || "https://mcp.codify-api.com";
22
119
  const ACCESS_KEY = process.env.CODIFY_ACCESS_KEY;
@@ -92,7 +189,6 @@ async function writeResource(resData, targetDir, folderName, ext) {
92
189
  const response = await axios.get(content, { responseType: "arraybuffer" });
93
190
  await fs.writeFile(filePath, response.data);
94
191
  } catch (err) {
95
- console.error(`[Codify MCP] 资源下载失败 ${content}:`, err.message);
96
192
  }
97
193
  } else if (typeof content === "string" && content.startsWith("data:image/")) {
98
194
  const parts = content.split(";base64,");
@@ -138,78 +234,6 @@ async function saveCodeAndResources({
138
234
  ]);
139
235
  return { targetDir, htmlFileName, htmlPath, shapeCount: stats[0], svgCount: stats[1], imageCount: stats[2], resourcePathMap: resPathMap };
140
236
  }
141
- const zh = {
142
- createPage: {
143
- description: `将代码发送到 Codify 插件转换为设计稿。
144
- 【极致性能要求】若纯 HTML 已保存为本地文件,你必须且只能通过 filePath 传入该文件的绝对路径。严禁使用 Read 工具读取文件内容,严禁将大段代码写入 code 参数。工具底层会自动读取并传输,以节省 Token。`,
145
- code: "【可选】要发送的 HTML 代码内容。仅当代码是临时生成且未保存为文件时使用。大段代码严禁使用此参数。",
146
- filePath: "【可选】本地 HTML 文件的绝对路径。若文件已存在本地,必须传此参数,工具会自动读取并执行落盘。",
147
- projectDir: "【必填】用户当前工作区的根目录绝对路径",
148
- saveCodeToLocal: "是否将插件返回的渲染结果保存到本地 .codify 目录(落盘机制)"
149
- },
150
- updateNode: {
151
- description: "将修改后的 HTML 代码发回 MasterGo 画布进行【局部修改】(如改颜色、加文字、改间距等)。⚠️ 【调用前置条件】:你必须且只能在通过 getSelectionCode 拿到该节点的最新代码后进行调用修改!完成代码修改后直接调用此类发送给插件。绝对不要去同步或修改本地基准文件!注意:仅传包含 data-node-id 的那一段 HTML 片段。",
152
- documentId: "当前 MasterGo 文档 ID。",
153
- documentPageId: "当前 MasterGo 页面 ID。",
154
- targetNodeId: "【选填】目标图层 ID (例如 123:456)。如果不传,则默认更新 MasterGo 中当前选中的图层。",
155
- code: "【必填】修改后的 HTML 代码片段。必须包含 data-node-id。"
156
- },
157
- syncToDesign: {
158
- description: "将本地完整的静态 HTML 文件内容同步覆盖到 MasterGo 画布进行【全量同步】(如保存整个页面、复杂的模块同步)。必须传入根节点 ID (rootId) 以确保层级正确。",
159
- documentId: "当前 MasterGo 文档 ID。",
160
- documentPageId: "当前 MasterGo 页面 ID。",
161
- targetNodeId: "【必填】页面的根节点 ID (rootId)。",
162
- filePath: "【必填】本地静态 HTML 文件的绝对路径(通常位于 .codify/... 目录下)。工具会自动读取内容。严禁传入 .vue/.tsx 业务代码路径!"
163
- },
164
- getSelectionCode: {
165
- description: "获取 MasterGo 中当前选中图层(或指定图层)的代码。⚠️ 【严禁盲改】:用户的每一次修改操作(如改色、改文字等),你都必须首先调用本工具拉取最新的节点代码作为上下文!如果你获取的是子节点,工具只会将代码作为纯文本返回给你进行局部修改上下文,绝对不会在本地生成烦人的 HTML 碎片文件!若是根节点则会自动将完整页面代码同步保存到 .codify 目录。",
166
- projectDir: "【必填】用户当前工作区的根目录绝对路径",
167
- targetNodeId: "【选填】MasterGo图层ID (例如 123:456)。如果提供,将直接拉取该ID的代码;如果不提供,将拉取当前选中图层的代码。",
168
- syncToBase: "【选填】是否将获取到的子图层代码同步回本地 .codify 目录下的基准 HTML 文件(合并更新)。默认为 true。"
169
- },
170
- createComponent: {
171
- description: '创建一个 MasterGo 母版组件或组件集(变体)。应当使用 HTML 格式并包含 data-type="component" 属性。',
172
- code: '组件的 HTML 结构。必须包含 data-type="component" 或 "component-set"。'
173
- },
174
- getDesignDiff: {
175
- description: `获取设计稿与本地代码的差异。
176
- 【标准双向同步规程】:
177
- 1. 准备:将当前项目业务代码(Vue/React)按 codify://generation-rules 规范物理逆推为纯静态 HTML。
178
- 2. 刷新:将转译产物覆盖写入 .codify 目录对应的基准 HTML 文件。
179
- 3. 比对:调用此工具比对“基准文件”与“画布现状”获得差异列表。
180
- 4. 决策:展示差异,由用户决定执行“全量同步到设计稿”或“按差异局部同步到项目”。`,
181
- projectDir: "【必填】用户当前工作区的根目录绝对路径",
182
- targetNodeId: "【选填】MasterGo图层ID (例如 123:456)。如果不传,则默认获取当前选中图层的代码进行对比。",
183
- filePath: "【选填】本地基准 HTML 文件的绝对路径。如果已通过 write_to_file 更新了基准文件,请直接传此路径。"
184
- },
185
- getCodeList: {
186
- description: "获取所有可用的代码列表",
187
- inputSchema: "无需参数获取代码列表"
188
- },
189
- design: {
190
- description: "根据需求生成符合 Codify 规范的 HTML+CSS 代码。生成完成后,应调用 agent_create_page 将代码发送到画布。",
191
- requirement: '界面需求描述,例如:"一个美观的登录页面"、"现代化的仪表盘界面"等。'
192
- },
193
- getUserInfo: {
194
- description: "获取当前登录用户的信息,包括配额、团队等",
195
- inputSchema: "获取当前用户信息"
196
- },
197
- getCode: {
198
- description: '【特定场景】通过 codify://getCode/{contentId} 从 Codify 插件获取代码。常规的"获取选中代码"请优先使用 get_selection_code 工具。',
199
- contentId: "从Codify插件复制图层的指令 (contentId)",
200
- documentId: "当前 MasterGo 文档 ID。",
201
- documentPageId: "当前 MasterGo 页面 ID。",
202
- projectDir: "【必填】用户当前工作区的根目录绝对路径",
203
- outDir: "【必填】保存代码和资源的绝对路径"
204
- },
205
- removeNode: {
206
- description: "在 MasterGo 画布中执行删除节点操作。支持通过 targetNodeId 指定 ID,或在不传 ID 时默认删除当前选中图层。",
207
- documentId: "当前 MasterGo 文档 ID。",
208
- documentPageId: "当前 MasterGo 页面 ID。",
209
- targetNodeId: "【选填】要删除的目标图层 ID (例如 123:456)。如果不传,则默认删除 MasterGo 中当前选中的图层。"
210
- }
211
- };
212
- const i18n = zh;
213
237
  const createComponentTool = {
214
238
  name: "agent_create_component",
215
239
  description: i18n.createComponent.description,
@@ -290,13 +314,10 @@ const designTool = {
290
314
  type: "text",
291
315
  text: `📋 收到需求:${requirement}
292
316
 
293
- ${generationRules}
317
+ ⚠️ 请立即基于你的设计哲学构思 UI,并严格遵循 reversal-protocol.md 规范(如禁用 margin,强制 flex,所有间距使用 gap/padding 等)。
294
318
 
295
- ---
296
-
297
- ## 发送代码:
298
- 代码生成并审计通过后,请立即调用 agent_create_page 工具发送代码
299
- - agent_create_page({ code: "生成的完整代码", projectDir: "当前工作目录" })`
319
+ 代码生成并审计通过后,请直接调用 agent_create_page 工具使用 code 参数发送完整代码至画布:
320
+ - agent_create_page({ code: "生成的完整 HTML 代码", projectDir: "当前工作目录" })`
300
321
  }
301
322
  ]
302
323
  };
@@ -424,9 +445,6 @@ const getSelectionCodeTool = {
424
445
  parentId
425
446
  } = nodeInfo;
426
447
  if (targetNodeId === rootId) {
427
- console.log(
428
- `[getSelectionCode] 当前节点ID: ${targetNodeId}, 根节点ID: ${rootId}`
429
- );
430
448
  const saveResult = await saveCodeAndResources({
431
449
  baseDir,
432
450
  documentId,
@@ -451,9 +469,6 @@ const getSelectionCodeTool = {
451
469
  ]
452
470
  };
453
471
  } else {
454
- console.log(
455
- `[getSelectionCode] 当前节点ID: ${targetNodeId}, 根节点ID: ${rootId}`
456
- );
457
472
  let mergedToFilePath = "";
458
473
  if (syncToBase && documentId && documentPageId) {
459
474
  const targetPageDir = path.join(
@@ -473,9 +488,6 @@ const getSelectionCodeTool = {
473
488
  };
474
489
  rootFile = await findRootFile();
475
490
  if (!rootFile && _depth === 0) {
476
- console.log(
477
- `[getSelectionCode] 基准文件不存在,自动拉取根节点: ${rootId}`
478
- );
479
491
  await getSelectionCodeTool.handler({
480
492
  projectDir,
481
493
  targetNodeId: rootId,
@@ -507,7 +519,6 @@ const getSelectionCodeTool = {
507
519
  mergedToFilePath = rootFilePath;
508
520
  }
509
521
  } catch (err) {
510
- console.error("合并到基准文件操作出错:", err);
511
522
  }
512
523
  }
513
524
  }
@@ -593,15 +604,85 @@ const updateNodeTool = {
593
604
  code: z.string().describe(i18n.updateNode.code)
594
605
  },
595
606
  handler: async (args) => {
596
- const { targetNodeId, documentId, documentPageId, code } = args;
607
+ const { documentId, documentPageId, code } = args;
608
+ let finalTargetNodeId = args.targetNodeId;
597
609
  if (!code) {
598
610
  return { content: [{ type: "text", text: `❌ 必须提供 code` }], isError: true };
599
611
  }
600
- const { data, error } = await callApi("POST", "/api/updateNode", { code, targetNodeId, documentId, documentPageId });
612
+ if (!finalTargetNodeId) {
613
+ try {
614
+ const rootNode = parse(code);
615
+ const firstElement = rootNode.querySelector("[data-node-id]");
616
+ if (firstElement) {
617
+ const id = firstElement.getAttribute("data-node-id");
618
+ if (id) {
619
+ finalTargetNodeId = id;
620
+ }
621
+ }
622
+ } catch (e) {
623
+ }
624
+ }
625
+ if (!finalTargetNodeId) {
626
+ return {
627
+ content: [{
628
+ type: "text",
629
+ text: `❌ 安全阻拦:未提供 targetNodeId,且无法从代码中解析出 data-node-id。为防止灾难性地覆盖当前选中的未知图层,本次操作已被拦截。请明确指定 targetNodeId 或在 HTML 包含 data-node-id。`
630
+ }],
631
+ isError: true
632
+ };
633
+ }
634
+ const { data, error } = await callApi("POST", "/api/updateNode", { code, targetNodeId: finalTargetNodeId, documentId, documentPageId });
601
635
  if (error) return { content: [{ type: "text", text: `❌ 局部更新失败: ${error.message}` }], isError: true };
602
636
  return {
603
637
  content: [{ type: "text", text: `✅ [局部修改] 指令已发送${data.targetNodeId ? `
604
- - 节点 ID: ${data.targetNodeId}` : ""}` }]
638
+ - 节点 ID: ${data.targetNodeId}` : ""}
639
+ 💡 提示:若当前阶段修改已全部完成,建议调用 get_selection_code 重新拉取一次根节点,以自动同步最新代码到本地 .codify 基准文件。` }]
640
+ };
641
+ }
642
+ };
643
+ const replaceNodeTool = {
644
+ name: "agent_replace_node",
645
+ description: i18n.replaceNode.description,
646
+ inputSchema: {
647
+ documentId: z.string().optional().describe(i18n.replaceNode.documentId),
648
+ documentPageId: z.string().optional().describe(i18n.replaceNode.documentPageId),
649
+ targetNodeId: z.string().optional().describe(i18n.replaceNode.targetNodeId),
650
+ code: z.string().describe(i18n.replaceNode.code)
651
+ },
652
+ handler: async (args) => {
653
+ const { documentId, documentPageId, code } = args;
654
+ let finalTargetNodeId = args.targetNodeId;
655
+ if (!code) {
656
+ return { content: [{ type: "text", text: `❌ 必须提供 code` }], isError: true };
657
+ }
658
+ if (!finalTargetNodeId) {
659
+ try {
660
+ const rootNode = parse(code);
661
+ const firstElement = rootNode.querySelector("[data-node-id]");
662
+ if (firstElement) {
663
+ const id = firstElement.getAttribute("data-node-id");
664
+ if (id) {
665
+ finalTargetNodeId = id;
666
+ }
667
+ }
668
+ } catch (e) {
669
+ }
670
+ }
671
+ if (!finalTargetNodeId) {
672
+ return {
673
+ content: [{
674
+ type: "text",
675
+ text: `❌ 安全阻拦:未提供 targetNodeId,且无法从代码中解析出 data-node-id。为防止把您当前极可能选中的大容器直接覆盖损毁,系统已拦截。请务必明确目标节点的 ID。`
676
+ }],
677
+ isError: true
678
+ };
679
+ }
680
+ const { data, error } = await callApi("POST", "/api/replaceNode", { code, targetNodeId: finalTargetNodeId, documentId, documentPageId });
681
+ if (error) return { content: [{ type: "text", text: `❌ 结构替换失败: ${error.message}` }], isError: true };
682
+ return {
683
+ content: [{ type: "text", text: `✅ [结构替换] 指令已发送${data.targetNodeId ? `
684
+ - 节点 ID: ${data.targetNodeId}` : ""}
685
+ 💡 提示:若当前阶段修改已全部完成,建议调用 get_selection_code 重新拉取一次根节点,以自动同步最新代码到本地 .codify 基准文件。` }]
605
686
  };
606
687
  }
607
688
  };
@@ -794,6 +875,7 @@ const syncToDesignTool = {
794
875
  }
795
876
  };
796
877
  const allTools = [
878
+ getCodifyGuidelinesTool,
797
879
  designTool,
798
880
  getCodeTool,
799
881
  getCodeListTool,
@@ -802,6 +884,7 @@ const allTools = [
802
884
  createComponentTool,
803
885
  getSelectionCodeTool,
804
886
  updateNodeTool,
887
+ replaceNodeTool,
805
888
  syncToDesignTool,
806
889
  getDesignDiffTool,
807
890
  removeNodeTool
@@ -859,8 +942,6 @@ registerAllTools(mcpServer);
859
942
  try {
860
943
  const transport = new StdioServerTransport();
861
944
  await mcpServer.connect(transport);
862
- console.error(`[Codify MCP] Client connected to ${SERVER_URL}`);
863
945
  } catch (error) {
864
- console.error(`[Codify MCP] Connection failed:`, error.message);
865
946
  process.exit(1);
866
947
  }
@@ -10,28 +10,6 @@
10
10
  **核心二:严苛的 Figma 协议编译器(负责“格式化”)**
11
11
  一旦视觉方案定型,你必须将这个绝美的界面,**100% 严格地“降维、压缩、翻译”**成符合底层规范的代码。你不再是设计师,而是一个没有感情的机器,确保每一行代码都能被程序完美逆向解析为 Figma 图层。任何非标代码都会导致转换失败系统崩溃。
12
12
 
13
- ## 🔄 标准双向同步协议 (Standard Bi-directional Sync Protocol)
14
-
15
- 当需要将本地工程与 MasterGo 设计稿进行比对或同步时,必须严格遵守以下“三步走”规程,严禁跳步:
16
-
17
- ### Step 1: 准备与逆向转译 (Prepare & Reverse Transpile)
18
- 你必须读取当前项目对应的业务代码(如 `.vue`, `.tsx`),并将其物理“解构”为符合本协议规范的纯静态 HTML。
19
- - **清除所有框架指令**:删除 `v-for`, `@click`, `v-if`, `props` 等所有动态逻辑。
20
- - **展开静态数据**:将变量替换为高逼真的静态假数据,将循环展开为重复的 HTML 结构。
21
- - **验证样式**:确保所有类名已转换为本协议要求的 Tailwind 任意值语法。
22
-
23
- ### Step 2: 刷新本地基准 (Refresh Local Base)
24
- 将 Step 1 产出的纯静态 HTML 代码,通过 `write_to_file` 覆盖写入到 `.codify` 目录下对应的基准 `.html` 文件中。
25
- - **理由**:通过此步骤,我们确保 `.codify` 目录实时映射了当前代码的真实视觉状态,使得后续的 Diff 能够准确反映“代码”与“设计”的差异,而非过期的快照。
26
-
27
- ### Step 3: 比对与决策 (Diff & Decision)
28
- 调用 `get_design_diff` 工具,获取基准文件与画布现状的差异 JSON。
29
- - **展示差异**:向用户展示变更列表(修改、插入、删除)。
30
- - **用户决策**:
31
- - **A. 同步到设计稿**:用户确认代码是正确的,调用 `agent_update_node` (带 `filePath`) 将文件强行覆盖到画布根节点。
32
- - **B. 同步到代码**:用户确认设计是正确的,你根据 Diff JSON 精准修补业务代码中的样式和结构。
33
-
34
- ---
35
13
 
36
14
  ## 📜 最终产出总纲 (Final Output Standards)
37
15
 
@@ -56,7 +34,10 @@
56
34
  - **编译红线自检**:我已确认弃用所有原生 input/button 标签;绝对禁止使用 m- (Margin);所有间距均将使用 Flex gap 或 p- 替代。
57
35
 
58
36
  > 🚨 **严格警告** 传给工具的 `code` 必须是**纯 HTML 字符串**,只能以 `<div` / `<main` 等 HTML 标签开头,不得包含任何 XML 标签、注释说明或 Markdown 内容。
59
- > 💡 **特别注意**:当用户明确说“修改这个设计”时,意味着你在完成代码层面的修改后,**必须**将修改后的 HTML 代码通过 `agent_update_node` 工具发回去,从而真实地更新 MasterGo 画布中的设计图层。
37
+ > 💡 **特别注意与工具选择指南**:请严格按照以下规则决定使用哪个工具:
38
+ > 1. `agent_update_node` 传入 `code`:用于**所有不涉及布局和排版**的局部修改(如修改文字内容、尺寸、颜色、边框、阴影、特效等)。
39
+ > 2. `agent_replace_node` 传入 `code`:用于**凡是修改了布局结构、排列顺序、或内容条数发生变化**的局部节点重写。
40
+ > 3. `agent_sync_design` 传入 `filePath`:用于**大规模、横跨多图层**的内容重构。强烈建议直接使用底层工具更改 `.codify/.../index.html` 源码,然后调用本工具传入 `filePath` 以节省 Token 代价。
60
41
 
61
42
  ## 🛑 红色警戒区 (Critical Constraints)
62
43
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codify-ai/mcp-client",
3
- "version": "1.0.28",
3
+ "version": "1.0.30",
4
4
  "description": "Codify MCP 客户端 - 连接到远程 Codify MCP 服务器,供 CLI 或 Cursor 等 IDE 使用",
5
5
  "type": "module",
6
6
  "bin": {
@@ -24,9 +24,11 @@
24
24
  },
25
25
  "devDependencies": {
26
26
  "@types/node": "^20.0.0",
27
+ "javascript-obfuscator": "^5.4.1",
27
28
  "terser": "^5.26.0",
28
29
  "typescript": "^5.0.0",
29
30
  "vite": "^5.0.0",
31
+ "vite-plugin-javascript-obfuscator": "^3.1.0",
30
32
  "vite-plugin-node-externals": "^0.0.1"
31
33
  },
32
34
  "dependencies": {