关于 Image Processor Mcp
Image Processor MCP 为 AI 助手提供图像处理工具,涵盖下载、压缩、目录批处理、文件名同步和本地 OCR。
主要功能
- 01通过 URL 或 Pexels 搜索下载图像
- 02压缩图像并转换格式
- 03批量处理目录并生成报告
- 04规范文件名并同步引用
- 05使用本地 OCR 提取文字
适用场景
- 01为项目下载并优化图像
- 02转换和调整单张图像尺寸
- 03保留目录结构压缩图像文件夹
- 04在本地提取图像中的文字
MCP 相关
它向 MCP 客户端提供图像下载、压缩、批处理、文件名同步和 OCR 工具。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
6收录的工具列表 · 更新于 3 周前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| url | string | 否 | 要下载的图像 URL(未提供 query 时必填) |
| query | string | 否 | 用于查找图像的搜索关键词(未提供 url 时必填) |
| outputPath | string | 否 | 保存下载图像的路径 |
| outputFormat | string | 否 | 输出格式:webp、avif、jpeg、png、tiff、gif(默认:保留原格式) |
| quality | number | 否 | 压缩质量 1-100(默认:85) |
| lossless | boolean | 否 | 使用无损压缩(默认:false) |
| effort | number | 否 | CPU 运算量 0-6,6 表示最慢但压缩效果最佳(默认:6) |
| width | number | 否 | 精确的目标宽度(仅指定 width 时保持纵横比) |
| height | number | 否 | 精确目标高度(仅指定高度时保持纵横比) |
| maxDimension | number | 否 | 宽度或高度超过此阈值时自动调整尺寸(默认值:2000) |
| recursiveCompress | boolean | 否 | 重新压缩,直到文件小于目标大小(默认值:false) |
| expectedSizeKB | number | 否 | 递归压缩的目标文件大小,单位为 KB(默认值:100) |
| qualityStepDown | number | 否 | 每次递归迭代的质量降幅(默认值:10) |
| minimumQualityFloor | number | 否 | 递归压缩期间允许的最低质量(默认值:10) |
详细介绍
Image Processor MCP — 完整文档
版本:0.2.3
软件包:image-processor-mcp
作者:Dhvanil Pansuriya
目录
- 概述
- 安装与配置
- 工具:download_image
- 工具:compress_image
- 工具:compress_directory
- 工具:sync_filenames
- 工具:extract_text
- 工具:get_acknowledgement
- 配置默认值
- 支持的格式
- 致谢与许可证
概述
一个 MCP(Model Context Protocol)服务,提供 6 个工具,用于图像下载、压缩、批量处理、文件名同步、OCR 文本提取和配置发现。
快速入门
npx -y image-processor-mcp
主要功能
- 从 URL 下载图像,或按关键词搜索(集成 Pexels)
- 压缩并转换单张图像,支持 10 种输出格式
- 递归压缩,将文件压缩至目标大小
- 支持无损和有损压缩,可配置质量和 CPU 运算量
- 自动调整过大图像的尺寸
- 批量处理整个目录,同时保留文件夹结构
- 规范化文件名和目录名
- 自动生成 Markdown 压缩报告
- 自动生成与报告配套的文件名映射
- 扫描项目文件,并将旧文件名替换为新文件名
- OCR 文本提取(支持 100 多种语言,完全在本地运行)
安装与配置
从 npm 安装
npm install image-processor-mcp
要求
- Node.js 18 或更高版本
MCP 配置
Claude Desktop
{
"mcpServers": {
"image-processor-mcp": {
"command": "npx",
"args": ["-y", "image-processor-mcp"],
"env": {
"IMAGE_SEARCH_API_KEY": "your_pexels_api_key_here"
}
}
}
}
VS Code(Cline / MCP 扩展)
{
"mcpServers": {
"image-processor-mcp": {
"command": "npx",
"args": ["-y", "image-processor-mcp"],
"env": {
"IMAGE_SEARCH_API_KEY": "your_pexels_api_key_here"
}
}
}
}
IMAGE_SEARCH_API_KEY为可选项——仅在download_image中按关键词搜索时需要。可在 https://www.pexels.com/api/key/ 免费获取密钥。
1. download_image
从 URL 下载图像,或按关键词搜索。还可选择压缩/转换图像。
模式
| 模式 | 参数 | 说明 |
|---|---|---|
| URL 模式 | url | 直接从图像 URL 下载 |
| 搜索模式 | query | 通过 Pexels 按关键词搜索 |
如果两者都已提供,则优先使用搜索模式。
参数
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
url | string | 否* | — | 图像直接链接 |
query | string | 否* | — | 搜索关键词 |
outputPath | string | 是 | — | 保存路径 |
outputFormat | string | 否 | 原始格式 | webp、avif、jpeg、png、tiff、gif |
quality | number | 否 | 85 | 1–100 |
lossless | boolean | 否 | false | 无损模式 |
effort | number | 否 | 6 | CPU 运算量,范围为 0–6 |
width | number | 否 | — | 目标宽度 |
height | number | 否 | — | 目标高度 |
maxDimension | number | 否 | 2000 | 自动调整尺寸的阈值 |
recursiveCompress | boolean | 否 | false | 迭代调整目标大小 |
expectedSizeKB | number | 否 | 100 | 目标大小(KB) |
qualityStepDown | number | 否 | 10 | 每次迭代的质量降幅 |
minimumQualityFloor | number | 否 | 10 | 最低质量 |
* 必须提供 url 或 query 其中之一。
数据流
Request → type guard → Controller
├─ Search mode? → Pexels API → first hit URL
└─ URL mode? → use directly
↓
axios.get(url, arraybuffer, 30s timeout)
↓
Write to temp file → check compression params
├─ YES → ImageService.compressImage → clean temp → return result
└─ NO → move to final path → return success
响应示例(搜索 + 压缩)
{
"query": "mountain sunset",
"imageId": 12345,
"width": 1920,
"height": 1080,
"imageSize": 0,
"user": "John Doe",
"tags": "mountain sunset",
"inputPath": "...",
"outputPath": "./images/mountain.webp",
"originalSize": 500000,
"compressedSize": 80000,
"compressionRatio": 84.00,
"success": true,
"iterations": 1,
"qualitySteps": [85],
"finalQuality": 85,
"exceededTarget": false
}
边缘情况
- 未提供 url 或 query:错误——必须提供其中一个
- 搜索结果为空:错误——未找到图像
- 缺少 API 密钥:错误——未设置
IMAGE_SEARCH_API_KEY - 下载超时(30 秒):错误——超出超时时间
- 压缩失败:清理临时文件,并返回错误
2. compress_image
压缩或转换单张图像,并全面控制质量、尺寸、格式和递归压缩。
参数
| 参数 | 类型 | 必填 | 默认值 | 描述 |
|---|---|---|---|---|
inputPath | string | 是 | — | 源图像路径 |
outputPath | string | 是 | — | 目标路径 |
outputFormat | string | 否 | webp | 输出格式 |
quality | number | 否 | 85 | 1–100 |
lossless | boolean | 否 | false | 无损模式 |
effort | number | 否 | 6 | CPU 运算量 0–6 |
width | number | 否 | — | 目标宽度 |
height | number | 否 | — | 目标高度 |
maxDimension | number | 否 | 2000 | 自动调整大小的阈值 |
recursiveCompress | boolean | 否 | false | 迭代式目标控制 |
expectedSizeKB | number | 否 | 100 | 目标 KB |
qualityStepDown | number | 否 | 10 | 每次迭代降低的幅度 |
minimumQualityFloor | number | 否 | 10 | 最低质量 |
核心压缩算法
1. Read original size + image metadata (sharp)
2. Compute resize:
- Explicit w/h? → use those
- Exceeds maxDimension? → scale proportionally
- Otherwise → no resize
3. Loop:
- sharp pipeline → apply format with quality
- If input === output → buffer (safe in-place)
- Else → write to file
- If recursive + over target + above floor → reduce quality, loop
- Else → break
4. Return CompressionResult
格式转换
| 格式 | Sharp 选项 |
|---|---|
webp | {quality, lossless, effort, smartSubsample, alphaQuality:90} |
avif | {quality, lossless, effort} |
jpeg | {quality, mozjpeg:true} |
png | {compressionLevel:9} |
tiff | {quality, compression:'lzw'} |
gif | gif()(无选项) |
| 其他 | 回退到 WebP |
响应示例
{
"inputPath": "./input/photo.jpg",
"outputPath": "./output/photo.webp",
"originalSize": 500000,
"compressedSize": 80000,
"compressionRatio": 84.00,
"success": true,
"iterations": 1,
"qualitySteps": [85],
"finalQuality": 85,
"exceededTarget": false
}
边缘情况
- 原位压缩(路径相同):基于缓冲区处理,以避免文件损坏
- 输入文件缺失:返回带有错误信息的
success: false - 未知格式:回退到 WebP
- 递归压缩下限:质量无法继续降低时停止
- 中止:返回部分结果,并设置
success: false
3. compress_directory
递归批量压缩目录中的所有图像,并保留文件夹结构。生成报告和映射。
参数
| 参数 | 类型 | 必填 | 默认值 | 描述 |
|---|---|---|---|---|
inputDir | string | 是 | — | 源目录 |
outputDir | string | 是 | — | 输出目录 |
outputFormat | string | 否 | webp | 输出格式 |
quality | number | 否 | 85 | 1–100 |
lossless | boolean | 否 | false | 无损模式 |
effort | number | 否 | 6 | CPU 运算量 0–6 |
maxDimension | number | 否 | 2000 | 自动调整大小的阈值 |
normalizeFilename | boolean | 否 | false | 清理文件名 |
filenameReplaceChars | string | 否 | , -, ., & | 要替换的字符 |
filenameCase | string | 否 | lowercase | 大小写转换 |
normalizeDirname | boolean | 否 | false | 清理目录名 |
dirnameReplaceChars | string | 否 | , -, ., & | 目录名替换字符 |
dirnameCase | string | 否 | lowercase | 目录名大小写 |
recursiveCompress | boolean | 否 | false | 迭代式目标控制 |
expectedSizeKB | number | 否 | 100 | 目标 KB |
qualityStepDown | number | 否 | 5(schema)/ 10(代码) | 每次迭代降低的幅度 |
minimumQualityFloor | number | 否 | 10 | 最低质量 |
数据流
Request → type guard → Controller
├─ Validate input directory exists
├─ Apply defaults, build FileNamingOptions
├─ Scan all image files (recursive)
├─ Filter out SVG (restricted)
├─ For each file:
│ ├─ Compute output path (preserves structure, normalizes, changes ext)
│ └─ Run ImageService.compressImage
├─ Generate report (.image-processor-mcp/reports/)
└─ Generate mappings JSON (.image-processor-mcp/mappings/)
输出路径算法
relativePath = inputDir → inputFile difference
1. Dir naming enabled → normalize each directory component
2. File naming enabled → normalize basename
3. Replace extension with .{outputFormat}
4. Prepend outputDir
normalizeName
Replace each char → '_', collapse multiple '_', trim edges, apply case
生成的文件
.image-processor-mcp/
├── reports/scan-report-{timestamp}.md
└── mappings/mappings-{timestamp}.json
响应示例
{
"success": true,
"totalFiles": 10,
"successful": 9,
"failed": 1,
"totalOriginalSize": 5000000,
"totalCompressedSize": 1200000,
"overallReduction": 76.00,
"durationMs": 5432,
"reportPath": ".image-processor-mcp/reports/scan-report-2026-05-29-15-30-00.md",
"mappingsPath": ".image-processor-mcp/mappings/mappings-2026-05-29-15-30-00.json"
}
边缘情况
- 输入目录不存在:提前报错,不写入任何文件
- SVG 文件:静默排除
- 批处理过程中中止:处理部分结果
- 所有文件均失败:不生成映射文件
- 未找到图像:生成空报告
4. sync_filenames
扫描项目文件,并在压缩后将所有旧文件名引用替换为新文件名。
参数
| 参数 | 类型 | 必填 | 默认值 | 描述 |
|---|---|---|---|---|
scanDir | string | 是 | — | 要扫描的目录 |
mappingFilePath | string | 否 | — | 映射 JSON 文件的路径 |
mappings | array | 否 | — | 内联 {oldFileName, newFileName}[] |
includeExtensions | array | 否 | html,js,ts,css,md,json... | 要扫描的扩展名 |
excludeDirectories | array | 否 | node_modules,.git,build,dist... | 要跳过的目录 |
dryRun | boolean | 否 | true | 仅预览 |
验证
使用 mappingFilePath 时:
- 文件存在且为有效 JSON
- 存在非空的映射数组
- 每个条目都包含
oldFileName和newFileName(字符串) - 从文件名
mappings-(.+).json中提取时间戳 - 必须存在匹配的报告:
reports/scan-report-{timestamp}.md
如果缺少匹配的报告 → 操作将被拒绝(防止使用过时的映射)。
数据流
Request → type guard → Controller
├─ mappingFilePath?
│ YES → ReferenceService.readMappingFile → validate against report
│ NO → use inline mappings
├─ dryRun = args.dryRun !== false
├─ ReferenceService.scanReferences:
│ ├─ Collect files recursively (filtered by extension, excluding dirs)
│ ├─ For each file:
│ │ ├─ For each mapping:
│ │ │ ├─ Regex-escape oldFileName, count occurrences
│ │ │ ├─ Build line-by-line details
│ │ │ ├─ Build preview (3 lines context)
│ │ │ └─ If !dryRun: replace in content
│ │ └─ If modified: write file
│ └─ Return UpdateRefResult
└─ ReportService.generateSyncReport → .image-processor-mcp/syncs/
生成的文件
.image-processor-mcp/syncs/sync-report-{timestamp}.md (live)
.image-processor-mcp/syncs/dry-run/sync-report-{timestamp}.md (dry-run)
响应示例
Dry run — no files were modified.
Mapping source: .image-processor-mcp/mappings/mappings-2026-05-29-15-30-00.json
Files scanned: 42
Files with matches: 3
Total replacements: 12
--- Matches ---
[src/components/Header.tsx]
"logo.png" → "logo.webp" (5x)
Preview: ... context lines ...
Line 15:23 "logo.png" → "logo.webp"
... and 3 more (see report)
Report saved to: .image-processor-mcp/syncs/dry-run/sync-report-2026-05-29-15-31-00.md
边缘情况
- 未找到映射文件:报错并显示路径
- JSON 无效:解析错误
- 没有匹配的报告:显示详细的拒绝消息
- 未提供任何来源:报错并说明可选项
- 未找到匹配项:提示信息
- 正则表达式特殊字符:正确转义
- 二进制文件:根据扩展名列表筛除
5. extract_text
使用 OCR(tesseract.js)从图像中提取可见的印刷文本。完全在本地运行。
参数
| 参数 | 类型 | 必填 | 默认值 | 描述 |
|---|---|---|---|---|
imagePath | string | 是 | — | 图像文件的路径 |
language | string | 否 | eng | 语言代码(多个语言使用 +) |
rectangle | object | 否 | — | 区域 {top, left, width, height} |
outputFormat | enum | 否 | text | text、blocks、hocr、tsv |
preprocess | boolean | 否 | true | 灰度处理 + 自动旋转 |
数据流
Request → type guard → OcrController → OcrService.recognize
├─ Validate path (exists, not dir, not empty, not SVG)
├─ Validate rectangle (positive dimensions)
├─ Build sharp pipeline:
│ ├─ Convert unsupported formats to PNG
│ └─ Preprocess: grayscale + rotate
├─ runOcr:
│ ├─ Serialized via jobQueue (one at a time)
│ ├─ getWorker: reuse / reinitialize / create
│ ├─ worker.recognize()
│ └─ Recycle worker every 500 jobs
└─ Return ExtractTextResult
Worker 生命周期
| 状态 | 操作 |
|---|---|
| Worker 存在且语言相同 | 直接复用 |
| Worker 存在但语言不同 | worker.reinitialize(language) |
| 没有 Worker | createWorker(language, 1, ...) |
| 执行 500 次任务后 | setImmediate(recycleWorker) |
| Worker 出错 | errorHandler 将引用置为 null |
| SIGINT / 传输关闭 | shutdown() → 终止 |
支持的输入格式
| 原生支持 | 自动转换为 PNG |
|---|---|
| bmp, jpg, jpeg, png | tiff, heic, heif, webp*, gif* |
| pbm, pgm, ppm | 以及所有其他格式 |
* 尽管存在转换检查,tesseract.js 原生支持 webp 和 gif。代码会转换所有不在 OCR_SUPPORTED_EXTENSIONS 中的扩展名。由于 webp 和 gif 都在该列表中,因此不会进行转换。
限制
- 手写文本:不支持
- SVG 文件:拒绝(矢量格式)
- 空图像:返回空字符串(不是错误)
- 首次运行:从 CDN 下载语言数据(约 2 MB)
响应示例(text 格式)
{
"success": true,
"text": "Extracted text content",
"confidence": 92.5,
"words": 42,
"language": "eng",
"durationMs": 1234,
"preprocessed": true
}
边缘情况
- SVG:明确拒绝
- 不支持的格式:自动转换为 PNG
- Worker 崩溃:自动恢复(errorHandler 将引用置为 null)
- 中止:捕获 DOMException → 返回已取消的结果
- 空文件(0 字节):明确拒绝
- 路径是目录:明确拒绝
6. get_acknowledgement
返回所有工具、默认值、格式和环境变量的完整文档。
参数
无——输入架构为空。
响应
根据所有配置常量动态生成的完整 Markdown 文档:
| 部分 | 来源 |
|---|---|
| 全部 6 个工具 | 硬编码的描述 |
| 配置默认值 | DEFAULTS + CONFIG_DESCRIPTIONS |
| 支持的输入格式 | SUPPORTED_EXTENSIONS + INPUT_EXTENSION_DESCRIPTIONS |
| 支持的输出格式 | SUPPORTED_OUTPUT_FORMATS + OUTPUT_FORMAT_DESCRIPTIONS |
| 同步扫描默认值 | DEFAULT_EXTENSIONS + DEFAULT_EXCLUDE_DIRS |
| 受限扩展名 | RESTRICTED_EXTENSIONS |
| 环境变量 | 硬编码 |
| 致谢 | 硬编码 |
实现
ToolController.handleGetAcknowledgement()
// Synchronous — no abort signal, no async
// Returns { content: [{ type: 'text', text: markdown }] }
边缘情况
无 — 始终成功,不需要参数,也不进行 I/O。
配置默认值
| 参数 | 默认值 | 描述 |
|---|---|---|
quality | 85 | 有损格式的压缩质量(1–100) |
lossless | false | 无损压缩(WebP、PNG、GIF、AVIF) |
effort | 6 | CPU 压缩力度(0–6)。数值越高,文件越小 |
maxDimension | 2000 | 自动调整尺寸的像素阈值 |
expectedSizeKB | 100 | 递归压缩的目标文件大小 |
qualityStepDown | 10 | 每次递归迭代降低的质量 |
minimumQualityFloor | 10 | 递归过程中的最低质量(1–100) |
outputFormat | webp | 默认输出格式 |
filenameCase | lowercase | 文件名大小写转换 |
dirnameCase | lowercase | 目录名大小写转换 |
filenameReplaceChars | [' ', '-', '.', '&'] | 在文件名中替换为 _ 的字符 |
dirnameReplaceChars | [' ', '-', '.', '&'] | 在目录名中替换为 _ 的字符 |
支持的格式
输入格式(35+)
.jpg、.jpeg、.png、.gif、.bmp、.tiff、.tif、.webp、.avif、.svg、.heic、.heif、.ico、.cur、.jp2、.j2k、.jpf、.jpx、.jpm、.mj2、.jxr、.wdp、.hdp、.psd、.psb、.dds、.tga、.vda、.icb、.vst、.pbm、.pgm、.ppm、.pnm、.pfm、.exr、.hdr、.hrd、.pic、.pict、.pct、.xbm、.xpm、.wal、.cut、.ras、.sun、.sgi、.rgb、.rgba、.bw、.pcx、.pcd、.iff、.lbm
输出格式(10)
| 格式 | 描述 |
|---|---|
webp | Google WebP。出色的有损/无损压缩。支持透明度。 |
avif | 基于 AV1。比 JPEG 小 30–50%。编码速度较慢。 |
jpeg | 通用兼容性。不支持透明度。 |
png | 支持透明度的无损格式。最适合图形/徽标。 |
tiff | 高质量。适用于印刷/出版。文件体积最大。 |
gif | 支持动画和透明度。256 色。 |
heif | 基于 HEVC。适用于 Apple 生态系统。 |
jp2 | JPEG 2000。基于小波。适用于医疗/电影。 |
jxl | JPEG XL。新一代格式。支持无损 JPEG 重新编码。 |
pdf | 将图像转换为 PDF 页面。 |
受限扩展名
.svg — 批量压缩时会跳过 SVG 文件(矢量格式 — sharp 无法在不破坏渲染效果的情况下安全压缩)。
致谢与许可证
作者
Dhvanil Pansuriya
| GitHub | https://github.com/pansuriyadhvanil/ |
| https://linkedin.com/in/dhvanil-pansuriya/ | |
| Package | npm 上的 image-processor-mcp |
技术致谢
| 技术 | 作用 |
|---|---|
| Model Context Protocol SDK | MCP 服务框架 |
| sharp | 高性能图像处理 |
| tesseract.js | OCR 文本提取 — 100% 本地运行,无需 API 密钥 |
| Pexels | 用于图像搜索的免费图库照片 API |
| axios | HTTP 客户端 |
| fs-extra | 扩展文件系统 |
许可证
MIT 许可证 — 详情请参阅 LICENSE。
Image-Processor-MCP
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。