Skip to main content
Glama

Vision MCP Server

English | 简体中文

lm-visual-mcp gives text-only LLMs and coding agents visual input through the Model Context Protocol. It is built from four modules — mcp, server, providers, and vision — and also ships a transparent HTTP proxy that rewrites image blocks in OpenAI or Anthropic requests into text descriptions before forwarding them upstream, plus Claude Code Auto classifier interoperability.

This version (v0.2.0) supports image recognition only. Video input is no longer declared or accepted anywhere in the stack.

Modules

Module

Responsibility

mcp

Thin stdio MCP entry. Every tool call is forwarded to the shared server — no embedded vision service, so rate limiting is always centralized.

server

The shared singleton process: POST /vision/analyze + the hook proxy (/proxy/<proto>/<base64url>...). Which hooks are active is pure configuration (hooks.image.enabled, hooks.classifier.enabled).

providers

Provider implementations behind a type registry with per-provider rate limiting (rpm / concurrency). Each provider implements one or both behavior groups: IMAGE (probe_image / analyze_image) and CLASSIFIER (rewrite_classifier_request / rewrite_classifier_response).

vision

Image-recognition orchestration: concurrency gate + the provider router that walks the configured image_chain. Prompts and the two behavior groups' shared types live here/next to providers.

Hooks

A hook's basic interface is process(ctx) -> HookResult: it may rewrite the request and let it continue down the pipeline, or intercept it by returning a response that goes straight back to the client. Hooks may also implement process_response to rewrite the upstream response (used by the classifier hook).

  • Image hook — detects image-bearing requests, describes each image once (SHA-256 cache) through the image chain, and replaces the image block with text. Every rewritten block records the image's absolute local path ([Image N: /abs/path.png]), and staged files persist, so the text model can reference or re-submit the image later.

  • Classifier hook — detects Claude Code Auto classifier requests and delegates to the classifier chain. Only API providers that implement classifier handling (rewrite_classifier_request / rewrite_classifier_response) rewrite these; local CLI providers (agy, codex) pass them through byte-for-byte untouched.

Both hooks accept a models allowlist — empty = apply to all models, non-empty = only the listed models run through the router; everything else passes through untouched.

Providers, dual chains & fallback

The top-level providers: section defines provider instances (the single source of truth, referenced by name). vision then references those names in two independent execution chains:

  • image_chain — image analysis fallback order (first success wins).

  • classifier_chain — classifier handling order (first provider that reports a changed rewrite wins; if none implements classifier handling, requests pass through untouched).

The router never hardcodes providers — type is resolved through a registry, so adding one is a class + one registry line + config.

