Metadata-Version: 2.4
Name: claude-vision-mcp
Version: 0.1.1
Summary: 基于 Claude API 的视觉识别 MCP server:为不支持视觉的主模型多一双眼睛。内部 http 直调视觉网关,图片放 user message 顶层 image content block,tool_result 仅返回文字描述,绕开 tool_result 内嵌 image 的网关兼容缺陷。
Project-URL: Homepage, https://cnb.cool/cnpc/mcp/claude-vision-mcp
Project-URL: Repository, https://cnb.cool/cnpc/mcp/claude-vision-mcp
Project-URL: Issues, https://cnb.cool/cnpc/mcp/claude-vision-mcp/-/issues
Author-email: cnpc <npc@cnb.cool>
License-Expression: MIT
Keywords: anthropic,claude,image,mcp,multimodal,vision
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Image Recognition
Requires-Python: >=3.10
Requires-Dist: httpx>=0.27.0
Requires-Dist: mcp>=1.2.0
Provides-Extra: dev
Requires-Dist: mypy>=1.10.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
Requires-Dist: pytest>=8.0.0; extra == 'dev'
Requires-Dist: ruff>=0.6.0; extra == 'dev'
Description-Content-Type: text/markdown

# claude-vision-mcp

> 基于 Claude API 的视觉识别 MCP server:为不支持视觉识别的主模型多一双眼睛。

[![PyPI](https://img.shields.io/pypi/v/claude-vision-mcp.svg)](https://pypi.org/project/claude-vision-mcp/)

## 这是什么

一个 stdio MCP server,暴露 `describe_image` 工具。主代理(如 Claude Code)调用它传入本地图片路径,工具内部用 http 直调视觉网关的 `/v1/messages`,**把图片放在 user message 顶层 image content block**(绕开网关只解析顶层 image、不解析 `tool_result` 内嵌 image 的兼容缺陷),视觉模型返回的文字作为 `tool_result` 返回给主代理。

**为什么需要它**:Claude Code 的 `Read` 工具读图时,会把图片塞进 `tool_result.content` 内嵌的 image block。但部分网关(如本环境的 `xopkimik26`)只解析 user message **顶层** image、不解析 `tool_result` 内嵌 image——结果主模型「看不到图」。本工具把识图挪到一个独立的 MCP server 里,**对主代理消息流可见的永远是纯文字 tool_result**,图片在工具内部的 http 调用中消化,从源头绕开缺陷。

详细背景见 [cnpc/claude #48](https://cnb.cool/cnpc/claude/-/issues/48)。

## 安装

### 方式一:`uvx`(推荐,Claude Code 集成)

无需 clone,直接在 Claude Code 配置里挂载:

```jsonc
// ~/.claude.json 或项目 .claude.json
{
  "mcpServers": {
    "claude-vision-mcp": {
      "command": "uvx",
      "args": ["claude-vision-mcp"],
      "env": {
        "ANTHROPIC_BASE_URL": "${ANTHROPIC_BASE_URL}",
        "ANTHROPIC_AUTH_TOKEN": "${ANTHROPIC_AUTH_TOKEN}",
        "VISION_MODEL": "${VISION_MODEL}"
      },
      "timeout": 120000
    }
  }
}
```

`uvx` 会自动从 PyPI 拉起最新版本,隔离虚拟环境,不污染系统 Python。

### 方式二:本地开发

```bash
git clone https://cnb.cool/cnpc/mcp/claude-vision-mcp.git
cd claude-vision-mcp
uv sync --extra dev           # 装依赖 + dev 依赖
uv run pytest                 # 跑测试
uv run python -m claude_vision_mcp.server   # 直接启动 stdio server
```

## 环境变量

### 网关配置(必填)

| 变量 | 必填 | 说明 |
| --- | --- | --- |
| `ANTHROPIC_BASE_URL` | 是 | 视觉网关基地址(如 `https://api.cnb.cool/...`),不带尾部斜杠 |
| `ANTHROPIC_AUTH_TOKEN` | 是 | 网关认证令牌,同时用作 `x-api-key` 与 `Authorization: Bearer` |
| `VISION_MODEL` | 是 | 支持视觉的多模态模型 ID(如 `xopkimik26`),由调用方注入不写死 |

### 超时/重试配置(选填,未设时用默认值)

网关调用对瞬态错误(超时、连接失败、429 限流、5xx)做有限重试,指数退避。以下变量可覆盖默认值,在 `.claude.json` 的 `env` 字段透传:

| 变量 | 默认值 | 说明 |
| --- | --- | --- |
| `VISION_CONNECT_TIMEOUT` | `10` | TCP 连接建立超时(秒) |
| `VISION_READ_TIMEOUT` | `60` | 等待响应超时(秒,视觉模型较慢需留足) |
| `VISION_WRITE_TIMEOUT` | `10` | 发送请求体超时(秒) |
| `VISION_POOL_TIMEOUT` | `10` | 从连接池获取连接超时(秒) |
| `VISION_MAX_RETRIES` | `2` | 最大重试次数(不含首次请求,`0` = 不重试) |
| `VISION_RETRY_BASE_DELAY` | `1` | 退避基础延迟(秒,第 n 次重试前等待 `base * 2^n`) |

所有变量均从环境读取,非法值(非数字、负数)回退默认值不抛错。

## 工具

### `describe_image`

识别本地图片并返回文字描述。

**参数:**

| 参数 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `image_path` | string | 是 | 本地图片文件路径(绝对或相对) |
| `instruction` | string | 否 | 视觉理解指令,覆盖默认通用描述提示词 |

**返回:** 纯文字描述(`tool_result` 只含 text block,绝不返回 image block)。

**失败处理:** 图片读取失败、网关配置缺失、网关调用失败均以文字诊断返回(不抛错),主代理据此向用户反馈或重试。

## 架构

```
src/claude_vision_mcp/
├── pure.py     # 纯函数:请求体构造/响应提取/错误诊断/脱敏/media_type 推断(单测覆盖)
├── gateway.py  # http 调用:httpx 直调 /v1/messages,图片放顶层 image block(不纳入单测)
├── server.py   # FastMCP stdio 入口:注册 describe_image 工具
└── __init__.py
```

**纪律:**

- 纯函数优先:网关请求体构造、响应文字提取为纯函数,单测覆盖;http 调用本身不纳入单测。
- 禁止硬编码端点/模型/密钥:全走环境变量。
- 日志脱敏:server 内部 stderr 日志经 `redact_sensitive` 脱敏,无密钥/图片 base64 明文泄露。
- MCP stdio 协议:日志走 stderr,stdout 仅供 JSON-RPC。

## 后续扩展

当前仅 `describe_image`。后续可在 `server.py` 增量注册 `describe_video` / `describe_audio` 等工具,共用 `gateway.py` 的网关调用层。包名 `claude-vision-mcp` 已为多模态预留命名空间。

## 开发

```bash
uv sync --extra dev
uv run pytest            # 测试(纯函数 + gateway 重试路径,共 68 用例)
uv run ruff check .      # lint
uv run ruff format .     # 格式化
uv run mypy src tests    # 类型检查
```

### 发布

打 `v*` tag 触发 CI 自动发布到公网 PyPI。**打 tag 前需更新版本号**(评审反馈 P2):

1. 改 `pyproject.toml` 的 `version = "0.x.y"`
2. 改 `src/claude_vision_mcp/__init__.py` 的 `__version__ = "0.x.y"`
3. 提交后打 tag:`git tag v0.x.y && git push origin v0.x.y`

> 未更新版本号会导致 `uv publish` 报 409(版本已存在)。

## License

MIT
