@codify-ai/mcp-client 1.0.9 → 1.0.11
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 +224 -105
- package/mcp-client.min.js +1 -1
- package/package.json +1 -1
package/mcp-client.js
CHANGED
|
@@ -9,7 +9,7 @@ import axios from 'axios'
|
|
|
9
9
|
|
|
10
10
|
function parseArgs() {
|
|
11
11
|
const args = process.argv.slice(2)
|
|
12
|
-
let serverUrl = process.env.CODIFY_SERVER_URL || '
|
|
12
|
+
let serverUrl = process.env.CODIFY_SERVER_URL || 'http://localhost:8080'
|
|
13
13
|
for (let i = 0; i < args.length; i++) {
|
|
14
14
|
const arg = args[i]
|
|
15
15
|
if (arg === '--help' || arg === '-h') {
|
|
@@ -75,8 +75,11 @@ const generationRules = `
|
|
|
75
75
|
**最终产出标准**
|
|
76
76
|
|
|
77
77
|
1. **视觉标准**:Dribbble/Behance 级别的商业 UI 审美。
|
|
78
|
-
2. **代码标准**:仅返回包含在 \`<main>\` 根容器内的代码,全部使用
|
|
79
|
-
3.
|
|
78
|
+
2. **代码标准**:仅返回包含在 \`<main>\` 根容器内的代码,全部使用 **Tailwind CSS Utility Classes**。
|
|
79
|
+
3. **语法标准**:为了保证 1:1 还原 Figma 数值,**必须**使用 Tailwind 的 **Arbitrary Values (任意值)** 语法 (e.g., \`w-[320px]\`, \`bg-[#F5F5F5]\`),严禁使用依赖 Theme 的默认类名 (如 \`w-1/2\`, \`bg-red-500\`)。
|
|
80
|
+
4. **结构标准**:完全符合下述的“图层原子化协议”。
|
|
81
|
+
5. **命名规范**:每个标签必须包含 \`data-name="..."\`,使用语义化英文 (e.g., \`card-container\`, \`user-avatar\`)。
|
|
82
|
+
6. **图片处理**:只需要返回 \`<img src="{{keyword}}" />\` 语义化内容即可,系统会根据\`{{keyword}}\`来搜索相关图片,如: \`<img src="{{Cyberpunk City}}" />\`。
|
|
80
83
|
|
|
81
84
|
## 🛑 红色警戒区 (Critical Constraints)
|
|
82
85
|
|
|
@@ -84,58 +87,46 @@ const generationRules = `
|
|
|
84
87
|
|
|
85
88
|
### 1. 🚫 绝对禁用的属性 (Blocklist)
|
|
86
89
|
|
|
87
|
-
-
|
|
90
|
+
- ❌ **严禁使用** margin (Zero Tolerance):
|
|
91
|
+
- 无论任何情况(包括不均匀间距),都**绝对禁止**出现 \`m-[...]\`, \`my-[...]\`, \`margin\` 等类名。
|
|
92
|
+
- **替代方案 A (均匀间距)**:在父容器 Flex 中使用 \`gap-[数值]px\`。
|
|
93
|
+
- **替代方案 B (内边距)**:如果是元素离边框的距离,使用父容器的 \`p-[数值]px\`。
|
|
94
|
+
- **替代方案 C (推挤布局)**:使用 \`justify-between\` 将首尾元素撑开。
|
|
95
|
+
- **替代方案 D (不均匀间距)**:必须通过“嵌套容器”解决。例如:若 A 与 B 间距 20px,B 与 C 间距 50px,则应将 A+B 包裹在一个 \`gap-[20px]\` 的 Div 中,再与 C 放在外层 \`gap-[50px]\` 的父容器中。
|
|
88
96
|
- **禁止** 使用相对单位: \`%\`, \`vw\`, \`vh\`, \`rem\`, \`em\`, \`calc()\` (**必须**锁定使用 \`px\` 整数)。
|
|
89
|
-
- **禁止** 使用具名颜色: \`red\`, \`blue\`, \`white\` (**必须**使用 Hex \`#FFFFFF\` 或 RGBA)。
|
|
90
|
-
- **禁止** 使用 \`width: 100%\` (如需撑满剩余空间请用 \`flex: 1\`,如是根容器或固定卡片请用 \`width: [数值]px\` 等具体数值)。
|
|
97
|
+
- **禁止** 使用具名颜色: \`bg-red-500\`, \`text-blue-600\`, \`bg-[white]\` (**必须**使用 Hex \`#FFFFFF\` 或 RGBA)。
|
|
91
98
|
- **禁止** 省略 Flex 默认值。
|
|
92
99
|
|
|
93
100
|
### 2. ⚠️ 强制显式声明 (Explicit Declaration)
|
|
94
101
|
|
|
95
|
-
所有
|
|
102
|
+
所有 Flex 容器**必须**写全以下 4 类属性,缺一不可:
|
|
96
103
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
align-items: [flex-start/center/stretch...];
|
|
102
|
-
/* 即使是默认值也要写 */
|
|
103
|
-
\`\`\`
|
|
104
|
-
|
|
105
|
-
## 🎨 动态视觉配置模块 (Visual Configuration Module)
|
|
106
|
-
|
|
107
|
-
**根据用户的任务类型,从以下风格中提取 CSS 策略:**
|
|
108
|
-
|
|
109
|
-
### Style A: 未来与深度 (Future & Depth)
|
|
104
|
+
1. \`flex\`
|
|
105
|
+
2. \`flex-row\` 或 \`flex-col\`
|
|
106
|
+
3. \`justify-start\` / \`justify-center\` / \`justify-between \`...
|
|
107
|
+
4. \`items-start\` / \`items-center\` / \`items-stretch\` ...
|
|
110
108
|
|
|
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**。
|
|
109
|
+
(示例: class="flex flex-col justify-start items-center ...")
|
|
116
110
|
|
|
117
|
-
|
|
111
|
+
## 🧠 核心设计哲学 (必须严格遵循)
|
|
118
112
|
|
|
119
|
-
|
|
120
|
-
- **CSS 策略**:
|
|
121
|
-
- 布局:高密度的 \`gap\` 控制,强调卡片间的整齐切割。
|
|
122
|
-
- 装饰:无多余阴影,利用高对比度的 \`border\` 或微弱的 \`background\` 区分区块。
|
|
113
|
+
**原则**:形式追随功能。所有的视觉决策(颜色、布局、材质)必须服务于用户的心理模型和业务目标。
|
|
123
114
|
|
|
124
|
-
###
|
|
115
|
+
### 1. 视觉风格与材质 (根据任务类型选择)
|
|
125
116
|
|
|
126
|
-
-
|
|
127
|
-
-
|
|
128
|
-
|
|
129
|
-
|
|
117
|
+
- **图标系统**:使用 FontAwesome \`fas\`, \`far\` 系列图标 (\`<i class="fas fa-...">\`),必须在 class 中使用 \`text-[size]\` \`text-[#color]\` 定义。
|
|
118
|
+
- **未来与深度**:使用 **磨砂玻璃特效** 配合 **暗色模式**。利用光晕和透明度制造层级感。
|
|
119
|
+
- **效率与速度**:使用 **洁净扁平风格** 配合 **便当盒网格布局 (Bento UI)**。强调清晰边界、模块化,减少装饰。
|
|
120
|
+
- **信任与专业**:使用 **瑞士极简风格**。**大量留白(Less Is More)**。依托排版和严格网格传达严谨性。
|
|
130
121
|
|
|
131
|
-
###
|
|
122
|
+
### 2. 色彩语义化系统
|
|
132
123
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
124
|
+
- **主色**:定义品牌情绪(如蓝色=逻辑,绿色=增长,紫色=创新)。
|
|
125
|
+
- **交互色**:
|
|
126
|
+
- **强引导**:使用主色的互补色(对比色),用于转化按钮。
|
|
127
|
+
- **柔引导**:使用主色的同类色,用于次要操作。
|
|
128
|
+
- **圆角**:根据风格选择,默认 \`rounded-[12px]\` 起步。
|
|
129
|
+
- **阴影**:必须使用弥散光影 如:\`shadow-[0_10px_30px_rgba(0,0,0,0.08)]\`,禁止生硬。
|
|
139
130
|
|
|
140
131
|
## 🧬 图层原子化协议 (Atomic Structure)
|
|
141
132
|
|
|
@@ -146,17 +137,14 @@ align-items: [flex-start/center/stretch...];
|
|
|
146
137
|
#### ❌ 错误示范 (混合样式):
|
|
147
138
|
|
|
148
139
|
\`\`\`html
|
|
149
|
-
<span
|
|
140
|
+
<span class="bg-[#000] p-[10px] rounded-[4px]">Submit</span>
|
|
150
141
|
\`\`\`
|
|
151
142
|
|
|
152
143
|
#### ✅ 正确示范 (物理隔离):
|
|
153
144
|
|
|
154
145
|
\`\`\`html
|
|
155
|
-
<div
|
|
156
|
-
data-name="btn-
|
|
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>
|
|
146
|
+
<div data-name="btn-bg" class="flex flex-row justify-center items-center p-[10px] bg-[#000000] rounded-[4px]">
|
|
147
|
+
<span data-name="btn-text" class="text-[#FFFFFF] text-[14px] leading-[1.2]">Submit</span>
|
|
160
148
|
</div>
|
|
161
149
|
\`\`\`
|
|
162
150
|
|
|
@@ -165,8 +153,8 @@ align-items: [flex-start/center/stretch...];
|
|
|
165
153
|
- **\`<div>\`**: 唯一的布局容器。
|
|
166
154
|
- **\`<p>\`**: 多行文本。
|
|
167
155
|
- **\`<span>\`**: 短语。
|
|
168
|
-
- **\`<i>\`**: FontAwesome
|
|
169
|
-
- **\`<img>\`**: 图片。优先使用固定 px
|
|
156
|
+
- **\`<i>\`**: FontAwesome 图标。
|
|
157
|
+
- **\`<img>\`**: 图片。优先使用固定 \`w-[px] h-[px]\`;如需随容器拉伸,**允许**使用 \`flex-1\` 或 \`w-full\`,但必须配合 \`object-cover\`。
|
|
170
158
|
|
|
171
159
|
### 规则 C:交互组件静态化 (Static Interaction)
|
|
172
160
|
|
|
@@ -176,26 +164,44 @@ align-items: [flex-start/center/stretch...];
|
|
|
176
164
|
- **必须**:使用 \`div\` 模拟外观。例如“输入框”应为一个带边框的 \`div\`,内部包含“占位符文字”。
|
|
177
165
|
- **状态**:默认生成“默认态 (Default)”,除非用户指定生成“激活态/报错态”。
|
|
178
166
|
|
|
179
|
-
## 🏗️
|
|
167
|
+
## 🏗️ 布局物理法则 (Layout Physics)
|
|
168
|
+
|
|
169
|
+
**为了适配 Figma 逆向解析,必须严格遵守以下“定宽法则”:**
|
|
180
170
|
|
|
181
171
|
1. **拉伸逻辑**:
|
|
182
|
-
- 主轴拉伸:子元素使用 \`flex
|
|
183
|
-
- 交叉轴拉伸:子元素使用 \`
|
|
184
|
-
|
|
172
|
+
- 主轴拉伸:子元素使用 \`flex-1\`。
|
|
173
|
+
- 交叉轴拉伸:子元素使用 \`self-stretch\`。
|
|
174
|
+
|
|
175
|
+
2. **禁止小数 Flex / 比例 Flex**:
|
|
176
|
+
- ❌ 错误:\`flex-[2.5]\` (试图按比例分配)
|
|
177
|
+
- ❌ 错误:\`flex-2\` vs \`flex-1\`
|
|
178
|
+
- ✅ 正确:\`w-[400px]\` (定宽侧边) vs \`flex-1\` (自适应主体)
|
|
179
|
+
|
|
180
|
+
3. **多栏布局强制模式**:
|
|
181
|
+
- 凡是涉及“左右结构”或“主次结构”,**次要容器必须写死 \`w-[px]\`**。
|
|
182
|
+
- **主要容器** 使用 \`flex-1\` 自动填满剩余空间。
|
|
183
|
+
\`\`\`HTML
|
|
184
|
+
<!-- 错误 -->
|
|
185
|
+
<div class="flex-[2.5]">Table</div>
|
|
186
|
+
<div class="flex-[1]">Activity Feed</div>
|
|
187
|
+
<!-- 正确 -->
|
|
188
|
+
<div class="flex-1">Table</div>
|
|
189
|
+
<div class="w-[360px]">Activity Feed</div>
|
|
190
|
+
\`\`\`
|
|
185
191
|
|
|
186
192
|
## 🛠️ 生成前自检程序 (Step-by-Step Thinking)
|
|
187
193
|
|
|
188
194
|
**在生成代码前,请在后台执行以下检查:**
|
|
189
195
|
|
|
190
|
-
1. **Check Margin**:
|
|
191
|
-
2. **Check Units**: 我是否使用了
|
|
196
|
+
1. **Check Margin**: 我是否移除了 \`m-\`类?。
|
|
197
|
+
2. **Check Units**: 我是否使用了 \`[...]\` 任意值语法 并且没有使用 \`vw\`、\`vh\`、\`rem\`、\`em\` 和 \`%\`?。
|
|
192
198
|
3. **Check Text**: 我的文字是否被 \`div\` 包裹?(文字节点严禁裸露在布局层)。
|
|
193
|
-
4. **Check Flex**: 我的每个 Flex 容器是否都有 4
|
|
199
|
+
4. **Check Flex**: 我的每个 Flex 容器是否都有 4 行显式属性(flex, dir, justify, align)?
|
|
194
200
|
5. **Tag Check**: 我是否使用了 \`<input>\`, \`<button>\`, \`<select>\`?(必须立即改为 \`div\` 结构)。
|
|
195
201
|
|
|
196
202
|
## 🚀 输出格式
|
|
197
203
|
|
|
198
|
-
仅输出包含在 \`<main>\` 标签内的 HTML 代码。\`<main>\` 必须有明确的 \`
|
|
204
|
+
仅输出包含在 \`<main>\` 标签内的 HTML 代码。\`<main>\` 必须有明确的 \`w-[px]\` 和 \`bg-[#color]\`。
|
|
199
205
|
|
|
200
206
|
## 🏆 黄金标准参考 (Reference Implementation)
|
|
201
207
|
|
|
@@ -204,73 +210,47 @@ align-items: [flex-start/center/stretch...];
|
|
|
204
210
|
\`\`\`html
|
|
205
211
|
<div
|
|
206
212
|
data-name="product-card"
|
|
207
|
-
|
|
213
|
+
class="flex w-[320px] flex-col items-stretch justify-start gap-[16px] rounded-[16px] bg-[#FFFFFF] p-[16px] shadow-[0_8px_24px_rgba(0,0,0,0.06)]"
|
|
208
214
|
>
|
|
209
|
-
<div
|
|
210
|
-
|
|
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
|
-
/>
|
|
215
|
+
<div data-name="image-wrapper" class="flex flex-col items-center justify-center overflow-hidden rounded-[12px]">
|
|
216
|
+
<img src="https://placehold.co/288x180/F5F5F7/999999" alt="Product" class="block h-[180px] w-[288px] object-cover" />
|
|
218
217
|
</div>
|
|
219
218
|
|
|
220
|
-
<div
|
|
221
|
-
data-name="
|
|
222
|
-
|
|
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>
|
|
219
|
+
<div data-name="content-body" class="flex flex-col items-start justify-start gap-[8px]">
|
|
220
|
+
<div data-name="badge-container" class="flex flex-row items-center justify-start rounded-[4px] bg-[#E6F0FF] px-[8px] py-[4px]">
|
|
221
|
+
<span data-name="badge-text" class="text-[10px] leading-[1] font-[600] text-[#0066FF]">NEW ARRIVAL</span>
|
|
229
222
|
</div>
|
|
230
223
|
|
|
231
|
-
<div
|
|
232
|
-
data-name="title-
|
|
233
|
-
|
|
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>
|
|
224
|
+
<div data-name="title-row" class="flex flex-row items-start justify-between self-stretch">
|
|
225
|
+
<div data-name="title-wrapper" class="flex flex-1 flex-col items-start justify-start">
|
|
226
|
+
<span data-name="product-title" class="text-[16px] leading-[1.4] font-[700] text-[#1A1A1A]">Ergonomic Chair</span>
|
|
240
227
|
</div>
|
|
241
|
-
<span data-name="price-text"
|
|
228
|
+
<span data-name="price-text" class="text-[16px] leading-[1.4] font-[700] text-[#1A1A1A]">$299</span>
|
|
242
229
|
</div>
|
|
243
230
|
|
|
244
|
-
<div data-name="desc-wrapper"
|
|
245
|
-
<p data-name="desc-text"
|
|
231
|
+
<div data-name="desc-wrapper" class="flex flex-col items-start justify-start">
|
|
232
|
+
<p data-name="desc-text" class="text-[12px] leading-[1.5] font-[400] text-[#666666]">
|
|
246
233
|
Ultimate comfort for your workspace with adjustable support.
|
|
247
234
|
</p>
|
|
248
235
|
</div>
|
|
249
236
|
</div>
|
|
250
237
|
|
|
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
|
-
>
|
|
238
|
+
<div data-name="action-row" class="flex flex-row items-center justify-between gap-[12px] border-t-[1px] border-[#F0F0F0] pt-[8px]">
|
|
255
239
|
<div
|
|
256
240
|
data-name="qty-input"
|
|
257
|
-
|
|
241
|
+
class="flex w-[80px] flex-row items-center justify-between rounded-[8px] border-[1px] border-[#E5E5E5] p-[8px]"
|
|
258
242
|
>
|
|
259
|
-
<i class="fas fa-minus
|
|
260
|
-
<span data-name="qty-val"
|
|
261
|
-
<i class="fas fa-plus
|
|
243
|
+
<i class="fas fa-minus text-[10px] text-[#999999]"></i>
|
|
244
|
+
<span data-name="qty-val" class="text-[12px] font-[500] text-[#333333]">1</span>
|
|
245
|
+
<i class="fas fa-plus text-[10px] text-[#333333]"></i>
|
|
262
246
|
</div>
|
|
263
247
|
|
|
264
|
-
<div
|
|
265
|
-
data-name="
|
|
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>
|
|
248
|
+
<div data-name="add-btn" class="flex flex-1 flex-row items-center justify-center rounded-[8px] bg-[#000000] p-[10px]">
|
|
249
|
+
<span data-name="btn-text" class="text-[12px] font-[600] text-[#FFFFFF]">Add to Cart</span>
|
|
269
250
|
</div>
|
|
270
251
|
</div>
|
|
271
252
|
</div>
|
|
272
253
|
\`\`\`
|
|
273
|
-
|
|
274
254
|
`
|
|
275
255
|
|
|
276
256
|
// 辅助函数:检查值是否为空
|
|
@@ -287,7 +267,7 @@ function isEmpty(value) {
|
|
|
287
267
|
const mcpServer = new McpServer(
|
|
288
268
|
{
|
|
289
269
|
name: 'Codify-MCP-Client',
|
|
290
|
-
version: '1.0.
|
|
270
|
+
version: '1.0.11'
|
|
291
271
|
},
|
|
292
272
|
{
|
|
293
273
|
capabilities: {
|
|
@@ -399,7 +379,7 @@ mcpServer.registerTool(
|
|
|
399
379
|
} catch (error) {
|
|
400
380
|
const status = error.response?.status
|
|
401
381
|
const errorData = error.response?.data || {}
|
|
402
|
-
|
|
382
|
+
|
|
403
383
|
switch (status) {
|
|
404
384
|
case 404:
|
|
405
385
|
return {
|
|
@@ -412,6 +392,20 @@ mcpServer.registerTool(
|
|
|
412
392
|
isError: true
|
|
413
393
|
}
|
|
414
394
|
case 403:
|
|
395
|
+
// 检查是否是配额不足错误
|
|
396
|
+
if (errorData.mcp_get_limit !== undefined || errorData.message?.includes('limit')) {
|
|
397
|
+
return {
|
|
398
|
+
content: [
|
|
399
|
+
{
|
|
400
|
+
type: 'text',
|
|
401
|
+
text: `❌ 配额不足\n- ${errorData.message || '您已达到获取代码的次数限制'}\n- 剩余配额: ${errorData.mcp_get_limit !== undefined ? errorData.mcp_get_limit : '未知'}\n- 请升级您的计划或联系支持团队`
|
|
402
|
+
}
|
|
403
|
+
],
|
|
404
|
+
isError: true
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// 普通权限错误
|
|
415
409
|
return {
|
|
416
410
|
content: [
|
|
417
411
|
{
|
|
@@ -589,9 +583,120 @@ mcpServer.registerTool(
|
|
|
589
583
|
}
|
|
590
584
|
}
|
|
591
585
|
)
|
|
586
|
+
// get_user_info
|
|
587
|
+
mcpServer.registerTool(
|
|
588
|
+
'get_user_info',
|
|
589
|
+
{
|
|
590
|
+
description: '获取当前登录用户的信息,包括配额、团队等',
|
|
591
|
+
inputSchema: z.object({}).describe('无需参数,直接调用即可获取当前用户信息')
|
|
592
|
+
},
|
|
593
|
+
async (args) => {
|
|
594
|
+
try {
|
|
595
|
+
// 添加认证头
|
|
596
|
+
const headers = {}
|
|
597
|
+
if (ACCESS_KEY) {
|
|
598
|
+
headers['Authorization'] = `Bearer ${ACCESS_KEY}`
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
// 通过 HTTP API 从远程服务器获取用户信息
|
|
602
|
+
const apiUrl = `${SERVER_URL}/api/getUserInfo`
|
|
603
|
+
try {
|
|
604
|
+
const response = await axios.get(apiUrl, { headers })
|
|
605
|
+
const data = response.data
|
|
606
|
+
|
|
607
|
+
// 构建返回信息
|
|
608
|
+
let resultText = `✅ 用户信息\n\n`
|
|
609
|
+
resultText += `👤 用户ID: ${data.userId}\n`
|
|
610
|
+
if (data.userName) {
|
|
611
|
+
resultText += `📧 用户名: ${data.userName}\n`
|
|
612
|
+
}
|
|
613
|
+
if (data.realname) {
|
|
614
|
+
resultText += `📝 真实姓名: ${data.realname}\n`
|
|
615
|
+
}
|
|
616
|
+
resultText += `\n📊 配额信息:\n`
|
|
617
|
+
if (data.quota) {
|
|
618
|
+
resultText += ` - 生成设计: ${data.quota.mcp_generate_count || 0} / ${data.quota.mcp_generate_limit || '无限制'}\n`
|
|
619
|
+
resultText += ` - 获取代码: ${data.quota.mcp_get_count || 0} / ${data.quota.mcp_get_limit || '无限制'}\n`
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
if (data.teams && data.teams.length > 0) {
|
|
623
|
+
resultText += `\n👥 团队信息 (共 ${data.teams.length} 个团队):\n`
|
|
624
|
+
data.teams.forEach((team, index) => {
|
|
625
|
+
resultText += ` ${index + 1}. ${team.name || team.teamId} (ID: ${team.teamId})\n`
|
|
626
|
+
if (team.auth) {
|
|
627
|
+
resultText += ` 权限: ${team.auth}\n`
|
|
628
|
+
}
|
|
629
|
+
})
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
if (data.currentTeamId) {
|
|
633
|
+
resultText += `\n🔖 当前团队ID: ${data.currentTeamId}`
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
return {
|
|
637
|
+
content: [
|
|
638
|
+
{
|
|
639
|
+
type: 'text',
|
|
640
|
+
text: resultText
|
|
641
|
+
}
|
|
642
|
+
]
|
|
643
|
+
}
|
|
644
|
+
} catch (error) {
|
|
645
|
+
const status = error.response?.status
|
|
646
|
+
const errorData = error.response?.data || {}
|
|
647
|
+
|
|
648
|
+
switch (status) {
|
|
649
|
+
case 401:
|
|
650
|
+
return {
|
|
651
|
+
content: [
|
|
652
|
+
{
|
|
653
|
+
type: 'text',
|
|
654
|
+
text: `❌ 认证失败 (401)\n\n请检查 CODIFY_ACCESS_KEY 是否正确`
|
|
655
|
+
}
|
|
656
|
+
],
|
|
657
|
+
isError: true
|
|
658
|
+
}
|
|
659
|
+
case 403:
|
|
660
|
+
return {
|
|
661
|
+
content: [
|
|
662
|
+
{
|
|
663
|
+
type: 'text',
|
|
664
|
+
text: `❌ 权限不足 (403)\n\n${errorData.message || '您没有权限访问该资源'}`
|
|
665
|
+
}
|
|
666
|
+
],
|
|
667
|
+
isError: true
|
|
668
|
+
}
|
|
669
|
+
default:
|
|
670
|
+
return {
|
|
671
|
+
content: [
|
|
672
|
+
{
|
|
673
|
+
type: 'text',
|
|
674
|
+
text: `❌ 获取用户信息失败: ${error.message || String(error)}${
|
|
675
|
+
errorData.message ? `\n详情: ${errorData.message}` : ''
|
|
676
|
+
}\n- 服务器: ${SERVER_URL}`
|
|
677
|
+
}
|
|
678
|
+
],
|
|
679
|
+
isError: true
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
} catch (error) {
|
|
684
|
+
return {
|
|
685
|
+
content: [
|
|
686
|
+
{
|
|
687
|
+
type: 'text',
|
|
688
|
+
text: `❌ 获取用户信息失败: ${error.message || String(error)}`
|
|
689
|
+
}
|
|
690
|
+
],
|
|
691
|
+
isError: true
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
)
|
|
592
696
|
// design
|
|
593
697
|
mcpServer.registerTool(
|
|
594
698
|
'design',
|
|
699
|
+
|
|
595
700
|
{
|
|
596
701
|
description: '根据需求生成符合 Codify 规范的 HTML+CSS 代码',
|
|
597
702
|
inputSchema: {
|
|
@@ -728,6 +833,20 @@ mcpServer.registerTool(
|
|
|
728
833
|
isError: true
|
|
729
834
|
}
|
|
730
835
|
} else if (status === 401 || status === 403) {
|
|
836
|
+
// 检查是否是配额不足错误
|
|
837
|
+
if (errorData.mcp_generate_limit !== undefined || errorData.message?.includes('limit')) {
|
|
838
|
+
return {
|
|
839
|
+
content: [
|
|
840
|
+
{
|
|
841
|
+
type: 'text',
|
|
842
|
+
text: `❌ 配额不足\n- ${errorData.message || '您已达到生成设计的次数限制'}\n- 剩余配额: ${errorData.mcp_generate_limit !== undefined ? errorData.mcp_generate_limit : '未知'}\n- 请升级您的计划或联系支持团队`
|
|
843
|
+
}
|
|
844
|
+
],
|
|
845
|
+
isError: true
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
// 普通认证错误
|
|
731
850
|
return {
|
|
732
851
|
content: [
|
|
733
852
|
{
|
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你是一个 **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)}
|
|
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 t=process.argv.slice(2);let e=process.env.CODIFY_SERVER_URL||"http://localhost:8080";for(let n=0;n<t.length;n++){const s=t[n];"--help"!==s&&"-h"!==s||(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"!==s&&"-v"!==s||process.exit(0),s.startsWith("--url=")?e=s.substring(6):"--url"===s&&n+1<t.length?e=t[++n]:s.startsWith("-")||(e=s)}return{serverUrl:e}}(),ACCESS_KEY=process.env.CODIFY_ACCESS_KEY,t='\n# 📐 HTML + CSS 视觉生成协议 (Visual Generation Protocol)\n\n## 🎯 任务目标\n\n你是一个 **Figma 图层逻辑转译引擎**。你的任务不是写网页,而是生成**结构绝对严谨、用于逆向解析**的 HTML 代码。你的输出将被程序直接读取并转换为 Design Tokens,任何非标代码都会导致转换失败。\n\n**最终产出标准**\n\n1. **视觉标准**:Dribbble/Behance 级别的商业 UI 审美。\n2. **代码标准**:仅返回包含在 `<main>` 根容器内的代码,全部使用 **Tailwind CSS Utility Classes**。\n3. **语法标准**:为了保证 1:1 还原 Figma 数值,**必须**使用 Tailwind 的 **Arbitrary Values (任意值)** 语法 (e.g., `w-[320px]`, `bg-[#F5F5F5]`),严禁使用依赖 Theme 的默认类名 (如 `w-1/2`, `bg-red-500`)。\n4. **结构标准**:完全符合下述的“图层原子化协议”。\n5. **命名规范**:每个标签必须包含 `data-name="..."`,使用语义化英文 (e.g., `card-container`, `user-avatar`)。\n6. **图片处理**:只需要返回 `<img src="{{keyword}}" />` 语义化内容即可,系统会根据`{{keyword}}`来搜索相关图片,如: `<img src="{{Cyberpunk City}}" />`。\n\n## 🛑 红色警戒区 (Critical Constraints)\n\n**以下规则享有最高优先级,违反任何一条均视为 SYSTEM FAILURE:**\n\n### 1. 🚫 绝对禁用的属性 (Blocklist)\n\n- ❌ **严禁使用** margin (Zero Tolerance):\n - 无论任何情况(包括不均匀间距),都**绝对禁止**出现 `m-[...]`, `my-[...]`, `margin` 等类名。\n - **替代方案 A (均匀间距)**:在父容器 Flex 中使用 `gap-[数值]px`。\n - **替代方案 B (内边距)**:如果是元素离边框的距离,使用父容器的 `p-[数值]px`。\n - **替代方案 C (推挤布局)**:使用 `justify-between` 将首尾元素撑开。\n - **替代方案 D (不均匀间距)**:必须通过“嵌套容器”解决。例如:若 A 与 B 间距 20px,B 与 C 间距 50px,则应将 A+B 包裹在一个 `gap-[20px]` 的 Div 中,再与 C 放在外层 `gap-[50px]` 的父容器中。\n- **禁止** 使用相对单位: `%`, `vw`, `vh`, `rem`, `em`, `calc()` (**必须**锁定使用 `px` 整数)。\n- **禁止** 使用具名颜色: `bg-red-500`, `text-blue-600`, `bg-[white]` (**必须**使用 Hex `#FFFFFF` 或 RGBA)。\n- **禁止** 省略 Flex 默认值。\n\n### 2. ⚠️ 强制显式声明 (Explicit Declaration)\n\n所有 Flex 容器**必须**写全以下 4 类属性,缺一不可:\n\n1. `flex`\n2. `flex-row` 或 `flex-col`\n3. `justify-start` / `justify-center` / `justify-between `...\n4. `items-start` / `items-center` / `items-stretch` ...\n\n(示例: class="flex flex-col justify-start items-center ...")\n\n## 🧠 核心设计哲学 (必须严格遵循)\n\n**原则**:形式追随功能。所有的视觉决策(颜色、布局、材质)必须服务于用户的心理模型和业务目标。\n\n### 1. 视觉风格与材质 (根据任务类型选择)\n\n- **图标系统**:使用 FontAwesome `fas`, `far` 系列图标 (`<i class="fas fa-...">`),必须在 class 中使用 `text-[size]` `text-[#color]` 定义。\n- **未来与深度**:使用 **磨砂玻璃特效** 配合 **暗色模式**。利用光晕和透明度制造层级感。\n- **效率与速度**:使用 **洁净扁平风格** 配合 **便当盒网格布局 (Bento UI)**。强调清晰边界、模块化,减少装饰。\n- **信任与专业**:使用 **瑞士极简风格**。**大量留白(Less Is More)**。依托排版和严格网格传达严谨性。\n\n### 2. 色彩语义化系统\n\n- **主色**:定义品牌情绪(如蓝色=逻辑,绿色=增长,紫色=创新)。\n- **交互色**:\n - **强引导**:使用主色的互补色(对比色),用于转化按钮。\n - **柔引导**:使用主色的同类色,用于次要操作。\n- **圆角**:根据风格选择,默认 `rounded-[12px]` 起步。\n- **阴影**:必须使用弥散光影 如:`shadow-[0_10px_30px_rgba(0,0,0,0.08)]`,禁止生硬。\n\n## 🧬 图层原子化协议 (Atomic Structure)\n\n### 规则 A:容器与内容物理隔离\n\n**原则**:`<div>` 仅负责容器样式(背景/边框/阴影/布局);`<span>/<p>` 仅负责文本样式。\n\n#### ❌ 错误示范 (混合样式):\n\n```html\n<span class="bg-[#000] p-[10px] rounded-[4px]">Submit</span>\n```\n\n#### ✅ 正确示范 (物理隔离):\n\n```html\n<div data-name="btn-bg" class="flex flex-row justify-center items-center p-[10px] bg-[#000000] rounded-[4px]">\n <span data-name="btn-text" class="text-[#FFFFFF] text-[14px] leading-[1.2]">Submit</span>\n</div>\n```\n\n### 规则 B:标签语义绑定\n\n- **`<div>`**: 唯一的布局容器。\n- **`<p>`**: 多行文本。\n- **`<span>`**: 短语。\n- **`<i>`**: FontAwesome 图标。\n- **`<img>`**: 图片。优先使用固定 `w-[px] h-[px]`;如需随容器拉伸,**允许**使用 `flex-1` 或 `w-full`,但必须配合 `object-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## 🏗️ 布局物理法则 (Layout Physics)\n\n**为了适配 Figma 逆向解析,必须严格遵守以下“定宽法则”:**\n\n1. **拉伸逻辑**:\n - 主轴拉伸:子元素使用 `flex-1`。\n - 交叉轴拉伸:子元素使用 `self-stretch`。\n\n2. **禁止小数 Flex / 比例 Flex**:\n - ❌ 错误:`flex-[2.5]` (试图按比例分配)\n - ❌ 错误:`flex-2` vs `flex-1`\n - ✅ 正确:`w-[400px]` (定宽侧边) vs `flex-1` (自适应主体)\n\n3. **多栏布局强制模式**:\n - 凡是涉及“左右结构”或“主次结构”,**次要容器必须写死 `w-[px]`**。\n - **主要容器** 使用 `flex-1` 自动填满剩余空间。\n ```HTML\n \x3c!-- 错误 --\x3e\n <div class="flex-[2.5]">Table</div>\n <div class="flex-[1]">Activity Feed</div>\n \x3c!-- 正确 --\x3e\n <div class="flex-1">Table</div>\n <div class="w-[360px]">Activity Feed</div>\n ```\n\n## 🛠️ 生成前自检程序 (Step-by-Step Thinking)\n\n**在生成代码前,请在后台执行以下检查:**\n\n1. **Check Margin**: 我是否移除了 `m-`类?。\n2. **Check Units**: 我是否使用了 `[...]` 任意值语法 并且没有使用 `vw`、`vh`、`rem`、`em` 和 `%`?。\n3. **Check Text**: 我的文字是否被 `div` 包裹?(文字节点严禁裸露在布局层)。\n4. **Check Flex**: 我的每个 Flex 容器是否都有 4 行显式属性(flex, dir, justify, align)?\n5. **Tag Check**: 我是否使用了 `<input>`, `<button>`, `<select>`?(必须立即改为 `div` 结构)。\n\n## 🚀 输出格式\n\n仅输出包含在 `<main>` 标签内的 HTML 代码。`<main>` 必须有明确的 `w-[px]` 和 `bg-[#color]`。\n\n## 🏆 黄金标准参考 (Reference Implementation)\n\n**必须严格模仿以下代码的结构逻辑(注意:全显式 Flex、无 input 标签、无 margin、物理隔离文本):**\n\n```html\n<div\n data-name="product-card"\n class="flex w-[320px] flex-col items-stretch justify-start gap-[16px] rounded-[16px] bg-[#FFFFFF] p-[16px] shadow-[0_8px_24px_rgba(0,0,0,0.06)]"\n>\n <div data-name="image-wrapper" class="flex flex-col items-center justify-center overflow-hidden rounded-[12px]">\n <img src="https://placehold.co/288x180/F5F5F7/999999" alt="Product" class="block h-[180px] w-[288px] object-cover" />\n </div>\n\n <div data-name="content-body" class="flex flex-col items-start justify-start gap-[8px]">\n <div data-name="badge-container" class="flex flex-row items-center justify-start rounded-[4px] bg-[#E6F0FF] px-[8px] py-[4px]">\n <span data-name="badge-text" class="text-[10px] leading-[1] font-[600] text-[#0066FF]">NEW ARRIVAL</span>\n </div>\n\n <div data-name="title-row" class="flex flex-row items-start justify-between self-stretch">\n <div data-name="title-wrapper" class="flex flex-1 flex-col items-start justify-start">\n <span data-name="product-title" class="text-[16px] leading-[1.4] font-[700] text-[#1A1A1A]">Ergonomic Chair</span>\n </div>\n <span data-name="price-text" class="text-[16px] leading-[1.4] font-[700] text-[#1A1A1A]">$299</span>\n </div>\n\n <div data-name="desc-wrapper" class="flex flex-col items-start justify-start">\n <p data-name="desc-text" class="text-[12px] leading-[1.5] font-[400] text-[#666666]">\n Ultimate comfort for your workspace with adjustable support.\n </p>\n </div>\n </div>\n\n <div data-name="action-row" class="flex flex-row items-center justify-between gap-[12px] border-t-[1px] border-[#F0F0F0] pt-[8px]">\n <div\n data-name="qty-input"\n class="flex w-[80px] flex-row items-center justify-between rounded-[8px] border-[1px] border-[#E5E5E5] p-[8px]"\n >\n <i class="fas fa-minus text-[10px] text-[#999999]"></i>\n <span data-name="qty-val" class="text-[12px] font-[500] text-[#333333]">1</span>\n <i class="fas fa-plus text-[10px] text-[#333333]"></i>\n </div>\n\n <div data-name="add-btn" class="flex flex-1 flex-row items-center justify-center rounded-[8px] bg-[#000000] p-[10px]">\n <span data-name="btn-text" class="text-[12px] font-[600] text-[#FFFFFF]">Add to Cart</span>\n </div>\n </div>\n</div>\n```\n';function e(t){return null==t||""===t||"string"==typeof t&&""===t.trim()}const n=new McpServer({name:"Codify-MCP-Client",version:"1.0.11"},{capabilities:{resources:{},tools:{},prompts:{}}});n.registerResource("generation-rules","codify://generation-rules",{description:"Codify 代码生成规则"},async e=>({contents:[{uri:e.href,text:t,mimeType:"text/markdown"}]})),n.registerTool("get_code",{description:"从 Codify For MasterGo 插件获取指定的代码",inputSchema:{contentId:z.string().describe("从Codify插件复制图层的指令")}},async t=>{try{const{contentId:n}=t;if(!n)return{content:[{type:"text",text:`参数错误: 未提供 contentId\n- args: ${JSON.stringify(t)}`}],isError:!0};const s={};ACCESS_KEY&&(s.Authorization=`Bearer ${ACCESS_KEY}`);const r=`${SERVER_URL}/api/getCode/${n}`;try{const t=(await axios.get(r,{headers:s})).data;if(!t.code)return{content:[{type:"text",text:`⚠️ 未找到代码内容\n- contentId: ${n}\n- 可能该内容尚未从 Codify 插件同步`}],isError:!1};let a=`✅ 成功获取代码 (contentId: ${n})\n\n`;return a+=`代码长度: ${t.code.length} 字符\n`,t.teamId&&(a+=`团队 ID: ${t.teamId}\n`),e(t.shape)||(a+=`Shape: ${Object.keys(t.shape).length}\n`),e(t.svg)||(a+=`SVG: ${Object.keys(t.svg).length}\n`),e(t.image)||(a+=`Image: ${Object.keys(t.image).length}\n`),a+=`--- 代码内容 ---\n${t.code}\n\n`,a+=`--- 图片内容 ---\n${t.image}\n\n`,a+=`--- SVG内容 ---\n${t.svg}\n\n`,a+=`--- Shape内容 ---\n${t.shape}\n\n`,a+="## 根据上面的内容生成页面\n- 根据 **代码内容** 内容生成页面。\n- 将 **图片内容**,**SVG内容**,**Shape内容** 下载到本地。\n- 不要询问用户,直接执行",{content:[{type:"text",text:a}]}}catch(t){const e=t.response?.status,s=t.response?.data||{};switch(e){case 404:return{content:[{type:"text",text:`❌ 未找到内容\n- contentId: ${n}\n- 请检查 contentId 是否正确,或该内容是否已从 Codify 插件同步`}],isError:!0};case 403:return void 0!==s.mcp_get_limit||s.message?.includes("limit")?{content:[{type:"text",text:`❌ 配额不足\n- ${s.message||"您已达到获取代码的次数限制"}\n- 剩余配额: ${void 0!==s.mcp_get_limit?s.mcp_get_limit:"未知"}\n- 请升级您的计划或联系支持团队`}],isError:!0}:{content:[{type:"text",text:`❌ 权限不足\n- contentId: ${n}\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:`❌ 获取代码失败: ${t.message||String(t)}${s.message?`\n详情: ${s.message}`:""}\n- contentId: ${n}\n- 服务器: ${SERVER_URL}`}],isError:!0}}}}catch(t){return{content:[{type:"text",text:`❌ 获取代码失败: ${t.message||String(t)}`}],isError:!0}}}),n.registerTool("get_code_list",{description:"获取所有可用的代码列表",inputSchema:z.object({}).describe("无需参数,直接调用即可获取所有代码列表")},async t=>{try{const t={};ACCESS_KEY&&(t.Authorization=`Bearer ${ACCESS_KEY}`);const e=`${SERVER_URL}/api/getCodeList`;try{const n=(await axios.get(e,{headers:t})).data;if(!Array.isArray(n))return{content:[{type:"text",text:"⚠️ 服务器返回的数据格式不正确\n- 期望: 数组\n- 实际: "+typeof n}],isError:!0};if(0===n.length)return{content:[{type:"text",text:"📋 代码列表为空\n\n当前没有可用的代码内容。\n请先从 Codify 插件同步代码。"}],isError:!1};let s=`✅ 成功获取代码列表 (共 ${n.length} 项)\n\n`;return s+="--- 代码列表 ---\n\n",n.forEach((t,e)=>{s+=`${e+1}. **${t.contentId}**\n`,s+=` - 代码长度: ${t.codeLength} 字符\n`,t.teamId&&(s+=` - 团队 ID: ${t.teamId}\n`),t.fileInfo&&(s+=` - 文件信息: ${JSON.stringify(t.fileInfo)}\n`),t.createdAt&&(s+=` - 创建时间: ${t.createdAt}\n`),t.updatedAt&&(s+=` - 更新时间: ${t.updatedAt}\n`),s+="\n"}),s+="\n--- 使用说明 ---\n",s+="使用 get_code 工具获取具体代码内容:\n",s+='- get_code({ contentId: "your-content-id" })',{content:[{type:"text",text:s}]}}catch(t){const e=t.response?.status,n=t.response?.data||{};switch(e){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:`❌ 获取代码列表失败: ${t.message||String(t)}${n.message?`\n详情: ${n.message}`:""}\n- 服务器: ${SERVER_URL}`}],isError:!0}}}}catch(t){return{content:[{type:"text",text:`❌ 获取代码列表失败: ${t.message||String(t)}`}],isError:!0}}}),n.registerTool("get_user_info",{description:"获取当前登录用户的信息,包括配额、团队等",inputSchema:z.object({}).describe("无需参数,直接调用即可获取当前用户信息")},async t=>{try{const t={};ACCESS_KEY&&(t.Authorization=`Bearer ${ACCESS_KEY}`);const e=`${SERVER_URL}/api/getUserInfo`;try{const n=(await axios.get(e,{headers:t})).data;let s="✅ 用户信息\n\n";return s+=`👤 用户ID: ${n.userId}\n`,n.userName&&(s+=`📧 用户名: ${n.userName}\n`),n.realname&&(s+=`📝 真实姓名: ${n.realname}\n`),s+="\n📊 配额信息:\n",n.quota&&(s+=` - 生成设计: ${n.quota.mcp_generate_count||0} / ${n.quota.mcp_generate_limit||"无限制"}\n`,s+=` - 获取代码: ${n.quota.mcp_get_count||0} / ${n.quota.mcp_get_limit||"无限制"}\n`),n.teams&&n.teams.length>0&&(s+=`\n👥 团队信息 (共 ${n.teams.length} 个团队):\n`,n.teams.forEach((t,e)=>{s+=` ${e+1}. ${t.name||t.teamId} (ID: ${t.teamId})\n`,t.auth&&(s+=` 权限: ${t.auth}\n`)})),n.currentTeamId&&(s+=`\n🔖 当前团队ID: ${n.currentTeamId}`),{content:[{type:"text",text:s}]}}catch(t){const e=t.response?.status,n=t.response?.data||{};switch(e){case 401:return{content:[{type:"text",text:"❌ 认证失败 (401)\n\n请检查 CODIFY_ACCESS_KEY 是否正确"}],isError:!0};case 403:return{content:[{type:"text",text:`❌ 权限不足 (403)\n\n${n.message||"您没有权限访问该资源"}`}],isError:!0};default:return{content:[{type:"text",text:`❌ 获取用户信息失败: ${t.message||String(t)}${n.message?`\n详情: ${n.message}`:""}\n- 服务器: ${SERVER_URL}`}],isError:!0}}}}catch(t){return{content:[{type:"text",text:`❌ 获取用户信息失败: ${t.message||String(t)}`}],isError:!0}}}),n.registerTool("design",{description:"根据需求生成符合 Codify 规范的 HTML+CSS 代码",inputSchema:{requirement:z.string().describe('界面需求描述,例如:"一个美观的登录页面"、"现代化的仪表盘界面"等。工具会根据 Codify 生成规则自动生成代码。')}},async e=>{try{const{requirement:n}=e;return n?{content:[{type:"text",text:`📋 收到需求:${n}\n\n${t}\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(t){return{content:[{type:"text",text:`❌ 处理失败: ${t.message||String(t)}`}],isError:!0}}}),n.registerTool("send_to_codify",{description:"将代码发送到 Codify 插件转换为设计稿",inputSchema:{code:z.string().describe("要发送的代码内容")}},async t=>{try{const{code:e}=t;if(!e)return{content:[{type:"text",text:"参数错误: 未提供代码内容"}],isError:!0};const n={"Content-Type":"application/json"};ACCESS_KEY&&(n.Authorization=`Bearer ${ACCESS_KEY}`);try{return(await axios.post(`${SERVER_URL}/api/sendToCanvas`,{code:e},{headers:n})).data.success,{content:[{type:"text",text:"✅ 代码已成功发送到 Codify 插件,等待转换为设计稿"}]}}catch(t){const e=t.response?.status,n=t.response?.data||{};return 404===e?{content:[{type:"text",text:"❌ 未找到活跃的 Codify 插件连接\n\n请确保:\n1. Codify 插件已打开并连接到服务器\n2. 使用了相同的 access_key"}],isError:!0}:400===e?{content:[{type:"text",text:`❌ 请求参数错误: ${n.message||"Bad request"}`}],isError:!0}:401===e||403===e?void 0!==n.mcp_generate_limit||n.message?.includes("limit")?{content:[{type:"text",text:`❌ 配额不足\n- ${n.message||"您已达到生成设计的次数限制"}\n- 剩余配额: ${void 0!==n.mcp_generate_limit?n.mcp_generate_limit:"未知"}\n- 请升级您的计划或联系支持团队`}],isError:!0}:{content:[{type:"text",text:`❌ 认证失败 (${e})\n\n请检查 CODIFY_ACCESS_KEY 是否正确`}],isError:!0}:{content:[{type:"text",text:`❌ 发送失败: ${t.message||String(t)}${n.message?`\n详情: ${n.message}`:""}`}],isError:!0}}}catch(t){return{content:[{type:"text",text:`❌ 代码发送失败: ${t.message||String(t)}`}],isError:!0}}});try{const t=new StdioServerTransport;await n.connect(t)}catch(t){process.exit(1)}
|