Skip to content

Repository files navigation

Shadow Vision — Open-source MCP vision service granting text-only LLMs image understanding, OCR, and visual analysis capabilities

Shadow Vision

English | 简体中文

Give text-only LLMs a pair of eyes. Shadow Vision is an open-source MCP vision service that enables AI Agents to see, understand, and analyze real-world information through vision_ocr, vision_inspect, vision_annotate, vision_layout, vision_reconstruct, and vision_compare — without switching host models.

Why It's Different

  • MCP-Native: Compatible with Codex, Claude Desktop, Cursor, and other MCP clients
  • Pluggable Backends: Ollama, OpenAI-compatible, Anthropic, Gemini
  • Local-First: Keep images and inference entirely on your machine when using Ollama
  • Versatile Input: Supports local file paths, base64 image data, or remote HTTP(S) URLs

How It Works

Text-only LLM calls vision_ocr and vision_inspect via MCP, which connects to Ollama, OpenAI-compatible, Anthropic, or Gemini

The text model calls Shadow Vision tools via MCP. Shadow Vision forwards the image and prompt to the configured vision backend and returns the text result back to the model.

Quick Start

0. One-Click Run (No Clone Needed)

No need to clone the repository. Run directly:

uvx shadow-vision # Python / uv users (recommended)# Or for Node users
npx shadow-vision # Requires uv installed locally

MCP configuration example:

[mcp_servers.vision]
command = "uvx"args = ["shadow-vision"]
env = { VISION_BACKEND = "ollama", VISION_MODEL = "qwen3-vl:2b-instruct" }

npx shadow-vision is a thin wrapper that invokes uvx shadow-vision internally and requires uv installed on your machine. Both entry points behave identically.

1. Install from Source (Development / Self-Hosting)

Requires Python 3.11+ and uv:

git clone https://github.com/WardLu/shadow-vision.git
cd shadow-vision
uv sync

2. Use Local Ollama (Recommended for Beginners)

First install Ollama. If not running the Ollama desktop app, start the service manually:

ollama serve
ollama pull qwen3-vl:2b-instruct
ollama list

qwen3-vl:2b-instruct is the default vision model (non-thinking version, faster response). If you need stronger reasoning/thinking capability, switch to qwen3-vl:2b (thinking version); or change VISION_MODEL to any other vision model listed in ollama list.

3. Register as an MCP Service

Codex can run directly:

codex mcp add vision -- uv run shadow-vision

Or add to ~/.codex/config.toml:

[mcp_servers.vision]
type = "stdio"command = "uv"args = ["run", "shadow-vision"]
cwd = "/path/to/shadow-vision"env = { VISION_BACKEND = "ollama", VISION_MODEL = "qwen3-vl:2b-instruct", OLLAMA_URL = "http://127.0.0.1:11434/api/chat" }

After restarting your MCP client, simply ask the model to "take a look at this image".

Switching Models and Backends

VISION_BACKEND determines how requests are routed, and VISION_MODEL specifies the vision model. Update the environment variables in your MCP configuration and restart your client.

Switch to a local model:

env = { VISION_BACKEND = "ollama", VISION_MODEL = "your-downloaded-vision-model", OLLAMA_URL = "http://127.0.0.1:11434/api/chat" }

Switch to an OpenAI-compatible service:

env = { VISION_BACKEND = "openai_compatible", VISION_MODEL = "provider-vision-model-name", OPENAI_API_BASE = "https://api.example.com/v1", OPENAI_API_KEY = "sk-...", OPENAI_MAX_TOKENS = "1024", OPENAI_MAX_TOKENS_FIELD = "max_tokens" }

OPENAI_* denotes the OpenAI Chat Completions compatible protocol, which also applies to LM Studio, vLLM, and any other service offering /v1/chat/completions.

Free vision example for Chinese platforms (Zhipu GLM-4V-Flash):

env = { VISION_BACKEND = "openai_compatible", VISION_MODEL = "glm-4v-flash", OPENAI_API_BASE = "https://open.bigmodel.cn/api/paas/v4", OPENAI_API_KEY = "your-zhipu-key" }

