oss-mcp-plus 1.0.11 → 1.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,14 +1,21 @@
1
1
  # OSS MCP Plus 🚀
2
2
 
3
- > Fork [1yhy/oss-mcp](https://github.com/1yhy/oss-mcp),新增批量重命名、目录列表、文件下载,以及图片批量压缩等实用重磅工具。
3
+ 一个基于 Model Context Protocol (MCP) 的服务器,用于将文件上传到阿里云 OSS。此服务器使大型语言模型能够直接将文件上传到阿里云对象存储服务,并提供文件管理相关的实用工具。
4
+
5
+ > Fork 自 [1yhy/oss-mcp](https://github.com/1yhy/oss-mcp),新增批量重命名、目录列表、文件下载,图片批量删除,以及图片批量压缩等实用重磅工具。
4
6
 
5
7
  <img width="1280" height="2034" alt="image" src="https://github.com/user-attachments/assets/c03c3716-109b-49a5-ab7c-113a6777c868" />
6
8
 
7
- 批量重命名:
9
+ **批量重命名**:
8
10
 
9
11
  ![oss-mcp-plus](https://github.com/user-attachments/assets/62689a86-92c8-4475-aa59-dde6eabd5bb2)
10
12
 
11
- 一个基于 Model Context Protocol (MCP) 的服务器,用于将文件上传到阿里云 OSS。此服务器使大型语言模型能够直接将文件上传到阿里云对象存储服务,并提供文件管理相关的实用工具。
13
+
14
+ **压缩图片**:
15
+
16
+ 内置tinypng和anywebp压缩引擎,会自动启用playwright mcp,进行oss上的图片压缩操作
17
+
18
+ <img width="758" height="699" alt="image" src="https://github.com/user-attachments/assets/e0d1c221-18c8-44dd-a76a-c87e75647830" />
12
19
 
13
20
  ## 💡 使用场景
14
21
 
@@ -29,11 +36,14 @@ OSS MCP服务器能够与其他MCP工具无缝集成,为您提供强大的工
29
36
  - 📥 支持从 URL 下载文件到本地
30
37
  - 📂 列出本地/OSS目录文件,支持通配符过滤
31
38
  - ✏️ 批量重命名文件,支持预览模式
39
+ - 🗑️ 批量删除文件,支持通配符和环境变量安全控制
32
40
  - 🗜️ 批量压缩图片(支持 TinyPNG / AnyWebP)
33
41
 
34
42
  ## 🔧 安装
35
43
 
36
- 您可以通过npm或从源码安装:
44
+ > 💡 **AI 工具用户**:如果你使用 Claude Code、Cursor、Windsurf 等 AI 工具,可直接跳转到 [与 AI 工具集成](#ai-integration) 章节进行配置。
45
+
46
+ 您可以通过 npm 或从源码安装:
37
47
 
38
48
  ### 使用npm安装
39
49
 
@@ -133,9 +143,10 @@ pnpm start:http
133
143
  pnpm inspect
134
144
  ```
135
145
 
146
+ <a id="ai-integration"></a>
136
147
  ## 🛠️ 与Claude/Cursor等AI工具集成
137
148
 
138
- ### Cursor配置方法
149
+ ### Cursor等配置方法
139
150
 
140
151
  1. 在Cursor中打开设置(Settings)
141
152
  2. 转到MCP服务器(MCP Servers)部分
@@ -175,8 +186,34 @@ pnpm inspect
175
186
  }
176
187
  ```
177
188
 
189
+ ### 启用删除功能(可选)
190
+
191
+ 出于安全考虑,删除 OSS 文件功能默认禁用。如需启用,请添加 `ALLOW_DELETE_OPERATION` 环境变量:
192
+
193
+ ```json
194
+ {
195
+ "mcpServers": {
196
+ "oss-mcp-plus": {
197
+ "command": "npx",
198
+ "args": [
199
+ "oss-mcp-plus",
200
+ "--oss-config='{...}'",
201
+ "--stdio"
202
+ ],
203
+ "env": {
204
+ "ALLOW_DELETE_OPERATION": "true"
205
+ }
206
+ }
207
+ }
208
+ }
209
+ ```
210
+
211
+ > ⚠️ **安全提示**: 仅在确实需要删除功能时才启用此选项。未配置时,`delete_oss_files` 工具将拒绝执行任何删除操作。
212
+
178
213
  ### 推荐方式:使用 MCP Switch 客户端
179
214
 
215
+ 现在AI编辑器太多了,且不同软件的配置方式有细微差别,比如claude有cli命令可以管理,因此为了抹平这个差异造成的心智负担:
216
+
180
217
  借助本作者的另一客户端软件 [MCP Switch](https://github.com/lovelyJason/mcp-switch),可以通过可视化界面轻松添加和管理 MCP 服务器:
181
218
 
182
219
  ![MCP Switch 界面](https://github.com/user-attachments/assets/b1630964-08ea-4dde-8ded-6fa00cf590bc)
@@ -235,7 +272,29 @@ pnpm inspect
235
272
  - `targetDir`: 保存文件的本地目录路径(必需)
236
273
  - `fileName`: 保存的文件名(可选,默认从 URL 提取)
237
274
 
238
- ### 7. 压缩图片 (`compress_images`)
275
+ ### 7. 删除OSS文件 (`delete_oss_files`) 🆕
276
+
277
+ 删除阿里云OSS中的文件,支持单个删除、批量删除和通配符匹配。
278
+
279
+ > ⚠️ **安全限制**: 此工具需要配置环境变量 `ALLOW_DELETE_OPERATION=true` 才能使用。详见 [启用删除功能](#启用删除功能可选) 章节。
280
+
281
+ **参数**:
282
+ - `directory`: OSS中的目录路径(必需),如 `images/icons`,根目录传空字符串 `''`
283
+ - `fileNames`: 要删除的文件名数组(与 `pattern` 二选一)
284
+ - `pattern`: 文件名通配符模式(与 `fileNames` 二选一),如 `*.tmp` 或 `test_*`
285
+ - `configName`: OSS配置名称(可选,默认为 `default`)
286
+ - `dryRun`: 是否为预览模式(可选,默认 false)。为 true 时只返回将要删除的文件列表,不实际删除
287
+
288
+ **使用示例**:
289
+
290
+ ```
291
+ 用户: 删除 OSS 上 temp 目录下所有 .tmp 文件
292
+ AI:
293
+ 1. 先用 dryRun=true 预览将要删除的文件
294
+ 2. 确认后用 dryRun=false 执行实际删除
295
+ ```
296
+
297
+ ### 8. 压缩图片 (`compress_images`)
239
298
 
240
299
  批量压缩图片工具,支持 TinyPNG 和 AnyWebP 两个在线压缩引擎。需配合 Playwright MCP 使用。
241
300
 
package/dist/index.js CHANGED
@@ -323,6 +323,52 @@ var OssService = class {
323
323
  }
324
324
  return results;
325
325
  }
326
+ /**
327
+ * 删除单个OSS文件
328
+ * @param key 文件路径
329
+ * @param configName 配置名称
330
+ * @returns 删除结果
331
+ */
332
+ async deleteFile(key, configName = "default") {
333
+ try {
334
+ const client = this.getClient(configName);
335
+ if (!client) {
336
+ return { success: false, error: `OSS config not found for: ${configName}` };
337
+ }
338
+ const normalizedKey = key.replace(/^\/+/, "");
339
+ try {
340
+ await client.head(normalizedKey);
341
+ } catch (_e) {
342
+ return { success: false, error: `\u6587\u4EF6\u4E0D\u5B58\u5728: ${normalizedKey}` };
343
+ }
344
+ await client.delete(normalizedKey);
345
+ return { success: true };
346
+ } catch (error) {
347
+ return { success: false, error: `\u5220\u9664\u5931\u8D25: ${error.message}` };
348
+ }
349
+ }
350
+ /**
351
+ * 批量删除OSS文件
352
+ * @param fileNames 文件名数组
353
+ * @param directory OSS目录路径
354
+ * @param configName 配置名称
355
+ * @returns 批量删除结果
356
+ */
357
+ async batchDeleteFiles(fileNames, directory = "", configName = "default") {
358
+ const results = [];
359
+ const normalizedDir = directory.replace(/^\/+|\/+$/g, "");
360
+ const dirPrefix = normalizedDir ? `${normalizedDir}/` : "";
361
+ for (const fileName of fileNames) {
362
+ const key = `${dirPrefix}${fileName}`;
363
+ const result = await this.deleteFile(key, configName);
364
+ results.push({
365
+ fileName,
366
+ success: result.success,
367
+ error: result.error
368
+ });
369
+ }
370
+ return results;
371
+ }
326
372
  };
327
373
  var ossService = new OssService();
328
374
 
@@ -678,6 +724,158 @@ ${configNames2.map((name) => `- ${name}`).join("\n")}`
678
724
  }
679
725
  }
680
726
  );
727
+ const allowDeleteOperation = process.env.ALLOW_DELETE_OPERATION === "true";
728
+ this.server.tool(
729
+ "delete_oss_files",
730
+ `\u5220\u9664\u963F\u91CC\u4E91OSS\u4E2D\u7684\u6587\u4EF6\u3002\u652F\u6301\u5355\u4E2A\u5220\u9664\u3001\u6279\u91CF\u5220\u9664\u548C\u901A\u914D\u7B26\u5339\u914D\u3002
731
+
732
+ \u3010\u26A0\uFE0F \u5B89\u5168\u9650\u5236\u3011\u6B64\u5DE5\u5177\u9700\u8981\u914D\u7F6E\u73AF\u5883\u53D8\u91CF ALLOW_DELETE_OPERATION=true \u624D\u80FD\u4F7F\u7528\u3002
733
+ \u5F53\u524D\u72B6\u6001: ${allowDeleteOperation ? "\u2705 \u5DF2\u542F\u7528" : '\u274C \u672A\u542F\u7528\uFF08\u9700\u8981\u5728 MCP \u914D\u7F6E\u4E2D\u6DFB\u52A0 "env": { "ALLOW_DELETE_OPERATION": "true" }\uFF09'}
734
+
735
+ \u3010\u91CD\u8981\u3011\u9996\u6B21\u8C03\u7528\u5FC5\u987B\u4F7F\u7528 dryRun=true \u9884\u89C8\uFF0C\u5C55\u793A\u7ED9\u7528\u6237\u786E\u8BA4\u540E\uFF0C\u7528\u6237\u540C\u610F\u624D\u80FD\u7528 dryRun=false \u6267\u884C\u5B9E\u9645\u5220\u9664\u3002\u7981\u6B62\u8DF3\u8FC7\u9884\u89C8\u76F4\u63A5\u6267\u884C\uFF01`,
736
+ {
737
+ directory: z3.string().describe("OSS\u4E2D\u7684\u76EE\u5F55\u8DEF\u5F84\uFF08\u5982 'images/icons'\uFF0C\u6839\u76EE\u5F55\u4F20\u7A7A\u5B57\u7B26\u4E32 ''\uFF09"),
738
+ fileNames: z3.array(z3.string()).optional().describe("\u8981\u5220\u9664\u7684\u6587\u4EF6\u540D\u6570\u7EC4\uFF08\u4E0E pattern \u4E8C\u9009\u4E00\uFF09"),
739
+ pattern: z3.string().optional().describe("\u6587\u4EF6\u540D\u901A\u914D\u7B26\u6A21\u5F0F\uFF08\u5982 '*.tmp' \u6216 'test_*'\uFF09\uFF0C\u4E0E fileNames \u4E8C\u9009\u4E00"),
740
+ configName: z3.string().optional().describe(`OSS\u914D\u7F6E\u540D\u79F0\uFF08\u9ED8\u8BA4\u4E3A'default'\uFF09\u3002\u53EF\u7528\u914D\u7F6E: ${configNames.join(", ") || "\u65E0"}`),
741
+ dryRun: z3.boolean().optional().describe("\u662F\u5426\u4E3A\u9884\u89C8\u6A21\u5F0F\uFF08\u9ED8\u8BA4false\uFF09\u3002\u4E3Atrue\u65F6\u53EA\u8FD4\u56DE\u5C06\u8981\u5220\u9664\u7684\u6587\u4EF6\u5217\u8868\uFF0C\u4E0D\u5B9E\u9645\u5220\u9664")
742
+ },
743
+ async ({ directory, fileNames, pattern, configName = "default", dryRun = false }) => {
744
+ try {
745
+ if (!allowDeleteOperation) {
746
+ return {
747
+ isError: true,
748
+ content: [{
749
+ type: "text",
750
+ text: `\u274C \u5220\u9664\u64CD\u4F5C\u88AB\u7981\u6B62\uFF01
751
+
752
+ \u8981\u542F\u7528\u5220\u9664\u529F\u80FD\uFF0C\u8BF7\u5728 MCP \u914D\u7F6E\u4E2D\u6DFB\u52A0\u73AF\u5883\u53D8\u91CF\uFF1A
753
+
754
+ {
755
+ "mcpServers": {
756
+ "oss-mcp-plus": {
757
+ "command": "npx",
758
+ "args": ["oss-mcp-plus", ...],
759
+ "env": {
760
+ "ALLOW_DELETE_OPERATION": "true"
761
+ }
762
+ }
763
+ }
764
+ }
765
+
766
+ \u8FD9\u662F\u4E00\u4E2A\u5B89\u5168\u63AA\u65BD\uFF0C\u9632\u6B62\u8BEF\u5220\u9664\u6587\u4EF6\u3002`
767
+ }]
768
+ };
769
+ }
770
+ Logger.log(`\u5220\u9664OSS\u6587\u4EF6: \u76EE\u5F55=${directory}, \u914D\u7F6E=${configName}, \u9884\u89C8\u6A21\u5F0F=${dryRun}`);
771
+ if (!fileNames && !pattern) {
772
+ return {
773
+ isError: true,
774
+ content: [{
775
+ type: "text",
776
+ text: "\u8BF7\u63D0\u4F9B fileNames\uFF08\u6587\u4EF6\u540D\u6570\u7EC4\uFF09\u6216 pattern\uFF08\u901A\u914D\u7B26\u6A21\u5F0F\uFF09\u4E4B\u4E00"
777
+ }]
778
+ };
779
+ }
780
+ let filesToDelete = [];
781
+ if (fileNames && fileNames.length > 0) {
782
+ filesToDelete = fileNames;
783
+ } else if (pattern) {
784
+ const listResult = await ossService.listFiles(directory, configName, pattern);
785
+ if (!listResult.success) {
786
+ return {
787
+ isError: true,
788
+ content: [{
789
+ type: "text",
790
+ text: `\u5217\u51FA\u6587\u4EF6\u5931\u8D25: ${listResult.error}`
791
+ }]
792
+ };
793
+ }
794
+ filesToDelete = (listResult.files || []).map((f) => f.name);
795
+ }
796
+ if (filesToDelete.length === 0) {
797
+ return {
798
+ content: [{
799
+ type: "text",
800
+ text: `\u6CA1\u6709\u627E\u5230\u5339\u914D\u7684\u6587\u4EF6${pattern ? ` (\u6A21\u5F0F: ${pattern})` : ""}
801
+ \u76EE\u5F55: ${directory || "\u6839\u76EE\u5F55"}
802
+ \u914D\u7F6E: ${configName}`
803
+ }]
804
+ };
805
+ }
806
+ if (dryRun) {
807
+ let resultText2 = `\u3010\u9884\u89C8\u6A21\u5F0F\u3011\u4EE5\u4E0B\u662F\u5C06\u8981\u5220\u9664\u7684\u6587\u4EF6:
808
+
809
+ `;
810
+ resultText2 += `\u914D\u7F6E: ${configName}
811
+ `;
812
+ resultText2 += `\u76EE\u5F55: ${directory || "\u6839\u76EE\u5F55"}
813
+ `;
814
+ if (pattern) {
815
+ resultText2 += `\u5339\u914D\u6A21\u5F0F: ${pattern}
816
+ `;
817
+ }
818
+ resultText2 += `\u6587\u4EF6\u6570\u91CF: ${filesToDelete.length}
819
+
820
+ `;
821
+ resultText2 += `\u6587\u4EF6\u5217\u8868:
822
+ `;
823
+ for (const fileName of filesToDelete) {
824
+ resultText2 += `\u{1F5D1}\uFE0F ${fileName}
825
+ `;
826
+ }
827
+ resultText2 += `
828
+ \u26A0\uFE0F \u786E\u8BA4\u8981\u5220\u9664\u8FD9\u4E9B\u6587\u4EF6\u540E\uFF0C\u8BF7\u4F7F\u7528 dryRun=false \u6267\u884C\u5B9E\u9645\u5220\u9664\u3002`;
829
+ return {
830
+ content: [{
831
+ type: "text",
832
+ text: resultText2
833
+ }]
834
+ };
835
+ }
836
+ const results = await ossService.batchDeleteFiles(filesToDelete, directory, configName);
837
+ const successCount = results.filter((r) => r.success).length;
838
+ const failCount = results.filter((r) => !r.success).length;
839
+ let resultText = `OSS\u6587\u4EF6\u5220\u9664\u5B8C\u6210:
840
+
841
+ `;
842
+ resultText += `\u914D\u7F6E: ${configName}
843
+ `;
844
+ resultText += `\u76EE\u5F55: ${directory || "\u6839\u76EE\u5F55"}
845
+ `;
846
+ resultText += `\u6210\u529F: ${successCount} \u4E2A, \u5931\u8D25: ${failCount} \u4E2A
847
+
848
+ `;
849
+ if (results.length > 0) {
850
+ resultText += "\u8BE6\u7EC6\u7ED3\u679C:\n";
851
+ for (const r of results) {
852
+ if (r.success) {
853
+ resultText += `\u2705 ${r.fileName} \u5DF2\u5220\u9664
854
+ `;
855
+ } else {
856
+ resultText += `\u274C ${r.fileName} (${r.error})
857
+ `;
858
+ }
859
+ }
860
+ }
861
+ return {
862
+ content: [{
863
+ type: "text",
864
+ text: resultText
865
+ }]
866
+ };
867
+ } catch (error) {
868
+ Logger.error(`\u5220\u9664OSS\u6587\u4EF6\u51FA\u9519:`, error);
869
+ return {
870
+ isError: true,
871
+ content: [{
872
+ type: "text",
873
+ text: `\u5220\u9664OSS\u6587\u4EF6\u5931\u8D25: ${error}`
874
+ }]
875
+ };
876
+ }
877
+ }
878
+ );
681
879
  this.server.tool(
682
880
  "download_file",
683
881
  "\u4ECE URL \u4E0B\u8F7D\u6587\u4EF6\u5230\u672C\u5730\u76EE\u5F55\u3002\u652F\u6301 HTTP/HTTPS \u94FE\u63A5\uFF0C\u53EF\u81EA\u5B9A\u4E49\u4FDD\u5B58\u6587\u4EF6\u540D\u3002",
@@ -802,7 +1000,9 @@ ${configNames2.map((name) => `- ${name}`).join("\n")}`
802
1000
 
803
1001
  \u7B2C 2 \u6B65\uFF1A\u5982\u679C\u56FE\u7247\u5728 OSS \u4E0A\uFF08\u5FC5\u987B\u5728\u8C03\u7528\u672C\u5DE5\u5177\u4E4B\u524D\u5B8C\u6210\uFF01\uFF09
804
1002
  - \u5148\u4F7F\u7528 list_oss_files \u5217\u51FA\u6587\u4EF6
805
- - \u4F7F\u7528 download_file \u5C06\u56FE\u7247\u4E0B\u8F7D\u5230\u672C\u5730\u4E34\u65F6\u76EE\u5F55\uFF08\u5982 /tmp/compress-images/\uFF09
1003
+ - \u26A0\uFE0F \u91CD\u8981\uFF1A\u4E0B\u8F7D\u5230\u3010\u9879\u76EE\u76EE\u5F55\u3011\u4E0B\u7684 .tmp-compress/ \u6587\u4EF6\u5939\uFF0C\u4E0D\u80FD\u7528 /tmp/\uFF01
1004
+ \uFF08Playwright MCP \u53EA\u80FD\u8BBF\u95EE\u9879\u76EE\u76EE\u5F55\u5185\u7684\u6587\u4EF6\uFF09
1005
+ - \u4F7F\u7528 download_file \u5C06\u56FE\u7247\u4E0B\u8F7D\u5230\u9879\u76EE\u76EE\u5F55\u4E0B
806
1006
  - \u53EA\u6709\u4E0B\u8F7D\u5230\u672C\u5730\u540E\u624D\u80FD\u8C03\u7528\u672C\u5DE5\u5177
807
1007
 
808
1008
  \u7B2C 3 \u6B65\uFF1A\u8C03\u7528\u672C\u5DE5\u5177
@@ -960,8 +1160,9 @@ ${JSON.stringify(questions, null, 2)}
960
1160
  \u2705 \u7B2C 1 \u6B65\uFF1A\u9A8C\u8BC1 Playwright MCP \u53EF\u7528
961
1161
  \u2192 \u8C03\u7528 browser_snapshot\uFF0C\u5982\u679C\u62A5\u9519\u5219\u505C\u6B62\u5E76\u63D0\u793A\u7528\u6237\u542F\u7528
962
1162
 
963
- \u2705 \u7B2C 2 \u6B65\uFF1A\u786E\u4FDD\u56FE\u7247\u5728\u672C\u5730
964
- \u2192 OSS \u56FE\u7247\u5FC5\u987B\u5148\u7528 download_file \u4E0B\u8F7D\u5230\u672C\u5730
1163
+ \u2705 \u7B2C 2 \u6B65\uFF1A\u786E\u4FDD\u56FE\u7247\u5728\u9879\u76EE\u76EE\u5F55\u5185
1164
+ \u2192 OSS \u56FE\u7247\u5FC5\u987B\u5148\u7528 download_file \u4E0B\u8F7D\u5230\u3010\u9879\u76EE\u76EE\u5F55\u3011\u4E0B\u7684 .tmp-compress/ \u6587\u4EF6\u5939
1165
+ \u2192 \u26A0\uFE0F \u4E0D\u80FD\u7528 /tmp/\uFF0CPlaywright \u65E0\u6CD5\u8BBF\u95EE\u9879\u76EE\u5916\u7684\u8DEF\u5F84\uFF01
965
1166
 
966
1167
  \u2705 \u7B2C 3 \u6B65\uFF1A\u8C03\u7528 check_compress_prerequisites
967
1168
  \u2192 \u9A8C\u8BC1\u6587\u4EF6\u5E76\u83B7\u53D6\u8BE2\u95EE\u6A21\u677F
@@ -1143,6 +1344,19 @@ ${JSON.stringify(resultInfo, null, 2)}
1143
1344
  // 生成 TinyPNG 自动化指令
1144
1345
  generateTinyPngInstructions(batches, outputFormat) {
1145
1346
  let instructions = "";
1347
+ instructions += `### \u26A0\uFE0F \u91CD\u8981\u63D0\u793A
1348
+
1349
+ `;
1350
+ instructions += `1. **\u56FE\u7247\u5FC5\u987B\u5728\u9879\u76EE\u76EE\u5F55\u5185**\uFF1APlaywright MCP \u53EA\u80FD\u8BBF\u95EE\u9879\u76EE\u76EE\u5F55\u4E0B\u7684\u6587\u4EF6\u3002
1351
+ `;
1352
+ instructions += ` - \u4E0D\u80FD\u4F7F\u7528 \`/tmp/\` \u7B49\u7CFB\u7EDF\u4E34\u65F6\u76EE\u5F55
1353
+ `;
1354
+ instructions += ` - \u8BF7\u5C06\u56FE\u7247\u4E0B\u8F7D\u5230\u9879\u76EE\u6839\u76EE\u5F55\u4E0B\u7684 \`.tmp-compress/\` \u6587\u4EF6\u5939
1355
+ `;
1356
+ instructions += `2. **\u4E0A\u4F20\u524D\u5FC5\u987B\u5148\u89E6\u53D1\u6587\u4EF6\u9009\u62E9\u6846**\uFF1A\u5148\u70B9\u51FB\u4E0A\u4F20\u533A\u57DF\uFF0C\u7B49\u5F85 Modal state \u663E\u793A "File chooser" \u540E\u518D\u8C03\u7528 \`browser_file_upload\`
1357
+
1358
+ `;
1359
+ const needsFormatConversion = outputFormat && outputFormat !== "png";
1146
1360
  for (let i = 0; i < batches.length; i++) {
1147
1361
  const batch = batches[i];
1148
1362
  instructions += `#### \u6279\u6B21 ${i + 1}/${batches.length}
@@ -1151,30 +1365,43 @@ ${JSON.stringify(resultInfo, null, 2)}
1151
1365
  instructions += `**\u6587\u4EF6**: ${batch.map((img) => path2.basename(img.path)).join(", ")}
1152
1366
 
1153
1367
  `;
1154
- instructions += `1. **\u6253\u5F00\u7F51\u7AD9**: \u4F7F\u7528 \`browser_navigate\` \u8BBF\u95EE \`https://tinypng.com/\`
1368
+ let stepNum = 1;
1369
+ instructions += `${stepNum++}. **\u6253\u5F00\u7F51\u7AD9**: \u4F7F\u7528 \`browser_navigate\` \u8BBF\u95EE \`https://tinypng.com/\`
1155
1370
  `;
1156
- instructions += `2. **\u7B49\u5F85\u52A0\u8F7D**: \u4F7F\u7528 \`browser_snapshot\` \u786E\u8BA4\u9875\u9762\u52A0\u8F7D\u5B8C\u6210
1371
+ instructions += `${stepNum++}. **\u7B49\u5F85\u52A0\u8F7D**: \u4F7F\u7528 \`browser_snapshot\` \u786E\u8BA4\u9875\u9762\u52A0\u8F7D\u5B8C\u6210
1157
1372
  `;
1158
- instructions += `3. **\u4E0A\u4F20\u6587\u4EF6**: \u4F7F\u7528 \`browser_file_upload\` \u4E0A\u4F20\u4EE5\u4E0B\u6587\u4EF6:
1373
+ if (needsFormatConversion) {
1374
+ instructions += `${stepNum++}. **\u5F00\u542F\u683C\u5F0F\u8F6C\u6362\u5F00\u5173**:
1159
1375
  `;
1160
- for (const img of batch) {
1161
- instructions += ` - \`${img.path}\`
1376
+ instructions += ` - \u5728\u9875\u9762\u5E95\u90E8\u627E\u5230 "Convert my images automatically" \u5F00\u5173
1377
+ `;
1378
+ instructions += ` - \u4F7F\u7528 \`browser_click\` \u70B9\u51FB\u5F00\u5173\u5F00\u542F\u5B83\uFF08\u5982\u679C\u662F\u5173\u95ED\u72B6\u6001\uFF09
1379
+ `;
1380
+ instructions += ` - \u5F00\u542F\u540E\u4F1A\u51FA\u73B0\u683C\u5F0F\u9009\u62E9\u9009\u9879
1381
+ `;
1382
+ instructions += `${stepNum++}. **\u9009\u62E9\u8F93\u51FA\u683C\u5F0F**:
1383
+ `;
1384
+ instructions += ` - \u70B9\u51FB\u9009\u62E9 "${outputFormat.toUpperCase()}" \u683C\u5F0F
1162
1385
  `;
1163
1386
  }
1164
- instructions += `4. **\u7B49\u5F85\u538B\u7F29**: \u4F7F\u7528 \`browser_wait_for\` \u7B49\u5F85 "Download all" \u6216\u5404\u6587\u4EF6\u7684 "download" \u6309\u94AE\u51FA\u73B0
1387
+ instructions += `${stepNum++}. **\u89E6\u53D1\u6587\u4EF6\u9009\u62E9\u6846**:
1165
1388
  `;
1166
- if (outputFormat && outputFormat !== "png") {
1167
- instructions += `5. **\u9009\u62E9\u8F93\u51FA\u683C\u5F0F**:
1389
+ instructions += ` - \u4F7F\u7528 \`browser_click\` \u70B9\u51FB\u4E0A\u4F20\u533A\u57DF\uFF08"Drop your .webp, .png or .jpg files here!" \u6587\u5B57\u533A\u57DF\uFF09
1168
1390
  `;
1169
- instructions += ` - \u70B9\u51FB\u538B\u7F29\u7ED3\u679C\u53F3\u4FA7\u7684\u683C\u5F0F\u9009\u62E9\u4E0B\u62C9\u6846
1391
+ instructions += ` - \u7B49\u5F85 \`browser_snapshot\` \u8FD4\u56DE\u7ED3\u679C\u4E2D Modal state \u663E\u793A "[File chooser]"
1170
1392
  `;
1171
- instructions += ` - \u9009\u62E9 "${outputFormat.toUpperCase()}"
1393
+ instructions += `${stepNum++}. **\u4E0A\u4F20\u6587\u4EF6**: \u4F7F\u7528 \`browser_file_upload\` \u4E0A\u4F20\u4EE5\u4E0B\u6587\u4EF6:
1394
+ `;
1395
+ for (const img of batch) {
1396
+ instructions += ` - \`${img.path}\`
1172
1397
  `;
1173
1398
  }
1174
- instructions += `${outputFormat && outputFormat !== "png" ? "6" : "5"}. **\u4E0B\u8F7D\u7ED3\u679C**: \u70B9\u51FB "Download all" \u6216\u9010\u4E2A\u4E0B\u8F7D
1399
+ instructions += `${stepNum++}. **\u7B49\u5F85\u538B\u7F29**: \u4F7F\u7528 \`browser_wait_for\` \u7B49\u5F85 "Download all" \u6216\u5404\u6587\u4EF6\u7684 "download" \u6309\u94AE\u51FA\u73B0
1400
+ `;
1401
+ instructions += `${stepNum++}. **\u4E0B\u8F7D\u7ED3\u679C**: \u70B9\u51FB "Download all" \u6216\u9010\u4E2A\u4E0B\u8F7D
1175
1402
  `;
1176
1403
  if (i < batches.length - 1) {
1177
- instructions += `${outputFormat && outputFormat !== "png" ? "7" : "6"}. **\u5237\u65B0\u9875\u9762**: \u4F7F\u7528 \`browser_navigate\` \u91CD\u65B0\u8BBF\u95EE \`https://tinypng.com/\` \u51C6\u5907\u4E0B\u4E00\u6279
1404
+ instructions += `${stepNum++}. **\u5237\u65B0\u9875\u9762**: \u4F7F\u7528 \`browser_navigate\` \u91CD\u65B0\u8BBF\u95EE \`https://tinypng.com/\` \u51C6\u5907\u4E0B\u4E00\u6279
1178
1405
  `;
1179
1406
  }
1180
1407
  instructions += `
@@ -1185,6 +1412,18 @@ ${JSON.stringify(resultInfo, null, 2)}
1185
1412
  // 生成 AnyWebP 自动化指令
1186
1413
  generateAnyWebPInstructions(batches) {
1187
1414
  let instructions = "";
1415
+ instructions += `### \u26A0\uFE0F \u91CD\u8981\u63D0\u793A
1416
+
1417
+ `;
1418
+ instructions += `1. **\u56FE\u7247\u5FC5\u987B\u5728\u9879\u76EE\u76EE\u5F55\u5185**\uFF1APlaywright MCP \u53EA\u80FD\u8BBF\u95EE\u9879\u76EE\u76EE\u5F55\u4E0B\u7684\u6587\u4EF6\u3002
1419
+ `;
1420
+ instructions += ` - \u4E0D\u80FD\u4F7F\u7528 \`/tmp/\` \u7B49\u7CFB\u7EDF\u4E34\u65F6\u76EE\u5F55
1421
+ `;
1422
+ instructions += ` - \u8BF7\u5C06\u56FE\u7247\u4E0B\u8F7D\u5230\u9879\u76EE\u6839\u76EE\u5F55\u4E0B\u7684 \`.tmp-compress/\` \u6587\u4EF6\u5939
1423
+ `;
1424
+ instructions += `2. **\u4E0A\u4F20\u524D\u5FC5\u987B\u5148\u89E6\u53D1\u6587\u4EF6\u9009\u62E9\u6846**\uFF1A\u5148\u70B9\u51FB\u4E0A\u4F20\u533A\u57DF\uFF0C\u7B49\u5F85 Modal state \u663E\u793A "File chooser" \u540E\u518D\u8C03\u7528 \`browser_file_upload\`
1425
+
1426
+ `;
1188
1427
  for (let i = 0; i < batches.length; i++) {
1189
1428
  const batch = batches[i];
1190
1429
  instructions += `#### \u6279\u6B21 ${i + 1}/${batches.length}
@@ -1193,22 +1432,29 @@ ${JSON.stringify(resultInfo, null, 2)}
1193
1432
  instructions += `**\u6587\u4EF6**: ${batch.map((img) => path2.basename(img.path)).join(", ")}
1194
1433
 
1195
1434
  `;
1196
- instructions += `1. **\u6253\u5F00\u7F51\u7AD9**: \u4F7F\u7528 \`browser_navigate\` \u8BBF\u95EE \`https://anywebp.com/convert-to-webp.html\`
1435
+ let stepNum = 1;
1436
+ instructions += `${stepNum++}. **\u6253\u5F00\u7F51\u7AD9**: \u4F7F\u7528 \`browser_navigate\` \u8BBF\u95EE \`https://anywebp.com/convert-to-webp.html\`
1437
+ `;
1438
+ instructions += `${stepNum++}. **\u7B49\u5F85\u52A0\u8F7D**: \u4F7F\u7528 \`browser_snapshot\` \u786E\u8BA4\u9875\u9762\u52A0\u8F7D\u5B8C\u6210
1439
+ `;
1440
+ instructions += `${stepNum++}. **\u89E6\u53D1\u6587\u4EF6\u9009\u62E9\u6846**:
1441
+ `;
1442
+ instructions += ` - \u4F7F\u7528 \`browser_click\` \u70B9\u51FB "Drop your images here!" \u4E0A\u4F20\u533A\u57DF
1197
1443
  `;
1198
- instructions += `2. **\u7B49\u5F85\u52A0\u8F7D**: \u4F7F\u7528 \`browser_snapshot\` \u786E\u8BA4\u9875\u9762\u52A0\u8F7D\u5B8C\u6210\uFF0C\u627E\u5230 "Drop your images here" \u533A\u57DF
1444
+ instructions += ` - \u7B49\u5F85 \`browser_snapshot\` \u8FD4\u56DE\u7ED3\u679C\u4E2D Modal state \u663E\u793A "[File chooser]"
1199
1445
  `;
1200
- instructions += `3. **\u4E0A\u4F20\u6587\u4EF6**: \u4F7F\u7528 \`browser_file_upload\` \u4E0A\u4F20\u4EE5\u4E0B\u6587\u4EF6:
1446
+ instructions += `${stepNum++}. **\u4E0A\u4F20\u6587\u4EF6**: \u4F7F\u7528 \`browser_file_upload\` \u4E0A\u4F20\u4EE5\u4E0B\u6587\u4EF6:
1201
1447
  `;
1202
1448
  for (const img of batch) {
1203
1449
  instructions += ` - \`${img.path}\`
1204
1450
  `;
1205
1451
  }
1206
- instructions += `4. **\u7B49\u5F85\u8F6C\u6362**: \u4F7F\u7528 \`browser_wait_for\` \u7B49\u5F85\u8F6C\u6362\u5B8C\u6210\uFF0C\u51FA\u73B0 "Download" \u6309\u94AE
1452
+ instructions += `${stepNum++}. **\u7B49\u5F85\u8F6C\u6362**: \u4F7F\u7528 \`browser_wait_for\` \u7B49\u5F85\u8F6C\u6362\u5B8C\u6210\uFF0C\u51FA\u73B0 "Download" \u6309\u94AE
1207
1453
  `;
1208
- instructions += `5. **\u4E0B\u8F7D\u7ED3\u679C**: \u70B9\u51FB "Download All" \u6216\u9010\u4E2A\u4E0B\u8F7D WebP \u6587\u4EF6
1454
+ instructions += `${stepNum++}. **\u4E0B\u8F7D\u7ED3\u679C**: \u70B9\u51FB "Download All" \u6216\u9010\u4E2A\u4E0B\u8F7D WebP \u6587\u4EF6
1209
1455
  `;
1210
1456
  if (i < batches.length - 1) {
1211
- instructions += `6. **\u5237\u65B0\u9875\u9762**: \u4F7F\u7528 \`browser_navigate\` \u91CD\u65B0\u8BBF\u95EE\u51C6\u5907\u4E0B\u4E00\u6279
1457
+ instructions += `${stepNum++}. **\u5237\u65B0\u9875\u9762**: \u4F7F\u7528 \`browser_navigate\` \u91CD\u65B0\u8BBF\u95EE\u51C6\u5907\u4E0B\u4E00\u6279
1212
1458
  `;
1213
1459
  }
1214
1460
  instructions += `
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/server.ts","../src/services/oss.service.ts","../src/config/oss.config.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * 实现阿里云OSS文件上传功能。\n * - 上传文件到阿里云OSS\n * - 获取可用的OSS配置\n */\n\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { OssMcpServer } from \"./server.js\";\nimport { getServerConfig } from \"./config/oss.config.js\";\nimport { resolve } from \"path\";\nimport { config } from \"dotenv\";\n\n// 加载当前工作目录中的.env文件\nconfig({ path: resolve(process.cwd(), \".env\") });\n\nexport async function startServer(): Promise<void> {\n // 检查是否在stdio模式下运行\n const isStdioMode = process.env.NODE_ENV === \"cli\" || process.argv.includes(\"--stdio\");\n\n // 获取服务器配置\n const serverConfig = getServerConfig(isStdioMode);\n\n // 创建OSS MCP服务器\n const server = new OssMcpServer();\n\n if (isStdioMode) {\n // 在stdio模式下运行\n const transport = new StdioServerTransport();\n await server.connect(transport);\n } else {\n // 在HTTP模式下运行\n console.log(`初始化OSS MCP服务器,HTTP模式,端口: ${serverConfig.port}...`);\n await server.startHttpServer(serverConfig.port);\n }\n}\n\n// 启动服务器\nstartServer().catch((error) => {\n console.error(\"启动服务器失败:\", error);\n process.exit(1);\n});\n","import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { ossService } from \"./services/oss.service.js\";\nimport express, { Request, Response } from \"express\";\nimport { SSEServerTransport } from \"@modelcontextprotocol/sdk/server/sse.js\";\nimport { IncomingMessage, ServerResponse } from \"http\";\nimport { Transport } from \"@modelcontextprotocol/sdk/shared/transport.js\";\nimport fs from 'fs';\nimport path from 'path';\nimport https from 'https';\nimport http from 'http';\n\nexport const Logger = {\n log: (...args: any[]) => {\n console.log(...args);\n },\n error: (...args: any[]) => {\n console.error(...args);\n }\n};\n\nexport class OssMcpServer {\n private readonly server: McpServer;\n private sseTransport: SSEServerTransport | null = null;\n\n constructor() {\n this.server = new McpServer(\n {\n name: \"@yhy2001/oss-mcp\",\n version: \"1.0.0\",\n },\n // 使用正确格式的capabilities配置\n {\n capabilities: {\n tools: { listChanged: true },\n resources: { listChanged: true },\n prompts: { listChanged: true },\n logging: {}\n }\n }\n );\n\n this.registerTools();\n }\n\n private registerTools(): void {\n // 获取可用的OSS配置\n const configs = ossService.getConfigs();\n const configNames = configs.map(config => config.id);\n\n // 工具:上传文件到OSS\n this.server.tool(\n \"upload_to_oss\",\n \"将文件上传到阿里云OSS\",\n {\n filePath: z.string().describe(\"要上传的本地文件路径\"),\n targetDir: z.string().optional().describe(\"OSS中的目标目录路径(可选)\"),\n fileName: z.string().optional().describe(\"上传后的文件名(可选,默认使用原文件名)\"),\n configName: z.string().optional().describe(`OSS配置名称(可选,默认为'default')。可用配置: ${configNames.join(', ') || '无'}`)\n },\n async ({ filePath, targetDir, fileName, configName }) => {\n try {\n Logger.log(`准备上传: ${filePath} 到 ${targetDir || '根目录'}`);\n\n if (!filePath) {\n throw new Error(\"文件路径是必需的\");\n }\n\n // 检查文件是否存在\n if (!fs.existsSync(filePath)) {\n throw new Error(`文件不存在: ${filePath}`);\n }\n\n // 执行上传\n const result = await ossService.uploadFile({\n filePath,\n targetDir,\n fileName,\n configName\n });\n\n if (result.success) {\n Logger.log(`上传成功: ${result.url}`);\n return {\n content: [{\n type: \"text\",\n text: `文件上传成功!\\n文件名: ${path.basename(filePath)}\\n目标位置: ${targetDir || '根目录'}\\nURL: ${result.url}\\n配置名称: ${result.ossConfigName}`\n }]\n };\n } else {\n Logger.error(`上传失败: ${result.error}`);\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `上传失败: ${result.error}`\n }]\n };\n }\n } catch (error) {\n Logger.error(`上传过程中出错:`, error);\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `上传出错: ${error}`\n }]\n };\n }\n }\n );\n\n // 工具:列出可用的OSS配置\n this.server.tool(\n \"list_oss_configs\",\n \"列出可用的阿里云OSS配置\",\n {},\n async () => {\n try {\n const configs = ossService.getConfigs();\n const configNames = configs.map(config => config.id);\n\n if (configNames.length === 0) {\n return {\n content: [{\n type: \"text\",\n text: \"未找到OSS配置。请检查环境变量设置。\"\n }]\n };\n }\n\n return {\n content: [{\n type: \"text\",\n text: `可用的OSS配置:\\n${configNames.map(name => `- ${name}`).join('\\n')}`\n }]\n };\n } catch (error) {\n Logger.error(`获取OSS配置列表时出错:`, error);\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `获取配置列表失败: ${error}`\n }]\n };\n }\n }\n );\n\n // 工具:批量重命名OSS文件\n this.server.tool(\n \"batch_rename_files\",\n \"批量重命名阿里云OSS文件。通过copy+delete实现。【重要】首次调用必须使用dryRun=true预览,展示给用户确认后,用户同意才能用dryRun=false执行实际重命名。禁止跳过预览直接执行!\",\n {\n directory: z.string().describe(\"OSS中的目录路径(如 'images/icons',根目录传空字符串 '')\"),\n renameRules: z.array(z.object({\n oldName: z.string().describe(\"原文件名\"),\n newName: z.string().describe(\"新文件名\")\n })).describe(\"重命名规则数组,每项包含原文件名和新文件名\"),\n configName: z.string().optional().describe(`OSS配置名称(默认为'default')。可用配置: ${configNames.join(', ') || '无'}`),\n dryRun: z.boolean().optional().describe(\"是否为预览模式(默认false)。为true时只返回将要执行的操作,不实际重命名\")\n },\n async ({ directory, renameRules, configName = 'default', dryRun = false }) => {\n try {\n Logger.log(`OSS批量重命名: 目录=${directory}, 规则数=${renameRules.length}, 配置=${configName}, 预览模式=${dryRun}`);\n\n let results: { oldName: string; newName: string; success: boolean; error?: string }[];\n\n if (dryRun) {\n // 预览模式:只返回将要执行的操作\n results = renameRules.map(rule => ({\n oldName: rule.oldName,\n newName: rule.newName,\n success: true\n }));\n } else {\n // 实际执行OSS重命名\n results = await ossService.batchRenameFiles(renameRules, directory, configName);\n }\n\n const successCount = results.filter(r => r.success).length;\n const failCount = results.filter(r => !r.success).length;\n\n let resultText = dryRun ? `【预览模式】以下是将要执行的OSS文件重命名操作:\\n\\n` : `OSS文件批量重命名完成:\\n\\n`;\n resultText += `配置: ${configName}\\n`;\n resultText += `目录: ${directory || '根目录'}\\n`;\n resultText += `成功: ${successCount} 个, 失败: ${failCount} 个\\n\\n`;\n\n if (results.length > 0) {\n resultText += '详细结果:\\n';\n for (const r of results) {\n if (r.success) {\n resultText += `✅ ${r.oldName} → ${r.newName}\\n`;\n } else {\n resultText += `❌ ${r.oldName} → ${r.newName} (${r.error})\\n`;\n }\n }\n }\n\n return {\n content: [{\n type: \"text\",\n text: resultText\n }]\n };\n } catch (error) {\n Logger.error(`OSS批量重命名出错:`, error);\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `OSS批量重命名失败: ${error}`\n }]\n };\n }\n }\n );\n\n // 工具:列出本地目录文件\n this.server.tool(\n \"list_directory_files\",\n \"列出本地文件系统中指定目录下的所有文件。注意:此工具仅支持本地路径,如果要列出 OSS 中的文件,请使用 list_oss_files 工具。\",\n {\n directory: z.string().describe(\"要查看的目录路径\"),\n pattern: z.string().optional().describe(\"文件名过滤模式(可选),如 '*.png' 或 'icon_*'\")\n },\n async ({ directory, pattern }) => {\n try {\n Logger.log(`列出目录文件: ${directory}, 过滤: ${pattern || '无'}`);\n\n // 检查目录是否存在\n if (!fs.existsSync(directory)) {\n throw new Error(`目录不存在: ${directory}`);\n }\n\n const stat = fs.statSync(directory);\n if (!stat.isDirectory()) {\n throw new Error(`路径不是目录: ${directory}`);\n }\n\n let files = fs.readdirSync(directory);\n\n // 过滤掉隐藏文件\n files = files.filter(f => !f.startsWith('.'));\n\n // 如果有 pattern,进行简单的通配符匹配\n if (pattern) {\n const regex = new RegExp(\n '^' + pattern\n .replace(/\\./g, '\\\\.')\n .replace(/\\*/g, '.*')\n .replace(/\\?/g, '.') + '$',\n 'i'\n );\n files = files.filter(f => regex.test(f));\n }\n\n // 获取文件信息\n const fileInfos = files.map(f => {\n const filePath = path.join(directory, f);\n const fileStat = fs.statSync(filePath);\n return {\n name: f,\n isDirectory: fileStat.isDirectory(),\n size: fileStat.size\n };\n });\n\n // 排序:目录在前,文件在后,按名称排序\n fileInfos.sort((a, b) => {\n if (a.isDirectory !== b.isDirectory) {\n return a.isDirectory ? -1 : 1;\n }\n return a.name.localeCompare(b.name);\n });\n\n if (fileInfos.length === 0) {\n return {\n content: [{\n type: \"text\",\n text: `目录 ${directory} 下没有找到匹配的文件${pattern ? ` (过滤: ${pattern})` : ''}`\n }]\n };\n }\n\n let resultText = `目录: ${directory}\\n`;\n if (pattern) {\n resultText += `过滤: ${pattern}\\n`;\n }\n resultText += `共 ${fileInfos.length} 个项目:\\n\\n`;\n\n for (const f of fileInfos) {\n if (f.isDirectory) {\n resultText += `📁 ${f.name}/\\n`;\n } else {\n const sizeStr = f.size < 1024\n ? `${f.size}B`\n : f.size < 1024 * 1024\n ? `${(f.size / 1024).toFixed(1)}KB`\n : `${(f.size / 1024 / 1024).toFixed(1)}MB`;\n resultText += `📄 ${f.name} (${sizeStr})\\n`;\n }\n }\n\n return {\n content: [{\n type: \"text\",\n text: resultText\n }]\n };\n } catch (error) {\n Logger.error(`列出目录文件出错:`, error);\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `列出目录失败: ${error}`\n }]\n };\n }\n }\n );\n\n // 工具:列出OSS目录文件\n this.server.tool(\n \"list_oss_files\",\n \"列出阿里云OSS指定目录下的所有文件。用于查看 OSS 中的文件以便进行重命名或其他操作。注意:如果要列出本地文件,请使用 list_directory_files 工具。\",\n {\n directory: z.string().describe(\"OSS中的目录路径(如 'images/icons',根目录传空字符串 '')\"),\n pattern: z.string().optional().describe(\"文件名过滤模式(可选),如 '*.png' 或 'icon_*'\"),\n configName: z.string().optional().describe(`OSS配置名称(默认为'default')。可用配置: ${configNames.join(', ') || '无'}`)\n },\n async ({ directory, pattern, configName = 'default' }) => {\n try {\n Logger.log(`列出OSS目录文件: ${directory || '根目录'}, 过滤: ${pattern || '无'}, 配置: ${configName}`);\n\n const result = await ossService.listFiles(directory, configName, pattern);\n\n if (!result.success) {\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `列出OSS文件失败: ${result.error}`\n }]\n };\n }\n\n const files = result.files || [];\n\n if (files.length === 0) {\n return {\n content: [{\n type: \"text\",\n text: `OSS目录 ${directory || '根目录'} 下没有找到匹配的文件${pattern ? ` (过滤: ${pattern})` : ''}\\n配置: ${configName}`\n }]\n };\n }\n\n const sizeStr = (size: number) => size < 1024\n ? `${size}B`\n : size < 1024 * 1024\n ? `${(size / 1024).toFixed(1)}KB`\n : `${(size / 1024 / 1024).toFixed(1)}MB`;\n\n let resultText = `OSS目录: ${directory || '根目录'}\\n`;\n resultText += `配置: ${configName}\\n`;\n if (pattern) {\n resultText += `过滤: ${pattern}\\n`;\n }\n resultText += `共 ${files.length} 个文件:\\n\\n`;\n\n for (const f of files) {\n resultText += `📄 ${f.name} (${sizeStr(f.size)})\\n`;\n }\n\n return {\n content: [{\n type: \"text\",\n text: resultText\n }]\n };\n } catch (error) {\n Logger.error(`列出OSS目录文件出错:`, error);\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `列出OSS目录失败: ${error}`\n }]\n };\n }\n }\n );\n\n // 工具:下载文件\n this.server.tool(\n \"download_file\",\n \"从 URL 下载文件到本地目录。支持 HTTP/HTTPS 链接,可自定义保存文件名。\",\n {\n url: z.string().describe(\"要下载的文件 URL\"),\n targetDir: z.string().describe(\"保存文件的本地目录路径\"),\n fileName: z.string().optional().describe(\"保存的文件名(可选,默认从 URL 提取)\")\n },\n async ({ url, targetDir, fileName }) => {\n try {\n Logger.log(`下载文件: ${url} 到 ${targetDir}`);\n\n // 检查目录是否存在,不存在则创建\n if (!fs.existsSync(targetDir)) {\n fs.mkdirSync(targetDir, { recursive: true });\n Logger.log(`创建目录: ${targetDir}`);\n }\n\n const stat = fs.statSync(targetDir);\n if (!stat.isDirectory()) {\n throw new Error(`路径不是目录: ${targetDir}`);\n }\n\n // 从 URL 提取文件名\n let finalFileName = fileName;\n if (!finalFileName) {\n const urlObj = new URL(url);\n finalFileName = path.basename(urlObj.pathname);\n // 如果 URL 没有文件名,生成一个\n if (!finalFileName || finalFileName === '/') {\n finalFileName = `download_${Date.now()}`;\n }\n }\n\n const filePath = path.join(targetDir, finalFileName);\n\n // 检查文件是否已存在\n if (fs.existsSync(filePath)) {\n throw new Error(`文件已存在: ${filePath}`);\n }\n\n // 下载文件\n await new Promise<void>((resolve, reject) => {\n const urlObj = new URL(url);\n const protocol = urlObj.protocol === 'https:' ? https : http;\n\n const request = protocol.get(url, (response) => {\n // 处理重定向\n if (response.statusCode === 301 || response.statusCode === 302) {\n const redirectUrl = response.headers.location;\n if (redirectUrl) {\n Logger.log(`重定向到: ${redirectUrl}`);\n const redirectProtocol = redirectUrl.startsWith('https:') ? https : http;\n redirectProtocol.get(redirectUrl, (redirectResponse) => {\n if (redirectResponse.statusCode !== 200) {\n reject(new Error(`下载失败,HTTP 状态码: ${redirectResponse.statusCode}`));\n return;\n }\n const fileStream = fs.createWriteStream(filePath);\n redirectResponse.pipe(fileStream);\n fileStream.on('finish', () => {\n fileStream.close();\n resolve();\n });\n fileStream.on('error', (err) => {\n fs.unlink(filePath, () => {});\n reject(err);\n });\n }).on('error', reject);\n return;\n }\n }\n\n if (response.statusCode !== 200) {\n reject(new Error(`下载失败,HTTP 状态码: ${response.statusCode}`));\n return;\n }\n\n const fileStream = fs.createWriteStream(filePath);\n response.pipe(fileStream);\n\n fileStream.on('finish', () => {\n fileStream.close();\n resolve();\n });\n\n fileStream.on('error', (err) => {\n fs.unlink(filePath, () => {});\n reject(err);\n });\n });\n\n request.on('error', (err) => {\n fs.unlink(filePath, () => {});\n reject(err);\n });\n\n request.setTimeout(60000, () => {\n request.destroy();\n fs.unlink(filePath, () => {});\n reject(new Error('下载超时(60秒)'));\n });\n });\n\n // 获取文件大小\n const downloadedStat = fs.statSync(filePath);\n const sizeStr = downloadedStat.size < 1024\n ? `${downloadedStat.size}B`\n : downloadedStat.size < 1024 * 1024\n ? `${(downloadedStat.size / 1024).toFixed(1)}KB`\n : `${(downloadedStat.size / 1024 / 1024).toFixed(1)}MB`;\n\n return {\n content: [{\n type: \"text\",\n text: `文件下载成功!\\n源URL: ${url}\\n保存路径: ${filePath}\\n文件大小: ${sizeStr}`\n }]\n };\n } catch (error) {\n Logger.error(`下载文件出错:`, error);\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `下载失败: ${error}`\n }]\n };\n }\n }\n );\n\n // 工具:检查压缩图片的前置条件\n this.server.tool(\n \"check_compress_prerequisites\",\n `检查图片压缩的前置条件并返回用户询问模板。\n\n【⚠️ 极其重要 - AI 必须严格按以下顺序执行】\n\n第 1 步:检查 Playwright MCP(必须在调用本工具之前完成!)\n- 调用 browser_snapshot 工具\n- 如果成功:继续执行\n- 如果失败/报错:立即停止,告知用户需要启用 Playwright MCP\n\n第 2 步:如果图片在 OSS 上(必须在调用本工具之前完成!)\n- 先使用 list_oss_files 列出文件\n- 使用 download_file 将图片下载到本地临时目录(如 /tmp/compress-images/)\n- 只有下载到本地后才能调用本工具\n\n第 3 步:调用本工具\n- 传入本地图片路径数组\n- 获取用户询问模板\n\n第 4 步:询问用户\n- 使用 AskUserQuestion 询问压缩引擎、输出格式等\n\n第 5 步:执行压缩\n- 调用 compress_images 工具`,\n {\n images: z.array(z.string()).describe(\"要压缩的【本地】图片路径数组。如果图片在 OSS 上,必须先用 download_file 下载到本地!\")\n },\n async ({ images }) => {\n try {\n // 验证图片文件\n const validImages: { path: string; name: string; ext: string; size: number }[] = [];\n const errors: string[] = [];\n\n for (const imgPath of images) {\n if (!fs.existsSync(imgPath)) {\n errors.push(`文件不存在: ${imgPath}`);\n continue;\n }\n const stat = fs.statSync(imgPath);\n if (stat.size > 5 * 1024 * 1024) {\n errors.push(`文件超过 5MB 限制: ${imgPath}`);\n continue;\n }\n const ext = path.extname(imgPath).toLowerCase().slice(1);\n if (!['png', 'jpg', 'jpeg', 'webp', 'gif', 'bmp', 'tiff'].includes(ext)) {\n errors.push(`不支持的格式: ${imgPath}`);\n continue;\n }\n validImages.push({\n path: imgPath,\n name: path.basename(imgPath, path.extname(imgPath)),\n ext: ext === 'jpg' ? 'jpeg' : ext,\n size: stat.size\n });\n }\n\n if (validImages.length === 0) {\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `没有有效的图片可处理:\\n${errors.join('\\n')}`\n }]\n };\n }\n\n // 构建需要询问用户的问题\n const questions = {\n playwrightCheck: {\n instruction: \"请先使用 browser_snapshot 工具测试 Playwright MCP 是否可用。如果报错说明未配置。\"\n },\n engineQuestion: {\n question: \"请选择压缩引擎\",\n header: \"压缩引擎\",\n options: [\n { label: \"TinyPNG (推荐)\", description: \"支持 PNG/JPEG/WebP 输出,压缩质量高,每批最多 3 个文件\" },\n { label: \"AnyWebP\", description: \"固定输出 WebP 格式,每批最多 20 个文件\" }\n ]\n },\n formatQuestion: {\n question: \"是否需要转换输出格式?\",\n header: \"输出格式\",\n options: [\n { label: \"保持原格式\", description: \"不转换格式,仅压缩\" },\n { label: \"转换为 WebP\", description: \"转换为 WebP 格式,体积更小\" },\n { label: \"转换为 JPEG\", description: \"转换为 JPEG 格式(仅 TinyPNG)\" },\n { label: \"转换为 PNG\", description: \"转换为 PNG 格式(仅 TinyPNG)\" }\n ]\n },\n deleteOriginalQuestion: {\n question: \"转换格式后是否删除原文件?\",\n header: \"删除原文件\",\n options: [\n { label: \"保留原文件\", description: \"在 OSS 上保留原格式文件\" },\n { label: \"删除原文件\", description: \"转换后删除 OSS 上的原格式文件\" }\n ],\n condition: \"仅当选择了转换格式时才需要询问\"\n }\n };\n\n const sizeStr = (size: number) => size < 1024\n ? `${size}B`\n : size < 1024 * 1024\n ? `${(size / 1024).toFixed(1)}KB`\n : `${(size / 1024 / 1024).toFixed(1)}MB`;\n\n let resultText = `## 图片压缩前置检查\\n\\n`;\n resultText += `### ✅ 有效图片 (${validImages.length} 个)\\n`;\n for (const img of validImages) {\n resultText += `- ${path.basename(img.path)} (${sizeStr(img.size)})\\n`;\n }\n\n if (errors.length > 0) {\n resultText += `\\n### ⚠️ 跳过的文件\\n`;\n for (const err of errors) {\n resultText += `- ${err}\\n`;\n }\n }\n\n resultText += `\\n### 📋 AI 执行步骤\\n\\n`;\n resultText += `1. **检查 Playwright**: 调用 \\`browser_snapshot\\` 测试是否可用\\n`;\n resultText += ` - 如果报错,提示用户需要配置 Playwright MCP\\n`;\n resultText += `2. **询问用户**: 使用 AskUserQuestion 一次性询问以下问题:\\n`;\n resultText += ` - 选择压缩引擎 (TinyPNG / AnyWebP)\\n`;\n resultText += ` - 是否转换格式 (保持原格式 / WebP / JPEG / PNG)\\n`;\n resultText += ` - 如果转格式,是否删除原文件\\n`;\n resultText += `3. **执行压缩**: 根据用户选择调用 \\`compress_images\\`\\n`;\n\n return {\n content: [\n {\n type: \"text\",\n text: resultText\n },\n {\n type: \"text\",\n text: `\\n---\\n**询问模板 (JSON)**:\\n\\`\\`\\`json\\n${JSON.stringify(questions, null, 2)}\\n\\`\\`\\``\n }\n ]\n };\n } catch (error) {\n Logger.error(`检查前置条件出错:`, error);\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `检查失败: ${error}`\n }]\n };\n }\n }\n );\n\n // 工具:压缩图片(生成压缩指令,由 AI 调用 Playwright MCP 执行)\n this.server.tool(\n \"compress_images\",\n `压缩图片工具。生成 Playwright 自动化压缩指令。\n\n【⚠️ 禁止直接调用!必须先完成以下步骤】\n\n✅ 第 1 步:验证 Playwright MCP 可用\n → 调用 browser_snapshot,如果报错则停止并提示用户启用\n\n✅ 第 2 步:确保图片在本地\n → OSS 图片必须先用 download_file 下载到本地\n\n✅ 第 3 步:调用 check_compress_prerequisites\n → 验证文件并获取询问模板\n\n✅ 第 4 步:询问用户偏好\n → 使用 AskUserQuestion 询问引擎、格式、是否删除原文件\n\n✅ 第 5 步:调用本工具\n → 传入用户选择的参数\n\n【后续流程】\n1. 按返回的指令使用 Playwright MCP 执行网页自动化\n2. 下载压缩结果\n3. 使用 upload_to_oss 上传回 OSS`,\n {\n images: z.array(z.string()).describe(\"要压缩的本地图片路径数组\"),\n engine: z.enum(['tinypng', 'anywebp']).describe(\"压缩引擎 (必须先询问用户选择)\"),\n outputFormat: z.enum(['png', 'jpeg', 'webp']).optional().describe(\"输出格式 (必须先询问用户选择,仅 tinypng 支持多格式)\"),\n deleteOriginal: z.boolean().optional().describe(\"转格式时是否删除原文件 (必须先询问用户选择)\"),\n ossDirectory: z.string().optional().describe(\"OSS 目标目录 (用于上传压缩后的文件)\"),\n configName: z.string().optional().describe(`OSS配置名称(默认为'default')。可用配置: ${configNames.join(', ') || '无'}`)\n },\n async ({ images, engine, outputFormat, deleteOriginal = false, ossDirectory, configName = 'default' }) => {\n try {\n Logger.log(`压缩图片: 引擎=${engine}, 格式=${outputFormat || '原格式'}, 图片数=${images.length}`);\n\n // 验证图片文件存在\n const validImages: { path: string; name: string; ext: string; size: number }[] = [];\n const errors: string[] = [];\n\n for (const imgPath of images) {\n if (!fs.existsSync(imgPath)) {\n errors.push(`文件不存在: ${imgPath}`);\n continue;\n }\n const stat = fs.statSync(imgPath);\n if (stat.size > 5 * 1024 * 1024) {\n errors.push(`文件超过 5MB 限制: ${imgPath}`);\n continue;\n }\n const ext = path.extname(imgPath).toLowerCase().slice(1);\n if (!['png', 'jpg', 'jpeg', 'webp', 'gif', 'bmp', 'tiff'].includes(ext)) {\n errors.push(`不支持的格式: ${imgPath}`);\n continue;\n }\n validImages.push({\n path: imgPath,\n name: path.basename(imgPath, path.extname(imgPath)),\n ext: ext === 'jpg' ? 'jpeg' : ext,\n size: stat.size\n });\n }\n\n if (validImages.length === 0) {\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `没有有效的图片可处理:\\n${errors.join('\\n')}`\n }]\n };\n }\n\n // 生成压缩指令\n const actualOutputFormat = engine === 'anywebp' ? 'webp' : (outputFormat || null);\n const batchSize = engine === 'tinypng' ? 3 : 20;\n const batches: typeof validImages[] = [];\n\n for (let i = 0; i < validImages.length; i += batchSize) {\n batches.push(validImages.slice(i, i + batchSize));\n }\n\n // 构建指令文本\n let instructions = `## 图片压缩指令\\n\\n`;\n instructions += `**引擎**: ${engine === 'tinypng' ? 'TinyPNG (https://tinypng.com/)' : 'AnyWebP (https://anywebp.com/convert-to-webp)'}\\n`;\n instructions += `**输出格式**: ${actualOutputFormat || '保持原格式'}\\n`;\n instructions += `**总图片数**: ${validImages.length}\\n`;\n instructions += `**批次数**: ${batches.length} (每批最多 ${batchSize} 个)\\n\\n`;\n\n if (errors.length > 0) {\n instructions += `### ⚠️ 跳过的文件\\n`;\n for (const err of errors) {\n instructions += `- ${err}\\n`;\n }\n instructions += `\\n`;\n }\n\n instructions += `### 📋 执行步骤\\n\\n`;\n instructions += `**前置检查**: 请确认 Playwright MCP 已配置并可用\\n\\n`;\n\n if (engine === 'tinypng') {\n instructions += this.generateTinyPngInstructions(batches, actualOutputFormat);\n } else {\n instructions += this.generateAnyWebPInstructions(batches);\n }\n\n // 添加后续处理指令\n instructions += `\\n### 📤 后续处理\\n\\n`;\n instructions += `压缩完成后,请执行以下操作:\\n\\n`;\n\n for (const img of validImages) {\n const newExt = actualOutputFormat || img.ext;\n const isFormatChange = newExt !== img.ext;\n const newFileName = `${img.name}.${newExt}`;\n const downloadPath = path.join(path.dirname(img.path), `${img.name}-compressed.${newExt}`);\n\n instructions += `**${path.basename(img.path)}**:\\n`;\n instructions += `1. 下载压缩结果到: \\`${downloadPath}\\`\\n`;\n\n if (ossDirectory) {\n if (isFormatChange) {\n instructions += `2. 上传到 OSS: \\`upload_to_oss(\"${downloadPath}\", \"${ossDirectory}\", \"${newFileName}\", \"${configName}\")\\`\\n`;\n if (deleteOriginal) {\n instructions += `3. 删除原文件: 在 OSS 上删除 \\`${ossDirectory}/${path.basename(img.path)}\\`\\n`;\n }\n } else {\n instructions += `2. 覆盖上传到 OSS: \\`upload_to_oss(\"${downloadPath}\", \"${ossDirectory}\", \"${path.basename(img.path)}\", \"${configName}\")\\`\\n`;\n }\n }\n instructions += `\\n`;\n }\n\n // 返回信息\n const resultInfo = {\n engine,\n outputFormat: actualOutputFormat,\n deleteOriginal: actualOutputFormat ? deleteOriginal : false,\n ossDirectory,\n configName,\n totalImages: validImages.length,\n batches: batches.length,\n batchSize,\n images: validImages.map(img => ({\n originalPath: img.path,\n originalName: path.basename(img.path),\n originalExt: img.ext,\n originalSize: img.size,\n newExt: actualOutputFormat || img.ext,\n isFormatChange: (actualOutputFormat || img.ext) !== img.ext\n }))\n };\n\n return {\n content: [\n {\n type: \"text\",\n text: instructions\n },\n {\n type: \"text\",\n text: `\\n---\\n**压缩任务数据 (JSON)**:\\n\\`\\`\\`json\\n${JSON.stringify(resultInfo, null, 2)}\\n\\`\\`\\``\n }\n ]\n };\n } catch (error) {\n Logger.error(`生成压缩指令出错:`, error);\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `生成压缩指令失败: ${error}`\n }]\n };\n }\n }\n );\n }\n\n // 生成 TinyPNG 自动化指令\n private generateTinyPngInstructions(batches: { path: string; name: string; ext: string; size: number }[][], outputFormat: string | null): string {\n let instructions = '';\n\n for (let i = 0; i < batches.length; i++) {\n const batch = batches[i];\n instructions += `#### 批次 ${i + 1}/${batches.length}\\n\\n`;\n instructions += `**文件**: ${batch.map(img => path.basename(img.path)).join(', ')}\\n\\n`;\n\n instructions += `1. **打开网站**: 使用 \\`browser_navigate\\` 访问 \\`https://tinypng.com/\\`\\n`;\n instructions += `2. **等待加载**: 使用 \\`browser_snapshot\\` 确认页面加载完成\\n`;\n instructions += `3. **上传文件**: 使用 \\`browser_file_upload\\` 上传以下文件:\\n`;\n for (const img of batch) {\n instructions += ` - \\`${img.path}\\`\\n`;\n }\n instructions += `4. **等待压缩**: 使用 \\`browser_wait_for\\` 等待 \"Download all\" 或各文件的 \"download\" 按钮出现\\n`;\n\n if (outputFormat && outputFormat !== 'png') {\n instructions += `5. **选择输出格式**: \\n`;\n instructions += ` - 点击压缩结果右侧的格式选择下拉框\\n`;\n instructions += ` - 选择 \"${outputFormat.toUpperCase()}\"\\n`;\n }\n\n instructions += `${outputFormat && outputFormat !== 'png' ? '6' : '5'}. **下载结果**: 点击 \"Download all\" 或逐个下载\\n`;\n\n if (i < batches.length - 1) {\n instructions += `${outputFormat && outputFormat !== 'png' ? '7' : '6'}. **刷新页面**: 使用 \\`browser_navigate\\` 重新访问 \\`https://tinypng.com/\\` 准备下一批\\n`;\n }\n instructions += `\\n`;\n }\n\n return instructions;\n }\n\n // 生成 AnyWebP 自动化指令\n private generateAnyWebPInstructions(batches: { path: string; name: string; ext: string; size: number }[][]): string {\n let instructions = '';\n\n for (let i = 0; i < batches.length; i++) {\n const batch = batches[i];\n instructions += `#### 批次 ${i + 1}/${batches.length}\\n\\n`;\n instructions += `**文件**: ${batch.map(img => path.basename(img.path)).join(', ')}\\n\\n`;\n\n instructions += `1. **打开网站**: 使用 \\`browser_navigate\\` 访问 \\`https://anywebp.com/convert-to-webp.html\\`\\n`;\n instructions += `2. **等待加载**: 使用 \\`browser_snapshot\\` 确认页面加载完成,找到 \"Drop your images here\" 区域\\n`;\n instructions += `3. **上传文件**: 使用 \\`browser_file_upload\\` 上传以下文件:\\n`;\n for (const img of batch) {\n instructions += ` - \\`${img.path}\\`\\n`;\n }\n instructions += `4. **等待转换**: 使用 \\`browser_wait_for\\` 等待转换完成,出现 \"Download\" 按钮\\n`;\n instructions += `5. **下载结果**: 点击 \"Download All\" 或逐个下载 WebP 文件\\n`;\n\n if (i < batches.length - 1) {\n instructions += `6. **刷新页面**: 使用 \\`browser_navigate\\` 重新访问准备下一批\\n`;\n }\n instructions += `\\n`;\n }\n\n return instructions;\n }\n\n async connect(transport: Transport): Promise<void> {\n try {\n await this.server.connect(transport);\n\n Logger.log = (...args: any[]) => {\n try {\n this.server.server.sendLoggingMessage({\n level: \"info\",\n data: args,\n });\n } catch (error) {\n console.log(...args);\n }\n };\n\n Logger.error = (...args: any[]) => {\n try {\n this.server.server.sendLoggingMessage({\n level: \"error\",\n data: args,\n });\n } catch (error) {\n console.error(...args);\n }\n };\n\n Logger.log(\"OSS MCP服务器已连接并准备处理请求\");\n } catch (error) {\n console.error(\"连接到传输时出错:\", error);\n }\n }\n\n async startHttpServer(port: number): Promise<void> {\n const app = express();\n\n // SSE连接端点 - 修复头部发送冲突\n app.get(\"/sse\", (req: Request, res: Response) => {\n // 初始化SSE传输,不再自己设置头部,而是让SDK处理\n this.sseTransport = new SSEServerTransport(\n \"/messages\",\n res as unknown as ServerResponse<IncomingMessage>\n );\n\n try {\n // 连接到传输层\n this.server.connect(this.sseTransport)\n .catch((err) => {\n console.error(\"连接到SSE传输时出错:\", err);\n });\n\n // 处理客户端断开连接\n req.on('close', () => {\n console.log('SSE客户端断开连接');\n this.sseTransport = null;\n });\n } catch (error) {\n console.error(\"建立SSE连接时出错:\", error);\n // 如果连接失败,关闭响应\n if (!res.writableEnded) {\n res.status(500).end();\n }\n }\n });\n\n // 消息端点\n app.post(\"/messages\", async (req: Request, res: Response) => {\n if (!this.sseTransport) {\n console.log(\"尝试发送消息,但SSE传输未初始化\");\n res.status(400).json({\n error: 'SSE连接未建立',\n message: '请先连接到/sse端点'\n });\n return;\n }\n\n try {\n await this.sseTransport.handlePostMessage(\n req as unknown as IncomingMessage,\n res as unknown as ServerResponse<IncomingMessage>\n );\n } catch (error) {\n console.error(\"处理消息时出错:\", error);\n if (!res.writableEnded) {\n res.status(500).json({\n error: \"内部服务器错误\",\n message: String(error)\n });\n }\n }\n });\n\n // 启动服务器\n app.listen(port, () => {\n Logger.log = console.log;\n Logger.error = console.error;\n\n Logger.log(`HTTP服务器监听端口: ${port}`);\n Logger.log(`SSE端点: http://localhost:${port}/sse`);\n Logger.log(`消息端点: http://localhost:${port}/messages`);\n });\n }\n}\n","import OSS from 'ali-oss';\nimport fs from 'fs';\nimport path from 'path';\nimport { OssConfig, getOssConfig, getAllOssConfigs } from '../config/oss.config.js';\nimport { z } from 'zod';\n\n// 上传文件参数验证Schema\nexport const UploadFileParamsSchema = z.object({\n filePath: z.string(),\n targetDir: z.string().optional(),\n fileName: z.string().optional(),\n configName: z.string().optional(),\n});\n\n// 导出上传文件参数类型\nexport type UploadFileParams = z.infer<typeof UploadFileParamsSchema>;\n\n// 上传结果验证Schema\nexport const UploadResultSchema = z.object({\n success: z.boolean(),\n url: z.string().optional(),\n error: z.string().optional(),\n ossConfigName: z.string().optional(),\n});\n\n// 导出上传结果类型\nexport type UploadResult = z.infer<typeof UploadResultSchema>;\n\n/**\n * OSS配置接口(包含ID和名称)\n */\nexport interface OssConfigWithMeta extends OssConfig {\n id: string;\n name: string;\n}\n\n/**\n * 阿里云OSS服务类\n */\nexport class OssService {\n private clients: Map<string, OSS> = new Map();\n\n /**\n * 获取所有OSS配置\n * @returns OSS配置列表\n */\n getConfigs(): OssConfigWithMeta[] {\n const configs: OssConfigWithMeta[] = [];\n const allConfigs = getAllOssConfigs();\n\n for (const [id, config] of Object.entries(allConfigs)) {\n configs.push({\n id,\n name: `${id.charAt(0).toUpperCase()}${id.slice(1)} 配置`,\n ...config\n });\n }\n\n return configs;\n }\n\n /**\n * 获取OSS客户端\n * @param configName 配置名称\n * @returns OSS客户端实例\n */\n private getClient(configName: string = 'default'): OSS | null {\n // 检查缓存中是否已有客户端\n if (this.clients.has(configName)) {\n return this.clients.get(configName) as OSS;\n }\n\n // 获取配置并创建客户端\n const config = getOssConfig(configName);\n if (!config) {\n return null;\n }\n\n try {\n const client = new OSS({\n region: config.region,\n accessKeyId: config.accessKeyId,\n accessKeySecret: config.accessKeySecret,\n bucket: config.bucket,\n endpoint: config.endpoint\n });\n\n // 缓存客户端实例\n this.clients.set(configName, client);\n return client;\n } catch (error) {\n console.error(`Failed to create OSS client for ${configName}:`, error);\n return null;\n }\n }\n\n /**\n * 上传文件到OSS\n * @param params 上传参数\n * @returns 上传结果\n */\n async uploadFile(params: UploadFileParams): Promise<UploadResult> {\n // 验证并解析参数\n const validParams = UploadFileParamsSchema.parse(params);\n const { filePath, targetDir = '', fileName, configName = 'default' } = validParams;\n\n try {\n // 检查文件是否存在\n if (!fs.existsSync(filePath)) {\n return UploadResultSchema.parse({\n success: false,\n error: `File not found: ${filePath}`,\n ossConfigName: configName\n });\n }\n\n // 获取OSS客户端\n const client = this.getClient(configName);\n if (!client) {\n return UploadResultSchema.parse({\n success: false,\n error: `OSS config not found for: ${configName}`,\n ossConfigName: configName\n });\n }\n\n // 确定文件名\n const actualFileName = fileName || path.basename(filePath);\n\n // 构建OSS路径,确保正斜杠格式\n let ossPath = actualFileName;\n if (targetDir) {\n // 规范化目标目录:移除头尾斜杠,然后加上结尾斜杠\n const normalizedDir = targetDir.replace(/^\\/+|\\/+$/g, '');\n ossPath = normalizedDir ? `${normalizedDir}/${actualFileName}` : actualFileName;\n }\n\n // 上传文件\n const result = await client.put(ossPath, filePath);\n\n return UploadResultSchema.parse({\n success: true,\n url: result.url,\n ossConfigName: configName\n });\n } catch (error) {\n return UploadResultSchema.parse({\n success: false,\n error: `Upload failed: ${(error as Error).message}`,\n ossConfigName: configName\n });\n }\n }\n\n /**\n * 重命名OSS文件(通过 copy + delete 实现)\n * @param oldKey 原文件路径\n * @param newKey 新文件路径\n * @param configName 配置名称\n * @returns 重命名结果\n */\n async renameFile(oldKey: string, newKey: string, configName: string = 'default'): Promise<{ success: boolean; error?: string }> {\n try {\n const client = this.getClient(configName);\n if (!client) {\n return { success: false, error: `OSS config not found for: ${configName}` };\n }\n\n // 规范化路径:移除开头的斜杠\n const normalizedOldKey = oldKey.replace(/^\\/+/, '');\n const normalizedNewKey = newKey.replace(/^\\/+/, '');\n\n // 检查源文件是否存在\n try {\n await client.head(normalizedOldKey);\n } catch (_e) {\n return { success: false, error: `源文件不存在: ${normalizedOldKey}` };\n }\n\n // 检查目标文件是否已存在\n try {\n await client.head(normalizedNewKey);\n // 如果到这里说明文件存在\n if (normalizedOldKey !== normalizedNewKey) {\n return { success: false, error: `目标文件已存在: ${normalizedNewKey}` };\n }\n } catch (_e) {\n // 文件不存在,可以继续\n }\n\n // Step 1: 复制文件到新位置\n await client.copy(normalizedNewKey, normalizedOldKey);\n\n // Step 2: 删除原文件\n await client.delete(normalizedOldKey);\n\n return { success: true };\n } catch (error) {\n return { success: false, error: `重命名失败: ${(error as Error).message}` };\n }\n }\n\n /**\n * 列出OSS目录下的文件\n * @param directory OSS目录路径\n * @param configName 配置名称\n * @param pattern 文件名过滤模式(可选)\n * @returns 文件列表\n */\n async listFiles(\n directory: string = '',\n configName: string = 'default',\n pattern?: string\n ): Promise<{ success: boolean; files?: Array<{ name: string; size: number; lastModified: Date }>; error?: string }> {\n try {\n const client = this.getClient(configName);\n if (!client) {\n return { success: false, error: `OSS config not found for: ${configName}` };\n }\n\n // 规范化目录路径\n const normalizedDir = directory.replace(/^\\/+|\\/+$/g, '');\n const prefix = normalizedDir ? `${normalizedDir}/` : '';\n\n // 列出文件\n const result = await client.list({\n prefix,\n delimiter: '/',\n 'max-keys': 1000\n }, {});\n\n const files: Array<{ name: string; size: number; lastModified: Date }> = [];\n\n // 处理文件对象\n if (result.objects) {\n for (const obj of result.objects) {\n // 跳过目录本身(以 / 结尾的)\n if (obj.name.endsWith('/')) continue;\n\n // 提取文件名(去掉目录前缀)\n const fileName = obj.name.replace(prefix, '');\n if (!fileName) continue;\n\n // 如果有 pattern,进行简单的通配符匹配\n if (pattern) {\n const regex = new RegExp(\n '^' + pattern\n .replace(/\\./g, '\\\\.')\n .replace(/\\*/g, '.*')\n .replace(/\\?/g, '.') + '$',\n 'i'\n );\n if (!regex.test(fileName)) continue;\n }\n\n files.push({\n name: fileName,\n size: obj.size,\n lastModified: new Date(obj.lastModified)\n });\n }\n }\n\n // 按文件名排序\n files.sort((a, b) => a.name.localeCompare(b.name));\n\n return { success: true, files };\n } catch (error) {\n return { success: false, error: `列出文件失败: ${(error as Error).message}` };\n }\n }\n\n /**\n * 批量重命名OSS文件\n * @param rules 重命名规则数组\n * @param directory OSS目录路径\n * @param configName 配置名称\n * @returns 批量重命名结果\n */\n async batchRenameFiles(\n rules: Array<{ oldName: string; newName: string }>,\n directory: string = '',\n configName: string = 'default'\n ): Promise<Array<{ oldName: string; newName: string; success: boolean; error?: string }>> {\n const results: Array<{ oldName: string; newName: string; success: boolean; error?: string }> = [];\n\n // 规范化目录路径\n const normalizedDir = directory.replace(/^\\/+|\\/+$/g, '');\n const dirPrefix = normalizedDir ? `${normalizedDir}/` : '';\n\n for (const rule of rules) {\n const oldKey = `${dirPrefix}${rule.oldName}`;\n const newKey = `${dirPrefix}${rule.newName}`;\n\n const result = await this.renameFile(oldKey, newKey, configName);\n results.push({\n oldName: rule.oldName,\n newName: rule.newName,\n success: result.success,\n error: result.error\n });\n }\n\n return results;\n }\n}\n\n// 导出单例实例\nexport const ossService = new OssService();\n","import { config } from \"dotenv\";\nimport yargs from \"yargs\";\nimport { hideBin } from \"yargs/helpers\";\nimport { z } from \"zod\";\n\nconfig();\n\n// OSS配置验证Schema\nexport const OssConfigSchema = z.object({\n region: z.string(),\n accessKeyId: z.string(),\n accessKeySecret: z.string(),\n bucket: z.string(),\n endpoint: z.string(),\n});\n\n// 导出OSS配置类型\nexport type OssConfig = z.infer<typeof OssConfigSchema>;\n\n// 服务器配置接口\nexport interface ServerConfig {\n port: number;\n ossConfig: Record<string, OssConfig>;\n configSources: {\n port: \"cli\" | \"env\" | \"default\";\n ossConfig: \"cli\" | \"env\" | \"default\";\n };\n}\n\n// 掩码函数,用于打印敏感信息\nfunction maskSecret(secret: string): string {\n if (secret.length <= 4) return \"****\";\n return `${secret.substring(0, 4)}****${secret.slice(-4)}`;\n}\n\n// 获取服务器配置\nexport function getServerConfig(isStdioMode: boolean = false): ServerConfig {\n // 解析命令行参数\n const argv = yargs(hideBin(process.argv))\n .options({\n \"oss-config\": {\n type: \"string\",\n description: \"OSS配置JSON字符串\",\n },\n port: {\n type: \"number\",\n description: \"服务器运行端口\",\n default: 3000,\n },\n })\n .help()\n .version(\"1.0.0\")\n .parseSync();\n\n const config: ServerConfig = {\n port: 3000,\n ossConfig: {},\n configSources: {\n port: \"default\",\n ossConfig: \"default\",\n },\n };\n\n // 处理端口配置\n if (argv.port) {\n config.port = argv.port;\n config.configSources.port = \"cli\";\n } else if (process.env.PORT) {\n config.port = parseInt(process.env.PORT, 10);\n config.configSources.port = \"env\";\n }\n\n // 处理OSS配置 - 首先检查命令行参数\n if (argv[\"oss-config\"]) {\n const allOssConfigs = JSON.parse(argv[\"oss-config\"] as string);\n\n if (allOssConfigs.region && allOssConfigs.accessKeyId) {\n config.ossConfig.default = OssConfigSchema.parse(allOssConfigs);\n } else {\n Object.entries(allOssConfigs).forEach(([name, cfg]) => {\n config.ossConfig[name.toLowerCase()] = OssConfigSchema.parse(cfg);\n });\n }\n config.configSources.ossConfig = \"cli\";\n } else if (process.env.OSS_CONFIG_DEFAULT) {\n const ossConfig = JSON.parse(process.env.OSS_CONFIG_DEFAULT)\n config.ossConfig.default = OssConfigSchema.parse(ossConfig);\n config.configSources.ossConfig = \"env\";\n }\n\n // 检查其他命名的OSS配置\n Object.entries(process.env).forEach(([key, value]) => {\n if (key.startsWith(\"OSS_CONFIG_\") && key !== \"OSS_CONFIG_DEFAULT\" && value) {\n try {\n const configName = key.replace(\"OSS_CONFIG_\", \"\").toLowerCase();\n const ossConfig = JSON.parse(value);\n config.ossConfig[configName] = OssConfigSchema.parse(ossConfig);\n } catch (error) {\n console.error(`解析环境变量${key}失败:`, error);\n }\n }\n });\n\n // 验证配置\n if (Object.keys(config.ossConfig).length === 0) {\n console.warn(\"未找到有效的OSS配置。服务器将启动,但上传功能将不可用。\");\n }\n\n // 打印配置信息(非stdio模式下)\n if (!isStdioMode) {\n console.log(\"\\n配置信息:\");\n console.log(`- 端口: ${config.port} (来源: ${config.configSources.port})`);\n\n if (Object.keys(config.ossConfig).length > 0) {\n console.log(\"- OSS配置:\");\n Object.entries(config.ossConfig).forEach(([name, cfg]) => {\n console.log(` - ${name}:`);\n console.log(` Region: ${cfg.region}`);\n console.log(` Endpoint: ${cfg.endpoint}`);\n console.log(` Bucket: ${cfg.bucket}`);\n console.log(` AccessKeyId: ${maskSecret(cfg.accessKeyId)}`);\n console.log(` AccessKeySecret: ${maskSecret(cfg.accessKeySecret)}`);\n });\n } else {\n console.log(\"- OSS配置: 未找到\");\n }\n console.log(); // 空行,增加可读性\n }\n\n return config;\n}\n\n// 获取所有OSS配置\nexport function getAllOssConfigs(): Record<string, OssConfig> {\n const { ossConfig } = getServerConfig(true);\n return ossConfig;\n}\n\n// 获取特定名称的OSS配置\nexport function getOssConfig(name: string = 'default'): OssConfig | null {\n const configs = getAllOssConfigs();\n const normalizedName = name.toLowerCase();\n return configs[normalizedName] || null;\n}\n\n// 获取可用的OSS配置名称列表\nexport function getAvailableOssConfigNames(): string[] {\n return Object.keys(getAllOssConfigs());\n}\n"],"mappings":"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","names":["z","config","z","config","fs","path","config","z","configs","configNames","resolve","fileStream","config"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/server.ts","../src/services/oss.service.ts","../src/config/oss.config.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * 实现阿里云OSS文件上传功能。\n * - 上传文件到阿里云OSS\n * - 获取可用的OSS配置\n */\n\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { OssMcpServer } from \"./server.js\";\nimport { getServerConfig } from \"./config/oss.config.js\";\nimport { resolve } from \"path\";\nimport { config } from \"dotenv\";\n\n// 加载当前工作目录中的.env文件\nconfig({ path: resolve(process.cwd(), \".env\") });\n\nexport async function startServer(): Promise<void> {\n // 检查是否在stdio模式下运行\n const isStdioMode = process.env.NODE_ENV === \"cli\" || process.argv.includes(\"--stdio\");\n\n // 获取服务器配置\n const serverConfig = getServerConfig(isStdioMode);\n\n // 创建OSS MCP服务器\n const server = new OssMcpServer();\n\n if (isStdioMode) {\n // 在stdio模式下运行\n const transport = new StdioServerTransport();\n await server.connect(transport);\n } else {\n // 在HTTP模式下运行\n console.log(`初始化OSS MCP服务器,HTTP模式,端口: ${serverConfig.port}...`);\n await server.startHttpServer(serverConfig.port);\n }\n}\n\n// 启动服务器\nstartServer().catch((error) => {\n console.error(\"启动服务器失败:\", error);\n process.exit(1);\n});\n","import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { ossService } from \"./services/oss.service.js\";\nimport express, { Request, Response } from \"express\";\nimport { SSEServerTransport } from \"@modelcontextprotocol/sdk/server/sse.js\";\nimport { IncomingMessage, ServerResponse } from \"http\";\nimport { Transport } from \"@modelcontextprotocol/sdk/shared/transport.js\";\nimport fs from 'fs';\nimport path from 'path';\nimport https from 'https';\nimport http from 'http';\n\nexport const Logger = {\n log: (...args: any[]) => {\n console.log(...args);\n },\n error: (...args: any[]) => {\n console.error(...args);\n }\n};\n\nexport class OssMcpServer {\n private readonly server: McpServer;\n private sseTransport: SSEServerTransport | null = null;\n\n constructor() {\n this.server = new McpServer(\n {\n name: \"@yhy2001/oss-mcp\",\n version: \"1.0.0\",\n },\n // 使用正确格式的capabilities配置\n {\n capabilities: {\n tools: { listChanged: true },\n resources: { listChanged: true },\n prompts: { listChanged: true },\n logging: {}\n }\n }\n );\n\n this.registerTools();\n }\n\n private registerTools(): void {\n // 获取可用的OSS配置\n const configs = ossService.getConfigs();\n const configNames = configs.map(config => config.id);\n\n // 工具:上传文件到OSS\n this.server.tool(\n \"upload_to_oss\",\n \"将文件上传到阿里云OSS\",\n {\n filePath: z.string().describe(\"要上传的本地文件路径\"),\n targetDir: z.string().optional().describe(\"OSS中的目标目录路径(可选)\"),\n fileName: z.string().optional().describe(\"上传后的文件名(可选,默认使用原文件名)\"),\n configName: z.string().optional().describe(`OSS配置名称(可选,默认为'default')。可用配置: ${configNames.join(', ') || '无'}`)\n },\n async ({ filePath, targetDir, fileName, configName }) => {\n try {\n Logger.log(`准备上传: ${filePath} 到 ${targetDir || '根目录'}`);\n\n if (!filePath) {\n throw new Error(\"文件路径是必需的\");\n }\n\n // 检查文件是否存在\n if (!fs.existsSync(filePath)) {\n throw new Error(`文件不存在: ${filePath}`);\n }\n\n // 执行上传\n const result = await ossService.uploadFile({\n filePath,\n targetDir,\n fileName,\n configName\n });\n\n if (result.success) {\n Logger.log(`上传成功: ${result.url}`);\n return {\n content: [{\n type: \"text\",\n text: `文件上传成功!\\n文件名: ${path.basename(filePath)}\\n目标位置: ${targetDir || '根目录'}\\nURL: ${result.url}\\n配置名称: ${result.ossConfigName}`\n }]\n };\n } else {\n Logger.error(`上传失败: ${result.error}`);\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `上传失败: ${result.error}`\n }]\n };\n }\n } catch (error) {\n Logger.error(`上传过程中出错:`, error);\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `上传出错: ${error}`\n }]\n };\n }\n }\n );\n\n // 工具:列出可用的OSS配置\n this.server.tool(\n \"list_oss_configs\",\n \"列出可用的阿里云OSS配置\",\n {},\n async () => {\n try {\n const configs = ossService.getConfigs();\n const configNames = configs.map(config => config.id);\n\n if (configNames.length === 0) {\n return {\n content: [{\n type: \"text\",\n text: \"未找到OSS配置。请检查环境变量设置。\"\n }]\n };\n }\n\n return {\n content: [{\n type: \"text\",\n text: `可用的OSS配置:\\n${configNames.map(name => `- ${name}`).join('\\n')}`\n }]\n };\n } catch (error) {\n Logger.error(`获取OSS配置列表时出错:`, error);\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `获取配置列表失败: ${error}`\n }]\n };\n }\n }\n );\n\n // 工具:批量重命名OSS文件\n this.server.tool(\n \"batch_rename_files\",\n \"批量重命名阿里云OSS文件。通过copy+delete实现。【重要】首次调用必须使用dryRun=true预览,展示给用户确认后,用户同意才能用dryRun=false执行实际重命名。禁止跳过预览直接执行!\",\n {\n directory: z.string().describe(\"OSS中的目录路径(如 'images/icons',根目录传空字符串 '')\"),\n renameRules: z.array(z.object({\n oldName: z.string().describe(\"原文件名\"),\n newName: z.string().describe(\"新文件名\")\n })).describe(\"重命名规则数组,每项包含原文件名和新文件名\"),\n configName: z.string().optional().describe(`OSS配置名称(默认为'default')。可用配置: ${configNames.join(', ') || '无'}`),\n dryRun: z.boolean().optional().describe(\"是否为预览模式(默认false)。为true时只返回将要执行的操作,不实际重命名\")\n },\n async ({ directory, renameRules, configName = 'default', dryRun = false }) => {\n try {\n Logger.log(`OSS批量重命名: 目录=${directory}, 规则数=${renameRules.length}, 配置=${configName}, 预览模式=${dryRun}`);\n\n let results: { oldName: string; newName: string; success: boolean; error?: string }[];\n\n if (dryRun) {\n // 预览模式:只返回将要执行的操作\n results = renameRules.map(rule => ({\n oldName: rule.oldName,\n newName: rule.newName,\n success: true\n }));\n } else {\n // 实际执行OSS重命名\n results = await ossService.batchRenameFiles(renameRules, directory, configName);\n }\n\n const successCount = results.filter(r => r.success).length;\n const failCount = results.filter(r => !r.success).length;\n\n let resultText = dryRun ? `【预览模式】以下是将要执行的OSS文件重命名操作:\\n\\n` : `OSS文件批量重命名完成:\\n\\n`;\n resultText += `配置: ${configName}\\n`;\n resultText += `目录: ${directory || '根目录'}\\n`;\n resultText += `成功: ${successCount} 个, 失败: ${failCount} 个\\n\\n`;\n\n if (results.length > 0) {\n resultText += '详细结果:\\n';\n for (const r of results) {\n if (r.success) {\n resultText += `✅ ${r.oldName} → ${r.newName}\\n`;\n } else {\n resultText += `❌ ${r.oldName} → ${r.newName} (${r.error})\\n`;\n }\n }\n }\n\n return {\n content: [{\n type: \"text\",\n text: resultText\n }]\n };\n } catch (error) {\n Logger.error(`OSS批量重命名出错:`, error);\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `OSS批量重命名失败: ${error}`\n }]\n };\n }\n }\n );\n\n // 工具:列出本地目录文件\n this.server.tool(\n \"list_directory_files\",\n \"列出本地文件系统中指定目录下的所有文件。注意:此工具仅支持本地路径,如果要列出 OSS 中的文件,请使用 list_oss_files 工具。\",\n {\n directory: z.string().describe(\"要查看的目录路径\"),\n pattern: z.string().optional().describe(\"文件名过滤模式(可选),如 '*.png' 或 'icon_*'\")\n },\n async ({ directory, pattern }) => {\n try {\n Logger.log(`列出目录文件: ${directory}, 过滤: ${pattern || '无'}`);\n\n // 检查目录是否存在\n if (!fs.existsSync(directory)) {\n throw new Error(`目录不存在: ${directory}`);\n }\n\n const stat = fs.statSync(directory);\n if (!stat.isDirectory()) {\n throw new Error(`路径不是目录: ${directory}`);\n }\n\n let files = fs.readdirSync(directory);\n\n // 过滤掉隐藏文件\n files = files.filter(f => !f.startsWith('.'));\n\n // 如果有 pattern,进行简单的通配符匹配\n if (pattern) {\n const regex = new RegExp(\n '^' + pattern\n .replace(/\\./g, '\\\\.')\n .replace(/\\*/g, '.*')\n .replace(/\\?/g, '.') + '$',\n 'i'\n );\n files = files.filter(f => regex.test(f));\n }\n\n // 获取文件信息\n const fileInfos = files.map(f => {\n const filePath = path.join(directory, f);\n const fileStat = fs.statSync(filePath);\n return {\n name: f,\n isDirectory: fileStat.isDirectory(),\n size: fileStat.size\n };\n });\n\n // 排序:目录在前,文件在后,按名称排序\n fileInfos.sort((a, b) => {\n if (a.isDirectory !== b.isDirectory) {\n return a.isDirectory ? -1 : 1;\n }\n return a.name.localeCompare(b.name);\n });\n\n if (fileInfos.length === 0) {\n return {\n content: [{\n type: \"text\",\n text: `目录 ${directory} 下没有找到匹配的文件${pattern ? ` (过滤: ${pattern})` : ''}`\n }]\n };\n }\n\n let resultText = `目录: ${directory}\\n`;\n if (pattern) {\n resultText += `过滤: ${pattern}\\n`;\n }\n resultText += `共 ${fileInfos.length} 个项目:\\n\\n`;\n\n for (const f of fileInfos) {\n if (f.isDirectory) {\n resultText += `📁 ${f.name}/\\n`;\n } else {\n const sizeStr = f.size < 1024\n ? `${f.size}B`\n : f.size < 1024 * 1024\n ? `${(f.size / 1024).toFixed(1)}KB`\n : `${(f.size / 1024 / 1024).toFixed(1)}MB`;\n resultText += `📄 ${f.name} (${sizeStr})\\n`;\n }\n }\n\n return {\n content: [{\n type: \"text\",\n text: resultText\n }]\n };\n } catch (error) {\n Logger.error(`列出目录文件出错:`, error);\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `列出目录失败: ${error}`\n }]\n };\n }\n }\n );\n\n // 工具:列出OSS目录文件\n this.server.tool(\n \"list_oss_files\",\n \"列出阿里云OSS指定目录下的所有文件。用于查看 OSS 中的文件以便进行重命名或其他操作。注意:如果要列出本地文件,请使用 list_directory_files 工具。\",\n {\n directory: z.string().describe(\"OSS中的目录路径(如 'images/icons',根目录传空字符串 '')\"),\n pattern: z.string().optional().describe(\"文件名过滤模式(可选),如 '*.png' 或 'icon_*'\"),\n configName: z.string().optional().describe(`OSS配置名称(默认为'default')。可用配置: ${configNames.join(', ') || '无'}`)\n },\n async ({ directory, pattern, configName = 'default' }) => {\n try {\n Logger.log(`列出OSS目录文件: ${directory || '根目录'}, 过滤: ${pattern || '无'}, 配置: ${configName}`);\n\n const result = await ossService.listFiles(directory, configName, pattern);\n\n if (!result.success) {\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `列出OSS文件失败: ${result.error}`\n }]\n };\n }\n\n const files = result.files || [];\n\n if (files.length === 0) {\n return {\n content: [{\n type: \"text\",\n text: `OSS目录 ${directory || '根目录'} 下没有找到匹配的文件${pattern ? ` (过滤: ${pattern})` : ''}\\n配置: ${configName}`\n }]\n };\n }\n\n const sizeStr = (size: number) => size < 1024\n ? `${size}B`\n : size < 1024 * 1024\n ? `${(size / 1024).toFixed(1)}KB`\n : `${(size / 1024 / 1024).toFixed(1)}MB`;\n\n let resultText = `OSS目录: ${directory || '根目录'}\\n`;\n resultText += `配置: ${configName}\\n`;\n if (pattern) {\n resultText += `过滤: ${pattern}\\n`;\n }\n resultText += `共 ${files.length} 个文件:\\n\\n`;\n\n for (const f of files) {\n resultText += `📄 ${f.name} (${sizeStr(f.size)})\\n`;\n }\n\n return {\n content: [{\n type: \"text\",\n text: resultText\n }]\n };\n } catch (error) {\n Logger.error(`列出OSS目录文件出错:`, error);\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `列出OSS目录失败: ${error}`\n }]\n };\n }\n }\n );\n\n // 工具:删除OSS文件\n // 检查环境变量是否允许删除操作\n const allowDeleteOperation = process.env.ALLOW_DELETE_OPERATION === 'true';\n\n this.server.tool(\n \"delete_oss_files\",\n `删除阿里云OSS中的文件。支持单个删除、批量删除和通配符匹配。\n\n【⚠️ 安全限制】此工具需要配置环境变量 ALLOW_DELETE_OPERATION=true 才能使用。\n当前状态: ${allowDeleteOperation ? '✅ 已启用' : '❌ 未启用(需要在 MCP 配置中添加 \"env\": { \"ALLOW_DELETE_OPERATION\": \"true\" })'}\n\n【重要】首次调用必须使用 dryRun=true 预览,展示给用户确认后,用户同意才能用 dryRun=false 执行实际删除。禁止跳过预览直接执行!`,\n {\n directory: z.string().describe(\"OSS中的目录路径(如 'images/icons',根目录传空字符串 '')\"),\n fileNames: z.array(z.string()).optional().describe(\"要删除的文件名数组(与 pattern 二选一)\"),\n pattern: z.string().optional().describe(\"文件名通配符模式(如 '*.tmp' 或 'test_*'),与 fileNames 二选一\"),\n configName: z.string().optional().describe(`OSS配置名称(默认为'default')。可用配置: ${configNames.join(', ') || '无'}`),\n dryRun: z.boolean().optional().describe(\"是否为预览模式(默认false)。为true时只返回将要删除的文件列表,不实际删除\")\n },\n async ({ directory, fileNames, pattern, configName = 'default', dryRun = false }) => {\n try {\n // 检查是否允许删除操作\n if (!allowDeleteOperation) {\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `❌ 删除操作被禁止!\n\n要启用删除功能,请在 MCP 配置中添加环境变量:\n\n{\n \"mcpServers\": {\n \"oss-mcp-plus\": {\n \"command\": \"npx\",\n \"args\": [\"oss-mcp-plus\", ...],\n \"env\": {\n \"ALLOW_DELETE_OPERATION\": \"true\"\n }\n }\n }\n}\n\n这是一个安全措施,防止误删除文件。`\n }]\n };\n }\n\n Logger.log(`删除OSS文件: 目录=${directory}, 配置=${configName}, 预览模式=${dryRun}`);\n\n // 必须提供 fileNames 或 pattern 之一\n if (!fileNames && !pattern) {\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: \"请提供 fileNames(文件名数组)或 pattern(通配符模式)之一\"\n }]\n };\n }\n\n let filesToDelete: string[] = [];\n\n if (fileNames && fileNames.length > 0) {\n // 直接使用提供的文件名\n filesToDelete = fileNames;\n } else if (pattern) {\n // 使用通配符匹配文件\n const listResult = await ossService.listFiles(directory, configName, pattern);\n if (!listResult.success) {\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `列出文件失败: ${listResult.error}`\n }]\n };\n }\n filesToDelete = (listResult.files || []).map(f => f.name);\n }\n\n if (filesToDelete.length === 0) {\n return {\n content: [{\n type: \"text\",\n text: `没有找到匹配的文件${pattern ? ` (模式: ${pattern})` : ''}\\n目录: ${directory || '根目录'}\\n配置: ${configName}`\n }]\n };\n }\n\n if (dryRun) {\n // 预览模式:只返回将要删除的文件列表\n let resultText = `【预览模式】以下是将要删除的文件:\\n\\n`;\n resultText += `配置: ${configName}\\n`;\n resultText += `目录: ${directory || '根目录'}\\n`;\n if (pattern) {\n resultText += `匹配模式: ${pattern}\\n`;\n }\n resultText += `文件数量: ${filesToDelete.length}\\n\\n`;\n resultText += `文件列表:\\n`;\n for (const fileName of filesToDelete) {\n resultText += `🗑️ ${fileName}\\n`;\n }\n resultText += `\\n⚠️ 确认要删除这些文件后,请使用 dryRun=false 执行实际删除。`;\n\n return {\n content: [{\n type: \"text\",\n text: resultText\n }]\n };\n }\n\n // 实际执行删除\n const results = await ossService.batchDeleteFiles(filesToDelete, directory, configName);\n\n const successCount = results.filter(r => r.success).length;\n const failCount = results.filter(r => !r.success).length;\n\n let resultText = `OSS文件删除完成:\\n\\n`;\n resultText += `配置: ${configName}\\n`;\n resultText += `目录: ${directory || '根目录'}\\n`;\n resultText += `成功: ${successCount} 个, 失败: ${failCount} 个\\n\\n`;\n\n if (results.length > 0) {\n resultText += '详细结果:\\n';\n for (const r of results) {\n if (r.success) {\n resultText += `✅ ${r.fileName} 已删除\\n`;\n } else {\n resultText += `❌ ${r.fileName} (${r.error})\\n`;\n }\n }\n }\n\n return {\n content: [{\n type: \"text\",\n text: resultText\n }]\n };\n } catch (error) {\n Logger.error(`删除OSS文件出错:`, error);\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `删除OSS文件失败: ${error}`\n }]\n };\n }\n }\n );\n\n // 工具:下载文件\n this.server.tool(\n \"download_file\",\n \"从 URL 下载文件到本地目录。支持 HTTP/HTTPS 链接,可自定义保存文件名。\",\n {\n url: z.string().describe(\"要下载的文件 URL\"),\n targetDir: z.string().describe(\"保存文件的本地目录路径\"),\n fileName: z.string().optional().describe(\"保存的文件名(可选,默认从 URL 提取)\")\n },\n async ({ url, targetDir, fileName }) => {\n try {\n Logger.log(`下载文件: ${url} 到 ${targetDir}`);\n\n // 检查目录是否存在,不存在则创建\n if (!fs.existsSync(targetDir)) {\n fs.mkdirSync(targetDir, { recursive: true });\n Logger.log(`创建目录: ${targetDir}`);\n }\n\n const stat = fs.statSync(targetDir);\n if (!stat.isDirectory()) {\n throw new Error(`路径不是目录: ${targetDir}`);\n }\n\n // 从 URL 提取文件名\n let finalFileName = fileName;\n if (!finalFileName) {\n const urlObj = new URL(url);\n finalFileName = path.basename(urlObj.pathname);\n // 如果 URL 没有文件名,生成一个\n if (!finalFileName || finalFileName === '/') {\n finalFileName = `download_${Date.now()}`;\n }\n }\n\n const filePath = path.join(targetDir, finalFileName);\n\n // 检查文件是否已存在\n if (fs.existsSync(filePath)) {\n throw new Error(`文件已存在: ${filePath}`);\n }\n\n // 下载文件\n await new Promise<void>((resolve, reject) => {\n const urlObj = new URL(url);\n const protocol = urlObj.protocol === 'https:' ? https : http;\n\n const request = protocol.get(url, (response) => {\n // 处理重定向\n if (response.statusCode === 301 || response.statusCode === 302) {\n const redirectUrl = response.headers.location;\n if (redirectUrl) {\n Logger.log(`重定向到: ${redirectUrl}`);\n const redirectProtocol = redirectUrl.startsWith('https:') ? https : http;\n redirectProtocol.get(redirectUrl, (redirectResponse) => {\n if (redirectResponse.statusCode !== 200) {\n reject(new Error(`下载失败,HTTP 状态码: ${redirectResponse.statusCode}`));\n return;\n }\n const fileStream = fs.createWriteStream(filePath);\n redirectResponse.pipe(fileStream);\n fileStream.on('finish', () => {\n fileStream.close();\n resolve();\n });\n fileStream.on('error', (err) => {\n fs.unlink(filePath, () => {});\n reject(err);\n });\n }).on('error', reject);\n return;\n }\n }\n\n if (response.statusCode !== 200) {\n reject(new Error(`下载失败,HTTP 状态码: ${response.statusCode}`));\n return;\n }\n\n const fileStream = fs.createWriteStream(filePath);\n response.pipe(fileStream);\n\n fileStream.on('finish', () => {\n fileStream.close();\n resolve();\n });\n\n fileStream.on('error', (err) => {\n fs.unlink(filePath, () => {});\n reject(err);\n });\n });\n\n request.on('error', (err) => {\n fs.unlink(filePath, () => {});\n reject(err);\n });\n\n request.setTimeout(60000, () => {\n request.destroy();\n fs.unlink(filePath, () => {});\n reject(new Error('下载超时(60秒)'));\n });\n });\n\n // 获取文件大小\n const downloadedStat = fs.statSync(filePath);\n const sizeStr = downloadedStat.size < 1024\n ? `${downloadedStat.size}B`\n : downloadedStat.size < 1024 * 1024\n ? `${(downloadedStat.size / 1024).toFixed(1)}KB`\n : `${(downloadedStat.size / 1024 / 1024).toFixed(1)}MB`;\n\n return {\n content: [{\n type: \"text\",\n text: `文件下载成功!\\n源URL: ${url}\\n保存路径: ${filePath}\\n文件大小: ${sizeStr}`\n }]\n };\n } catch (error) {\n Logger.error(`下载文件出错:`, error);\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `下载失败: ${error}`\n }]\n };\n }\n }\n );\n\n // 工具:检查压缩图片的前置条件\n this.server.tool(\n \"check_compress_prerequisites\",\n `检查图片压缩的前置条件并返回用户询问模板。\n\n【⚠️ 极其重要 - AI 必须严格按以下顺序执行】\n\n第 1 步:检查 Playwright MCP(必须在调用本工具之前完成!)\n- 调用 browser_snapshot 工具\n- 如果成功:继续执行\n- 如果失败/报错:立即停止,告知用户需要启用 Playwright MCP\n\n第 2 步:如果图片在 OSS 上(必须在调用本工具之前完成!)\n- 先使用 list_oss_files 列出文件\n- ⚠️ 重要:下载到【项目目录】下的 .tmp-compress/ 文件夹,不能用 /tmp/!\n (Playwright MCP 只能访问项目目录内的文件)\n- 使用 download_file 将图片下载到项目目录下\n- 只有下载到本地后才能调用本工具\n\n第 3 步:调用本工具\n- 传入本地图片路径数组\n- 获取用户询问模板\n\n第 4 步:询问用户\n- 使用 AskUserQuestion 询问压缩引擎、输出格式等\n\n第 5 步:执行压缩\n- 调用 compress_images 工具`,\n {\n images: z.array(z.string()).describe(\"要压缩的【本地】图片路径数组。如果图片在 OSS 上,必须先用 download_file 下载到本地!\")\n },\n async ({ images }) => {\n try {\n // 验证图片文件\n const validImages: { path: string; name: string; ext: string; size: number }[] = [];\n const errors: string[] = [];\n\n for (const imgPath of images) {\n if (!fs.existsSync(imgPath)) {\n errors.push(`文件不存在: ${imgPath}`);\n continue;\n }\n const stat = fs.statSync(imgPath);\n if (stat.size > 5 * 1024 * 1024) {\n errors.push(`文件超过 5MB 限制: ${imgPath}`);\n continue;\n }\n const ext = path.extname(imgPath).toLowerCase().slice(1);\n if (!['png', 'jpg', 'jpeg', 'webp', 'gif', 'bmp', 'tiff'].includes(ext)) {\n errors.push(`不支持的格式: ${imgPath}`);\n continue;\n }\n validImages.push({\n path: imgPath,\n name: path.basename(imgPath, path.extname(imgPath)),\n ext: ext === 'jpg' ? 'jpeg' : ext,\n size: stat.size\n });\n }\n\n if (validImages.length === 0) {\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `没有有效的图片可处理:\\n${errors.join('\\n')}`\n }]\n };\n }\n\n // 构建需要询问用户的问题\n const questions = {\n playwrightCheck: {\n instruction: \"请先使用 browser_snapshot 工具测试 Playwright MCP 是否可用。如果报错说明未配置。\"\n },\n engineQuestion: {\n question: \"请选择压缩引擎\",\n header: \"压缩引擎\",\n options: [\n { label: \"TinyPNG (推荐)\", description: \"支持 PNG/JPEG/WebP 输出,压缩质量高,每批最多 3 个文件\" },\n { label: \"AnyWebP\", description: \"固定输出 WebP 格式,每批最多 20 个文件\" }\n ]\n },\n formatQuestion: {\n question: \"是否需要转换输出格式?\",\n header: \"输出格式\",\n options: [\n { label: \"保持原格式\", description: \"不转换格式,仅压缩\" },\n { label: \"转换为 WebP\", description: \"转换为 WebP 格式,体积更小\" },\n { label: \"转换为 JPEG\", description: \"转换为 JPEG 格式(仅 TinyPNG)\" },\n { label: \"转换为 PNG\", description: \"转换为 PNG 格式(仅 TinyPNG)\" }\n ]\n },\n deleteOriginalQuestion: {\n question: \"转换格式后是否删除原文件?\",\n header: \"删除原文件\",\n options: [\n { label: \"保留原文件\", description: \"在 OSS 上保留原格式文件\" },\n { label: \"删除原文件\", description: \"转换后删除 OSS 上的原格式文件\" }\n ],\n condition: \"仅当选择了转换格式时才需要询问\"\n }\n };\n\n const sizeStr = (size: number) => size < 1024\n ? `${size}B`\n : size < 1024 * 1024\n ? `${(size / 1024).toFixed(1)}KB`\n : `${(size / 1024 / 1024).toFixed(1)}MB`;\n\n let resultText = `## 图片压缩前置检查\\n\\n`;\n resultText += `### ✅ 有效图片 (${validImages.length} 个)\\n`;\n for (const img of validImages) {\n resultText += `- ${path.basename(img.path)} (${sizeStr(img.size)})\\n`;\n }\n\n if (errors.length > 0) {\n resultText += `\\n### ⚠️ 跳过的文件\\n`;\n for (const err of errors) {\n resultText += `- ${err}\\n`;\n }\n }\n\n resultText += `\\n### 📋 AI 执行步骤\\n\\n`;\n resultText += `1. **检查 Playwright**: 调用 \\`browser_snapshot\\` 测试是否可用\\n`;\n resultText += ` - 如果报错,提示用户需要配置 Playwright MCP\\n`;\n resultText += `2. **询问用户**: 使用 AskUserQuestion 一次性询问以下问题:\\n`;\n resultText += ` - 选择压缩引擎 (TinyPNG / AnyWebP)\\n`;\n resultText += ` - 是否转换格式 (保持原格式 / WebP / JPEG / PNG)\\n`;\n resultText += ` - 如果转格式,是否删除原文件\\n`;\n resultText += `3. **执行压缩**: 根据用户选择调用 \\`compress_images\\`\\n`;\n\n return {\n content: [\n {\n type: \"text\",\n text: resultText\n },\n {\n type: \"text\",\n text: `\\n---\\n**询问模板 (JSON)**:\\n\\`\\`\\`json\\n${JSON.stringify(questions, null, 2)}\\n\\`\\`\\``\n }\n ]\n };\n } catch (error) {\n Logger.error(`检查前置条件出错:`, error);\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `检查失败: ${error}`\n }]\n };\n }\n }\n );\n\n // 工具:压缩图片(生成压缩指令,由 AI 调用 Playwright MCP 执行)\n this.server.tool(\n \"compress_images\",\n `压缩图片工具。生成 Playwright 自动化压缩指令。\n\n【⚠️ 禁止直接调用!必须先完成以下步骤】\n\n✅ 第 1 步:验证 Playwright MCP 可用\n → 调用 browser_snapshot,如果报错则停止并提示用户启用\n\n✅ 第 2 步:确保图片在项目目录内\n → OSS 图片必须先用 download_file 下载到【项目目录】下的 .tmp-compress/ 文件夹\n → ⚠️ 不能用 /tmp/,Playwright 无法访问项目外的路径!\n\n✅ 第 3 步:调用 check_compress_prerequisites\n → 验证文件并获取询问模板\n\n✅ 第 4 步:询问用户偏好\n → 使用 AskUserQuestion 询问引擎、格式、是否删除原文件\n\n✅ 第 5 步:调用本工具\n → 传入用户选择的参数\n\n【后续流程】\n1. 按返回的指令使用 Playwright MCP 执行网页自动化\n2. 下载压缩结果\n3. 使用 upload_to_oss 上传回 OSS`,\n {\n images: z.array(z.string()).describe(\"要压缩的本地图片路径数组\"),\n engine: z.enum(['tinypng', 'anywebp']).describe(\"压缩引擎 (必须先询问用户选择)\"),\n outputFormat: z.enum(['png', 'jpeg', 'webp']).optional().describe(\"输出格式 (必须先询问用户选择,仅 tinypng 支持多格式)\"),\n deleteOriginal: z.boolean().optional().describe(\"转格式时是否删除原文件 (必须先询问用户选择)\"),\n ossDirectory: z.string().optional().describe(\"OSS 目标目录 (用于上传压缩后的文件)\"),\n configName: z.string().optional().describe(`OSS配置名称(默认为'default')。可用配置: ${configNames.join(', ') || '无'}`)\n },\n async ({ images, engine, outputFormat, deleteOriginal = false, ossDirectory, configName = 'default' }) => {\n try {\n Logger.log(`压缩图片: 引擎=${engine}, 格式=${outputFormat || '原格式'}, 图片数=${images.length}`);\n\n // 验证图片文件存在\n const validImages: { path: string; name: string; ext: string; size: number }[] = [];\n const errors: string[] = [];\n\n for (const imgPath of images) {\n if (!fs.existsSync(imgPath)) {\n errors.push(`文件不存在: ${imgPath}`);\n continue;\n }\n const stat = fs.statSync(imgPath);\n if (stat.size > 5 * 1024 * 1024) {\n errors.push(`文件超过 5MB 限制: ${imgPath}`);\n continue;\n }\n const ext = path.extname(imgPath).toLowerCase().slice(1);\n if (!['png', 'jpg', 'jpeg', 'webp', 'gif', 'bmp', 'tiff'].includes(ext)) {\n errors.push(`不支持的格式: ${imgPath}`);\n continue;\n }\n validImages.push({\n path: imgPath,\n name: path.basename(imgPath, path.extname(imgPath)),\n ext: ext === 'jpg' ? 'jpeg' : ext,\n size: stat.size\n });\n }\n\n if (validImages.length === 0) {\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `没有有效的图片可处理:\\n${errors.join('\\n')}`\n }]\n };\n }\n\n // 生成压缩指令\n const actualOutputFormat = engine === 'anywebp' ? 'webp' : (outputFormat || null);\n const batchSize = engine === 'tinypng' ? 3 : 20;\n const batches: typeof validImages[] = [];\n\n for (let i = 0; i < validImages.length; i += batchSize) {\n batches.push(validImages.slice(i, i + batchSize));\n }\n\n // 构建指令文本\n let instructions = `## 图片压缩指令\\n\\n`;\n instructions += `**引擎**: ${engine === 'tinypng' ? 'TinyPNG (https://tinypng.com/)' : 'AnyWebP (https://anywebp.com/convert-to-webp)'}\\n`;\n instructions += `**输出格式**: ${actualOutputFormat || '保持原格式'}\\n`;\n instructions += `**总图片数**: ${validImages.length}\\n`;\n instructions += `**批次数**: ${batches.length} (每批最多 ${batchSize} 个)\\n\\n`;\n\n if (errors.length > 0) {\n instructions += `### ⚠️ 跳过的文件\\n`;\n for (const err of errors) {\n instructions += `- ${err}\\n`;\n }\n instructions += `\\n`;\n }\n\n instructions += `### 📋 执行步骤\\n\\n`;\n instructions += `**前置检查**: 请确认 Playwright MCP 已配置并可用\\n\\n`;\n\n if (engine === 'tinypng') {\n instructions += this.generateTinyPngInstructions(batches, actualOutputFormat);\n } else {\n instructions += this.generateAnyWebPInstructions(batches);\n }\n\n // 添加后续处理指令\n instructions += `\\n### 📤 后续处理\\n\\n`;\n instructions += `压缩完成后,请执行以下操作:\\n\\n`;\n\n for (const img of validImages) {\n const newExt = actualOutputFormat || img.ext;\n const isFormatChange = newExt !== img.ext;\n const newFileName = `${img.name}.${newExt}`;\n const downloadPath = path.join(path.dirname(img.path), `${img.name}-compressed.${newExt}`);\n\n instructions += `**${path.basename(img.path)}**:\\n`;\n instructions += `1. 下载压缩结果到: \\`${downloadPath}\\`\\n`;\n\n if (ossDirectory) {\n if (isFormatChange) {\n instructions += `2. 上传到 OSS: \\`upload_to_oss(\"${downloadPath}\", \"${ossDirectory}\", \"${newFileName}\", \"${configName}\")\\`\\n`;\n if (deleteOriginal) {\n instructions += `3. 删除原文件: 在 OSS 上删除 \\`${ossDirectory}/${path.basename(img.path)}\\`\\n`;\n }\n } else {\n instructions += `2. 覆盖上传到 OSS: \\`upload_to_oss(\"${downloadPath}\", \"${ossDirectory}\", \"${path.basename(img.path)}\", \"${configName}\")\\`\\n`;\n }\n }\n instructions += `\\n`;\n }\n\n // 返回信息\n const resultInfo = {\n engine,\n outputFormat: actualOutputFormat,\n deleteOriginal: actualOutputFormat ? deleteOriginal : false,\n ossDirectory,\n configName,\n totalImages: validImages.length,\n batches: batches.length,\n batchSize,\n images: validImages.map(img => ({\n originalPath: img.path,\n originalName: path.basename(img.path),\n originalExt: img.ext,\n originalSize: img.size,\n newExt: actualOutputFormat || img.ext,\n isFormatChange: (actualOutputFormat || img.ext) !== img.ext\n }))\n };\n\n return {\n content: [\n {\n type: \"text\",\n text: instructions\n },\n {\n type: \"text\",\n text: `\\n---\\n**压缩任务数据 (JSON)**:\\n\\`\\`\\`json\\n${JSON.stringify(resultInfo, null, 2)}\\n\\`\\`\\``\n }\n ]\n };\n } catch (error) {\n Logger.error(`生成压缩指令出错:`, error);\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `生成压缩指令失败: ${error}`\n }]\n };\n }\n }\n );\n }\n\n // 生成 TinyPNG 自动化指令\n private generateTinyPngInstructions(batches: { path: string; name: string; ext: string; size: number }[][], outputFormat: string | null): string {\n let instructions = '';\n\n // 添加重要提示\n instructions += `### ⚠️ 重要提示\\n\\n`;\n instructions += `1. **图片必须在项目目录内**:Playwright MCP 只能访问项目目录下的文件。\\n`;\n instructions += ` - 不能使用 \\`/tmp/\\` 等系统临时目录\\n`;\n instructions += ` - 请将图片下载到项目根目录下的 \\`.tmp-compress/\\` 文件夹\\n`;\n instructions += `2. **上传前必须先触发文件选择框**:先点击上传区域,等待 Modal state 显示 \"File chooser\" 后再调用 \\`browser_file_upload\\`\\n\\n`;\n\n const needsFormatConversion = outputFormat && outputFormat !== 'png';\n\n for (let i = 0; i < batches.length; i++) {\n const batch = batches[i];\n instructions += `#### 批次 ${i + 1}/${batches.length}\\n\\n`;\n instructions += `**文件**: ${batch.map(img => path.basename(img.path)).join(', ')}\\n\\n`;\n\n let stepNum = 1;\n instructions += `${stepNum++}. **打开网站**: 使用 \\`browser_navigate\\` 访问 \\`https://tinypng.com/\\`\\n`;\n instructions += `${stepNum++}. **等待加载**: 使用 \\`browser_snapshot\\` 确认页面加载完成\\n`;\n\n // 如果需要转换格式(WebP/JPEG),先开启开关\n if (needsFormatConversion) {\n instructions += `${stepNum++}. **开启格式转换开关**: \\n`;\n instructions += ` - 在页面底部找到 \"Convert my images automatically\" 开关\\n`;\n instructions += ` - 使用 \\`browser_click\\` 点击开关开启它(如果是关闭状态)\\n`;\n instructions += ` - 开启后会出现格式选择选项\\n`;\n instructions += `${stepNum++}. **选择输出格式**: \\n`;\n instructions += ` - 点击选择 \"${outputFormat!.toUpperCase()}\" 格式\\n`;\n }\n\n instructions += `${stepNum++}. **触发文件选择框**: \\n`;\n instructions += ` - 使用 \\`browser_click\\` 点击上传区域(\"Drop your .webp, .png or .jpg files here!\" 文字区域)\\n`;\n instructions += ` - 等待 \\`browser_snapshot\\` 返回结果中 Modal state 显示 \"[File chooser]\"\\n`;\n instructions += `${stepNum++}. **上传文件**: 使用 \\`browser_file_upload\\` 上传以下文件:\\n`;\n for (const img of batch) {\n instructions += ` - \\`${img.path}\\`\\n`;\n }\n instructions += `${stepNum++}. **等待压缩**: 使用 \\`browser_wait_for\\` 等待 \"Download all\" 或各文件的 \"download\" 按钮出现\\n`;\n instructions += `${stepNum++}. **下载结果**: 点击 \"Download all\" 或逐个下载\\n`;\n\n if (i < batches.length - 1) {\n instructions += `${stepNum++}. **刷新页面**: 使用 \\`browser_navigate\\` 重新访问 \\`https://tinypng.com/\\` 准备下一批\\n`;\n }\n instructions += `\\n`;\n }\n\n return instructions;\n }\n\n // 生成 AnyWebP 自动化指令\n private generateAnyWebPInstructions(batches: { path: string; name: string; ext: string; size: number }[][]): string {\n let instructions = '';\n\n // 添加重要提示\n instructions += `### ⚠️ 重要提示\\n\\n`;\n instructions += `1. **图片必须在项目目录内**:Playwright MCP 只能访问项目目录下的文件。\\n`;\n instructions += ` - 不能使用 \\`/tmp/\\` 等系统临时目录\\n`;\n instructions += ` - 请将图片下载到项目根目录下的 \\`.tmp-compress/\\` 文件夹\\n`;\n instructions += `2. **上传前必须先触发文件选择框**:先点击上传区域,等待 Modal state 显示 \"File chooser\" 后再调用 \\`browser_file_upload\\`\\n\\n`;\n\n for (let i = 0; i < batches.length; i++) {\n const batch = batches[i];\n instructions += `#### 批次 ${i + 1}/${batches.length}\\n\\n`;\n instructions += `**文件**: ${batch.map(img => path.basename(img.path)).join(', ')}\\n\\n`;\n\n let stepNum = 1;\n instructions += `${stepNum++}. **打开网站**: 使用 \\`browser_navigate\\` 访问 \\`https://anywebp.com/convert-to-webp.html\\`\\n`;\n instructions += `${stepNum++}. **等待加载**: 使用 \\`browser_snapshot\\` 确认页面加载完成\\n`;\n instructions += `${stepNum++}. **触发文件选择框**: \\n`;\n instructions += ` - 使用 \\`browser_click\\` 点击 \"Drop your images here!\" 上传区域\\n`;\n instructions += ` - 等待 \\`browser_snapshot\\` 返回结果中 Modal state 显示 \"[File chooser]\"\\n`;\n instructions += `${stepNum++}. **上传文件**: 使用 \\`browser_file_upload\\` 上传以下文件:\\n`;\n for (const img of batch) {\n instructions += ` - \\`${img.path}\\`\\n`;\n }\n instructions += `${stepNum++}. **等待转换**: 使用 \\`browser_wait_for\\` 等待转换完成,出现 \"Download\" 按钮\\n`;\n instructions += `${stepNum++}. **下载结果**: 点击 \"Download All\" 或逐个下载 WebP 文件\\n`;\n\n if (i < batches.length - 1) {\n instructions += `${stepNum++}. **刷新页面**: 使用 \\`browser_navigate\\` 重新访问准备下一批\\n`;\n }\n instructions += `\\n`;\n }\n\n return instructions;\n }\n\n async connect(transport: Transport): Promise<void> {\n try {\n await this.server.connect(transport);\n\n Logger.log = (...args: any[]) => {\n try {\n this.server.server.sendLoggingMessage({\n level: \"info\",\n data: args,\n });\n } catch (error) {\n console.log(...args);\n }\n };\n\n Logger.error = (...args: any[]) => {\n try {\n this.server.server.sendLoggingMessage({\n level: \"error\",\n data: args,\n });\n } catch (error) {\n console.error(...args);\n }\n };\n\n Logger.log(\"OSS MCP服务器已连接并准备处理请求\");\n } catch (error) {\n console.error(\"连接到传输时出错:\", error);\n }\n }\n\n async startHttpServer(port: number): Promise<void> {\n const app = express();\n\n // SSE连接端点 - 修复头部发送冲突\n app.get(\"/sse\", (req: Request, res: Response) => {\n // 初始化SSE传输,不再自己设置头部,而是让SDK处理\n this.sseTransport = new SSEServerTransport(\n \"/messages\",\n res as unknown as ServerResponse<IncomingMessage>\n );\n\n try {\n // 连接到传输层\n this.server.connect(this.sseTransport)\n .catch((err) => {\n console.error(\"连接到SSE传输时出错:\", err);\n });\n\n // 处理客户端断开连接\n req.on('close', () => {\n console.log('SSE客户端断开连接');\n this.sseTransport = null;\n });\n } catch (error) {\n console.error(\"建立SSE连接时出错:\", error);\n // 如果连接失败,关闭响应\n if (!res.writableEnded) {\n res.status(500).end();\n }\n }\n });\n\n // 消息端点\n app.post(\"/messages\", async (req: Request, res: Response) => {\n if (!this.sseTransport) {\n console.log(\"尝试发送消息,但SSE传输未初始化\");\n res.status(400).json({\n error: 'SSE连接未建立',\n message: '请先连接到/sse端点'\n });\n return;\n }\n\n try {\n await this.sseTransport.handlePostMessage(\n req as unknown as IncomingMessage,\n res as unknown as ServerResponse<IncomingMessage>\n );\n } catch (error) {\n console.error(\"处理消息时出错:\", error);\n if (!res.writableEnded) {\n res.status(500).json({\n error: \"内部服务器错误\",\n message: String(error)\n });\n }\n }\n });\n\n // 启动服务器\n app.listen(port, () => {\n Logger.log = console.log;\n Logger.error = console.error;\n\n Logger.log(`HTTP服务器监听端口: ${port}`);\n Logger.log(`SSE端点: http://localhost:${port}/sse`);\n Logger.log(`消息端点: http://localhost:${port}/messages`);\n });\n }\n}\n","import OSS from 'ali-oss';\nimport fs from 'fs';\nimport path from 'path';\nimport { OssConfig, getOssConfig, getAllOssConfigs } from '../config/oss.config.js';\nimport { z } from 'zod';\n\n// 上传文件参数验证Schema\nexport const UploadFileParamsSchema = z.object({\n filePath: z.string(),\n targetDir: z.string().optional(),\n fileName: z.string().optional(),\n configName: z.string().optional(),\n});\n\n// 导出上传文件参数类型\nexport type UploadFileParams = z.infer<typeof UploadFileParamsSchema>;\n\n// 上传结果验证Schema\nexport const UploadResultSchema = z.object({\n success: z.boolean(),\n url: z.string().optional(),\n error: z.string().optional(),\n ossConfigName: z.string().optional(),\n});\n\n// 导出上传结果类型\nexport type UploadResult = z.infer<typeof UploadResultSchema>;\n\n/**\n * OSS配置接口(包含ID和名称)\n */\nexport interface OssConfigWithMeta extends OssConfig {\n id: string;\n name: string;\n}\n\n/**\n * 阿里云OSS服务类\n */\nexport class OssService {\n private clients: Map<string, OSS> = new Map();\n\n /**\n * 获取所有OSS配置\n * @returns OSS配置列表\n */\n getConfigs(): OssConfigWithMeta[] {\n const configs: OssConfigWithMeta[] = [];\n const allConfigs = getAllOssConfigs();\n\n for (const [id, config] of Object.entries(allConfigs)) {\n configs.push({\n id,\n name: `${id.charAt(0).toUpperCase()}${id.slice(1)} 配置`,\n ...config\n });\n }\n\n return configs;\n }\n\n /**\n * 获取OSS客户端\n * @param configName 配置名称\n * @returns OSS客户端实例\n */\n private getClient(configName: string = 'default'): OSS | null {\n // 检查缓存中是否已有客户端\n if (this.clients.has(configName)) {\n return this.clients.get(configName) as OSS;\n }\n\n // 获取配置并创建客户端\n const config = getOssConfig(configName);\n if (!config) {\n return null;\n }\n\n try {\n const client = new OSS({\n region: config.region,\n accessKeyId: config.accessKeyId,\n accessKeySecret: config.accessKeySecret,\n bucket: config.bucket,\n endpoint: config.endpoint\n });\n\n // 缓存客户端实例\n this.clients.set(configName, client);\n return client;\n } catch (error) {\n console.error(`Failed to create OSS client for ${configName}:`, error);\n return null;\n }\n }\n\n /**\n * 上传文件到OSS\n * @param params 上传参数\n * @returns 上传结果\n */\n async uploadFile(params: UploadFileParams): Promise<UploadResult> {\n // 验证并解析参数\n const validParams = UploadFileParamsSchema.parse(params);\n const { filePath, targetDir = '', fileName, configName = 'default' } = validParams;\n\n try {\n // 检查文件是否存在\n if (!fs.existsSync(filePath)) {\n return UploadResultSchema.parse({\n success: false,\n error: `File not found: ${filePath}`,\n ossConfigName: configName\n });\n }\n\n // 获取OSS客户端\n const client = this.getClient(configName);\n if (!client) {\n return UploadResultSchema.parse({\n success: false,\n error: `OSS config not found for: ${configName}`,\n ossConfigName: configName\n });\n }\n\n // 确定文件名\n const actualFileName = fileName || path.basename(filePath);\n\n // 构建OSS路径,确保正斜杠格式\n let ossPath = actualFileName;\n if (targetDir) {\n // 规范化目标目录:移除头尾斜杠,然后加上结尾斜杠\n const normalizedDir = targetDir.replace(/^\\/+|\\/+$/g, '');\n ossPath = normalizedDir ? `${normalizedDir}/${actualFileName}` : actualFileName;\n }\n\n // 上传文件\n const result = await client.put(ossPath, filePath);\n\n return UploadResultSchema.parse({\n success: true,\n url: result.url,\n ossConfigName: configName\n });\n } catch (error) {\n return UploadResultSchema.parse({\n success: false,\n error: `Upload failed: ${(error as Error).message}`,\n ossConfigName: configName\n });\n }\n }\n\n /**\n * 重命名OSS文件(通过 copy + delete 实现)\n * @param oldKey 原文件路径\n * @param newKey 新文件路径\n * @param configName 配置名称\n * @returns 重命名结果\n */\n async renameFile(oldKey: string, newKey: string, configName: string = 'default'): Promise<{ success: boolean; error?: string }> {\n try {\n const client = this.getClient(configName);\n if (!client) {\n return { success: false, error: `OSS config not found for: ${configName}` };\n }\n\n // 规范化路径:移除开头的斜杠\n const normalizedOldKey = oldKey.replace(/^\\/+/, '');\n const normalizedNewKey = newKey.replace(/^\\/+/, '');\n\n // 检查源文件是否存在\n try {\n await client.head(normalizedOldKey);\n } catch (_e) {\n return { success: false, error: `源文件不存在: ${normalizedOldKey}` };\n }\n\n // 检查目标文件是否已存在\n try {\n await client.head(normalizedNewKey);\n // 如果到这里说明文件存在\n if (normalizedOldKey !== normalizedNewKey) {\n return { success: false, error: `目标文件已存在: ${normalizedNewKey}` };\n }\n } catch (_e) {\n // 文件不存在,可以继续\n }\n\n // Step 1: 复制文件到新位置\n await client.copy(normalizedNewKey, normalizedOldKey);\n\n // Step 2: 删除原文件\n await client.delete(normalizedOldKey);\n\n return { success: true };\n } catch (error) {\n return { success: false, error: `重命名失败: ${(error as Error).message}` };\n }\n }\n\n /**\n * 列出OSS目录下的文件\n * @param directory OSS目录路径\n * @param configName 配置名称\n * @param pattern 文件名过滤模式(可选)\n * @returns 文件列表\n */\n async listFiles(\n directory: string = '',\n configName: string = 'default',\n pattern?: string\n ): Promise<{ success: boolean; files?: Array<{ name: string; size: number; lastModified: Date }>; error?: string }> {\n try {\n const client = this.getClient(configName);\n if (!client) {\n return { success: false, error: `OSS config not found for: ${configName}` };\n }\n\n // 规范化目录路径\n const normalizedDir = directory.replace(/^\\/+|\\/+$/g, '');\n const prefix = normalizedDir ? `${normalizedDir}/` : '';\n\n // 列出文件\n const result = await client.list({\n prefix,\n delimiter: '/',\n 'max-keys': 1000\n }, {});\n\n const files: Array<{ name: string; size: number; lastModified: Date }> = [];\n\n // 处理文件对象\n if (result.objects) {\n for (const obj of result.objects) {\n // 跳过目录本身(以 / 结尾的)\n if (obj.name.endsWith('/')) continue;\n\n // 提取文件名(去掉目录前缀)\n const fileName = obj.name.replace(prefix, '');\n if (!fileName) continue;\n\n // 如果有 pattern,进行简单的通配符匹配\n if (pattern) {\n const regex = new RegExp(\n '^' + pattern\n .replace(/\\./g, '\\\\.')\n .replace(/\\*/g, '.*')\n .replace(/\\?/g, '.') + '$',\n 'i'\n );\n if (!regex.test(fileName)) continue;\n }\n\n files.push({\n name: fileName,\n size: obj.size,\n lastModified: new Date(obj.lastModified)\n });\n }\n }\n\n // 按文件名排序\n files.sort((a, b) => a.name.localeCompare(b.name));\n\n return { success: true, files };\n } catch (error) {\n return { success: false, error: `列出文件失败: ${(error as Error).message}` };\n }\n }\n\n /**\n * 批量重命名OSS文件\n * @param rules 重命名规则数组\n * @param directory OSS目录路径\n * @param configName 配置名称\n * @returns 批量重命名结果\n */\n async batchRenameFiles(\n rules: Array<{ oldName: string; newName: string }>,\n directory: string = '',\n configName: string = 'default'\n ): Promise<Array<{ oldName: string; newName: string; success: boolean; error?: string }>> {\n const results: Array<{ oldName: string; newName: string; success: boolean; error?: string }> = [];\n\n // 规范化目录路径\n const normalizedDir = directory.replace(/^\\/+|\\/+$/g, '');\n const dirPrefix = normalizedDir ? `${normalizedDir}/` : '';\n\n for (const rule of rules) {\n const oldKey = `${dirPrefix}${rule.oldName}`;\n const newKey = `${dirPrefix}${rule.newName}`;\n\n const result = await this.renameFile(oldKey, newKey, configName);\n results.push({\n oldName: rule.oldName,\n newName: rule.newName,\n success: result.success,\n error: result.error\n });\n }\n\n return results;\n }\n\n /**\n * 删除单个OSS文件\n * @param key 文件路径\n * @param configName 配置名称\n * @returns 删除结果\n */\n async deleteFile(key: string, configName: string = 'default'): Promise<{ success: boolean; error?: string }> {\n try {\n const client = this.getClient(configName);\n if (!client) {\n return { success: false, error: `OSS config not found for: ${configName}` };\n }\n\n // 规范化路径:移除开头的斜杠\n const normalizedKey = key.replace(/^\\/+/, '');\n\n // 检查文件是否存在\n try {\n await client.head(normalizedKey);\n } catch (_e) {\n return { success: false, error: `文件不存在: ${normalizedKey}` };\n }\n\n // 删除文件\n await client.delete(normalizedKey);\n\n return { success: true };\n } catch (error) {\n return { success: false, error: `删除失败: ${(error as Error).message}` };\n }\n }\n\n /**\n * 批量删除OSS文件\n * @param fileNames 文件名数组\n * @param directory OSS目录路径\n * @param configName 配置名称\n * @returns 批量删除结果\n */\n async batchDeleteFiles(\n fileNames: string[],\n directory: string = '',\n configName: string = 'default'\n ): Promise<Array<{ fileName: string; success: boolean; error?: string }>> {\n const results: Array<{ fileName: string; success: boolean; error?: string }> = [];\n\n // 规范化目录路径\n const normalizedDir = directory.replace(/^\\/+|\\/+$/g, '');\n const dirPrefix = normalizedDir ? `${normalizedDir}/` : '';\n\n for (const fileName of fileNames) {\n const key = `${dirPrefix}${fileName}`;\n const result = await this.deleteFile(key, configName);\n results.push({\n fileName,\n success: result.success,\n error: result.error\n });\n }\n\n return results;\n }\n}\n\n// 导出单例实例\nexport const ossService = new OssService();\n","import { config } from \"dotenv\";\nimport yargs from \"yargs\";\nimport { hideBin } from \"yargs/helpers\";\nimport { z } from \"zod\";\n\nconfig();\n\n// OSS配置验证Schema\nexport const OssConfigSchema = z.object({\n region: z.string(),\n accessKeyId: z.string(),\n accessKeySecret: z.string(),\n bucket: z.string(),\n endpoint: z.string(),\n});\n\n// 导出OSS配置类型\nexport type OssConfig = z.infer<typeof OssConfigSchema>;\n\n// 服务器配置接口\nexport interface ServerConfig {\n port: number;\n ossConfig: Record<string, OssConfig>;\n configSources: {\n port: \"cli\" | \"env\" | \"default\";\n ossConfig: \"cli\" | \"env\" | \"default\";\n };\n}\n\n// 掩码函数,用于打印敏感信息\nfunction maskSecret(secret: string): string {\n if (secret.length <= 4) return \"****\";\n return `${secret.substring(0, 4)}****${secret.slice(-4)}`;\n}\n\n// 获取服务器配置\nexport function getServerConfig(isStdioMode: boolean = false): ServerConfig {\n // 解析命令行参数\n const argv = yargs(hideBin(process.argv))\n .options({\n \"oss-config\": {\n type: \"string\",\n description: \"OSS配置JSON字符串\",\n },\n port: {\n type: \"number\",\n description: \"服务器运行端口\",\n default: 3000,\n },\n })\n .help()\n .version(\"1.0.0\")\n .parseSync();\n\n const config: ServerConfig = {\n port: 3000,\n ossConfig: {},\n configSources: {\n port: \"default\",\n ossConfig: \"default\",\n },\n };\n\n // 处理端口配置\n if (argv.port) {\n config.port = argv.port;\n config.configSources.port = \"cli\";\n } else if (process.env.PORT) {\n config.port = parseInt(process.env.PORT, 10);\n config.configSources.port = \"env\";\n }\n\n // 处理OSS配置 - 首先检查命令行参数\n if (argv[\"oss-config\"]) {\n const allOssConfigs = JSON.parse(argv[\"oss-config\"] as string);\n\n if (allOssConfigs.region && allOssConfigs.accessKeyId) {\n config.ossConfig.default = OssConfigSchema.parse(allOssConfigs);\n } else {\n Object.entries(allOssConfigs).forEach(([name, cfg]) => {\n config.ossConfig[name.toLowerCase()] = OssConfigSchema.parse(cfg);\n });\n }\n config.configSources.ossConfig = \"cli\";\n } else if (process.env.OSS_CONFIG_DEFAULT) {\n const ossConfig = JSON.parse(process.env.OSS_CONFIG_DEFAULT)\n config.ossConfig.default = OssConfigSchema.parse(ossConfig);\n config.configSources.ossConfig = \"env\";\n }\n\n // 检查其他命名的OSS配置\n Object.entries(process.env).forEach(([key, value]) => {\n if (key.startsWith(\"OSS_CONFIG_\") && key !== \"OSS_CONFIG_DEFAULT\" && value) {\n try {\n const configName = key.replace(\"OSS_CONFIG_\", \"\").toLowerCase();\n const ossConfig = JSON.parse(value);\n config.ossConfig[configName] = OssConfigSchema.parse(ossConfig);\n } catch (error) {\n console.error(`解析环境变量${key}失败:`, error);\n }\n }\n });\n\n // 验证配置\n if (Object.keys(config.ossConfig).length === 0) {\n console.warn(\"未找到有效的OSS配置。服务器将启动,但上传功能将不可用。\");\n }\n\n // 打印配置信息(非stdio模式下)\n if (!isStdioMode) {\n console.log(\"\\n配置信息:\");\n console.log(`- 端口: ${config.port} (来源: ${config.configSources.port})`);\n\n if (Object.keys(config.ossConfig).length > 0) {\n console.log(\"- OSS配置:\");\n Object.entries(config.ossConfig).forEach(([name, cfg]) => {\n console.log(` - ${name}:`);\n console.log(` Region: ${cfg.region}`);\n console.log(` Endpoint: ${cfg.endpoint}`);\n console.log(` Bucket: ${cfg.bucket}`);\n console.log(` AccessKeyId: ${maskSecret(cfg.accessKeyId)}`);\n console.log(` AccessKeySecret: ${maskSecret(cfg.accessKeySecret)}`);\n });\n } else {\n console.log(\"- OSS配置: 未找到\");\n }\n console.log(); // 空行,增加可读性\n }\n\n return config;\n}\n\n// 获取所有OSS配置\nexport function getAllOssConfigs(): Record<string, OssConfig> {\n const { ossConfig } = getServerConfig(true);\n return ossConfig;\n}\n\n// 获取特定名称的OSS配置\nexport function getOssConfig(name: string = 'default'): OssConfig | null {\n const configs = getAllOssConfigs();\n const normalizedName = name.toLowerCase();\n return configs[normalizedName] || null;\n}\n\n// 获取可用的OSS配置名称列表\nexport function getAvailableOssConfigNames(): string[] {\n return Object.keys(getAllOssConfigs());\n}\n"],"mappings":"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","names":["z","config","z","config","fs","path","config","z","configs","configNames","resultText","resolve","fileStream","config"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oss-mcp-plus",
3
- "version": "1.0.11",
3
+ "version": "1.0.13",
4
4
  "description": "本地MCP服务器,用于将文件上传到阿里云OSS,支持多配置和目录指定",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -40,7 +40,11 @@
40
40
  "upload",
41
41
  "mcp",
42
42
  "typescript",
43
- "ai"
43
+ "ai",
44
+ "oss-mcp",
45
+ "oss-mcp-plus",
46
+ "oss mcp",
47
+ "oss mcp plus"
44
48
  ],
45
49
  "author": "",
46
50
  "license": "MIT",