Skip to main content
Glama

🖼️ vision-mcp

自托管多模态 VLM 图片识别 MCP 服务器

TUI 终端粘贴图片 → AI 客户端自动识别返回 · 数据不出内网

MCP TypeScript Node Tests Build License: MIT Transport

Claude Code · Codex · OpenCode · 任何 MCP 兼容客户端


✨ 为什么用它

优势

说明

🔒

私有部署,数据不出网

直连你自托管的 VLM,图片不经过第三方云

🔌

OpenAI 兼容,后端可换

vLLM / Ollama / GLM-4V / Qwen-VL 任选,换 base URL 即可,不改代码

🖼️

TUI 粘图即用

终端粘贴图片,客户端自动调工具识别,体验对齐智谱图片识别 MCP

🧩

四个专用工具

通用理解 / OCR / 图表理解 / UI 转码,各带预设 system prompt 与结构化输出

📥

三种图片输入

本地路径 · http(s) URL · data: URI,客户端给哪种收哪种

🛡️

错误不泄漏

错误串仅静态/状态码,绝不把 VLM 响应体或栈泄漏给客户端

轻量单进程

stdio,客户端按需拉起子进程,无常驻、无服务端状态

🔁

内置韧性

5xx/超时自动重试一次、4xx 不重试、请求超时、图片大小上限

TDD 全覆盖

35 个测试 + 端到端往返(假 VLM + InMemoryTransport)

📐 架构

flowchart LR
    A["🖥️ TUI 客户端<br/>(Claude Code / Codex / OpenCode)"] -- stdio JSON-RPC --> B
    subgraph B["vision-mcp (Node, stdio)"]
        direction TB
        C["tools ×4<br/>analyze_image / extract_text /<br/>understand_diagram / ui_to_code"]
        C --> D["analyze()<br/>共享核心"]
        D --> E["imageSource<br/>路径/URL/data-URI → 归一化"]
        D --> F["vlmClient<br/>OpenAI 兼容 + 重试"]
    end
    F -- HTTPS chat/completions --> G["🧠 自托管 VLM<br/>(qwen-vl / glm-4v / ...)"]
    G -- JSON --> B
    B -- tool result --> A

🛠️ 工具

全部共享 image_source(本地路径 | http(s) URL | data: URI)。

工具

专有参数

输出

analyze_image

prompt(必填)

自然语言描述 / 问答

extract_text

prompt?programming_language?

OCR 文本(代码截图带语言标注)

understand_diagram

diagram_type?(省略或 auto)、prompt?

结构化描述 + mermaid/markdown 复刻

ui_to_code

output_typecode/spec/description)、framework?html/react-tailwind)、prompt?

对应 code/spec/description

🚀 快速开始

克隆并构建

git clone https://github.com/skyone123/vision-mcp.git
cd vision-mcp
npm install
npm run build      # 产出 dist/index.js + dist/index.d.ts
npm test           # 可选:35/35 测试

客户端只用到 dist/index.js记下它的绝对路径(下文记作 $DIST),配置里要用。

例:Linux/macOS /home/you/vision-mcp/dist/index.js;Windows D:/git/vision-mcp/dist/index.js

环境变量

变量

默认

必填

说明

VLM_BASE_URL

OpenAI 兼容 base,如 http://localhost:8000/v1(带 /v1

VLM_MODEL

qwen-vl-max

模型名

VLM_API_KEY

""

Bearer token;后端要鉴权才填,留空不带 Authorization

VLM_TIMEOUT_MS

60000

单次请求超时

VLM_MAX_IMAGE_BYTES

10485760

图片上限 10MB

VLM_MAX_TOKENS

2048

返回 token 上限

VLM_BASE_URL 启动即报错退出,不会静默失败。

🔧 配置

第 1 步 · 判断后端要不要 API key

curl http://localhost:8000/v1/models
  • 200 + 模型列表 → 不用 key

  • 401/403要 key,带 key 再试:curl http://localhost:8000/v1/models -H "Authorization: Bearer 你的token"

模型名从返回里挑视觉模型:

curl -s http://localhost:8000/v1/models | grep '"id"'

实测视觉能力能吃图(最关键):