Other OpenAI-compatible providers only require modifying OPENAI_API_BASE and VISION_MODEL: SiliconFlow https://api.siliconflow.cn/v1, Alibaba Bailian https://dashscope.aliyuncs.com/compatible-mode/v1, StepFun https://api.stepfun.com/v1, Tencent Hunyuan https://api.hunyuan.cloud.tencent.com/v1, Moonshot https://api.moonshot.cn/v1, etc.

Privacy Notice: API backends (including third-party platforms) send image content as base64 to the respective provider's servers. For confidential or sensitive images, use the local ollama backend to prevent external data transfer.

Configuring Backends

General Variables

VariableDefaultDescription
VISION_BACKENDollamaollama / openai_compatible / anthropic / gemini
VISION_MODELqwen3-vl:2b-instructVision model name
VISION_TIMEOUT180Read timeout (seconds), compatibility alias for VISION_READ_TIMEOUT
VISION_CONNECT_TIMEOUT10Connect timeout (seconds)
VISION_READ_TIMEOUT180Read timeout (seconds)
VISION_MAX_RETRIES2Retry attempts for transient / 5xx errors (total requests = 1 + this value)
VISION_RETRY_BASE_DELAY1.0Exponential backoff base delay in seconds

Advanced Configuration (Images & Security)

VariableDefaultDescription
VISION_AUTO_COMPRESStrueWhether to automatically compress large images
VISION_MAX_LONG_EDGE1800Compression threshold: long-edge pixels
VISION_MAX_PIXELS3500000Compression threshold: total pixels
VISION_COMPRESS_QUALITY85JPEG re-encoding quality
VISION_AUTO_TILEtrueWhether to automatically tile extra-long images
VISION_TILE_LONG_EDGE3600Tiling threshold: long-edge pixels
VISION_TILE_OVERLAP100Tiling overlap pixels
VISION_MAX_TILES8Maximum number of tiles per image
VISION_TASK_ROUTINGtrueWhether to enable heuristic task routing for vision_inspect
VISION_ALLOW_REMOTE_URLtrueWhether to allow remote URL image inputs
VISION_MAX_REMOTE_SIZE20971520Maximum remote image size in bytes (20MB)
VISION_FETCH_TIMEOUT30Remote fetch timeout (seconds)
VISION_SSRF_ALLOW_PRIVATEfalseWhether to allow private / intranet addresses (strongly discouraged)
VISION_MAX_BATCH_IMAGES5Maximum number of images per vision_compare call

Ollama

VariableDefaultDescription
OLLAMA_URLhttp://127.0.0.1:11434/api/chatOllama chat endpoint

Run ollama pull <vision-model-name> before use to download the model.

OpenAI-compatible

VariableDefaultDescription
OPENAI_API_BASEhttp://127.0.0.1:11434/v1Compatible service base URL
OPENAI_API_KEYemptyUsually left blank for local services
OPENAI_MAX_TOKENSunsetOptional max output tokens; omits token limit field when unset
OPENAI_MAX_TOKENS_FIELDmax_tokensOptional: max_tokens or max_completion_tokens

Different providers support different token limit fields: use max_tokens for legacy fields, max_completion_tokens for newer APIs, or leave OPENAI_MAX_TOKENS unset if neither is accepted. Legacy environment variables VISION_API_BASE, VISION_API_KEY, VISION_MAX_TOKENS, and VISION_MAX_TOKENS_FIELD remain supported for backward compatibility.

Anthropic / Gemini

VISION_BACKEND=anthropic ANTHROPIC_API_KEY=sk-ant-... VISION_MODEL=your-claude-vision-model uv run shadow-vision
VISION_BACKEND=gemini GEMINI_API_KEY=AIza... VISION_MODEL=your-gemini-vision-model uv run shadow-vision

Anthropic also supports ANTHROPIC_BASE_URL, ANTHROPIC_VERSION, and ANTHROPIC_MAX_TOKENS; Gemini also supports GEMINI_BASE_URL and GEMINI_MAX_TOKENS.