Rate limiting lives inside each provider (rate_limit: {rpm, concurrency} per entry, both optional). When a limit is hit the provider raises rate_limited and the router immediately downgrades to the next provider in its chain.

  • agy — AGY CLI (-p + --add-dir + sandbox), unsupported-vision verdict caching. IMAGE only; no classifier handling.

  • codex — codex exec with --output-schema, read-only sandbox. IMAGE only.

  • gemini — google-genai API (api_key_env: GEMINI_API_KEY). IMAGE + classifier (honors disable_thinking).

  • opencode — direct OpenAI-compatible API; mode: go (default, https://opencode.ai/zen/go/v1) or mode: zen, base_url overrides mode. IMAGE + classifier; no local CLI required.

  • volcengine — Volcano Ark; mode: agent (Anthropic Messages /v1/messages over api/plan), mode: coding (api/coding), or mode: api (OpenAI chat-completions api/v3). IMAGE + classifier.

Related MCP server: Vision MCP Server

Architecture

MCP client process (agent config: --start-server / --no-start-server)
    │ stdio
    ▼
mcp module (thin client)
    │ loopback HTTP  POST /vision/analyze
    ▼
server module (shared singleton)
    ├── vision endpoint ──► vision module
    │                          ├── concurrency gate
    │                          └── router walks image_chain: provider₁ → provider₂ → …
    │                                (each with its own rpm/concurrency limiter;
    │                                 limit hit → fall back to the next)
    └── hook proxy  /proxy/<proto>/<base64url>[/suffix]
           ├── image hook      → image chain (description rewrite, model-allowlist)
           ├── classifier hook → classifier chain (API-provider rewrite, model-allowlist /
           │                      byte-level passthrough when no provider handles it)
           └── byte-level passthrough when no hook applies

Quick start

# agent MCP config (stdio) — starts the shared server if absent:
lm-visual-mcp

# …or never start the server from the MCP process (use an already-running one):
lm-visual-mcp --no-start-server      # env: LM_VISUAL_MCP_START_SERVER=0

lm-visual-mcp start | stop | restart  # manage the server singleton
lm-visual-mcp server                  # run the server in the foreground
lm-visual-mcp doctor                  # inspect configuration and providers

Copy config.example.yaml to lm-visual-mcp.yaml (or ~/.config/lm-visual-mcp/) to configure the listen address, hooks, providers and the two chains. Root nodes are server / hooks / providers / vision / media / logging. There is no mcp: section in the file — the start-server decision belongs to the agent's MCP config, not to the YAML.

Example (top-level providers defines instances; vision declares the chains):

server:
  host: 127.0.0.1
  port: 8787

hooks:
  image:      { enabled: true, models: [] }   # models empty = all models
  classifier: { enabled: true, models: [] }

providers:
  - name: agy
    type: agy
    command: agy
    model: gemini-3.6-flash
    effort: high
    rate_limit: { rpm: 30, concurrency: 2 }
  - name: gemini
    type: gemini
    api_key_env: GEMINI_API_KEY
    disable_thinking: true
  - name: opencode
    type: opencode
    mode: go                              # go | zen
    api_key_env: OPENCODE_API_KEY
  - name: volcengine
    type: volcengine
    mode: agent                           # agent | coding | api
    api_key_env: VOLCENGINE_API_KEY

vision:
  timeout: 120
  max_concurrency: 2
  image_chain: [agy, gemini, opencode]      # first success wins
  classifier_chain: [gemini]                # only API providers belong here

Local CLI providers (agy, codex) have no classifier handling; put only API providers (gemini / opencode / volcengine) on the classifier_chain.

Tools

ui_to_artifact, extract_text_from_screenshot, diagnose_error_screenshot, understand_technical_diagram, analyze_data_visualization, ui_diff_check, analyze_image (+ image_analysis alias). Provider, model, credentials, fallback policy, timeout are server configuration and never appear in tool schemas.

Available Tools

10 tools
analyze_data_visualizationB

Analyze a chart/plot (trends, anomalies, comparisons, distribution).

Inputs: prompt (required), image_source (required), analysis_focus.

analyze_imageC

General visual analysis of an image.

Inputs: prompt (required), image_source (required).

analyze_videoD

Analyze a video (mp4/mov/m4v).

Inputs: prompt (required), video_source (required).

diagnose_error_screenshotB

Diagnose an error/stack trace shown in a screenshot.

Inputs: prompt (required), image_source (required), context.

extract_text_from_screenshotC

Extract visible text (OCR), source code, terminal/config content verbatim.

Inputs: prompt (required), image_source (required), programming_language.

image_analysisD

Alias for analyze_image.

Inputs: prompt (required), image_source (required).

ui_diff_checkB

Compare EXPECTED (first) vs ACTUAL (second) UI for visual regression.

Inputs: prompt (required), actual_image_source (required), expected_image_source (required).

ui_to_artifactB

Convert a UI screenshot into an artifact (code, prompt, spec or description).

Inputs: prompt (required), output_type (required), image_source (required).

understand_technical_diagramC

Understand an architecture/flowchart/UML/ER/system diagram.

Inputs: prompt (required), image_source (required), diagram_type.

video_analysisD

Alias for analyze_video.

Inputs: prompt (required), video_source (required).

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    C2.4/5.0

    Scored across 10 tools

    Disambiguation2/5

    Several tools have direct aliases (analyze_image/image_analysis, analyze_video/video_analysis) causing ambiguity. The other image-analysis tools (data visualization, UI diff, error screenshot, diagram understanding) overlap in scope but have distinct purposes, so some differentiation is possible.

    Naming Consistency2/5

    Most tools follow a verb_noun pattern (analyze_data_visualization, analyze_image, extract_text_from_screenshot), but several deviate (ui_diff_check, ui_to_artifact, image_analysis, video_analysis). The mixed conventions and presence of aliases make naming inconsistent.

    Tool Count4/5

    Ten tools is within a reasonable range for a vision server, but two are redundant aliases, effectively reducing the count to eight. The set is not bloated, but the aliases needlessly inflate the number.

    Completeness4/5

    The server covers a broad range of vision tasks: general image/video analysis, chart understanding, UI regression, OCR, error diagnosis, diagram comprehension, and UI conversion. Missing some common vision features like object detection or face analysis, but for its UI/development focus, the coverage is solid.

    Maintenance

    ActivityInactive
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers