Claude Code 接入纯文本 API 后,如何优雅实现图片识别?
一个「MCP 识图工具 + 本地透明代理 + 一键切换」的组合方案,让不支持视觉的主模型也能看懂粘贴的图片,且 Claude Code / Codex 都能复用。
背景
我的 Claude Code 走本地 CC Switch 代理,最终接到一个纯文本模型(deepseek-v4-flash)。后果就是:在主链路里给 Claude 发图片会被直接拒绝——"无法看到图片"。
需求很简单:让粘贴图片在 Claude Code 里可用,同时不破坏已有的文本对话链路。
方案选型:MCP,而不是 Skill
第一反应是装个"识图 skill"。调研之后发现关键区别:
| 方案 | 本质 | 问题 |
|---|---|---|
| Skill | 提示词 + 工具的封装 | 最终还是要有"看图"的能力来源,绕不开 |
| MCP | 标准工具协议,注入工具 | Claude Code 在模型能力检查时就识别图片内容,配合工具最顺 |
最终选用 **mcp-vision-server**(goehou/Visual-Enhancement-mcp,npm 名 mcp-vision-server)。它把识图能力变成两个标准 MCP 工具:
vision_analyze—— 通用图片理解/描述vision_ocr—— OCR 文字提取
两者都支持本地路径 / 远程 URL / base64 三种图片输入,够用。
整体架构
┌─────────────┐ ①请求 ┌──────────────────┐ ②转发 ┌────────────┐ ③ ┌────────────┐
│ Claude Code │ ─────────▶ │ vision-proxy:15722│ ───────▶ │ CC Switch │ ──▶ │ 文本模型 API│
└─────────────┘ └──────────────────┘ │ :15721 │ └────────────┘
│ │ │ └────────────┘
│ │ │ 含图请求自动拦截
│ │ ▼ 调视觉模型转文字
│ │ ┌──────────────────┐
│ └──────────▶│ vision MCP │ 手动识图(vision_analyze / vision_ocr)
│ │ (mcp-vision-server)│
│ └──────────────────┘
└─ 直连模式(Claude Code → CC Switch :15721)
两条识图路径,互不冲突:
- 自动(proxy 模式):粘贴图片 → 本地
vision-proxy拦截 image block → 调用视觉模型把图片转成文字 → 再把文字交给文本模型回答。对用户完全透明。 - 手动(直连模式):Claude Code / Codex 直连 CC Switch,需要识图时让模型调用
vision MCP的vision_analyze/vision_ocr工具。
组件拆解
1. vision MCP —— 识图工具
注册一个标准 stdio MCP(命令 + 参数 + 环境变量):
{
"type": "stdio",
"command": "npx",
"args": [
"-y", "mcp-vision-server",
"--api-base-url", "https://你的-new-api 地址",
"--api-path", "/v1/chat/completions",
"--model", "agnes-2.5-flash",
"--timeout-ms", "60000",
"--max-tokens", "4096"
],
"env": {
"VISION_API_KEY": "sk-你的-key(从配置文件读取,不写死在源码)"
}
}
关键点:视觉模型走 OpenAI 兼容的 /v1/chat/completions,用支持视觉的模型(如 agnes-2.5-flash)做图片理解。
2. vision-proxy —— 透明的"图片转文字"代理
一个 Node 零依赖的本地 HTTP 代理(约 200 行),监听 127.0.0.1:15722:
- 上游指向 CC Switch(
127.0.0.1:15721) - 无图请求:纯透传,零额外开销
- 含图请求:递归遍历 messages,把每个
imageblock 抽取出来 → 调视觉模型描述 → 替换成textblock → 再转发 - 流式 SSE 透传:
response.pipe(),不破坏 Claude Code 的流式体验 - 用 LaunchAgent(
KeepAlive)守护,登录自启
核心逻辑伪代码:
// 遍历 messages,找到所有 image block
collect(node) {
if (node.type === 'image' && node.source) images.push(node);
Object.values(node).forEach(collect);
}
// 每个图片用视觉模型描述成文字
const desc = await describeImage(dataUrl); // POST /v1/chat/completions
// 把 image block 替换为 text block
node[i] = { type: 'text', text: `[Image] ${desc} [/Image]` };
3. switch.sh —— 一键切换连接模式
因为"要直连、还是要自动转文字"是两种心态,做一个一键切换脚本(bash + 内联 python 改 JSON,无 jq 依赖):
switch.sh direct # 直连 CC Switch(15721),识图用 vision MCP 工具
switch.sh proxy # 走 vision-proxy(15722),粘贴图片自动转文字
switch.sh status # 查看当前模式
每次切换前自动对 settings.json 做时间戳备份,切换后重开 Claude Code 生效。
同步到 CC Switch & Codex
CC Switch 用 SQLite 管理跨工具 MCP(表 mcp_servers,字段 enabled_claude / enabled_codex / …)。
把 vision MCP 登记进 CC Switch:
- 向
mcp_servers表插入一条 vision 记录(enabled_claude=1, enabled_codex=1) - 在
~/.codex/config.toml追加[mcp_servers.vision]+[mcp_servers.vision.env] codex mcp list验证
结果:**Codex 也能直接调用 vision_analyze / vision_ocr**。实测 MCP 握手返回 mcp-vision-server 0.1.3,工具列表正常。
踩过的坑(都是真金白银)
- 模型价格未配置:
agnes-2.5-flash在 new-api 后台没配价格时直接报"价格未配置",要在后台配上。 - 某些模型无可用渠道:
agnes-1.5-flash没挂渠道就报 "No available channel"。 - **响应走
reasoning_content**:部分模型content为空、内容在reasoning_content字段,代理/工具要兜底兼容。 vision模式直连 new-api 会 503:Claude Code 启动会用claude-opus-4-8/claude-sonnet-4-6等模型名,如果 new-api 只配了视觉模型渠道,就会"分组下模型无可用渠道"。结论:直连就直连 CC Switch,别直连 new-api。- MCP 配置往往是别的进程的 key 来源:本地代理的 key 从
~/.claude.json的mcpServers.vision.env读取。删 MCP = 删 key 来源 = 代理启动崩溃。删任何配置前先理清依赖链。 - **CC Switch 切换 provider 可能覆盖
BASE_URL**:切换供应商后要确认 settings 里的地址没被改回去。 - zsh alias 是解析时展开:同一行里定义又使用会误报
command not found,要重开终端验证。
使用方式
- 默认直连 CC Switch,文本聊天 + 需要识图时让模型调 MCP 工具
- 想"粘贴图片自动转文字",
switch.sh proxy切过去即可 - Codex 里直接说"用
vision_analyze识别这个图片:<路径>"就行
结语
这套方案的取舍是:主链路保持纯文本稳定,识图能力通过标准 MCP 工具 + 可选的透明代理注入。既不用换掉现有的文本模型,也不用把图片能力写死在业务里,Claude Code 和 Codex 还能复用同一个识图通道。
所有 key / token 均从配置读取、不写入源码与文章。
暂无评论。