@mcpcn/mcp-image-compressor 1.0.3 → 1.0.4
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 +286 -286
- package/dist/index.js +124 -30
- package/package.json +42 -42
package/README.md
CHANGED
|
@@ -1,287 +1,287 @@
|
|
|
1
|
-
# Image Processor MCP Server
|
|
2
|
-
|
|
3
|
-
这是一个用于图片处理的 MCP 服务器,提供了多种图片处理功能。
|
|
4
|
-
|
|
5
|
-
## 功能特性
|
|
6
|
-
|
|
7
|
-
1. 图片格式转换
|
|
8
|
-
2. 图片压缩到指定大小
|
|
9
|
-
3. 图片压缩到原始大小的指定百分比
|
|
10
|
-
4. 图片尺寸缩放
|
|
11
|
-
5. 读取图片元数据
|
|
12
|
-
6. 图像旋转
|
|
13
|
-
7. 图像翻转
|
|
14
|
-
8. 添加文字水印
|
|
15
|
-
|
|
16
|
-
## 安装
|
|
17
|
-
|
|
18
|
-
```bash
|
|
19
|
-
npm install
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
## 构建
|
|
23
|
-
|
|
24
|
-
```bash
|
|
25
|
-
npm run build
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
## 运行服务器
|
|
29
|
-
|
|
30
|
-
```bash
|
|
31
|
-
npm start
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
## 开发模式
|
|
35
|
-
|
|
36
|
-
```bash
|
|
37
|
-
npm run dev
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
## MCP 工具
|
|
41
|
-
|
|
42
|
-
### 1. image_convert_format
|
|
43
|
-
|
|
44
|
-
将图片转换为指定格式。
|
|
45
|
-
|
|
46
|
-
**参数:**
|
|
47
|
-
|
|
48
|
-
- `inputPaths`: 原图片路径数组,支持图片文件和目录
|
|
49
|
-
- `targetFormat`: 要转换为的格式(jpg/png/webp/bmp等)
|
|
50
|
-
- `outputPath`: 转换后的保存路径(可选,不指定则和原图片目录相同)
|
|
51
|
-
|
|
52
|
-
**示例:**
|
|
53
|
-
|
|
54
|
-
```json
|
|
55
|
-
{
|
|
56
|
-
"inputPaths": ["path/to/image.jpg", "path/to/images/"],
|
|
57
|
-
"targetFormat": "png",
|
|
58
|
-
"outputPath": "path/to/output/"
|
|
59
|
-
}
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
### 2. image_compress_to_size
|
|
63
|
-
|
|
64
|
-
将图片压缩到指定的文件大小。
|
|
65
|
-
|
|
66
|
-
**参数:**
|
|
67
|
-
|
|
68
|
-
- `inputPaths`: 原图片路径数组,支持图片文件和目录
|
|
69
|
-
- `size`: 要压缩到的的目标大小(如1mb/500kb)
|
|
70
|
-
- `overwrite`: 是否覆盖原图(可选,默认为false)
|
|
71
|
-
- `outputPath`: 存储路径(可选,不覆盖原图时如果没传则为原图所在目录)
|
|
72
|
-
|
|
73
|
-
**示例:**
|
|
74
|
-
|
|
75
|
-
```json
|
|
76
|
-
{
|
|
77
|
-
"inputPaths": ["path/to/image.jpg"],
|
|
78
|
-
"size": "500kb",
|
|
79
|
-
"overwrite": false,
|
|
80
|
-
"outputPath": "path/to/output/"
|
|
81
|
-
}
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
### 3. image_compress_to_percent
|
|
85
|
-
|
|
86
|
-
将图片压缩到原始大小的指定百分比。
|
|
87
|
-
|
|
88
|
-
**参数:**
|
|
89
|
-
|
|
90
|
-
- `inputPaths`: 原图片路径数组,支持图片文件和目录
|
|
91
|
-
- `percent`: 要压缩到原size的百分之多少(如50或50%)
|
|
92
|
-
- `overwrite`: 是否覆盖原图(可选,默认为false)
|
|
93
|
-
- `outputPath`: 存储路径(可选,不覆盖原图时如果没传则为原图所在目录)
|
|
94
|
-
|
|
95
|
-
**示例:**
|
|
96
|
-
|
|
97
|
-
```json
|
|
98
|
-
{
|
|
99
|
-
"inputPaths": ["path/to/image.jpg"],
|
|
100
|
-
"percent": "50%",
|
|
101
|
-
"overwrite": false,
|
|
102
|
-
"outputPath": "path/to/output/"
|
|
103
|
-
}
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
### 4. image_resize
|
|
107
|
-
|
|
108
|
-
调整图片尺寸。
|
|
109
|
-
|
|
110
|
-
**参数:**
|
|
111
|
-
|
|
112
|
-
- `inputPaths`: 原图片路径数组,支持图片文件和目录
|
|
113
|
-
- `width`: 宽修改为多少(可选)
|
|
114
|
-
- `height`: 高修改为多少(可选)
|
|
115
|
-
- `overwrite`: 是否覆盖原图(可选,默认为false)
|
|
116
|
-
- `outputPath`: 存储路径(可选,不覆盖原图时如果没传则为原图所在目录)
|
|
117
|
-
|
|
118
|
-
**示例:**
|
|
119
|
-
|
|
120
|
-
```json
|
|
121
|
-
{
|
|
122
|
-
"inputPaths": ["path/to/image.jpg"],
|
|
123
|
-
"width": 800,
|
|
124
|
-
"height": 600,
|
|
125
|
-
"overwrite": false,
|
|
126
|
-
"outputPath": "path/to/output/"
|
|
127
|
-
}
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
### 5. image_metadata
|
|
131
|
-
|
|
132
|
-
读取图片元数据。
|
|
133
|
-
|
|
134
|
-
**参数:**
|
|
135
|
-
|
|
136
|
-
- `inputPaths`: 待处理图片路径
|
|
137
|
-
|
|
138
|
-
**示例:**
|
|
139
|
-
|
|
140
|
-
```json
|
|
141
|
-
{
|
|
142
|
-
"inputPaths": ["path/to/image.jpg"]
|
|
143
|
-
}
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
**返回示例:**
|
|
147
|
-
|
|
148
|
-
```json
|
|
149
|
-
{
|
|
150
|
-
"success": true,
|
|
151
|
-
"message": "成功读取了 1 个图片的元数据",
|
|
152
|
-
"metadata": {
|
|
153
|
-
"image.jpg": {
|
|
154
|
-
"format": "jpeg",
|
|
155
|
-
"width": 1920,
|
|
156
|
-
"height": 1080,
|
|
157
|
-
"space": "srgb",
|
|
158
|
-
"channels": 3,
|
|
159
|
-
"depth": "uchar",
|
|
160
|
-
"density": 72,
|
|
161
|
-
"isProgressive": false,
|
|
162
|
-
"hasProfile": true,
|
|
163
|
-
"hasAlpha": false
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
### 6. image_rotate
|
|
170
|
-
|
|
171
|
-
图像旋转。
|
|
172
|
-
|
|
173
|
-
**参数:**
|
|
174
|
-
|
|
175
|
-
- `inputPaths`: 待处理图片路径
|
|
176
|
-
- `angle`: 旋转角度(顺时针为正值,逆时针为负值)
|
|
177
|
-
- `overwrite`: 是否覆盖原图(可选,默认为false)
|
|
178
|
-
- `outputPath`: 存储路径(可选,不覆盖原图时如果没传则为原图所在目录)
|
|
179
|
-
|
|
180
|
-
**示例:**
|
|
181
|
-
|
|
182
|
-
```json
|
|
183
|
-
{
|
|
184
|
-
"inputPaths": ["path/to/image.jpg"],
|
|
185
|
-
"angle": 90,
|
|
186
|
-
"overwrite": false,
|
|
187
|
-
"outputPath": "path/to/output/"
|
|
188
|
-
}
|
|
189
|
-
```
|
|
190
|
-
|
|
191
|
-
### 7. image_flip
|
|
192
|
-
|
|
193
|
-
图像翻转。
|
|
194
|
-
|
|
195
|
-
**参数:**
|
|
196
|
-
|
|
197
|
-
- `inputPaths`: 待处理图片路径
|
|
198
|
-
- `flipType`: 翻转方式("horizontal": 水平翻转, "vertical": 垂直翻转)
|
|
199
|
-
- `overwrite`: 是否覆盖原图(可选,默认为false)
|
|
200
|
-
- `outputPath`: 存储路径(可选,不覆盖原图时如果没传则为原图所在目录)
|
|
201
|
-
|
|
202
|
-
**示例:**
|
|
203
|
-
|
|
204
|
-
```json
|
|
205
|
-
{
|
|
206
|
-
"inputPaths": ["path/to/image.jpg"],
|
|
207
|
-
"flipType": "horizontal",
|
|
208
|
-
"overwrite": false,
|
|
209
|
-
"outputPath": "path/to/output/"
|
|
210
|
-
}
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
### 8. image_watermark
|
|
214
|
-
|
|
215
|
-
添加文字水印。
|
|
216
|
-
|
|
217
|
-
**参数:**
|
|
218
|
-
|
|
219
|
-
- `inputPaths`: 待处理图片路径
|
|
220
|
-
- `text`: 水印内容(文字)
|
|
221
|
-
- `position`: 水印添加方式(必填,可能的取值为:top-left(左上)、top-right(右上)、bottom-left(左下)、bottom-right(右下)、center(中央)、tile(铺满))
|
|
222
|
-
- `density`: 满铺密度(可选参数,在水印添加方式为铺满时有效,取值范围为1-10的整数,1最稀疏,10最密)
|
|
223
|
-
- `fontSize`: 文字尺寸(可选参数,单位为像素,不传则根据图片尺寸自动计算)
|
|
224
|
-
- `overwrite`: 是否覆盖原图(可选,默认为false)
|
|
225
|
-
- `outputPath`: 存储路径(可选,不覆盖原图时如果没传则为原图所在目录)
|
|
226
|
-
|
|
227
|
-
**示例:**
|
|
228
|
-
|
|
229
|
-
```json
|
|
230
|
-
{
|
|
231
|
-
"inputPaths": ["path/to/image.jpg"],
|
|
232
|
-
"text": "版权所有",
|
|
233
|
-
"position": "bottom-right",
|
|
234
|
-
"overwrite": false,
|
|
235
|
-
"outputPath": "path/to/output/"
|
|
236
|
-
}
|
|
237
|
-
```
|
|
238
|
-
|
|
239
|
-
铺满水印示例:
|
|
240
|
-
|
|
241
|
-
```json
|
|
242
|
-
{
|
|
243
|
-
"inputPaths": ["path/to/image.jpg"],
|
|
244
|
-
"text": "CONFIDENTIAL",
|
|
245
|
-
"position": "tile",
|
|
246
|
-
"density": 5,
|
|
247
|
-
"fontSize": 20,
|
|
248
|
-
"overwrite": false,
|
|
249
|
-
"outputPath": "path/to/output/"
|
|
250
|
-
}
|
|
251
|
-
```
|
|
252
|
-
|
|
253
|
-
## 注意事项
|
|
254
|
-
|
|
255
|
-
1. 所有功能都支持批量处理多个文件
|
|
256
|
-
2. 支持处理整个目录中的图片(仅处理目录下的图片文件,不处理子目录中的)
|
|
257
|
-
3. 支持的图片格式:jpeg、jpg、png、webp、gif、avif、tiff、bmp
|
|
258
|
-
4. 压缩功能使用二分查找算法来找到最佳的压缩质量
|
|
259
|
-
5. 当指定输出目录时,如果目录不存在会自动创建
|
|
260
|
-
6. 所有错误都会被优雅地处理并记录,不会中断批处理过程
|
|
261
|
-
|
|
262
|
-
## 使用作为 MCP 服务器
|
|
263
|
-
|
|
264
|
-
该服务器实现了 Model Context Protocol (MCP) 规范,可以连接到支持 MCP 的客户端。
|
|
265
|
-
|
|
266
|
-
### 配置环境变量
|
|
267
|
-
|
|
268
|
-
在使用前,确保已安装所有依赖,并构建项目:
|
|
269
|
-
|
|
270
|
-
```bash
|
|
271
|
-
npm install
|
|
272
|
-
npm run build
|
|
273
|
-
```
|
|
274
|
-
|
|
275
|
-
### 启动服务器
|
|
276
|
-
|
|
277
|
-
```bash
|
|
278
|
-
npm start
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
## 依赖
|
|
284
|
-
|
|
285
|
-
- sharp: 用于图片处理
|
|
286
|
-
- @modelcontextprotocol/sdk: MCP SDK
|
|
1
|
+
# Image Processor MCP Server
|
|
2
|
+
|
|
3
|
+
这是一个用于图片处理的 MCP 服务器,提供了多种图片处理功能。
|
|
4
|
+
|
|
5
|
+
## 功能特性
|
|
6
|
+
|
|
7
|
+
1. 图片格式转换
|
|
8
|
+
2. 图片压缩到指定大小
|
|
9
|
+
3. 图片压缩到原始大小的指定百分比
|
|
10
|
+
4. 图片尺寸缩放
|
|
11
|
+
5. 读取图片元数据
|
|
12
|
+
6. 图像旋转
|
|
13
|
+
7. 图像翻转
|
|
14
|
+
8. 添加文字水印
|
|
15
|
+
|
|
16
|
+
## 安装
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## 构建
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm run build
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## 运行服务器
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm start
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## 开发模式
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npm run dev
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## MCP 工具
|
|
41
|
+
|
|
42
|
+
### 1. image_convert_format
|
|
43
|
+
|
|
44
|
+
将图片转换为指定格式。
|
|
45
|
+
|
|
46
|
+
**参数:**
|
|
47
|
+
|
|
48
|
+
- `inputPaths`: 原图片路径数组,支持图片文件和目录
|
|
49
|
+
- `targetFormat`: 要转换为的格式(jpg/png/webp/bmp等)
|
|
50
|
+
- `outputPath`: 转换后的保存路径(可选,不指定则和原图片目录相同)
|
|
51
|
+
|
|
52
|
+
**示例:**
|
|
53
|
+
|
|
54
|
+
```json
|
|
55
|
+
{
|
|
56
|
+
"inputPaths": ["path/to/image.jpg", "path/to/images/"],
|
|
57
|
+
"targetFormat": "png",
|
|
58
|
+
"outputPath": "path/to/output/"
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### 2. image_compress_to_size
|
|
63
|
+
|
|
64
|
+
将图片压缩到指定的文件大小。
|
|
65
|
+
|
|
66
|
+
**参数:**
|
|
67
|
+
|
|
68
|
+
- `inputPaths`: 原图片路径数组,支持图片文件和目录
|
|
69
|
+
- `size`: 要压缩到的的目标大小(如1mb/500kb)
|
|
70
|
+
- `overwrite`: 是否覆盖原图(可选,默认为false)
|
|
71
|
+
- `outputPath`: 存储路径(可选,不覆盖原图时如果没传则为原图所在目录)
|
|
72
|
+
|
|
73
|
+
**示例:**
|
|
74
|
+
|
|
75
|
+
```json
|
|
76
|
+
{
|
|
77
|
+
"inputPaths": ["path/to/image.jpg"],
|
|
78
|
+
"size": "500kb",
|
|
79
|
+
"overwrite": false,
|
|
80
|
+
"outputPath": "path/to/output/"
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### 3. image_compress_to_percent
|
|
85
|
+
|
|
86
|
+
将图片压缩到原始大小的指定百分比。
|
|
87
|
+
|
|
88
|
+
**参数:**
|
|
89
|
+
|
|
90
|
+
- `inputPaths`: 原图片路径数组,支持图片文件和目录
|
|
91
|
+
- `percent`: 要压缩到原size的百分之多少(如50或50%)
|
|
92
|
+
- `overwrite`: 是否覆盖原图(可选,默认为false)
|
|
93
|
+
- `outputPath`: 存储路径(可选,不覆盖原图时如果没传则为原图所在目录)
|
|
94
|
+
|
|
95
|
+
**示例:**
|
|
96
|
+
|
|
97
|
+
```json
|
|
98
|
+
{
|
|
99
|
+
"inputPaths": ["path/to/image.jpg"],
|
|
100
|
+
"percent": "50%",
|
|
101
|
+
"overwrite": false,
|
|
102
|
+
"outputPath": "path/to/output/"
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### 4. image_resize
|
|
107
|
+
|
|
108
|
+
调整图片尺寸。
|
|
109
|
+
|
|
110
|
+
**参数:**
|
|
111
|
+
|
|
112
|
+
- `inputPaths`: 原图片路径数组,支持图片文件和目录
|
|
113
|
+
- `width`: 宽修改为多少(可选)
|
|
114
|
+
- `height`: 高修改为多少(可选)
|
|
115
|
+
- `overwrite`: 是否覆盖原图(可选,默认为false)
|
|
116
|
+
- `outputPath`: 存储路径(可选,不覆盖原图时如果没传则为原图所在目录)
|
|
117
|
+
|
|
118
|
+
**示例:**
|
|
119
|
+
|
|
120
|
+
```json
|
|
121
|
+
{
|
|
122
|
+
"inputPaths": ["path/to/image.jpg"],
|
|
123
|
+
"width": 800,
|
|
124
|
+
"height": 600,
|
|
125
|
+
"overwrite": false,
|
|
126
|
+
"outputPath": "path/to/output/"
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### 5. image_metadata
|
|
131
|
+
|
|
132
|
+
读取图片元数据。
|
|
133
|
+
|
|
134
|
+
**参数:**
|
|
135
|
+
|
|
136
|
+
- `inputPaths`: 待处理图片路径
|
|
137
|
+
|
|
138
|
+
**示例:**
|
|
139
|
+
|
|
140
|
+
```json
|
|
141
|
+
{
|
|
142
|
+
"inputPaths": ["path/to/image.jpg"]
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**返回示例:**
|
|
147
|
+
|
|
148
|
+
```json
|
|
149
|
+
{
|
|
150
|
+
"success": true,
|
|
151
|
+
"message": "成功读取了 1 个图片的元数据",
|
|
152
|
+
"metadata": {
|
|
153
|
+
"image.jpg": {
|
|
154
|
+
"format": "jpeg",
|
|
155
|
+
"width": 1920,
|
|
156
|
+
"height": 1080,
|
|
157
|
+
"space": "srgb",
|
|
158
|
+
"channels": 3,
|
|
159
|
+
"depth": "uchar",
|
|
160
|
+
"density": 72,
|
|
161
|
+
"isProgressive": false,
|
|
162
|
+
"hasProfile": true,
|
|
163
|
+
"hasAlpha": false
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### 6. image_rotate
|
|
170
|
+
|
|
171
|
+
图像旋转。
|
|
172
|
+
|
|
173
|
+
**参数:**
|
|
174
|
+
|
|
175
|
+
- `inputPaths`: 待处理图片路径
|
|
176
|
+
- `angle`: 旋转角度(顺时针为正值,逆时针为负值)
|
|
177
|
+
- `overwrite`: 是否覆盖原图(可选,默认为false)
|
|
178
|
+
- `outputPath`: 存储路径(可选,不覆盖原图时如果没传则为原图所在目录)
|
|
179
|
+
|
|
180
|
+
**示例:**
|
|
181
|
+
|
|
182
|
+
```json
|
|
183
|
+
{
|
|
184
|
+
"inputPaths": ["path/to/image.jpg"],
|
|
185
|
+
"angle": 90,
|
|
186
|
+
"overwrite": false,
|
|
187
|
+
"outputPath": "path/to/output/"
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### 7. image_flip
|
|
192
|
+
|
|
193
|
+
图像翻转。
|
|
194
|
+
|
|
195
|
+
**参数:**
|
|
196
|
+
|
|
197
|
+
- `inputPaths`: 待处理图片路径
|
|
198
|
+
- `flipType`: 翻转方式("horizontal": 水平翻转, "vertical": 垂直翻转)
|
|
199
|
+
- `overwrite`: 是否覆盖原图(可选,默认为false)
|
|
200
|
+
- `outputPath`: 存储路径(可选,不覆盖原图时如果没传则为原图所在目录)
|
|
201
|
+
|
|
202
|
+
**示例:**
|
|
203
|
+
|
|
204
|
+
```json
|
|
205
|
+
{
|
|
206
|
+
"inputPaths": ["path/to/image.jpg"],
|
|
207
|
+
"flipType": "horizontal",
|
|
208
|
+
"overwrite": false,
|
|
209
|
+
"outputPath": "path/to/output/"
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### 8. image_watermark
|
|
214
|
+
|
|
215
|
+
添加文字水印。
|
|
216
|
+
|
|
217
|
+
**参数:**
|
|
218
|
+
|
|
219
|
+
- `inputPaths`: 待处理图片路径
|
|
220
|
+
- `text`: 水印内容(文字)
|
|
221
|
+
- `position`: 水印添加方式(必填,可能的取值为:top-left(左上)、top-right(右上)、bottom-left(左下)、bottom-right(右下)、center(中央)、tile(铺满))
|
|
222
|
+
- `density`: 满铺密度(可选参数,在水印添加方式为铺满时有效,取值范围为1-10的整数,1最稀疏,10最密)
|
|
223
|
+
- `fontSize`: 文字尺寸(可选参数,单位为像素,不传则根据图片尺寸自动计算)
|
|
224
|
+
- `overwrite`: 是否覆盖原图(可选,默认为false)
|
|
225
|
+
- `outputPath`: 存储路径(可选,不覆盖原图时如果没传则为原图所在目录)
|
|
226
|
+
|
|
227
|
+
**示例:**
|
|
228
|
+
|
|
229
|
+
```json
|
|
230
|
+
{
|
|
231
|
+
"inputPaths": ["path/to/image.jpg"],
|
|
232
|
+
"text": "版权所有",
|
|
233
|
+
"position": "bottom-right",
|
|
234
|
+
"overwrite": false,
|
|
235
|
+
"outputPath": "path/to/output/"
|
|
236
|
+
}
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
铺满水印示例:
|
|
240
|
+
|
|
241
|
+
```json
|
|
242
|
+
{
|
|
243
|
+
"inputPaths": ["path/to/image.jpg"],
|
|
244
|
+
"text": "CONFIDENTIAL",
|
|
245
|
+
"position": "tile",
|
|
246
|
+
"density": 5,
|
|
247
|
+
"fontSize": 20,
|
|
248
|
+
"overwrite": false,
|
|
249
|
+
"outputPath": "path/to/output/"
|
|
250
|
+
}
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
## 注意事项
|
|
254
|
+
|
|
255
|
+
1. 所有功能都支持批量处理多个文件
|
|
256
|
+
2. 支持处理整个目录中的图片(仅处理目录下的图片文件,不处理子目录中的)
|
|
257
|
+
3. 支持的图片格式:jpeg、jpg、png、webp、gif、avif、tiff、bmp
|
|
258
|
+
4. 压缩功能使用二分查找算法来找到最佳的压缩质量
|
|
259
|
+
5. 当指定输出目录时,如果目录不存在会自动创建
|
|
260
|
+
6. 所有错误都会被优雅地处理并记录,不会中断批处理过程
|
|
261
|
+
|
|
262
|
+
## 使用作为 MCP 服务器
|
|
263
|
+
|
|
264
|
+
该服务器实现了 Model Context Protocol (MCP) 规范,可以连接到支持 MCP 的客户端。
|
|
265
|
+
|
|
266
|
+
### 配置环境变量
|
|
267
|
+
|
|
268
|
+
在使用前,确保已安装所有依赖,并构建项目:
|
|
269
|
+
|
|
270
|
+
```bash
|
|
271
|
+
npm install
|
|
272
|
+
npm run build
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### 启动服务器
|
|
276
|
+
|
|
277
|
+
```bash
|
|
278
|
+
npm start
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
## 依赖
|
|
284
|
+
|
|
285
|
+
- sharp: 用于图片处理
|
|
286
|
+
- @modelcontextprotocol/sdk: MCP SDK
|
|
287
287
|
- TypeScript: 用于类型检查和编译
|
package/dist/index.js
CHANGED
|
@@ -161,29 +161,45 @@ async function compressToSize(inputPaths, targetSize, overwrite = false, outputP
|
|
|
161
161
|
const errors = [];
|
|
162
162
|
for (const imagePath of images) {
|
|
163
163
|
try {
|
|
164
|
-
|
|
164
|
+
let outputFilePath = overwrite
|
|
165
165
|
? imagePath
|
|
166
166
|
: generateOutputPath(imagePath, outputPath, `_${targetSize.toLowerCase()}`);
|
|
167
|
-
const image = sharp.default(imagePath);
|
|
168
|
-
const metadata = await image.metadata();
|
|
169
167
|
const stats = fs.statSync(imagePath);
|
|
170
|
-
|
|
168
|
+
const originalSize = stats.size;
|
|
169
|
+
if (originalSize <= targetBytes) {
|
|
171
170
|
if (!overwrite) {
|
|
172
171
|
await fs.promises.copyFile(imagePath, outputFilePath);
|
|
173
172
|
}
|
|
174
173
|
results.push(outputFilePath);
|
|
175
174
|
continue;
|
|
176
175
|
}
|
|
176
|
+
const ext = path.extname(imagePath).toLowerCase().slice(1);
|
|
177
|
+
const encodeWithQuality = async (q) => {
|
|
178
|
+
const img = sharp.default(imagePath);
|
|
179
|
+
if (ext === "jpg" || ext === "jpeg") {
|
|
180
|
+
return img.jpeg({ quality: q, mozjpeg: true }).toBuffer();
|
|
181
|
+
}
|
|
182
|
+
if (ext === "png") {
|
|
183
|
+
const clamped = Math.max(1, Math.min(q, 100));
|
|
184
|
+
return img.png({ quality: clamped, compressionLevel: 9, palette: true }).toBuffer();
|
|
185
|
+
}
|
|
186
|
+
if (ext === "webp") {
|
|
187
|
+
return img.webp({ quality: q }).toBuffer();
|
|
188
|
+
}
|
|
189
|
+
if (ext === "avif") {
|
|
190
|
+
return img.avif({ quality: q }).toBuffer();
|
|
191
|
+
}
|
|
192
|
+
// 其他统一转jpeg
|
|
193
|
+
return img.jpeg({ quality: q, mozjpeg: true }).toBuffer();
|
|
194
|
+
};
|
|
177
195
|
// 使用二分查找找到合适的质量值
|
|
178
|
-
let left =
|
|
179
|
-
let right =
|
|
180
|
-
let bestQuality = 100;
|
|
196
|
+
let left = 5;
|
|
197
|
+
let right = 95;
|
|
181
198
|
let bestBuffer = null;
|
|
182
199
|
while (left <= right) {
|
|
183
200
|
const quality = Math.floor((left + right) / 2);
|
|
184
|
-
const buffer = await
|
|
201
|
+
const buffer = await encodeWithQuality(quality);
|
|
185
202
|
if (buffer.length <= targetBytes) {
|
|
186
|
-
bestQuality = quality;
|
|
187
203
|
bestBuffer = buffer;
|
|
188
204
|
left = quality + 1;
|
|
189
205
|
}
|
|
@@ -191,13 +207,61 @@ async function compressToSize(inputPaths, targetSize, overwrite = false, outputP
|
|
|
191
207
|
right = quality - 1;
|
|
192
208
|
}
|
|
193
209
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
210
|
+
let outputBuffer = bestBuffer;
|
|
211
|
+
let outputExt = path.extname(outputFilePath).toLowerCase();
|
|
212
|
+
if (!outputBuffer) {
|
|
213
|
+
const fallback = await encodeWithQuality(Math.max(10, right));
|
|
214
|
+
if (fallback.length < originalSize) {
|
|
215
|
+
outputBuffer = fallback;
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
// PNG等难以降体积时,尝试有损WebP再二分
|
|
219
|
+
const tryWebp = async () => {
|
|
220
|
+
let l = 5, r = 95;
|
|
221
|
+
let buf = null;
|
|
222
|
+
while (l <= r) {
|
|
223
|
+
const q = Math.floor((l + r) / 2);
|
|
224
|
+
const b = await sharp.default(imagePath).webp({ quality: q }).toBuffer();
|
|
225
|
+
if (b.length <= targetBytes) {
|
|
226
|
+
buf = b;
|
|
227
|
+
l = q + 1;
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
r = q - 1;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
// 兜底:若仍未达到,但比原图小,也接受
|
|
234
|
+
if (!buf) {
|
|
235
|
+
const b = await sharp.default(imagePath).webp({ quality: Math.max(10, r) }).toBuffer();
|
|
236
|
+
if (b.length < originalSize)
|
|
237
|
+
return b;
|
|
238
|
+
}
|
|
239
|
+
return buf;
|
|
240
|
+
};
|
|
241
|
+
const webpBuf = await tryWebp();
|
|
242
|
+
if (webpBuf) {
|
|
243
|
+
outputBuffer = webpBuf;
|
|
244
|
+
// 将输出后缀改为 .webp
|
|
245
|
+
if (!overwrite) {
|
|
246
|
+
const parsed = path.parse(outputFilePath);
|
|
247
|
+
outputFilePath = path.join(parsed.dir, `${parsed.name}.webp`);
|
|
248
|
+
outputExt = '.webp';
|
|
249
|
+
}
|
|
250
|
+
else {
|
|
251
|
+
outputExt = '.webp';
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
if (!overwrite) {
|
|
256
|
+
await fs.promises.copyFile(imagePath, outputFilePath);
|
|
257
|
+
}
|
|
258
|
+
results.push(outputFilePath);
|
|
259
|
+
continue;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
200
262
|
}
|
|
263
|
+
await fs.promises.writeFile(outputFilePath, outputBuffer);
|
|
264
|
+
results.push(outputFilePath);
|
|
201
265
|
}
|
|
202
266
|
catch (error) {
|
|
203
267
|
const errorMsg = `Error compressing ${imagePath}: ${error instanceof Error ? error.message : String(error)}`;
|
|
@@ -221,19 +285,40 @@ async function compressToPercent(inputPaths, percent, overwrite = false, outputP
|
|
|
221
285
|
? imagePath
|
|
222
286
|
: generateOutputPath(imagePath, outputPath, `_${targetPercent}%`);
|
|
223
287
|
const stats = fs.statSync(imagePath);
|
|
224
|
-
const
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
288
|
+
const originalSize = stats.size;
|
|
289
|
+
if (targetPercent >= 100) {
|
|
290
|
+
if (!overwrite) {
|
|
291
|
+
await fs.promises.copyFile(imagePath, outputFilePath);
|
|
292
|
+
}
|
|
293
|
+
results.push(outputFilePath);
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
const targetBytes = Math.floor(originalSize * (targetPercent / 100));
|
|
297
|
+
const ext = path.extname(imagePath).toLowerCase().slice(1);
|
|
298
|
+
const encodeWithQuality = async (q) => {
|
|
299
|
+
const img = sharp.default(imagePath);
|
|
300
|
+
if (ext === "jpg" || ext === "jpeg") {
|
|
301
|
+
return img.jpeg({ quality: q, mozjpeg: true }).toBuffer();
|
|
302
|
+
}
|
|
303
|
+
if (ext === "png") {
|
|
304
|
+
const clamped = Math.max(1, Math.min(q, 100));
|
|
305
|
+
return img.png({ quality: clamped, compressionLevel: 9, palette: true }).toBuffer();
|
|
306
|
+
}
|
|
307
|
+
if (ext === "webp") {
|
|
308
|
+
return img.webp({ quality: q }).toBuffer();
|
|
309
|
+
}
|
|
310
|
+
if (ext === "avif") {
|
|
311
|
+
return img.avif({ quality: q }).toBuffer();
|
|
312
|
+
}
|
|
313
|
+
return img.jpeg({ quality: q, mozjpeg: true }).toBuffer();
|
|
314
|
+
};
|
|
315
|
+
let left = 5;
|
|
316
|
+
let right = 95;
|
|
231
317
|
let bestBuffer = null;
|
|
232
318
|
while (left <= right) {
|
|
233
319
|
const quality = Math.floor((left + right) / 2);
|
|
234
|
-
const buffer = await
|
|
320
|
+
const buffer = await encodeWithQuality(quality);
|
|
235
321
|
if (buffer.length <= targetBytes) {
|
|
236
|
-
bestQuality = quality;
|
|
237
322
|
bestBuffer = buffer;
|
|
238
323
|
left = quality + 1;
|
|
239
324
|
}
|
|
@@ -241,13 +326,22 @@ async function compressToPercent(inputPaths, percent, overwrite = false, outputP
|
|
|
241
326
|
right = quality - 1;
|
|
242
327
|
}
|
|
243
328
|
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
329
|
+
let outputBuffer = bestBuffer;
|
|
330
|
+
if (!outputBuffer) {
|
|
331
|
+
const fallback = await encodeWithQuality(Math.max(10, right));
|
|
332
|
+
if (fallback.length < originalSize) {
|
|
333
|
+
outputBuffer = fallback;
|
|
334
|
+
}
|
|
335
|
+
else {
|
|
336
|
+
if (!overwrite) {
|
|
337
|
+
await fs.promises.copyFile(imagePath, outputFilePath);
|
|
338
|
+
}
|
|
339
|
+
results.push(outputFilePath);
|
|
340
|
+
continue;
|
|
341
|
+
}
|
|
250
342
|
}
|
|
343
|
+
await fs.promises.writeFile(outputFilePath, outputBuffer);
|
|
344
|
+
results.push(outputFilePath);
|
|
251
345
|
}
|
|
252
346
|
catch (error) {
|
|
253
347
|
const errorMsg = `Error compressing ${imagePath}: ${error instanceof Error ? error.message : String(error)}`;
|
package/package.json
CHANGED
|
@@ -1,42 +1,42 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@mcpcn/mcp-image-compressor",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "MCP server for image compression",
|
|
5
|
-
"main": "dist/index.js",
|
|
6
|
-
"scripts": {
|
|
7
|
-
"build": "tsc
|
|
8
|
-
"start": "node dist/index.js",
|
|
9
|
-
"dev": "ts-node index.ts",
|
|
10
|
-
"serve": "node dist/index.js"
|
|
11
|
-
},
|
|
12
|
-
"bin": {
|
|
13
|
-
"mcp-image-compressor": "dist/index.js"
|
|
14
|
-
},
|
|
15
|
-
"files": [
|
|
16
|
-
"dist",
|
|
17
|
-
"README.md"
|
|
18
|
-
],
|
|
19
|
-
"publishConfig": {
|
|
20
|
-
"access": "public"
|
|
21
|
-
},
|
|
22
|
-
"keywords": [
|
|
23
|
-
"mcp",
|
|
24
|
-
"image",
|
|
25
|
-
"compressor",
|
|
26
|
-
"sharp",
|
|
27
|
-
"model-context-protocol"
|
|
28
|
-
],
|
|
29
|
-
"author": "mcpcn",
|
|
30
|
-
"license": "MIT",
|
|
31
|
-
"dependencies": {
|
|
32
|
-
"@modelcontextprotocol/sdk": "1.12.0",
|
|
33
|
-
"sharp": "^0.33.5",
|
|
34
|
-
"semver": "^7.6.3"
|
|
35
|
-
},
|
|
36
|
-
"devDependencies": {
|
|
37
|
-
"@types/node": "^20.0.0",
|
|
38
|
-
"@types/sharp": "^0.31.1",
|
|
39
|
-
"ts-node": "^10.9.1",
|
|
40
|
-
"typescript": "^5.0.0"
|
|
41
|
-
}
|
|
42
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@mcpcn/mcp-image-compressor",
|
|
3
|
+
"version": "1.0.4",
|
|
4
|
+
"description": "MCP server for image compression",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "tsc",
|
|
8
|
+
"start": "node dist/index.js",
|
|
9
|
+
"dev": "ts-node index.ts",
|
|
10
|
+
"serve": "node dist/index.js"
|
|
11
|
+
},
|
|
12
|
+
"bin": {
|
|
13
|
+
"mcp-image-compressor": "dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md"
|
|
18
|
+
],
|
|
19
|
+
"publishConfig": {
|
|
20
|
+
"access": "public"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"mcp",
|
|
24
|
+
"image",
|
|
25
|
+
"compressor",
|
|
26
|
+
"sharp",
|
|
27
|
+
"model-context-protocol"
|
|
28
|
+
],
|
|
29
|
+
"author": "mcpcn",
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"@modelcontextprotocol/sdk": "1.12.0",
|
|
33
|
+
"sharp": "^0.33.5",
|
|
34
|
+
"semver": "^7.6.3"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@types/node": "^20.0.0",
|
|
38
|
+
"@types/sharp": "^0.31.1",
|
|
39
|
+
"ts-node": "^10.9.1",
|
|
40
|
+
"typescript": "^5.0.0"
|
|
41
|
+
}
|
|
42
|
+
}
|