@codify-ai/mcp-client 1.0.8 → 1.0.9
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/mcp-client.js +170 -83
- package/mcp-client.min.js +1 -1
- package/package.json +1 -1
package/mcp-client.js
CHANGED
|
@@ -70,120 +70,207 @@ const generationRules = `
|
|
|
70
70
|
|
|
71
71
|
## 🎯 任务目标
|
|
72
72
|
|
|
73
|
-
|
|
73
|
+
你是一个 **Figma 图层逻辑转译引擎**。你的任务不是写网页,而是生成**结构绝对严谨、用于逆向解析**的 HTML 代码。你的输出将被程序直接读取并转换为 Design Tokens,任何非标代码都会导致转换失败。
|
|
74
74
|
|
|
75
|
-
|
|
76
|
-
- **你的专业能力**:代码生成的界面必须看起来像是一个经过精心打磨的商业级产品,而非粗糙的原型。
|
|
77
|
-
- **输出限制**:仅返回包含在 \`<main>\` 根容器内的代码,全部使用 **内联 CSS**。
|
|
78
|
-
- **工作流**:代码生成后,将立即被解析并转换为Figma设计工具图层,因此代码结构必须严格符合转换逻辑。
|
|
75
|
+
**最终产出标准**
|
|
79
76
|
|
|
80
|
-
|
|
77
|
+
1. **视觉标准**:Dribbble/Behance 级别的商业 UI 审美。
|
|
78
|
+
2. **代码标准**:仅返回包含在 \`<main>\` 根容器内的代码,全部使用 **内联 CSS**,无外部 CSS,无 \`<style>\` 标签。
|
|
79
|
+
3. **结构标准**:完全符合下述的“图层原子化协议”。
|
|
81
80
|
|
|
82
|
-
|
|
83
|
-
- 💡 **逻辑冲突**:\`flex: 1\` 会吞噬剩余空间,导致 \`space-between\` 失效或计算错误,这在转换为设计稿时会导致子元素被强制锁定为固定宽度 (Fixed),从而丢失自适应性。
|
|
84
|
-
- ✅ **正确做法**:如果子元素需要拉伸 (\`flex: 1\`),父容器必须使用 \`justify-content: flex-start\` (或 center/end),仅依靠子元素的增长来填充空间。
|
|
81
|
+
## 🛑 红色警戒区 (Critical Constraints)
|
|
85
82
|
|
|
83
|
+
**以下规则享有最高优先级,违反任何一条均视为 SYSTEM FAILURE:**
|
|
86
84
|
|
|
87
|
-
|
|
85
|
+
### 1. 🚫 绝对禁用的属性 (Blocklist)
|
|
88
86
|
|
|
89
|
-
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
87
|
+
- **禁止** 使用 \`margin\`。如果子元素间距不均匀,**必须**在该元素外包裹一层 \`div\`,并使用该包裹容器的 \`padding\` 来实现“间距”。
|
|
88
|
+
- **禁止** 使用相对单位: \`%\`, \`vw\`, \`vh\`, \`rem\`, \`em\`, \`calc()\` (**必须**锁定使用 \`px\` 整数)。
|
|
89
|
+
- **禁止** 使用具名颜色: \`red\`, \`blue\`, \`white\` (**必须**使用 Hex \`#FFFFFF\` 或 RGBA)。
|
|
90
|
+
- **禁止** 使用 \`width: 100%\` (如需撑满剩余空间请用 \`flex: 1\`,如是根容器或固定卡片请用 \`width: [数值]px\` 等具体数值)。
|
|
91
|
+
- **禁止** 省略 Flex 默认值。
|
|
94
92
|
|
|
95
|
-
|
|
96
|
-
- ❌ **布局属性**: \`margin\` (全用 gap/padding), \`grid\`, \`calc()\`,\`<style>\`标签。必须由父容器的 \`gap\` 属性承担。
|
|
97
|
-
- ❌ **对齐属性**: \`space-around\`, \`space-evenly\` (用 \`space-between\` + \`padding\` 替代)。
|
|
98
|
-
- ❌ **单位**: \`%\`, \`vw\`, \`vh\`, \`em\`, \`rem\`, \`auto\` (锁死使用 **px** 整数)。
|
|
99
|
-
- ❌ **颜色**: \`white\`, \`black\`, \`red\` 等具名颜色。必须使用 Hex (#ffffff) 或 RGBA。
|
|
100
|
-
- ❌ **文本标签污染**: 禁止为 \`<span>/<p>\` 设置 \`background\`, \`padding\`, \`border\`, \`flex\`。
|
|
101
|
-
- ❌ **尺寸限制**: 禁止 \`width: 100%\`。拉伸必须通过 Flex 关系 (\`flex: 1\` 或 \`align-self: stretch\`) 实现。
|
|
93
|
+
### 2. ⚠️ 强制显式声明 (Explicit Declaration)
|
|
102
94
|
|
|
103
|
-
|
|
95
|
+
所有 \`display: flex\` 容器**必须**写全以下 4 行属性,缺一不可:
|
|
104
96
|
|
|
105
|
-
|
|
97
|
+
\`\`\`css
|
|
98
|
+
display: flex;
|
|
99
|
+
flex-direction: [row/column];
|
|
100
|
+
justify-content: [flex-start/center/space-between...];
|
|
101
|
+
align-items: [flex-start/center/stretch...];
|
|
102
|
+
/* 即使是默认值也要写 */
|
|
103
|
+
\`\`\`
|
|
106
104
|
|
|
107
|
-
|
|
108
|
-
- **文档流**:所有布局必须使用 Flexbox。
|
|
109
|
-
- **根节点**:<main> 元素必须明确声明具体的 width: [数值]px。
|
|
110
|
-
- **命名规范**:所有标签必须包含 data-name="..." 属性,用于描述图层用途(如 data-name="submit-btn")。
|
|
105
|
+
## 🎨 动态视觉配置模块 (Visual Configuration Module)
|
|
111
106
|
|
|
112
|
-
|
|
107
|
+
**根据用户的任务类型,从以下风格中提取 CSS 策略:**
|
|
113
108
|
|
|
114
|
-
|
|
109
|
+
### Style A: 未来与深度 (Future & Depth)
|
|
115
110
|
|
|
116
|
-
-
|
|
117
|
-
|
|
118
|
-
-
|
|
111
|
+
- **核心特征**:磨砂玻璃、光晕、层级感。
|
|
112
|
+
- **CSS 策略**:
|
|
113
|
+
- 背景:使用 \`rgba(255, 255, 255, 0.05)\` 配合 \`backdrop-filter: blur(20px)\` (注意:虽然后端可能不解析 blur,但需保留以示意)。
|
|
114
|
+
- 边框:\`border: 1px solid rgba(255, 255, 255, 0.1)\`。
|
|
115
|
+
- 模式:默认倾向于 **Dark Mode**。
|
|
119
116
|
|
|
120
|
-
|
|
117
|
+
### Style B: 效率与速度 (Efficiency & Speed)
|
|
121
118
|
|
|
122
|
-
|
|
123
|
-
|
|
119
|
+
- **核心特征**:洁净扁平、Bento UI (便当盒布局)、模块化。
|
|
120
|
+
- **CSS 策略**:
|
|
121
|
+
- 布局:高密度的 \`gap\` 控制,强调卡片间的整齐切割。
|
|
122
|
+
- 装饰:无多余阴影,利用高对比度的 \`border\` 或微弱的 \`background\` 区分区块。
|
|
124
123
|
|
|
125
|
-
|
|
124
|
+
### Style C: 信任与专业 (Trust & Professional)
|
|
126
125
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
-
|
|
126
|
+
- **核心特征**:瑞士极简、大量留白 (Less Is More)、严谨网格。
|
|
127
|
+
- **CSS 策略**:
|
|
128
|
+
- 间距:使用极大的 \`padding\` (如 40px, 60px) 和 \`gap\` 来制造呼吸感。
|
|
129
|
+
- 排版:利用字重 (\`font-weight\`) 和字号对比构建层级,而非依赖色块。
|
|
130
130
|
|
|
131
|
+
### 🧬 通用视觉原子 (Design System)
|
|
131
132
|
|
|
132
|
-
|
|
133
|
-
-
|
|
133
|
+
1. **色彩语义**:
|
|
134
|
+
- **主色**:定义品牌情绪 (e.g., 科技蓝、创新紫)。
|
|
135
|
+
- **交互色**:主色互补色用于 \`Call-to-Action\`,同类色用于次要按钮。
|
|
136
|
+
2. **阴影规范**:禁止生硬黑影。**必须**使用弥散光影,例如 \`box-shadow: 0 10px 30px rgba(0,0,0, 0.08)\`。
|
|
137
|
+
3. **圆角规范**:默认 \`12px\` 起步,卡片类推荐 \`16px\` 或 \`24px\`。
|
|
138
|
+
4. **图标规范**:使用 \`<i class="fas fa-...">\`,且**必须**在 style 中显式声明 \`color\` 和 \`font-size\`。
|
|
134
139
|
|
|
135
|
-
##
|
|
140
|
+
## 🧬 图层原子化协议 (Atomic Structure)
|
|
141
|
+
|
|
142
|
+
### 规则 A:容器与内容物理隔离
|
|
136
143
|
|
|
137
|
-
|
|
138
|
-
- **拉伸的主动性**:父容器不应强制拉伸子元素。拉伸行为必须由子元素主动声明:
|
|
139
|
-
- 主轴拉伸:使用 \`flex: 1\`。
|
|
140
|
-
- 交叉轴拉伸:使用 \`align-self: stretch\`。
|
|
141
|
-
- **传递性**:当深层子元素需要拉伸时,其所有父级路径上的容器通常也需要具备拉伸属性。
|
|
142
|
-
- **防挤压 (Anti-Crush)**:所有固定宽高的元素(如图标、头像、按钮容器)必须显式添加 \`flex: none\`。
|
|
143
|
-
- **🌟 关键:拉伸的“链式传递” (Chain Reaction)**
|
|
144
|
-
- **规则**:当子元素需要“撑满” (\`align-self: stretch\`, \`flex: 1\`),或者子元素是长文本 \`<p>\` 时,**父容器必须也处于拉伸状态**。
|
|
145
|
-
- **逻辑**:如果父容器是“收缩包裹”状态,子元素的“撑满”将无效。
|
|
144
|
+
**原则**:\`<div>\` 仅负责容器样式(背景/边框/阴影/布局);\`<span>/<p>\` 仅负责文本样式。
|
|
146
145
|
|
|
147
|
-
|
|
146
|
+
#### ❌ 错误示范 (混合样式):
|
|
148
147
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
148
|
+
\`\`\`html
|
|
149
|
+
<span style="background: #000; padding: 10px; border-radius: 4px;">Submit</span>
|
|
150
|
+
\`\`\`
|
|
152
151
|
|
|
153
|
-
|
|
152
|
+
#### ✅ 正确示范 (物理隔离):
|
|
154
153
|
|
|
155
|
-
|
|
154
|
+
\`\`\`html
|
|
155
|
+
<div
|
|
156
|
+
data-name="btn-bg"
|
|
157
|
+
style="display: flex; flex-direction: row; justify-content: center; align-items: center; padding: 10px; background: #000; border-radius: 4px;"
|
|
158
|
+
>
|
|
159
|
+
<span data-name="btn-text" style="color: #FFF; font-size: 14px; line-height: 1.2;">Submit</span>
|
|
160
|
+
</div>
|
|
161
|
+
\`\`\`
|
|
156
162
|
|
|
157
|
-
|
|
158
|
-
- 拒绝扁平的单一字重。标题必须使用加粗 (font-weight: 600/700),正文使用常规 (400)。
|
|
159
|
-
- 使用有层次的文字颜色:主要标题使用深色 (如 #1e293b),次级文本/描述必须使用浅灰色 (如 #64748b),不仅是为了美观,也是为了信息降噪。
|
|
163
|
+
### 规则 B:标签语义绑定
|
|
160
164
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
+
- **\`<div>\`**: 唯一的布局容器。
|
|
166
|
+
- **\`<p>\`**: 多行文本。
|
|
167
|
+
- **\`<span>\`**: 短语。
|
|
168
|
+
- **\`<i>\`**: FontAwesome 图标 (class="fas fa-..."). 必须为 \`<i>\` 显式声明 \`color\`、\`font-size\`。
|
|
169
|
+
- **\`<img>\`**: 图片。优先使用固定 px;如需随容器拉伸,**允许**使用 \`width: 100%\` 或 \`flex: 1\`,但必须配合 \`object-fit: cover\`。
|
|
165
170
|
|
|
166
|
-
|
|
167
|
-
- **绝对禁止**使用默认的、生硬的黑色阴影 (如 \`box-shadow: 2px 2px 5px black\`)。
|
|
168
|
-
- 必须使用 **"弥散光影" (Soft/Diffused Shadows)**:使用多层、高模糊半径、低透明度的阴影来创造悬浮感。
|
|
169
|
-
- 参考值:\`box-shadow: 0 10px 40px -10px rgba(0,0,0,0.08);\`
|
|
171
|
+
### 规则 C:交互组件静态化 (Static Interaction)
|
|
170
172
|
|
|
171
|
-
|
|
172
|
-
- 不要使用纯黑 (#000000) 或纯灰。背景色应带有极微弱的色调倾向 (如 Slate, Zinc, 或极淡的蓝灰色 #f8fafc)。
|
|
173
|
-
- 主色调 (Primary Color) 应选择高饱和度且现代的颜色 (如 皇家蓝 #4f46e5 或 活力紫 #8b5cf6)。
|
|
173
|
+
**原则**:所有交互元素(Input, Select, Toggle)必须“冻结”为静态视觉层,严禁使用原生控件。
|
|
174
174
|
|
|
175
|
-
|
|
175
|
+
- **禁止**:\`<input>\`, \`<select>\`, \`<textarea>\`, \`<button>\`, \`<form>\`。
|
|
176
|
+
- **必须**:使用 \`div\` 模拟外观。例如“输入框”应为一个带边框的 \`div\`,内部包含“占位符文字”。
|
|
177
|
+
- **状态**:默认生成“默认态 (Default)”,除非用户指定生成“激活态/报错态”。
|
|
178
|
+
|
|
179
|
+
## 🏗️ 布局与拉伸
|
|
176
180
|
|
|
177
|
-
|
|
181
|
+
1. **拉伸逻辑**:
|
|
182
|
+
- 主轴拉伸:子元素使用 \`flex: 1\`。
|
|
183
|
+
- 交叉轴拉伸:子元素使用 \`align-self: stretch\`。
|
|
184
|
+
2. **命名规范**:每个标签必须包含 \`data-name="..."\`,使用语义化英文 (e.g., \`card-container\`, \`user-avatar\`, \`primary-btn\`)。
|
|
185
|
+
|
|
186
|
+
## 🛠️ 生成前自检程序 (Step-by-Step Thinking)
|
|
187
|
+
|
|
188
|
+
**在生成代码前,请在后台执行以下检查:**
|
|
189
|
+
|
|
190
|
+
1. **Check Margin**: 我是否使用了 \`margin\`?(如有,必须改为 \`gap\` 如果间距不均匀才使用\`margin\`。
|
|
191
|
+
2. **Check Units**: 我是否使用了 \`%\`?(如有,改为 \`px\` 或 \`flex\` 关系)。
|
|
192
|
+
3. **Check Text**: 我的文字是否被 \`div\` 包裹?(文字节点严禁裸露在布局层)。
|
|
193
|
+
4. **Check Flex**: 我的每个 Flex 容器是否都有 4 行显式属性?
|
|
194
|
+
5. **Tag Check**: 我是否使用了 \`<input>\`, \`<button>\`, \`<select>\`?(必须立即改为 \`div\` 结构)。
|
|
195
|
+
|
|
196
|
+
## 🚀 输出格式
|
|
197
|
+
|
|
198
|
+
仅输出包含在 \`<main>\` 标签内的 HTML 代码。\`<main>\` 必须有明确的 \`width: [px]\` 和 \`background-color\`。
|
|
199
|
+
|
|
200
|
+
## 🏆 黄金标准参考 (Reference Implementation)
|
|
201
|
+
|
|
202
|
+
**必须严格模仿以下代码的结构逻辑(注意:全显式 Flex、无 input 标签、无 margin、物理隔离文本):**
|
|
203
|
+
|
|
204
|
+
\`\`\`html
|
|
205
|
+
<div
|
|
206
|
+
data-name="product-card"
|
|
207
|
+
style="display: flex; flex-direction: column; justify-content: flex-start; align-items: stretch; gap: 16px; padding: 16px; background: #FFFFFF; border-radius: 16px; box-shadow: 0 8px 24px rgba(0,0,0,0.06); width: 320px;"
|
|
208
|
+
>
|
|
209
|
+
<div
|
|
210
|
+
data-name="image-wrapper"
|
|
211
|
+
style="display: flex; flex-direction: column; justify-content: center; align-items: center; border-radius: 12px; overflow: hidden;"
|
|
212
|
+
>
|
|
213
|
+
<img
|
|
214
|
+
src="https://placehold.co/288x180/F5F5F7/999999"
|
|
215
|
+
alt="Product"
|
|
216
|
+
style="width: 288px; height: 180px; object-fit: cover; display: block;"
|
|
217
|
+
/>
|
|
218
|
+
</div>
|
|
219
|
+
|
|
220
|
+
<div
|
|
221
|
+
data-name="content-body"
|
|
222
|
+
style="display: flex; flex-direction: column; justify-content: flex-start; align-items: flex-start; gap: 8px;"
|
|
223
|
+
>
|
|
224
|
+
<div
|
|
225
|
+
data-name="badge-container"
|
|
226
|
+
style="display: flex; flex-direction: row; justify-content: flex-start; align-items: center; padding: 4px 8px; background: #E6F0FF; border-radius: 4px;"
|
|
227
|
+
>
|
|
228
|
+
<span data-name="badge-text" style="color: #0066FF; font-size: 10px; font-weight: 600; line-height: 1;">NEW ARRIVAL</span>
|
|
229
|
+
</div>
|
|
230
|
+
|
|
231
|
+
<div
|
|
232
|
+
data-name="title-row"
|
|
233
|
+
style="display: flex; flex-direction: row; justify-content: space-between; align-items: flex-start; align-self: stretch;"
|
|
234
|
+
>
|
|
235
|
+
<div
|
|
236
|
+
data-name="title-wrapper"
|
|
237
|
+
style="display: flex; flex-direction: column; justify-content: flex-start; align-items: flex-start; flex: 1;"
|
|
238
|
+
>
|
|
239
|
+
<span data-name="product-title" style="color: #1A1A1A; font-size: 16px; font-weight: 700; line-height: 1.4;">Ergonomic Chair</span>
|
|
240
|
+
</div>
|
|
241
|
+
<span data-name="price-text" style="color: #1A1A1A; font-size: 16px; font-weight: 700; line-height: 1.4;">$299</span>
|
|
242
|
+
</div>
|
|
243
|
+
|
|
244
|
+
<div data-name="desc-wrapper" style="display: flex; flex-direction: column; justify-content: flex-start; align-items: flex-start;">
|
|
245
|
+
<p data-name="desc-text" style="color: #666666; font-size: 12px; font-weight: 400; line-height: 1.5; margin: 0;">
|
|
246
|
+
Ultimate comfort for your workspace with adjustable support.
|
|
247
|
+
</p>
|
|
248
|
+
</div>
|
|
249
|
+
</div>
|
|
250
|
+
|
|
251
|
+
<div
|
|
252
|
+
data-name="action-row"
|
|
253
|
+
style="display: flex; flex-direction: row; justify-content: space-between; align-items: center; gap: 12px; padding-top: 8px; border-top: 1px solid #F0F0F0;"
|
|
254
|
+
>
|
|
255
|
+
<div
|
|
256
|
+
data-name="qty-input"
|
|
257
|
+
style="display: flex; flex-direction: row; justify-content: space-between; align-items: center; width: 80px; padding: 8px; border: 1px solid #E5E5E5; border-radius: 8px;"
|
|
258
|
+
>
|
|
259
|
+
<i class="fas fa-minus" style="color: #999; font-size: 10px;"></i>
|
|
260
|
+
<span data-name="qty-val" style="color: #333; font-size: 12px; font-weight: 500;">1</span>
|
|
261
|
+
<i class="fas fa-plus" style="color: #333; font-size: 10px;"></i>
|
|
262
|
+
</div>
|
|
263
|
+
|
|
264
|
+
<div
|
|
265
|
+
data-name="add-btn"
|
|
266
|
+
style="display: flex; flex-direction: row; justify-content: center; align-items: center; flex: 1; padding: 10px; background: #000000; border-radius: 8px; cursor: pointer;"
|
|
267
|
+
>
|
|
268
|
+
<span data-name="btn-text" style="color: #FFFFFF; font-size: 12px; font-weight: 600;">Add to Cart</span>
|
|
269
|
+
</div>
|
|
270
|
+
</div>
|
|
271
|
+
</div>
|
|
272
|
+
\`\`\`
|
|
178
273
|
|
|
179
|
-
1. [ ] **显式声明检查**:flex 四属性是否写全?
|
|
180
|
-
2. [ ] **拉伸逻辑检查**:拉伸行为是否由子元素主动声明;代码中是否彻底消灭了 \`width: 100%\` 和 \`%\` 符号?
|
|
181
|
-
3. [ ] **尺寸检查**:所有尺寸都使用了 \`[具体px值]\` 格式?
|
|
182
|
-
4. [ ] **文本检查**:所有 \`<span>\` 和 \`<p>\` 是否都配置了 \`line-height\`?
|
|
183
|
-
5. [ ] **margin检查**:是否移除了所有 \`margin\` 属性?
|
|
184
|
-
6. [ ] **容器补全**:如果发现没有父容器可以承载 \`padding\`,请务必新建一个 \`data-name="padding-wrapper"\` 的 div 来包裹目标元素。
|
|
185
|
-
7. [ ] **设计系统检查**:是否有遵循设计系统的规范和设计原则来创建一个美观的页面?
|
|
186
|
-
8. [ ] **代码检查**:请确保页面布局符合上面的规范。
|
|
187
274
|
`
|
|
188
275
|
|
|
189
276
|
// 辅助函数:检查值是否为空
|
|
@@ -200,7 +287,7 @@ function isEmpty(value) {
|
|
|
200
287
|
const mcpServer = new McpServer(
|
|
201
288
|
{
|
|
202
289
|
name: 'Codify-MCP-Client',
|
|
203
|
-
version: '1.0.
|
|
290
|
+
version: '1.0.9'
|
|
204
291
|
},
|
|
205
292
|
{
|
|
206
293
|
capabilities: {
|
package/mcp-client.min.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{McpServer}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport}from"@modelcontextprotocol/sdk/server/stdio.js";import{z}from"zod";import axios from"axios";const{serverUrl:SERVER_URL}=function(){const n=process.argv.slice(2);let t=process.env.CODIFY_SERVER_URL||"https://mcp.codify-api.com";for(let e=0;e<n.length;e++){const r=n[e];"--help"!==r&&"-h"!==r||(console.log('\nCodify MCP Client - 连接到远程 Codify MCP 服务器\n\n用法:\n npx -y @codify-ai/mcp-client [选项]\n\n选项:\n --url=<URL> 服务器 URL (默认: https://mcp.codify-api.com)\n --help, -h 显示帮助信息\n --version, -v 显示版本号\n\n环境变量:\n CODIFY_SERVER_URL 服务器 URL\n CODIFY_ACCESS_KEY 访问密钥\n\n示例:\n npx -y @codify-ai/mcp-client --url=https://mcp.codify-api.com\n CODIFY_ACCESS_KEY=sk-xxx npx -y @codify-ai/mcp-client\n\nCursor 配置示例 (~/.cursor/mcp.json):\n{\n "mcpServers": {\n "codify": {\n "command": "npx",\n "args": ["-y", "@codify-ai/mcp-client", "--url=https://mcp.codify-api.com"],\n "env": {\n "CODIFY_ACCESS_KEY": "sk-your-access-key"\n }\n }\n }\n}\n'),process.exit(0)),"--version"!==r&&"-v"!==r||process.exit(0),r.startsWith("--url=")?t=r.substring(6):"--url"===r&&e+1<n.length?t=n[++e]:r.startsWith("-")||(t=r)}return{serverUrl:t}}(),ACCESS_KEY=process.env.CODIFY_ACCESS_KEY,n='\n# 📐 HTML + CSS 视觉生成协议 (Visual Generation Protocol)\n\n## 🎯 任务目标\n\n你是一位拥有顶级审美的 UI/UX 设计师,同时也是精通 HTML/CSS 的前端开发专家。你的目标是生成在视觉上媲美 Dribbble 或 Behance 热门作品的 HTML/CSS 代码。\n\n- **核心职责**:生成高保真、美观、结构健壮的 HTML 代码,用于**逻辑建模**(转译为设计稿),而非用于浏览器生产环境。\n- **你的专业能力**:代码生成的界面必须看起来像是一个经过精心打磨的商业级产品,而非粗糙的原型。\n- **输出限制**:仅返回包含在 `<main>` 根容器内的代码,全部使用 **内联 CSS**。\n- **工作流**:代码生成后,将立即被解析并转换为Figma设计工具图层,因此代码结构必须严格符合转换逻辑。\n\n## 🛑 关键约束 (违反即失败)\n\n1. **布局策略互斥**:\n - 💡 **逻辑冲突**:`flex: 1` 会吞噬剩余空间,导致 `space-between` 失效或计算错误,这在转换为设计稿时会导致子元素被强制锁定为固定宽度 (Fixed),从而丢失自适应性。\n - ✅ **正确做法**:如果子元素需要拉伸 (`flex: 1`),父容器必须使用 `justify-content: flex-start` (或 center/end),仅依靠子元素的增长来填充空间。\n\n\n2. **Flex 属性全显式声明**:\n\n- 所有 Flex 容器 (含 `<main>`) 必须显式声明全部四个要素,禁止省略默认值:\n - `display: flex;`\n - `flex-direction: ...;`\n - `justify-content: ...;` (即使是 flex-start 也要写)\n - `align-items: ...;` (即使是 stretch 或 flex-start 也要写)\n\n3. **属性与单位黑名单**:\n - ❌ **布局属性**: `margin` (全用 gap/padding), `grid`, `calc()`,`<style>`标签。必须由父容器的 `gap` 属性承担。\n - ❌ **对齐属性**: `space-around`, `space-evenly` (用 `space-between` + `padding` 替代)。\n - ❌ **单位**: `%`, `vw`, `vh`, `em`, `rem`, `auto` (锁死使用 **px** 整数)。\n - ❌ **颜色**: `white`, `black`, `red` 等具名颜色。必须使用 Hex (#ffffff) 或 RGBA。\n - ❌ **文本标签污染**: 禁止为 `<span>/<p>` 设置 `background`, `padding`, `border`, `flex`。\n - ❌ **尺寸限制**: 禁止 `width: 100%`。拉伸必须通过 Flex 关系 (`flex: 1` 或 `align-self: stretch`) 实现。\n\n## 📜 HTML 结构与原子化规范\n\n1. **层级树协议**:\n\n- **节点层级**:严格遵守 main (画布) > section (模块) > div (容器) > span/p/i/img (原子内容)。\n- **文档流**:所有布局必须使用 Flexbox。\n- **根节点**:<main> 元素必须明确声明具体的 width: [数值]px。\n- **命名规范**:所有标签必须包含 data-name="..." 属性,用于描述图层用途(如 data-name="submit-btn")。\n\n2. **文本原子化**:\n\n- **重要:文本节点与容器节点必须物理分离。**\n\n- **语义定义**:\n - `<div>` = 容器(负责背景、边框、尺寸、阴影、布局)。\n - `<span>/<p>` = 纯文本(仅负责字号、颜色、行高、字重)。\n\n- ❌ **严禁行为**:\n\n - 禁止为 `<span>` 或 `<p>` 设置 `background`, `padding`, `border`, `width`, `height`, `display: flex`。\n - 禁止在 `<p>` 内嵌套 `<span>`。\n\n- ⚠️ **重要:标签选择策略**:\n\n - `<span>`:仅用于极短的行内文本(如标签、按钮文字、时间、短语)。\n - `<p>`:用于所有正文、长标题、多行文本或包含标点符号的完整句子。\n - **占位符**:构建 `Input/Select` 组件结构时,必须包含 `<span>placeholder</span>`。\n\n\n3 **图片与图标**:\n- 使用 FontAwesome `<i>` 标签 (class="fas fa-..."). 必须为 `<i>` 显式声明 `color`、`font-size` 属性\n\n## 🏗️ 布局与拉伸\n\n- **保持自适应特性**:无论生成何种类型的页面,都尽可能保持自适应布局的能力;\n- **拉伸的主动性**:父容器不应强制拉伸子元素。拉伸行为必须由子元素主动声明:\n - 主轴拉伸:使用 `flex: 1`。\n - 交叉轴拉伸:使用 `align-self: stretch`。\n- **传递性**:当深层子元素需要拉伸时,其所有父级路径上的容器通常也需要具备拉伸属性。\n- **防挤压 (Anti-Crush)**:所有固定宽高的元素(如图标、头像、按钮容器)必须显式添加 `flex: none`。\n- **🌟 关键:拉伸的“链式传递” (Chain Reaction)**\n - **规则**:当子元素需要“撑满” (`align-self: stretch`, `flex: 1`),或者子元素是长文本 `<p>` 时,**父容器必须也处于拉伸状态**。\n - **逻辑**:如果父容器是“收缩包裹”状态,子元素的“撑满”将无效。\n\n## 📐 视觉与间距规范\n\n- **边距控制**:仅允许使用 `padding` 控制内边距。\n- **间距控制**:仅允许使用 `gap` 控制子元素间距。\n- **视觉修饰**:边框使用 `border: 1px solid #XXXXXX`,圆角使用 `border-radius: Xpx`。\n\n## 🎨 设计系统严格规范\n\n在生成代码时,必须强制执行以下审美标准:\n\n1. **视觉层级 (Visual Hierarchy):**\n - 拒绝扁平的单一字重。标题必须使用加粗 (font-weight: 600/700),正文使用常规 (400)。\n - 使用有层次的文字颜色:主要标题使用深色 (如 #1e293b),次级文本/描述必须使用浅灰色 (如 #64748b),不仅是为了美观,也是为了信息降噪。\n\n2. **现代盒模型与空间感 (Modern Box Model):**\n - **大留白:** 拒绝拥挤。卡片和容器内部必须有宽裕的 padding (至少 24px 或 32px)。\n - **大圆角:** 容器和卡片应使用明显的圆角 (border-radius: 16px 或 24px) 以呈现亲和力,避免尖锐的直角。\n - **微边框:** 避免使用粗黑边框。使用极其微妙的边框来界定边界 (例如: `border: 1px solid rgba(0,0,0,0.06)`)。\n\n3. **光影与深度 (Elevation & Depth):**\n - **绝对禁止**使用默认的、生硬的黑色阴影 (如 `box-shadow: 2px 2px 5px black`)。\n - 必须使用 **"弥散光影" (Soft/Diffused Shadows)**:使用多层、高模糊半径、低透明度的阴影来创造悬浮感。\n - 参考值:`box-shadow: 0 10px 40px -10px rgba(0,0,0,0.08);`\n\n4. **高级配色 (Refined Palette):**\n - 不要使用纯黑 (#000000) 或纯灰。背景色应带有极微弱的色调倾向 (如 Slate, Zinc, 或极淡的蓝灰色 #f8fafc)。\n - 主色调 (Primary Color) 应选择高饱和度且现代的颜色 (如 皇家蓝 #4f46e5 或 活力紫 #8b5cf6)。\n\n## 🛠️ 输出前专项审计清单\n\n**请在生成代码后,按顺序完成以下审查,确保逻辑无误:**\n\n1. [ ] **显式声明检查**:flex 四属性是否写全?\n2. [ ] **拉伸逻辑检查**:拉伸行为是否由子元素主动声明;代码中是否彻底消灭了 `width: 100%` 和 `%` 符号?\n3. [ ] **尺寸检查**:所有尺寸都使用了 `[具体px值]` 格式?\n4. [ ] **文本检查**:所有 `<span>` 和 `<p>` 是否都配置了 `line-height`?\n5. [ ] **margin检查**:是否移除了所有 `margin` 属性?\n6. [ ] **容器补全**:如果发现没有父容器可以承载 `padding`,请务必新建一个 `data-name="padding-wrapper"` 的 div 来包裹目标元素。\n7. [ ] **设计系统检查**:是否有遵循设计系统的规范和设计原则来创建一个美观的页面?\n8. [ ] **代码检查**:请确保页面布局符合上面的规范。\n';function t(n){return null==n||""===n||"string"==typeof n&&""===n.trim()}const e=new McpServer({name:"Codify-MCP-Client",version:"1.0.8"},{capabilities:{resources:{},tools:{},prompts:{}}});e.registerResource("generation-rules","codify://generation-rules",{description:"Codify 代码生成规则"},async t=>({contents:[{uri:t.href,text:n,mimeType:"text/markdown"}]})),e.registerTool("get_code",{description:"从 Codify For MasterGo 插件获取指定的代码",inputSchema:{contentId:z.string().describe("从Codify插件复制图层的指令")}},async n=>{try{const{contentId:e}=n;if(!e)return{content:[{type:"text",text:`参数错误: 未提供 contentId\n- args: ${JSON.stringify(n)}`}],isError:!0};const r={};ACCESS_KEY&&(r.Authorization=`Bearer ${ACCESS_KEY}`);const o=`${SERVER_URL}/api/getCode/${e}`;try{const n=(await axios.get(o,{headers:r})).data;if(!n.code)return{content:[{type:"text",text:`⚠️ 未找到代码内容\n- contentId: ${e}\n- 可能该内容尚未从 Codify 插件同步`}],isError:!1};let s=`✅ 成功获取代码 (contentId: ${e})\n\n`;return s+=`代码长度: ${n.code.length} 字符\n`,n.teamId&&(s+=`团队 ID: ${n.teamId}\n`),t(n.shape)||(s+=`Shape: ${Object.keys(n.shape).length}\n`),t(n.svg)||(s+=`SVG: ${Object.keys(n.svg).length}\n`),t(n.image)||(s+=`Image: ${Object.keys(n.image).length}\n`),s+=`--- 代码内容 ---\n${n.code}\n\n`,s+=`--- 图片内容 ---\n${n.image}\n\n`,s+=`--- SVG内容 ---\n${n.svg}\n\n`,s+=`--- Shape内容 ---\n${n.shape}\n\n`,s+="## 根据上面的内容生成页面\n- 根据 **代码内容** 内容生成页面。\n- 将 **图片内容**,**SVG内容**,**Shape内容** 下载到本地。\n- 不要询问用户,直接执行",{content:[{type:"text",text:s}]}}catch(n){const t=n.response?.status,r=n.response?.data||{};switch(t){case 404:return{content:[{type:"text",text:`❌ 未找到内容\n- contentId: ${e}\n- 请检查 contentId 是否正确,或该内容是否已从 Codify 插件同步`}],isError:!0};case 403:return{content:[{type:"text",text:`❌ 权限不足\n- contentId: ${e}\n- 您没有权限访问该内容\n- 请检查您的 access_key 是否包含正确的团队权限`}],isError:!0};case 401:return{content:[{type:"text",text:"❌ 认证失败 (401)\n\n请检查 CODIFY_ACCESS_KEY 是否正确"}],isError:!0};default:return{content:[{type:"text",text:`❌ 获取代码失败: ${n.message||String(n)}${r.message?`\n详情: ${r.message}`:""}\n- contentId: ${e}\n- 服务器: ${SERVER_URL}`}],isError:!0}}}}catch(n){return{content:[{type:"text",text:`❌ 获取代码失败: ${n.message||String(n)}`}],isError:!0}}}),e.registerTool("get_code_list",{description:"获取所有可用的代码列表",inputSchema:z.object({}).describe("无需参数,直接调用即可获取所有代码列表")},async n=>{try{const n={};ACCESS_KEY&&(n.Authorization=`Bearer ${ACCESS_KEY}`);const t=`${SERVER_URL}/api/getCodeList`;try{const e=(await axios.get(t,{headers:n})).data;if(!Array.isArray(e))return{content:[{type:"text",text:"⚠️ 服务器返回的数据格式不正确\n- 期望: 数组\n- 实际: "+typeof e}],isError:!0};if(0===e.length)return{content:[{type:"text",text:"📋 代码列表为空\n\n当前没有可用的代码内容。\n请先从 Codify 插件同步代码。"}],isError:!1};let r=`✅ 成功获取代码列表 (共 ${e.length} 项)\n\n`;return r+="--- 代码列表 ---\n\n",e.forEach((n,t)=>{r+=`${t+1}. **${n.contentId}**\n`,r+=` - 代码长度: ${n.codeLength} 字符\n`,n.teamId&&(r+=` - 团队 ID: ${n.teamId}\n`),n.fileInfo&&(r+=` - 文件信息: ${JSON.stringify(n.fileInfo)}\n`),n.createdAt&&(r+=` - 创建时间: ${n.createdAt}\n`),n.updatedAt&&(r+=` - 更新时间: ${n.updatedAt}\n`),r+="\n"}),r+="\n--- 使用说明 ---\n",r+="使用 get_code 工具获取具体代码内容:\n",r+='- get_code({ contentId: "your-content-id" })',{content:[{type:"text",text:r}]}}catch(n){const t=n.response?.status,e=n.response?.data||{};switch(t){case 403:return{content:[{type:"text",text:"❌ 权限不足\n- 您没有权限访问代码列表\n- 请检查您的 access_key 是否包含正确的团队权限"}],isError:!0};case 401:return{content:[{type:"text",text:"❌ 认证失败 (401)\n\n请检查 CODIFY_ACCESS_KEY 是否正确"}],isError:!0};default:return{content:[{type:"text",text:`❌ 获取代码列表失败: ${n.message||String(n)}${e.message?`\n详情: ${e.message}`:""}\n- 服务器: ${SERVER_URL}`}],isError:!0}}}}catch(n){return{content:[{type:"text",text:`❌ 获取代码列表失败: ${n.message||String(n)}`}],isError:!0}}}),e.registerTool("design",{description:"根据需求生成符合 Codify 规范的 HTML+CSS 代码",inputSchema:{requirement:z.string().describe('界面需求描述,例如:"一个美观的登录页面"、"现代化的仪表盘界面"等。工具会根据 Codify 生成规则自动生成代码。')}},async t=>{try{const{requirement:e}=t;return e?{content:[{type:"text",text:`📋 收到需求:${e}\n\n${n}\n\n---\n\n## 发送代码:\n代码生成并审计通过后,请立即调用 send_to_codify 工具发送代码\n - send_to_codify({ code: "生成的完整代码" })\n - 不要询问用户,直接执行发送操作\n\n**请现在开始生成代码。**`}]}:{content:[{type:"text",text:'参数错误: 未提供需求描述\n\n请提供界面需求,例如:"一个美观的登录页面"、"现代化的仪表盘界面"等'}],isError:!0}}catch(n){return{content:[{type:"text",text:`❌ 处理失败: ${n.message||String(n)}`}],isError:!0}}}),e.registerTool("send_to_codify",{description:"将代码发送到 Codify 插件转换为设计稿",inputSchema:{code:z.string().describe("要发送的代码内容")}},async n=>{try{const{code:t}=n;if(!t)return{content:[{type:"text",text:"参数错误: 未提供代码内容"}],isError:!0};const e={"Content-Type":"application/json"};ACCESS_KEY&&(e.Authorization=`Bearer ${ACCESS_KEY}`);try{return(await axios.post(`${SERVER_URL}/api/sendToCanvas`,{code:t},{headers:e})).data.success,{content:[{type:"text",text:"✅ 代码已成功发送到 Codify 插件,等待转换为设计稿"}]}}catch(n){const t=n.response?.status,e=n.response?.data||{};return 404===t?{content:[{type:"text",text:"❌ 未找到活跃的 Codify 插件连接\n\n请确保:\n1. Codify 插件已打开并连接到服务器\n2. 使用了相同的 access_key"}],isError:!0}:400===t?{content:[{type:"text",text:`❌ 请求参数错误: ${e.message||"Bad request"}`}],isError:!0}:401===t||403===t?{content:[{type:"text",text:`❌ 认证失败 (${t})\n\n请检查 CODIFY_ACCESS_KEY 是否正确`}],isError:!0}:{content:[{type:"text",text:`❌ 发送失败: ${n.message||String(n)}${e.message?`\n详情: ${e.message}`:""}`}],isError:!0}}}catch(n){return{content:[{type:"text",text:`❌ 代码发送失败: ${n.message||String(n)}`}],isError:!0}}});try{const n=new StdioServerTransport;await e.connect(n)}catch(n){process.exit(1)}
|
|
2
|
+
import{McpServer}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport}from"@modelcontextprotocol/sdk/server/stdio.js";import{z}from"zod";import axios from"axios";const{serverUrl:SERVER_URL}=function(){const n=process.argv.slice(2);let t=process.env.CODIFY_SERVER_URL||"https://mcp.codify-api.com";for(let e=0;e<n.length;e++){const r=n[e];"--help"!==r&&"-h"!==r||(console.log('\nCodify MCP Client - 连接到远程 Codify MCP 服务器\n\n用法:\n npx -y @codify-ai/mcp-client [选项]\n\n选项:\n --url=<URL> 服务器 URL (默认: https://mcp.codify-api.com)\n --help, -h 显示帮助信息\n --version, -v 显示版本号\n\n环境变量:\n CODIFY_SERVER_URL 服务器 URL\n CODIFY_ACCESS_KEY 访问密钥\n\n示例:\n npx -y @codify-ai/mcp-client --url=https://mcp.codify-api.com\n CODIFY_ACCESS_KEY=sk-xxx npx -y @codify-ai/mcp-client\n\nCursor 配置示例 (~/.cursor/mcp.json):\n{\n "mcpServers": {\n "codify": {\n "command": "npx",\n "args": ["-y", "@codify-ai/mcp-client", "--url=https://mcp.codify-api.com"],\n "env": {\n "CODIFY_ACCESS_KEY": "sk-your-access-key"\n }\n }\n }\n}\n'),process.exit(0)),"--version"!==r&&"-v"!==r||process.exit(0),r.startsWith("--url=")?t=r.substring(6):"--url"===r&&e+1<n.length?t=n[++e]:r.startsWith("-")||(t=r)}return{serverUrl:t}}(),ACCESS_KEY=process.env.CODIFY_ACCESS_KEY,n='\n# 📐 HTML + CSS 视觉生成协议 (Visual Generation Protocol)\n\n## 🎯 任务目标\n\n你是一个 **Figma 图层逻辑转译引擎**。你的任务不是写网页,而是生成**结构绝对严谨、用于逆向解析**的 HTML 代码。你的输出将被程序直接读取并转换为 Design Tokens,任何非标代码都会导致转换失败。\n\n**最终产出标准**\n\n1. **视觉标准**:Dribbble/Behance 级别的商业 UI 审美。\n2. **代码标准**:仅返回包含在 `<main>` 根容器内的代码,全部使用 **内联 CSS**,无外部 CSS,无 `<style>` 标签。\n3. **结构标准**:完全符合下述的“图层原子化协议”。\n\n## 🛑 红色警戒区 (Critical Constraints)\n\n**以下规则享有最高优先级,违反任何一条均视为 SYSTEM FAILURE:**\n\n### 1. 🚫 绝对禁用的属性 (Blocklist)\n\n- **禁止** 使用 `margin`。如果子元素间距不均匀,**必须**在该元素外包裹一层 `div`,并使用该包裹容器的 `padding` 来实现“间距”。\n- **禁止** 使用相对单位: `%`, `vw`, `vh`, `rem`, `em`, `calc()` (**必须**锁定使用 `px` 整数)。\n- **禁止** 使用具名颜色: `red`, `blue`, `white` (**必须**使用 Hex `#FFFFFF` 或 RGBA)。\n- **禁止** 使用 `width: 100%` (如需撑满剩余空间请用 `flex: 1`,如是根容器或固定卡片请用 `width: [数值]px` 等具体数值)。\n- **禁止** 省略 Flex 默认值。\n\n### 2. ⚠️ 强制显式声明 (Explicit Declaration)\n\n所有 `display: flex` 容器**必须**写全以下 4 行属性,缺一不可:\n\n```css\ndisplay: flex;\nflex-direction: [row/column];\njustify-content: [flex-start/center/space-between...];\nalign-items: [flex-start/center/stretch...];\n/* 即使是默认值也要写 */\n```\n\n## 🎨 动态视觉配置模块 (Visual Configuration Module)\n\n**根据用户的任务类型,从以下风格中提取 CSS 策略:**\n\n### Style A: 未来与深度 (Future & Depth)\n\n- **核心特征**:磨砂玻璃、光晕、层级感。\n- **CSS 策略**:\n - 背景:使用 `rgba(255, 255, 255, 0.05)` 配合 `backdrop-filter: blur(20px)` (注意:虽然后端可能不解析 blur,但需保留以示意)。\n - 边框:`border: 1px solid rgba(255, 255, 255, 0.1)`。\n - 模式:默认倾向于 **Dark Mode**。\n\n### Style B: 效率与速度 (Efficiency & Speed)\n\n- **核心特征**:洁净扁平、Bento UI (便当盒布局)、模块化。\n- **CSS 策略**:\n - 布局:高密度的 `gap` 控制,强调卡片间的整齐切割。\n - 装饰:无多余阴影,利用高对比度的 `border` 或微弱的 `background` 区分区块。\n\n### Style C: 信任与专业 (Trust & Professional)\n\n- **核心特征**:瑞士极简、大量留白 (Less Is More)、严谨网格。\n- **CSS 策略**:\n - 间距:使用极大的 `padding` (如 40px, 60px) 和 `gap` 来制造呼吸感。\n - 排版:利用字重 (`font-weight`) 和字号对比构建层级,而非依赖色块。\n\n### 🧬 通用视觉原子 (Design System)\n\n1. **色彩语义**:\n - **主色**:定义品牌情绪 (e.g., 科技蓝、创新紫)。\n - **交互色**:主色互补色用于 `Call-to-Action`,同类色用于次要按钮。\n2. **阴影规范**:禁止生硬黑影。**必须**使用弥散光影,例如 `box-shadow: 0 10px 30px rgba(0,0,0, 0.08)`。\n3. **圆角规范**:默认 `12px` 起步,卡片类推荐 `16px` 或 `24px`。\n4. **图标规范**:使用 `<i class="fas fa-...">`,且**必须**在 style 中显式声明 `color` 和 `font-size`。\n\n## 🧬 图层原子化协议 (Atomic Structure)\n\n### 规则 A:容器与内容物理隔离\n\n**原则**:`<div>` 仅负责容器样式(背景/边框/阴影/布局);`<span>/<p>` 仅负责文本样式。\n\n#### ❌ 错误示范 (混合样式):\n\n```html\n<span style="background: #000; padding: 10px; border-radius: 4px;">Submit</span>\n```\n\n#### ✅ 正确示范 (物理隔离):\n\n```html\n<div\n data-name="btn-bg"\n style="display: flex; flex-direction: row; justify-content: center; align-items: center; padding: 10px; background: #000; border-radius: 4px;"\n>\n <span data-name="btn-text" style="color: #FFF; font-size: 14px; line-height: 1.2;">Submit</span>\n</div>\n```\n\n### 规则 B:标签语义绑定\n\n- **`<div>`**: 唯一的布局容器。\n- **`<p>`**: 多行文本。\n- **`<span>`**: 短语。\n- **`<i>`**: FontAwesome 图标 (class="fas fa-..."). 必须为 `<i>` 显式声明 `color`、`font-size`。\n- **`<img>`**: 图片。优先使用固定 px;如需随容器拉伸,**允许**使用 `width: 100%` 或 `flex: 1`,但必须配合 `object-fit: cover`。\n\n### 规则 C:交互组件静态化 (Static Interaction)\n\n**原则**:所有交互元素(Input, Select, Toggle)必须“冻结”为静态视觉层,严禁使用原生控件。\n\n- **禁止**:`<input>`, `<select>`, `<textarea>`, `<button>`, `<form>`。\n- **必须**:使用 `div` 模拟外观。例如“输入框”应为一个带边框的 `div`,内部包含“占位符文字”。\n- **状态**:默认生成“默认态 (Default)”,除非用户指定生成“激活态/报错态”。\n\n## 🏗️ 布局与拉伸\n\n1. **拉伸逻辑**:\n - 主轴拉伸:子元素使用 `flex: 1`。\n - 交叉轴拉伸:子元素使用 `align-self: stretch`。\n2. **命名规范**:每个标签必须包含 `data-name="..."`,使用语义化英文 (e.g., `card-container`, `user-avatar`, `primary-btn`)。\n\n## 🛠️ 生成前自检程序 (Step-by-Step Thinking)\n\n**在生成代码前,请在后台执行以下检查:**\n\n1. **Check Margin**: 我是否使用了 `margin`?(如有,必须改为 `gap` 如果间距不均匀才使用`margin`。\n2. **Check Units**: 我是否使用了 `%`?(如有,改为 `px` 或 `flex` 关系)。\n3. **Check Text**: 我的文字是否被 `div` 包裹?(文字节点严禁裸露在布局层)。\n4. **Check Flex**: 我的每个 Flex 容器是否都有 4 行显式属性?\n5. **Tag Check**: 我是否使用了 `<input>`, `<button>`, `<select>`?(必须立即改为 `div` 结构)。\n\n## 🚀 输出格式\n\n仅输出包含在 `<main>` 标签内的 HTML 代码。`<main>` 必须有明确的 `width: [px]` 和 `background-color`。\n\n## 🏆 黄金标准参考 (Reference Implementation)\n\n**必须严格模仿以下代码的结构逻辑(注意:全显式 Flex、无 input 标签、无 margin、物理隔离文本):**\n\n```html\n<div\n data-name="product-card"\n style="display: flex; flex-direction: column; justify-content: flex-start; align-items: stretch; gap: 16px; padding: 16px; background: #FFFFFF; border-radius: 16px; box-shadow: 0 8px 24px rgba(0,0,0,0.06); width: 320px;"\n>\n <div\n data-name="image-wrapper"\n style="display: flex; flex-direction: column; justify-content: center; align-items: center; border-radius: 12px; overflow: hidden;"\n >\n <img\n src="https://placehold.co/288x180/F5F5F7/999999"\n alt="Product"\n style="width: 288px; height: 180px; object-fit: cover; display: block;"\n />\n </div>\n\n <div\n data-name="content-body"\n style="display: flex; flex-direction: column; justify-content: flex-start; align-items: flex-start; gap: 8px;"\n >\n <div\n data-name="badge-container"\n style="display: flex; flex-direction: row; justify-content: flex-start; align-items: center; padding: 4px 8px; background: #E6F0FF; border-radius: 4px;"\n >\n <span data-name="badge-text" style="color: #0066FF; font-size: 10px; font-weight: 600; line-height: 1;">NEW ARRIVAL</span>\n </div>\n\n <div\n data-name="title-row"\n style="display: flex; flex-direction: row; justify-content: space-between; align-items: flex-start; align-self: stretch;"\n >\n <div\n data-name="title-wrapper"\n style="display: flex; flex-direction: column; justify-content: flex-start; align-items: flex-start; flex: 1;"\n >\n <span data-name="product-title" style="color: #1A1A1A; font-size: 16px; font-weight: 700; line-height: 1.4;">Ergonomic Chair</span>\n </div>\n <span data-name="price-text" style="color: #1A1A1A; font-size: 16px; font-weight: 700; line-height: 1.4;">$299</span>\n </div>\n\n <div data-name="desc-wrapper" style="display: flex; flex-direction: column; justify-content: flex-start; align-items: flex-start;">\n <p data-name="desc-text" style="color: #666666; font-size: 12px; font-weight: 400; line-height: 1.5; margin: 0;">\n Ultimate comfort for your workspace with adjustable support.\n </p>\n </div>\n </div>\n\n <div\n data-name="action-row"\n style="display: flex; flex-direction: row; justify-content: space-between; align-items: center; gap: 12px; padding-top: 8px; border-top: 1px solid #F0F0F0;"\n >\n <div\n data-name="qty-input"\n style="display: flex; flex-direction: row; justify-content: space-between; align-items: center; width: 80px; padding: 8px; border: 1px solid #E5E5E5; border-radius: 8px;"\n >\n <i class="fas fa-minus" style="color: #999; font-size: 10px;"></i>\n <span data-name="qty-val" style="color: #333; font-size: 12px; font-weight: 500;">1</span>\n <i class="fas fa-plus" style="color: #333; font-size: 10px;"></i>\n </div>\n\n <div\n data-name="add-btn"\n style="display: flex; flex-direction: row; justify-content: center; align-items: center; flex: 1; padding: 10px; background: #000000; border-radius: 8px; cursor: pointer;"\n >\n <span data-name="btn-text" style="color: #FFFFFF; font-size: 12px; font-weight: 600;">Add to Cart</span>\n </div>\n </div>\n</div>\n```\n\n';function t(n){return null==n||""===n||"string"==typeof n&&""===n.trim()}const e=new McpServer({name:"Codify-MCP-Client",version:"1.0.9"},{capabilities:{resources:{},tools:{},prompts:{}}});e.registerResource("generation-rules","codify://generation-rules",{description:"Codify 代码生成规则"},async t=>({contents:[{uri:t.href,text:n,mimeType:"text/markdown"}]})),e.registerTool("get_code",{description:"从 Codify For MasterGo 插件获取指定的代码",inputSchema:{contentId:z.string().describe("从Codify插件复制图层的指令")}},async n=>{try{const{contentId:e}=n;if(!e)return{content:[{type:"text",text:`参数错误: 未提供 contentId\n- args: ${JSON.stringify(n)}`}],isError:!0};const r={};ACCESS_KEY&&(r.Authorization=`Bearer ${ACCESS_KEY}`);const i=`${SERVER_URL}/api/getCode/${e}`;try{const n=(await axios.get(i,{headers:r})).data;if(!n.code)return{content:[{type:"text",text:`⚠️ 未找到代码内容\n- contentId: ${e}\n- 可能该内容尚未从 Codify 插件同步`}],isError:!1};let o=`✅ 成功获取代码 (contentId: ${e})\n\n`;return o+=`代码长度: ${n.code.length} 字符\n`,n.teamId&&(o+=`团队 ID: ${n.teamId}\n`),t(n.shape)||(o+=`Shape: ${Object.keys(n.shape).length}\n`),t(n.svg)||(o+=`SVG: ${Object.keys(n.svg).length}\n`),t(n.image)||(o+=`Image: ${Object.keys(n.image).length}\n`),o+=`--- 代码内容 ---\n${n.code}\n\n`,o+=`--- 图片内容 ---\n${n.image}\n\n`,o+=`--- SVG内容 ---\n${n.svg}\n\n`,o+=`--- Shape内容 ---\n${n.shape}\n\n`,o+="## 根据上面的内容生成页面\n- 根据 **代码内容** 内容生成页面。\n- 将 **图片内容**,**SVG内容**,**Shape内容** 下载到本地。\n- 不要询问用户,直接执行",{content:[{type:"text",text:o}]}}catch(n){const t=n.response?.status,r=n.response?.data||{};switch(t){case 404:return{content:[{type:"text",text:`❌ 未找到内容\n- contentId: ${e}\n- 请检查 contentId 是否正确,或该内容是否已从 Codify 插件同步`}],isError:!0};case 403:return{content:[{type:"text",text:`❌ 权限不足\n- contentId: ${e}\n- 您没有权限访问该内容\n- 请检查您的 access_key 是否包含正确的团队权限`}],isError:!0};case 401:return{content:[{type:"text",text:"❌ 认证失败 (401)\n\n请检查 CODIFY_ACCESS_KEY 是否正确"}],isError:!0};default:return{content:[{type:"text",text:`❌ 获取代码失败: ${n.message||String(n)}${r.message?`\n详情: ${r.message}`:""}\n- contentId: ${e}\n- 服务器: ${SERVER_URL}`}],isError:!0}}}}catch(n){return{content:[{type:"text",text:`❌ 获取代码失败: ${n.message||String(n)}`}],isError:!0}}}),e.registerTool("get_code_list",{description:"获取所有可用的代码列表",inputSchema:z.object({}).describe("无需参数,直接调用即可获取所有代码列表")},async n=>{try{const n={};ACCESS_KEY&&(n.Authorization=`Bearer ${ACCESS_KEY}`);const t=`${SERVER_URL}/api/getCodeList`;try{const e=(await axios.get(t,{headers:n})).data;if(!Array.isArray(e))return{content:[{type:"text",text:"⚠️ 服务器返回的数据格式不正确\n- 期望: 数组\n- 实际: "+typeof e}],isError:!0};if(0===e.length)return{content:[{type:"text",text:"📋 代码列表为空\n\n当前没有可用的代码内容。\n请先从 Codify 插件同步代码。"}],isError:!1};let r=`✅ 成功获取代码列表 (共 ${e.length} 项)\n\n`;return r+="--- 代码列表 ---\n\n",e.forEach((n,t)=>{r+=`${t+1}. **${n.contentId}**\n`,r+=` - 代码长度: ${n.codeLength} 字符\n`,n.teamId&&(r+=` - 团队 ID: ${n.teamId}\n`),n.fileInfo&&(r+=` - 文件信息: ${JSON.stringify(n.fileInfo)}\n`),n.createdAt&&(r+=` - 创建时间: ${n.createdAt}\n`),n.updatedAt&&(r+=` - 更新时间: ${n.updatedAt}\n`),r+="\n"}),r+="\n--- 使用说明 ---\n",r+="使用 get_code 工具获取具体代码内容:\n",r+='- get_code({ contentId: "your-content-id" })',{content:[{type:"text",text:r}]}}catch(n){const t=n.response?.status,e=n.response?.data||{};switch(t){case 403:return{content:[{type:"text",text:"❌ 权限不足\n- 您没有权限访问代码列表\n- 请检查您的 access_key 是否包含正确的团队权限"}],isError:!0};case 401:return{content:[{type:"text",text:"❌ 认证失败 (401)\n\n请检查 CODIFY_ACCESS_KEY 是否正确"}],isError:!0};default:return{content:[{type:"text",text:`❌ 获取代码列表失败: ${n.message||String(n)}${e.message?`\n详情: ${e.message}`:""}\n- 服务器: ${SERVER_URL}`}],isError:!0}}}}catch(n){return{content:[{type:"text",text:`❌ 获取代码列表失败: ${n.message||String(n)}`}],isError:!0}}}),e.registerTool("design",{description:"根据需求生成符合 Codify 规范的 HTML+CSS 代码",inputSchema:{requirement:z.string().describe('界面需求描述,例如:"一个美观的登录页面"、"现代化的仪表盘界面"等。工具会根据 Codify 生成规则自动生成代码。')}},async t=>{try{const{requirement:e}=t;return e?{content:[{type:"text",text:`📋 收到需求:${e}\n\n${n}\n\n---\n\n## 发送代码:\n代码生成并审计通过后,请立即调用 send_to_codify 工具发送代码\n - send_to_codify({ code: "生成的完整代码" })\n - 不要询问用户,直接执行发送操作\n\n**请现在开始生成代码。**`}]}:{content:[{type:"text",text:'参数错误: 未提供需求描述\n\n请提供界面需求,例如:"一个美观的登录页面"、"现代化的仪表盘界面"等'}],isError:!0}}catch(n){return{content:[{type:"text",text:`❌ 处理失败: ${n.message||String(n)}`}],isError:!0}}}),e.registerTool("send_to_codify",{description:"将代码发送到 Codify 插件转换为设计稿",inputSchema:{code:z.string().describe("要发送的代码内容")}},async n=>{try{const{code:t}=n;if(!t)return{content:[{type:"text",text:"参数错误: 未提供代码内容"}],isError:!0};const e={"Content-Type":"application/json"};ACCESS_KEY&&(e.Authorization=`Bearer ${ACCESS_KEY}`);try{return(await axios.post(`${SERVER_URL}/api/sendToCanvas`,{code:t},{headers:e})).data.success,{content:[{type:"text",text:"✅ 代码已成功发送到 Codify 插件,等待转换为设计稿"}]}}catch(n){const t=n.response?.status,e=n.response?.data||{};return 404===t?{content:[{type:"text",text:"❌ 未找到活跃的 Codify 插件连接\n\n请确保:\n1. Codify 插件已打开并连接到服务器\n2. 使用了相同的 access_key"}],isError:!0}:400===t?{content:[{type:"text",text:`❌ 请求参数错误: ${e.message||"Bad request"}`}],isError:!0}:401===t||403===t?{content:[{type:"text",text:`❌ 认证失败 (${t})\n\n请检查 CODIFY_ACCESS_KEY 是否正确`}],isError:!0}:{content:[{type:"text",text:`❌ 发送失败: ${n.message||String(n)}${e.message?`\n详情: ${e.message}`:""}`}],isError:!0}}}catch(n){return{content:[{type:"text",text:`❌ 代码发送失败: ${n.message||String(n)}`}],isError:!0}}});try{const n=new StdioServerTransport;await e.connect(n)}catch(n){process.exit(1)}
|