@zzp123/mcp-zentao 1.2.2 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -990,49 +990,100 @@ server.tool("uploadFile", {
990
990
  return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }] };
991
991
  });
992
992
  server.tool("uploadImageFromClipboard", {
993
- base64Data: z.string(),
994
993
  filename: z.string().optional(),
995
994
  uid: z.string().optional()
996
- }, async ({ base64Data, filename, uid }) => {
995
+ }, async ({ filename, uid }) => {
997
996
  if (!zentaoApi)
998
997
  throw new Error("Please initialize Zentao API first");
999
998
  const fs = await import('fs');
1000
999
  const path = await import('path');
1001
- // 创建 img 文件夹(如果不存在)
1002
- const imgDir = path.join(process.cwd(), 'img');
1003
- if (!fs.existsSync(imgDir)) {
1004
- fs.mkdirSync(imgDir, { recursive: true });
1005
- }
1006
- let fileBuffer;
1007
- let finalFilename;
1008
- // 处理 base64 数据
1009
- const matches = base64Data.match(/^data:image\/(\w+);base64,(.+)$/);
1010
- if (matches) {
1011
- const ext = matches[1];
1012
- const data = matches[2];
1013
- fileBuffer = Buffer.from(data, 'base64');
1014
- finalFilename = filename || `clipboard_${Date.now()}.${ext}`;
1000
+ const { execSync } = await import('child_process');
1001
+ try {
1002
+ // 读取系统剪贴板图片
1003
+ let base64Data;
1004
+ let fileBuffer;
1005
+ let finalFilename;
1006
+ // Windows: 使用 PowerShell 读取剪贴板
1007
+ if (process.platform === 'win32') {
1008
+ const psScript = `
1009
+ Add-Type -AssemblyName System.Windows.Forms
1010
+ $img = [System.Windows.Forms.Clipboard]::GetImage()
1011
+ if ($img -ne $null) {
1012
+ $ms = New-Object System.IO.MemoryStream
1013
+ $img.Save($ms, [System.Drawing.Imaging.ImageFormat]::Png)
1014
+ [Convert]::ToBase64String($ms.ToArray())
1015
+ } else {
1016
+ Write-Error "NoImage"
1017
+ }
1018
+ `;
1019
+ const result = execSync(`powershell -Command "${psScript.replace(/\n/g, ' ')}"`, {
1020
+ encoding: 'utf8',
1021
+ maxBuffer: 10 * 1024 * 1024,
1022
+ stdio: ['pipe', 'pipe', 'pipe']
1023
+ }).trim();
1024
+ if (!result || result.includes('NoImage') || result.includes('Error')) {
1025
+ throw new Error('剪贴板中没有图片。请先复制图片,不要粘贴到输入框,直接告诉我"上传"。');
1026
+ }
1027
+ base64Data = result;
1028
+ fileBuffer = Buffer.from(base64Data, 'base64');
1029
+ finalFilename = filename || `clipboard_${Date.now()}.png`;
1030
+ }
1031
+ // macOS: 使用 pngpaste
1032
+ else if (process.platform === 'darwin') {
1033
+ const tempFile = path.join(process.cwd(), '.temp_clipboard.png');
1034
+ try {
1035
+ execSync(`pngpaste "${tempFile}"`);
1036
+ fileBuffer = fs.readFileSync(tempFile);
1037
+ fs.unlinkSync(tempFile);
1038
+ finalFilename = filename || `clipboard_${Date.now()}.png`;
1039
+ }
1040
+ catch {
1041
+ throw new Error('剪贴板中没有图片。请先安装 pngpaste: brew install pngpaste');
1042
+ }
1043
+ }
1044
+ // Linux: 使用 xclip
1045
+ else {
1046
+ try {
1047
+ const buffer = execSync('xclip -selection clipboard -t image/png -o', {
1048
+ maxBuffer: 10 * 1024 * 1024
1049
+ });
1050
+ fileBuffer = buffer;
1051
+ finalFilename = filename || `clipboard_${Date.now()}.png`;
1052
+ }
1053
+ catch {
1054
+ throw new Error('剪贴板中没有图片。请先安装 xclip: sudo apt-get install xclip');
1055
+ }
1056
+ }
1057
+ // 创建 img 文件夹
1058
+ const imgDir = path.join(process.cwd(), 'img');
1059
+ if (!fs.existsSync(imgDir)) {
1060
+ fs.mkdirSync(imgDir, { recursive: true });
1061
+ }
1062
+ // 保存到 img 文件夹
1063
+ const savedPath = path.join(imgDir, finalFilename);
1064
+ fs.writeFileSync(savedPath, fileBuffer);
1065
+ // 上传到禅道
1066
+ const result = await zentaoApi.uploadFile({
1067
+ file: fileBuffer,
1068
+ filename: finalFilename,
1069
+ uid
1070
+ });
1071
+ const response = {
1072
+ upload: result,
1073
+ savedPath: savedPath,
1074
+ message: `图片已保存到本地并上传到禅道`
1075
+ };
1076
+ return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }] };
1015
1077
  }
