Skip to main content
Glama
groxaxo
by groxaxo
README.md
# DeepSeek Vision MCP

[![CI](https://github.com/groxaxo/deepseek-vision-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/groxaxo/deepseek-vision-mcp/actions/workflows/ci.yml)
[![Python](https://img.shields.io/badge/Python-3.11%2B-3776AB)](https://www.python.org/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)

A secure, lightweight MCP v2 server that gives any MCP-compatible agent
DeepSeek V4 Flash Vision: image analysis, faithful descriptions, OCR,
comparisons, UI/object localization, and reusable Files API uploads.

The server exposes the exact model `deepseek-v4-flash-vision-exp` without
embedding provider-specific image payloads in every agent integration.

## Tools

| Tool | Purpose |
|---|---|
| `vision_analyze` | General one/multi-image analysis with a custom prompt |
| `vision_describe` | Faithful scene/UI description |
| `vision_ocr` | Screenshot/document OCR |
| `vision_compare` | Compare two or more images |
| `vision_locate` | Best-effort UI/object localization to normalized coordinates |
| `vision_upload` | Upload a local image to DeepSeek Files API |
| `vision_files_list` | List reusable uploaded images |
| `vision_files_delete` | Delete an uploaded image |

Accepted image references:

- local server-side path
- public `http://` or `https://` URL
- `data:image/...;base64,...`
- DeepSeek `file-api-...` file ID

## Why this server is thin

The MCP server does not run a local vision model. It only validates/normalizes
image inputs and delegates inference to DeepSeek. That makes CPU/RAM usage tiny
and lets any MCP-capable agent gain vision without embedding DeepSeek-specific
payload shapes in the agent itself.

## Install with uv

```bash
git clone https://github.com/groxaxo/deepseek-vision-mcp.git
cd deepseek-vision-mcp

uv sync --locked

cp .env.example .env
# Set DEEPSEEK_API_KEY and allowed roots in .env
```

For a local MCP host, `stdio` is the preferred transport:

```bash
DEEPSEEK_API_KEY="..." \
DEEPSEEK_VISION_ALLOWED_ROOTS="/home/you/Pictures:/tmp/vision" \
uv run deepseek-vision-mcp
```

For development with MCP Inspector:

```bash
DEEPSEEK_API_KEY="..." uv run mcp dev src/deepseek_vision_mcp/server.py
```

## MCP host configuration

Typical stdio configuration:

```json
{
  "mcpServers": {
    "deepseek-vision": {
      "command": "uv",
      "args": [
        "--directory",
        "/absolute/path/to/deepseek-vision-mcp",
        "run",
        "deepseek-vision-mcp"
      ],
      "env": {
        "DEEPSEEK_API_KEY": "YOUR_KEY",
        "DEEPSEEK_VISION_ALLOWED_ROOTS": "/home/you/Pictures:/tmp/vision"
      }
    }
  }
}
```

Do not commit the API key. If the host can inherit environment variables,
prefer injecting `DEEPSEEK_API_KEY` from your secret manager or shell.

### Safe shared launcher

`run-mcp.py` is useful when several local agents share one installation. It:

- reads only approved DeepSeek variables from the process or an env file;
- filters unrelated inherited secrets before launching the MCP;
- forces `stdio` transport;
- restricts local images to explicit roots.

By default it reads `~/.hermes/.env` and allows the usual image-working
directories under the current home directory plus `/tmp`. Override those
choices without editing the script:

```bash
export DEEPSEEK_VISION_ENV_FILE="$HOME/.config/deepseek-vision.env"
export DEEPSEEK_VISION_ALLOWED_ROOTS="$HOME/Pictures:/tmp/vision"
./run-mcp.py
```

Hermes configuration:

```yaml
mcp_servers:
  deepseek-vision:
    command: "/absolute/path/to/deepseek-vision-mcp/run-mcp.py"
    args: []
    enabled: true
```

OpenCode configuration:

```json
{
  "mcp": {
    "deepseek-vision": {
      "type": "local",
      "command": ["/absolute/path/to/deepseek-vision-mcp/run-mcp.py"],
      "enabled": true
    }
  }
}
```

OMP and other standard MCP hosts can use the `mcpServers` example above with
`run-mcp.py` as the command.

## Streamable HTTP

```bash
export DEEPSEEK_API_KEY="..."
export MCP_TRANSPORT=streamable-http
export MCP_HOST=127.0.0.1
export MCP_PORT=8000

uv run deepseek-vision-mcp
```

The MCP endpoint is:

```text
http://127.0.0.1:8000/mcp
```

Use TLS and authentication in front of the server before exposing it outside a
trusted machine/network.

## Example tool calls

### Analyze a screenshot

```json
{
  "images": ["/home/you/Pictures/screen.png"],
  "prompt": "What application is open, what is the current state, and what should I click next?",
  "detail": "original"
}
```

### Fast coarse screen read

```json
{
  "images": ["/home/you/Pictures/screen.png"],
  "prompt": "Is a modal dialog visible? Answer briefly.",
  "detail": "low"
}
```

### OCR

```json
{
  "image": "/home/you/Pictures/error.png",
  "detail": "original"
}
```

### Best-effort UI grounding

```json
{
  "image": "/home/you/Pictures/screen.png",
  "target": "the blue Save button"
}
```

`vision_locate` returns coordinates normalized to 0..1000. It is intentionally
described as best-effort: a generative VLM is not a deterministic detector.
Validate its target before high-impact clicks.

## Security model

> Images analyzed by this MCP are sent to DeepSeek's external API. Do not send
> private or sensitive images without informed user intent.

Local paths are restricted to `DEEPSEEK_VISION_ALLOWED_ROOTS`. If no roots are
configured, the server only allows images under its current working directory.

This matters: an unrestricted `vision_analyze("/etc/...")` style tool would let
an MCP host turn image analysis into arbitrary local-file exfiltration.

The server also rejects obvious localhost/private-IP external URLs.

The shared launcher passes only baseline process variables, `XDG_*`, and the
four approved DeepSeek settings to the child process. It never sources an
entire credentials file.

## DeepSeek image behavior reflected by this server

- JPEG, PNG, GIF, WebP
- local/base64 inline image: max 32 MiB each
- DeepSeek Files API image: max 64 MiB
- max 600 images/request
- external URL length: max 8192 chars
- max dimension: 8192 px/side, or 4096 px/side for 15+ images
- `detail=low`: DeepSeek downsamples to 512x512
- `detail=original` / `high`: preserve original detail
- images are only sent in the user message

## Large/reused images

Upload once:

```json
{
  "local_path": "/home/you/Pictures/large.png",
  "expires_seconds": 86400
}
```

Then pass the returned `file-api-...` ID into `vision_analyze`. Use `null` for
`expires_seconds` only when you intentionally want permanent DeepSeek storage.

## Docker

```bash
docker build -t deepseek-vision-mcp .
docker run --rm \
  -p 127.0.0.1:8000:8000 \
  -e DEEPSEEK_API_KEY="$DEEPSEEK_API_KEY" \
  deepseek-vision-mcp
```

For local image paths in Docker, mount only the directories the MCP needs and
set `DEEPSEEK_VISION_ALLOWED_ROOTS` to the container-side path.

## Architecture

```text
MCP host / agent
      |
      | MCP tool call
      v
DeepSeek Vision MCP
  - validates source
  - restricts local paths
  - encodes local files
  - shapes DeepSeek payload
      |
      | HTTPS
      v
api.deepseek.com
  deepseek-v4-flash-vision-exp
      |
      v
structured MCP result
```

## Development

```bash
uv sync --locked --extra dev
uv run ruff check .
uv run pytest
```

CI runs the same gates on Python 3.11 and 3.13. Contributions and focused bug
reports are welcome.

TDQS

B3.4/5.0

Scored across 8 tools

Disambiguation5/5

Each tool targets a distinct visual task: custom analysis, faithful description, OCR, comparison, semantic localization, upload, listing, and deletion. The overlaps between analyze and describe are minor and clearly differentiated by purpose and usage notes.

Naming Consistency3/5

Most tools follow a vision_<verb> pattern (analyze, describe, compare, locate, upload), but vision_files_list and vision_files_delete reverse the noun-verb order, and vision_ocr is an abbreviation noun. The shared prefix provides some consistency, but the mixed conventions prevent a higher score.

Tool Count5/5

Eight tools is a well-scoped number for a vision MCP server, covering both image analysis and file lifecycle management without unnecessary bloat. Each tool serves a clear purpose.

Completeness5/5

The tool surface covers the major vision tasks (analysis, description, OCR, comparison, localization) and includes full file management via upload, list, and delete. There are no obvious dead ends; images are immutable so update/delete semantics are appropriately handled.

Maintenance

ActivityMaintained
ResponsivenessNo issues