curl http://localhost:8000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer 你的token" \
  -d '{
    "model": "qwen-vl-max",
    "messages": [{"role":"user","content":[
      {"type":"text","text":"一句话描述这张图"},
      {"type":"image_url","image_url":{"url":"https://upload.wikimedia.org/wikipedia/commons/thumb/4/47/PNG_transparency_demonstration_1.png/640px-PNG_transparency_demonstration_1.png"}}
    ]}]
  }'

返回正常文字 → 端点可用,照搬这些值填进 env

第 2 步 · 写进客户端

把下面的 $DIST 换成上一步记下的 dist/index.js 绝对路径,commandnode

claude mcp add vision-mcp --scope user \
  --env VLM_BASE_URL=http://localhost:8000/v1 \
  --env VLM_MODEL=qwen-vl-max \
  -- node "$DIST"

要 key 就再加一行 --env VLM_API_KEY=你的token

{
  "command": "node",
  "args": ["/absolute/path/to/vision-mcp/dist/index.js"],
  "env": {
    "VLM_BASE_URL": "http://localhost:8000/v1",
    "VLM_MODEL": "qwen-vl-max"
  }
}

带 key 就在 env"VLM_API_KEY": "你的token"

{
  "mcpServers": {
    "vision-mcp": {
      "command": "node",
      "args": ["/absolute/path/to/vision-mcp/dist/index.js"],
      "env": { "VLM_BASE_URL": "http://localhost:8000/v1", "VLM_MODEL": "qwen-vl-max" }
    }
  }
}
[mcp_servers.vision-mcp]
command = "node"
args = ["/absolute/path/to/vision-mcp/dist/index.js"]
env = { VLM_BASE_URL = "http://localhost:8000/v1", VLM_MODEL = "qwen-vl-max" }
{
  "mcp": {
    "vision-mcp": {
      "type": "local",
      "command": ["node", "/absolute/path/to/vision-mcp/dist/index.js"],
      "environment": {
        "VLM_BASE_URL": "http://localhost:8000/v1",
        "VLM_MODEL": "qwen-vl-max"
      }
    }
  }
}

OpenCode 不同版本字段名可能微调,若工具不出现对照其官方 MCP 文档。

第 3 步 · 验证

claude mcp list          # 应看到 vision-mcp,状态 connected

MCP server 无需手动常驻——客户端按需拉起子进程。然后在对话里粘贴一张图问"图里有什么",客户端自动调 analyze_image;或显式:

用 analyze_image 工具看一下这张图:<粘贴图片>

💻 开发

npm run dev              # tsx 直接跑源码(开发期)
npm run build            # tsup 打包 dist/index.js
npm test                 # vitest,35/35
npx tsc --noEmit         # 类型检查

源码结构:

src/
  config.ts          # env → VlmConfig
  imageSource.ts     # loadImage: 路径/URL/data-URI 归一化
  vlmClient.ts       # complete: 调 OpenAI 兼容端点 + 重试/超时
  analyze.ts         # 共享核心: loadImage + complete
  server.ts          # McpServer 注册 + stdio + main
  index.ts           # #!/usr/bin/env node 入口
  tools/
    analyzeImage.ts
    extractText.ts
    understandDiagram.ts
    uiToCode.ts

每个文件单一职责,可独立测试;四个工具是 analyze() 的薄封装,各烘焙自己的 system prompt。

🗺️ 路线图(可选扩展)

当前范围:仅 stdio · 单后端 · 单图 · 无持久化。以下为按需扩展项:

候选

价值

建议

流式输出

ui_to_code 输出可能很长,流式能边出边看

👍 值得做,UX 提升

图片预处理

发送前按长边缩放/压缩,省 token、降超时

👍 值得做,降本

结构化输出

extract_text/understand_diagram 返回 JSON

🤔 看场景

HTTP/SSE 传输

多客户端共享、远程部署

🤔 当前 stdio 够用,按需

多后端路由

不同任务路由到不同 VLM

❌ YAGNI

视频/多图批处理

❌ 超出当前定位

服务端缓存

相同图重复识别

❌ YAGNI

📄 许可证

MIT © 2026 luyuxin