1016
- else {
1017
- // 直接的 base64 数据,没有 data URL 前缀
1018
- fileBuffer = Buffer.from(base64Data, 'base64');
1019
- finalFilename = filename || `clipboard_${Date.now()}.png`;
1078
+ catch (error) {
1079
+ return {
1080
+ content: [{
1081
+ type: "text",
1082
+ text: `错误: ${error.message}\n\n提示:\n1. 确保已复制图片到剪贴板\n2. 不要粘贴到输入框,直接说"上传剪贴板图片"\n3. 或使用命令行: upload.bat`
1083
+ }],
1084
+ isError: true
1085
+ };
1020
1086
  }
1021
- // 保存到 img 文件夹
1022
- const savedPath = path.join(imgDir, finalFilename);
1023
- fs.writeFileSync(savedPath, fileBuffer);
1024
- // 上传到禅道
1025
- const result = await zentaoApi.uploadFile({
1026
- file: fileBuffer,
1027
- filename: finalFilename,
1028
- uid
1029
- });
1030
- const response = {
1031
- upload: result,
1032
- savedPath: savedPath,
1033
- message: `图片已保存到本地并上传到禅道`
1034
- };
1035
- return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }] };
1036
1087
  });
1037
1088
  server.tool("downloadFile", {
1038
1089
  fileId: z.number(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zzp123/mcp-zentao",
3
- "version": "1.2.2",
3
+ "version": "1.3.1",
4
4
  "description": "禅道项目管理系统的高级API集成包,提供任务管理、Bug跟踪等功能的完整封装,专为Cursor IDE设计的MCP扩展",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -11,7 +11,8 @@
11
11
  "files": [
12
12
  "dist",
13
13
  "README.md",
14
- "json-args.js"
14
+ "json-args.js",
15
+ "scripts"
15
16
  ],
16
17
  "scripts": {
17
18
  "build": "tsc",
@@ -21,7 +22,9 @@
21
22
  "test:watch": "jest --watch",
22
23
  "test:manual": "ts-node test/manual-test.ts",
23
24
  "prepublishOnly": "npm run test && npm run build",
24
- "start": "node json-args.js"
25
+ "start": "node json-args.js",
26
+ "upload:clipboard": "node scripts/upload-clipboard-image.js",
27
+ "upload:clipboard:py": "python scripts/upload-clipboard-image.py"
25
28
  },
26
29
  "keywords": [
27
30
  "mcp",
@@ -0,0 +1,298 @@
1
+ # MCP 集成说明
2
+
3
+ ## 🔗 与 MCP 工具联动
4
+
5
+ 本项目提供了两种方式来上传剪贴板图片到禅道:
6
+
7
+ ### 方式1: 通过 MCP 工具(在 Claude Code 中使用)
8
+
9
+ **适用场景:** 在 Claude Code 对话中直接上传图片
10
+
11
+ **已有的 MCP 工具:**
12
+
13
+ 1. **uploadImageFromClipboard** - 专门用于上传剪贴板图片
14
+ ```json
15
+ {
16
+ "base64Data": "data:image/png;base64,...",
17
+ "filename": "optional-name.png",
18
+ "uid": "optional-uid"
19
+ }
20
+ ```
21
+
22
+ 2. **uploadFile** - 通用文件上传(支持路径或 base64)
23
+ ```json
24
+ {
25
+ "filePath": "/path/to/file.png",
26
+ "base64Data": "data:image/png;base64,...",
27
+ "filename": "optional-name.png",
28
+ "uid": "optional-uid"
29
+ }
30
+ ```
31
+
32
+ **使用方法:**
33
+
34
+ 在 Claude Code 中:
35
+ ```
36
+ 你:复制一张图片后,对 Claude 说:
37
+ "上传这张图片到禅道"
38
+
39
+ Claude 会自动:
40
+ 1. 获取剪贴板中的图片 base64 数据
41
+ 2. 调用 uploadImageFromClipboard 工具
42
+ 3. 保存图片到 ./img/ 文件夹
43
+ 4. 上传到禅道
44
+ 5. 返回文件 ID 和 URL
45
+ ```
46
+
47
+ ---
48
+
49
+ ### 方式2: 通过命令行脚本(独立使用)
50
+
51
+ **适用场景:** 在终端/命令行中快速上传图片,不需要启动 Claude Code
52
+
53
+ **提供的脚本:**
54
+
55
+ 1. **Node.js 脚本** - `scripts/upload-clipboard-image.js`
56
+ 2. **PowerShell 脚本** - `scripts/upload-clipboard-image.ps1`
57
+ 3. **Python 脚本** - `scripts/upload-clipboard-image.py`
58
+
59
+ **使用方法:**
60
+
61
+ ```bash
62
+ # 方式1: 使用 npm scripts
63
+ npm run upload:clipboard
64
+
65
+ # 方式2: 直接运行脚本
66
+ node scripts/upload-clipboard-image.js
67
+ python scripts/upload-clipboard-image.py
68
+ .\scripts\upload-clipboard-image.ps1 -ZentaoUrl "..." -Username "..." -Password "..."
69
+ ```
70
+
71
+ ---
72
+
73
+ ## 🔄 两种方式的区别
74
+
75
+ | 对比项 | MCP 工具 | 命令行脚本 |
76
+ |--------|---------|-----------|
77
+ | **使用场景** | 在 Claude Code 对话中 | 独立在终端使用 |
78
+ | **启动方式** | 告诉 Claude 上传图片 | 运行命令 |
79
+ | **配置方式** | MCP 工具自动获取配置 | 需要设置环境变量 |
80
+ | **图片来源** | Claude 自动获取剪贴板 | 脚本读取剪贴板 |
81
+ | **适合人群** | 使用 Claude Code 的用户 | 喜欢命令行的开发者 |
82
+ | **优势** | 无缝集成,对话式操作 | 快速、独立、可自动化 |
83
+
84
+ ---
85
+
86
+ ## 💡 推荐使用场景
87
+
88
+ ### 场景1: 在编写需求/Bug时上传截图
89
+
90
+ **使用 MCP 工具:**
91
+ ```
92
+ 你:我要创建一个Bug
93
+ Claude:好的,请提供Bug的详细信息
94
+ 你:[复制截图] 上传这张截图
95
+ Claude:✓ 图片已上传,文件ID: 123, URL: /zentao/file-read-123.png
96
+ 你:创建Bug,标题是"登录页面样式错误",把刚才的图片加到描述里
97
+ Claude:✓ Bug已创建,ID: 456
98
+ ```
99
+
100
+ ### 场景2: 批量处理图片
101
+
102
+ **使用命令行脚本:**
103
+ ```bash
104
+ # 创建一个循环脚本
105
+ while true; do
106
+ echo "按任意键上传当前剪贴板图片..."
107
+ read
108
+ npm run upload:clipboard
109
+ done
110
+ ```
111
+
112
+ ### 场景3: 与其他工具集成
113
+
114
+ **使用命令行脚本:**
115
+ ```bash
116
+ # 截图后自动上传
117
+ # Mac Automator / Windows Task Scheduler
118
+
119
+ # 1. 截图工具保存图片
120
+ screencapture -c
121
+
122
+ # 2. 自动上传
123
+ npm run upload:clipboard
124
+
125
+ # 3. 将URL复制到剪贴板
126
+ # (脚本可修改为返回URL到剪贴板)
127
+ ```
128
+
129
+ ---
130
+
131
+ ## 🛠️ 高级集成
132
+
133
+ ### 1. 添加为 MCP 子命令
134
+
135
+ 可以创建一个 MCP 工具来调用命令行脚本:
136
+
137
+ ```typescript
138
+ // 在 src/index.ts 中添加
139
+ server.tool("uploadClipboardViaCLI", {
140
+ method: z.enum(['node', 'python', 'powershell']).optional()
141
+ }, async ({ method = 'node' }) => {
142
+ const { exec } = await import('child_process');
143
+ const { promisify } = await import('util');
144
+ const execAsync = promisify(exec);
145
+
146
+ const command = method === 'python'
147
+ ? 'python scripts/upload-clipboard-image.py'
148
+ : method === 'powershell'
149
+ ? 'powershell -File scripts/upload-clipboard-image.ps1 ...'
150
+ : 'node scripts/upload-clipboard-image.js';
151
+
152
+ const { stdout, stderr } = await execAsync(command);
153
+ return { content: [{ type: "text", text: stdout || stderr }] };
154
+ });
155
+ ```
156
+
157
+ ### 2. 创建快捷命令
158
+
159
+ **package.json:**
160
+ ```json
161
+ {
162
+ "scripts": {
163
+ "zentao:upload": "npm run upload:clipboard",
164
+ "zentao:upload:watch": "nodemon --watch clipboard --exec 'npm run upload:clipboard'"
165
+ }
166
+ }
167
+ ```
168
+
169
+ ### 3. 集成到 Git Hooks
170
+
171
+ **.git/hooks/pre-commit:**
172
+ ```bash
173
+ #!/bin/bash
174
+ # 在提交前自动上传截图到禅道
175
+
176
+ if git diff --cached --name-only | grep -q "screenshots/"; then
177
+ echo "检测到新截图,自动上传到禅道..."
178
+ npm run upload:clipboard
179
+ fi
180
+ ```
181
+
182
+ ### 4. VS Code Task 集成
183
+
184
+ **.vscode/tasks.json:**
185
+ ```json
186
+ {
187
+ "version": "2.0.0",
188
+ "tasks": [
189
+ {
190
+ "label": "Upload Clipboard to Zentao",
191
+ "type": "npm",
192
+ "script": "upload:clipboard",
193
+ "problemMatcher": [],
194
+ "presentation": {
195
+ "reveal": "always",
196
+ "panel": "new"
197
+ }
198
+ }
199
+ ]
200
+ }
201
+ ```
202
+
203
+ 然后使用 `Ctrl+Shift+B` 选择任务运行。
204
+
205
+ ---
206
+
207
+ ## 📊 完整工作流示例
208
+
209
+ ### 工作流1: 使用 MCP(推荐)
210
+
211
+ ```mermaid
212
+ graph LR
213
+ A[复制图片] --> B[告诉Claude上传]
214
+ B --> C[Claude调用MCP工具]
215
+ C --> D[自动保存到img/]
216
+ D --> E[上传到禅道]
217
+ E --> F[返回ID和URL]
218
+ F --> G[在对话中使用]
219
+ ```
220
+
221
+ **优点:**
222
+ - ✅ 无需离开对话界面
223
+ - ✅ 自动化程度高
224
+ - ✅ 与其他操作串联
225
+ - ✅ 无需配置环境变量
226
+
227
+ ### 工作流2: 使用命令行
228
+
229
+ ```mermaid
230
+ graph LR
231
+ A[复制图片] --> B[运行脚本]
232
+ B --> C[读取剪贴板]
233
+ C --> D[保存到img/]
234
+ D --> E[上传到禅道]
235
+ E --> F[显示结果]
236
+ F --> G[手动复制URL]
237
+ ```
238
+
239
+ **优点:**
240
+ - ✅ 快速独立
241
+ - ✅ 可自动化
242
+ - ✅ 可集成到其他工具
243
+ - ✅ 不依赖 Claude Code
244
+
245
+ ---
246
+
247
+ ## 🎯 最佳实践
248
+
249
+ ### 1. 选择合适的方式
250
+
251
+ - **日常使用** → MCP 工具(在 Claude Code 中)
252
+ - **批量处理** → 命令行脚本
253
+ - **自动化集成** → 命令行脚本 + 系统工具
254
+
255
+ ### 2. 配置管理
256
+
257
+ **MCP 方式:**
258
+ - 使用 `.zentao/config.json` 存储配置
259
+ - 通过 `initZentao` 工具初始化
260
+
261
+ **命令行方式:**
262
+ - 创建 `.env` 文件
263
+ - 或创建 `upload.bat` / `upload.sh` 包含配置
264
+ - 不要将密码提交到版本控制
265
+
266
+ ### 3. 图片管理
267
+
268
+ - 所有图片统一保存到 `./img/` 文件夹
269
+ - 文件名格式:`clipboard_时间戳.png` 或 `image_时间戳.png`
270
+ - 可在 `.gitignore` 中添加 `img/` 避免提交
271
+
272
+ ### 4. 错误处理
273
+
274
+ - 检查剪贴板是否有图片
275
+ - 验证禅道连接
276
+ - 记录上传日志
277
+ - 自动重试机制(可选)
278
+
279
+ ---
280
+
281
+ ## 📚 相关文档
282
+
283
+ - [脚本使用说明](./README.md)
284
+ - [MCP 工具列表](../README.md#api-tools)
285
+ - [禅道API文档](../禅道API文档.md)
286
+
287
+ ---
288
+
289
+ ## 🤝 贡献
290
+
291
+ 如果您有更好的集成方案,欢迎提交 PR!
292
+
293
+ 可能的改进方向:
294
+ - 监听剪贴板变化自动上传
295
+ - 支持拖拽上传
296
+ - 批量上传多张图片
297
+ - 图片压缩/优化
298
+ - 上传历史记录