Tools

vision_ocr

Extract text from screenshots, invoices/receipts, documents, or tables:

vision_ocr(image_path="/tmp/receipt.png")

vision_inspect

Describe an image, or answer questions about an image:

vision_inspect(image_path="/tmp/design.png", question="List any UI bugs you see.")

Both tools also support:

  • task: Optional task guidance (vision_ocr: general/error/table; vision_inspect: general/ui_structure/ui_bug/chart)
  • image_path: Server-readable local image path
  • image_base64 + mime_type: Base64-encoded image data
  • image_url: Remote HTTP(S) image URL (with automatic SSRF protection)

All image tools accept one of image_path / image_base64 / image_url as input, with precedence: image_base64 > image_path > image_url.

vision_annotate

Identify bounding boxes, arrows, underlines, highlights, strikethroughs, handwritten notes, and other user annotations. Outputs structured JSON with annotation → target relationships, types, bounding boxes (bbox), and confidence scores:

vision_annotate(image_path="/tmp/marked.png", focus="Explain changes in order of markup")

vision_layout

Analyze image and UI layout structure, outputting structured JSON with canvas, containers, element bounding boxes (bbox), typography styles, and element hierarchy:

vision_layout(image_path="/tmp/ui.png")

vision_reconstruct

Reconstruct screenshots into code (html / react / svg), generating markup along with model self-inspection. Optionally provide JSON from vision_layout as layout reference:

vision_reconstruct(image_path="/tmp/ui.png", target_format="html", reference_layout="<layout json>")

vision_compare

Analyze multiple related images in a single call (diff / compare / sequence), with optional per-image label for easy reference:

vision_compare(images=[{"image_path": "/tmp/a.png", "label": "before"}, {"image_path": "/tmp/b.png", "label": "after"}], task="diff")

Local Model Selection and Benchmarking

The Ollama model library lists download size, context window, and vision capabilities, but model package size does not represent minimum memory requirements. It is recommended to start with qwen3-vl:2b-instruct (non-thinking version, low latency); if OCR or complex chart comprehension is insufficient, benchmark against qwen3-vl:4b, qwen3-vl:8b, or document-OCR oriented minicpm-v4.5:q4_0.

Prepare 3–5 real-world images covering OCR, UI screenshots, charts, and hard edge cases, and compare models using identical prompts:

MODEL=qwen3-vl:2b-instruct
IMAGE=/absolute/path/to/test.png
time ollama run "$MODEL""$IMAGE""Transcribe all text from the image accurately, outputting only the text."time ollama run "$MODEL""$IMAGE""Describe the image contents and list any areas of uncertainty."

Track OCR error count, accuracy of key objects and relationships, hallucinations, full round-trip latency, and processor status in ollama ps. Testing Ollama directly first before testing through vision_ocr / vision_inspect via MCP helps distinguish model capability limits from MCP configuration issues.

Recommended resources:

Supported Agents

All agents launch the same command: uv run shadow-vision.

AgentConfiguration File
Codex~/.codex/config.toml
Claude Code.mcp.json
Cursor.cursor/mcp.json
VS Code Copilot.vscode/mcp.json
Windsurf.windsurf/mcp_config.json
Claude Desktopclaude_desktop_config.json
OpenCodeopencode.json

Development

uv sync
uv run python -c "import vision_mcp.server; print('ok')"
uv run pytest

Contact

If you are interested in B2B products, AI product development, supply chain digitization, or Shadow series products, feel free to get in touch:

  • X (Twitter): @Gollumgulu
  • WeChat Official Account: Ward 的 AI 产品实战

Ward's AI Product in Action WeChat QR Code

Open for 1-on-1 consulting and advisory: Product Diagnostics · AI Implementation · Workflows / Skills · Custom Solutions

License

MIT

About

Open-source MCP vision server that gives text-only LLMs and AI agents image understanding, OCR, visual analysis, UI inspection, and multimodal capabilities.

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages