Skip to main content
Glama
README.md
# Vision MCP Server

基于 VLM(视觉语言模型)的 MCP 服务,提供视觉问答、图像解读、目标检测、OCR 以及完整的图像处理工具链。通过 OpenAI 兼容 API 接入任意 VLM 大模型,以 stdio 方势提供服务。

## 特性

- **4 个 VLM 工具**:视觉问答、视觉解读、目标检测(归一化包围盒)、OCR 文字提取
- **8 个图像处理工具**:元信息、缩放、裁剪、旋转、镜像、拼接、画方框、写文字(中文)
- **忠实还原**:所有 VLM 调用内置"不脑补"指令 + `temperature: 0`
- **自动缩放**:VLM 工具内置 `max_dimension` 参数,发送前自动等比缩放,避免超限
- **坐标归一化**:检测工具通过系统提示词 + 后处理双重保障,始终返回 0-1 归一化包围盒
- **双输入模式**:支持本地文件路径和 URL 两种图片输入方式
- **双输出模式**:图像处理工具返回 base64 图片内容,可选 `output_path` 保存到文件

## 快速开始

### 环境要求

- Node.js >= 18
- 任意 OpenAI 兼容的 VLM API(如 OpenAI GPT-4o、Qwen-VL、GLM-4V 等)

### 安装

```bash
git clone <repo-url>
cd vision-mcp
npm install
npm run build
```

### 配置

通过环境变量配置:

| 环境变量 | 必需 | 说明 | 示例 |
|---------|------|------|------|
| `VLM_BASE_URL` | 是 | VLM API 基础地址 | `https://api.openai.com/v1` |
| `VLM_API_KEY` | 是 | API 密钥 | `sk-xxxx` |
| `VLM_MODEL_ID` | 是 | 模型 ID | `gpt-4o` |
| `VISION_MCP_FONT_PATH` | 否 | 中文字体文件路径(.ttf/.otf) | `fonts/SimHei.ttf` |

### 启动

```bash
VLM_BASE_URL=https://api.openai.com/v1 \
VLM_API_KEY=sk-xxxx \
VLM_MODEL_ID=gpt-4o \
node dist/index.js
```

### 在 MCP 客户端中配置

**Claude Desktop** (`claude_desktop_config.json`):

```json
{
  "mcpServers": {
    "vision": {
      "command": "node",
      "args": ["/path/to/vision-mcp/dist/index.js"],
      "env": {
        "VLM_BASE_URL": "https://api.openai.com/v1",
        "VLM_API_KEY": "sk-xxxx",
        "VLM_MODEL_ID": "gpt-4o",
        "VISION_MCP_FONT_PATH": "/path/to/vision-mcp/fonts/SimHei.ttf"
      }
    }
  }
}
```

**Cursor / 其他 MCP 客户端**:参照各客户端文档,使用 `node dist/index.js` 作为启动命令,传入上述环境变量。

### 使用 MCP Inspector 调试

```bash
VLM_BASE_URL=... VLM_API_KEY=... VLM_MODEL_ID=... \
npm run inspector
```

## 工具列表

### VLM 工具(4 个)

通过 OpenAI 兼容 API 调用 VLM 大模型完成视觉任务。所有 VLM 工具:
- 接受 `images`(路径或 URL 数组,1-8 张)
- 内置 `max_dimension`(默认 2048)自动缩放
- 使用 `temperature: 0` + 忠实性提示词确保不脑补

#### `vision_qa` — 视觉问答

对图片提问,返回基于图片内容的文字回答。适用于检查 Web 页面、PPT 页面是否符合要求等场景。

| 参数 | 类型 | 必需 | 默认值 | 说明 |
|------|------|------|--------|------|
| `images` | `string[]` | 是 | — | 图片路径或 URL 列表 |
| `question` | `string` | 是 | — | 要提问的问题 |
| `max_dimension` | `number` | 否 | 2048 | 发送前自动缩放最大边长,设 0 禁用 |

#### `vision_describe` — 视觉解读

详细、真实地解读图片内容,不推测或脑补。

| 参数 | 类型 | 必需 | 默认值 | 说明 |
|------|------|------|--------|------|
| `images` | `string[]` | 是 | — | 图片路径或 URL 列表 |
| `detail_level` | `"brief"\|"normal"\|"detailed"` | 否 | `"normal"` | 描述详细程度 |
| `max_dimension` | `number` | 否 | 2048 | 发送前自动缩放最大边长 |

#### `vision_detect` — 视觉检测

在图片中检测指定目标,返回 0-1 归一化包围盒。

系统提示词强制要求归一化坐标;后处理函数自动检测像素坐标(值 > 1)并除以图片尺寸归一化,双重保障。

| 参数 | 类型 | 必需 | 默认值 | 说明 |
|------|------|------|--------|------|
| `images` | `string[]` | 是 | — | 图片路径或 URL 列表 |
| `target` | `string` | 是 | — | 要检测的目标描述 |
| `max_dimension` | `number` | 否 | 2048 | 发送前自动缩放最大边长 |

返回结构:

```json
{
  "detections": [
    {
      "label": "对象描述",
      "bbox": { "x_min": 0.1, "y_min": 0.2, "x_max": 0.3, "y_max": 0.4 },
      "confidence": 0.95
    }
  ]
}
```

#### `vision_ocr` — 视觉 OCR

提取图片中所有可见文字。

| 参数 | 类型 | 必需 | 默认值 | 说明 |
|------|------|------|--------|------|
| `images` | `string[]` | 是 | — | 图片路径或 URL 列表 |
| `max_dimension` | `number` | 否 | 2048 | 发送前自动缩放最大边长 |

返回结构:

```json
{
  "text_blocks": [
    { "text": "文字内容", "bbox": { "x_min": 0.1, "y_min": 0.2, "x_max": 0.3, "y_max": 0.4 } }
  ],
  "full_text": "所有文字按阅读顺序拼接"
}
```

### 图像处理工具(8 个)

基于 sharp 和 @napi-rs/canvas 的程序化图像操作。所有工具:
- 返回 base64 PNG 图片内容(MCP `image` content type)
- 提供 `structuredContent`(宽高、格式、大小)
- 支持可选 `output_path` 参数保存到文件

#### `image_get_metadata` — 获取图片元信息

返回宽度、高度、格式、通道数、色彩空间、DPI、Alpha 通道、EXIF 方向等。

| 参数 | 类型 | 必需 | 说明 |
|------|------|------|------|
| `image` | `string` | 是 | 图片路径或 URL |

#### `image_resize` — 图片缩放

| 参数 | 类型 | 必需 | 默认值 | 说明 |
|------|------|------|--------|------|
| `image` | `string` | 是 | — | 图片路径或 URL |
| `width` | `number` | 否 | — | 目标宽度(像素) |
| `height` | `number` | 否 | — | 目标高度(像素) |
| `scale` | `number` | 否 | — | 缩放比例(0.01-10) |
| `fit` | `string` | 否 | `"inside"` | 缩放模式:cover/contain/fill/inside/outside |
| `output_path` | `string` | 否 | — | 保存路径 |

> `width`/`height` 和 `scale` 三选一。`width`/`height` 同时指定时按 `fit` 模式处理。

#### `image_crop` — 图片裁剪

| 参数 | 类型 | 必需 | 默认值 | 说明 |
|------|------|------|--------|------|
| `image` | `string` | 是 | — | 图片路径或 URL |
| `left` | `number` | 是 | — | 裁剪区域左上角 x 坐标 |
| `top` | `number` | 是 | — | 裁剪区域左上角 y 坐标 |
| `width` | `number` | 是 | — | 裁剪区域宽度 |
| `height` | `number` | 是 | — | 裁剪区域高度 |
| `normalized` | `boolean` | 否 | `false` | 坐标是否为 0-1 归一化值 |
| `output_path` | `string` | 否 | — | 保存路径 |

#### `image_rotate` — 图片旋转

| 参数 | 类型 | 必需 | 说明 |
|------|------|------|------|
| `image` | `string` | 是 | 图片路径或 URL |
| `angle` | `number` | 是 | 旋转角度(正数为顺时针) |
| `output_path` | `string` | 否 | 保存路径 |

#### `image_flip` — 图片镜像

| 参数 | 类型 | 必需 | 说明 |
|------|------|------|------|
| `image` | `string` | 是 | 图片路径或 URL |
| `direction` | `"horizontal"\|"vertical"` | 是 | horizontal=左右翻转,vertical=上下翻转 |
| `output_path` | `string` | 否 | 保存路径 |

#### `image_concat` — 图片拼接

| 参数 | 类型 | 必需 | 默认值 | 说明 |
|------|------|------|--------|------|
| `images` | `string[]` | 是 | — | 图片路径或 URL 列表 |
| `layout` | `"horizontal"\|"vertical"\|"grid"` | 是 | — | 拼接布局 |
| `cols` | `number` | 否 | — | grid 布局的列数 |
| `gap` | `number` | 否 | 0 | 图片间距(像素) |
| `background` | `string` | 否 | `"#FFFFFF"` | 背景色 |
| `output_path` | `string` | 否 | — | 保存路径 |

#### `image_draw_box` — 添加方框标记

| 参数 | 类型 | 必需 | 默认值 | 说明 |
|------|------|------|--------|------|
| `image` | `string` | 是 | — | 图片路径或 URL |
| `boxes` | `object[]` | 是 | — | 方框列表 |
| `boxes[].x` | `number` | 是 | — | 方框左上角 x 坐标 |
| `boxes[].y` | `number` | 是 | — | 方框左上角 y 坐标 |
| `boxes[].w` | `number` | 是 | — | 方框宽度 |
| `boxes[].h` | `number` | 是 | — | 方框高度 |
| `boxes[].color` | `string` | 否 | `"#FF0000"` | 方框颜色 |
| `boxes[].label` | `string` | 否 | — | 方框标签文字 |
| `boxes[].line_width` | `number` | 否 | 自适应 | 线宽(像素) |
| `normalized` | `boolean` | 否 | `false` | 坐标是否为 0-1 归一化值 |
| `output_path` | `string` | 否 | — | 保存路径 |

#### `image_draw_text` — 添加文字标记

支持中文,中文字体通过 `VISION_MCP_FONT_PATH` 指定,或自动检测系统 CJK 字体。

| 参数 | 类型 | 必需 | 默认值 | 说明 |
|------|------|------|--------|------|
| `image` | `string` | 是 | — | 图片路径或 URL |
| `texts` | `object[]` | 是 | — | 文字列表 |
| `texts[].x` | `number` | 是 | — | 文字左上角 x 坐标 |
| `texts[].y` | `number` | 是 | — | 文字左上角 y 坐标 |
| `texts[].text` | `string` | 是 | — | 文字内容(支持中文) |
| `texts[].font_size` | `number` | 否 | 24 | 字号(像素) |
| `texts[].color` | `string` | 否 | `"#FF0000"` | 文字颜色 |
| `texts[].background_color` | `string` | 否 | — | 文字背景色 |
| `normalized` | `boolean` | 否 | `false` | 坐标是否为 0-1 归一化值 |
| `output_path` | `string` | 否 | — | 保存路径 |

## 使用示例

### 示例 1:检测图片中的目标并标注

```
Agent: 我要找到这张图片中所有人的位置并标注出来

工具调用流程:
1. vision_detect(images=["photo.jpg"], target="人")
   → { detections: [{ label: "人", bbox: {x_min:0.1, y_min:0.2, x_max:0.3, y_max:0.5}, confidence: 0.9 }] }

2. image_draw_box(
     image="photo.jpg",
     boxes=[{ x:0.1, y:0.2, w:0.2, h:0.3, color:"#FF0000", label:"人" }],
     normalized=true,
     output_path="annotated.png"
   )
   → 返回标注后的图片
```

### 示例 2:提取 PPT 中的文字

```
Agent: 提取这页 PPT 的所有文字内容

工具调用:
1. vision_ocr(images=["slide.png"])
   → { text_blocks: [...], full_text: "标题\n正文内容..." }
```

### 示例 3:检查 Web 页面是否符合设计要求

```
Agent: 检查这个页面截图的导航栏是否在顶部,按钮颜色是否为蓝色

工具调用:
1. vision_qa(images=["screenshot.png"], question="导航栏是否在页面顶部?按钮颜色是什么?")
   → "导航栏在页面顶部。按钮颜色为蓝色。"
```

### 示例 4:拼接多张截图后整体解读

```
Agent: 把这三张页面截图拼在一起,然后整体描述

工具调用:
1. image_concat(images=["p1.png","p2.png","p3.png"], layout="vertical")
   → 返回拼接后的图片

2. vision_describe(images=[拼接结果], detail_level="detailed")
   → 整体描述
```

## 项目结构

```
vision-mcp/
├── package.json
├── tsconfig.json
├── src/
│   ├── index.ts                    # 入口:server 初始化 + stdio 传输
│   ├── constants.ts                # 环境变量、默认值、忠实性提示词
│   ├── types.ts                    # 共享类型定义
│   ├── schemas.ts                  # Zod 输入/输出 schema
│   ├── services/
│   │   ├── image-loader.ts         # 路径/URL → buffer + metadata
│   │   ├── vlm-client.ts           # OpenAI 兼容 VLM API 客户端
│   │   ├── image-processor.ts      # sharp: resize/crop/rotate/flip/concat
│   │   └── image-annotator.ts      # @napi-rs/canvas: draw_box/draw_text
│   └── tools/
│       ├── vlm.ts                  # 4 个 VLM 工具
│       └── image.ts                # 8 个图像处理工具
├── eval/
│   ├── setup.mjs                   # 测试图片生成
│   ├── evaluation.xml              # 评估问题
│   ├── test-all.mjs                # 完整测试脚本
│   └── images/                     # 测试图片
└── dist/                           # 编译输出
```

## 技术栈

| 组件 | 技术 | 用途 |
|------|------|------|
| MCP SDK | `@modelcontextprotocol/server` v2 | MCP 协议实现 |
| Schema 校验 | Zod v4 | 输入/输出验证 |
| 图像变换 | sharp | resize/crop/rotate/flip/concat/metadata |
| 图像标注 | @napi-rs/canvas | draw_box/draw_text(中文支持) |
| VLM 调用 | 原生 fetch | OpenAI 兼容 API,零额外依赖 |
| 传输方式 | stdio | 本地集成,单用户场景 |

## 设计要点

### 忠实性保障

- 所有 VLM prompt 内置忠实性指令:"只描述你在图片中能直接看到的内容,不要推测、脑补或添加图片中不存在的信息"
- `temperature: 0` 确保确定性输出
- 检测工具要求模型在不确定时返回空结果

### 坐标归一化双重保障

1. **系统提示词**:`DETECTION_SYSTEM_PROMPT` 强制要求 0-1 归一化坐标,说明归一化公式
2. **后处理**:`normalizeBbox` 自动检测像素坐标(任一值 > 1.0),除以图片尺寸归一化

### 图像输出

- 始终返回 base64 PNG 图片内容(MCP `image` content type),LLM 可直接看到处理结果
- 可选 `output_path` 参数保存到文件,适合大图和后续使用
- `structuredContent` 包含结果图片的宽高、格式、大小

### 中文字体支持

`image_draw_text` 的字体查找优先级:

1. `VISION_MCP_FONT_PATH` 指定的字体文件
2. 系统 CJK 字体(Microsoft YaHei / SimHei / PingFang SC / Noto Sans CJK SC 等)
3. `sans-serif` 回退(可能无法渲染中文)

## 开发

```bash
# 开发模式(热重载)
npm run dev

# 构建
npm run build

# 运行测试
node eval/test-all.mjs

# 生成测试图片
node eval/setup.mjs
```

## 许可证

MIT

TDQS

A3.9/5.0

Scored across 12 tools

Disambiguation4/5

The vision_ tools are mostly distinct: qa asks questions, describe gives faithful descriptions, detect returns bounding boxes, and ocr extracts text. The image_ tools are clearly separated by operation. There is slight overlap between vision_qa and vision_describe, but the parameter and return descriptions make their use cases reasonably distinguishable.

Naming Consistency4/5

Tool names follow a clear two-prefix convention: vision_* for understanding tasks and image_* for manipulation/annotation tasks. Minor deviations exist—vision_qa and vision_ocr are noun-like rather than verb-like, and image_get_metadata uses get while other image tools do not—but overall the naming is readable and predictable.

Tool Count5/5

Twelve tools is a well-sized surface for a vision MCP server: four vision analysis tools and eight image processing/annotation tools. Each tool covers a distinct operation without bloat.

Completeness4/5

The tool set covers the core vision workflow well: understand, describe, detect, OCR, transform, and annotate images. Minor gaps like explicit format conversion or color/quality adjustments exist, but they are not critical for typical visual QA and image inspection use cases.

Maintenance

ActivitySlowing
ResponsivenessNo issues