Skip to main content
Glama
AVS845

screenshot-vision-mcp

by AVS845

screenshot-vision-mcp

When reviewing a PR locally, you need to see if the UI looks right. Normally that means Claude takes a screenshot — which burns vision tokens and sends your screen contents to Anthropic. This MCP server routes screenshots through a local Ollama vision model instead: zero token cost, nothing leaves your machine.

Tool

When to use

analyze_screenshot

Public or local URLs — headless browser, no session needed

capture_window

Any app window on screen — sees your real logged-in session

locate_element

Native macOS apps only — returns click coordinates without Claude seeing the image

Chrome automation: for clicking elements in Chrome, prefer javascript_tool + getBoundingClientRect — it's exact and requires no vision at all. See Clicking in Chrome.


Requirements

  • macOS (window capture uses screencapture and AppleScript)

  • Node.js 22+

  • Ollama installed (the server auto-starts it if it isn't running)

  • A multimodal Ollama model — default is gemma4:e4b:

    ollama pull gemma4:e4b

Related MCP server: Screen Vision MCP Server

Installation

git clone git@github.com:AVS845/screenshot-vision-mcp.git
cd screenshot-vision-mcp
npm install
npx playwright install chromium
npm run build

Claude Code configuration

Add this to your ~/.claude/settings.json under mcpServers:

{
  "mcpServers": {
    "screenshot-vision": {
      "command": "node",
      "args": ["/absolute/path/to/screenshot-vision-mcp/dist/index.js"]
    }
  }
}

Replace /absolute/path/to/ with the actual path. Then restart Claude Code.

Two optional env vars let you configure the server without touching call sites:

Variable

Default

Description

OLLAMA_URL

http://localhost:11434

Ollama host

OLLAMA_MODEL

gemma4:e4b

Default vision model for all tools


Tools

analyze_screenshot

Takes a screenshot of a URL in a headless Playwright browser and analyzes it with Ollama.

analyze_screenshot(
  url: "http://localhost:3004/dashboard",
  question: "Does the revenue chart render correctly? Are there any layout issues?"
)

Parameter

Type

Default

Description

url

string

URL to screenshot

question

string

What to analyze

model

string

gemma4:e4b

Ollama vision model

viewport_width

number

1280

Viewport width in px

viewport_height

number

800

Viewport height in px

wait_ms

number

2000

Wait after page load (ms)

full_page

boolean

false

Capture full page height, not just the visible viewport

max_slices

number

8

Max slices when full_page is true (1–20)

When full_page: true, the page is sliced into viewport_height-tall segments and sent as multiple images. This matters because Gemma 4's image token budget is ~280 tokens — a single tall screenshot gets crushed into noise, while properly-proportioned slices each get full detail.


capture_window

Captures an app window currently on screen and analyzes it. Use this when the page requires a login — it sees your real browser session.

capture_window(
  app_name: "Google Chrome",
  question: "Is the form validation error displaying correctly under the email field?"
)

Parameter

Type

Default

Description

app_name

string

Exact macOS app name, e.g. "Google Chrome"

question

string

What to analyze

model

string

gemma4:e4b

Ollama vision model

window_index

number

1

Which window (1 = frontmost)

scale

number

1

Upscale factor (2–3 helps with small text)

crop

object

Crop to a sub-region before analysis

The crop parameter uses fractional values (0–1). To inspect just the bottom half of the window:

{ "x": 0, "y": 0.5, "width": 1, "height": 0.5 }

locate_element

Finds a UI element in a native macOS app window and returns its click coordinates. Designed for apps where DOM access isn't available — Terminal, Figma, Xcode, etc.

Not recommended for Chrome. Chrome's DOM gives exact coordinates with no vision model involved. See Clicking in Chrome.

Parameter

Type

Default

Description

app_name

string

Exact macOS app name, e.g. "Figma", "Terminal"

element_description

string

Natural language description of the element

model

string

gemma4:e4b

Ollama vision model

window_index

number

1

Which window (1 = frontmost)

viewport_bounds

object

Screen-coordinate bounds of the region to capture

Returns { x, y, coordinate_mode, clamped }. clamped: true means the model returned out-of-range coordinates that were corrected — treat as low confidence.

Accuracy note: gemma4:e4b has a ~280-token image budget, which limits spatial precision. Elements in the center of the screen locate reliably; elements near edges may have 50–150px errors. Use a larger model (e.g. gemma4:26b) if precision matters, at the cost of slower inference.


Clicking in Chrome

For Chrome browser automation, skip locate_element entirely. Use javascript_tool to get exact coordinates from the DOM:

// In javascript_tool — returns perfect viewport coordinates, no vision needed
const el = document.querySelector('button.submit');
const r = el.getBoundingClientRect();
JSON.stringify({ x: Math.round(r.left + r.width/2), y: Math.round(r.top + r.height/2) });

Only fall back to locate_element for elements that genuinely can't be DOM-queried (canvas content, rendered images, visually-composed widgets without CSS selectors). When you do, pass viewport_bounds so the returned coordinates are viewport-relative and work directly with the Chrome computer tool.

Measure viewport bounds immediately before the call — the Chrome automation InfoBar shifts innerHeight, causing ~40px errors with stale bounds:

// javascript_tool — run right before locate_element
JSON.stringify({
  screenX: window.screenX,
  screenY: window.screenY,
  outerHeight: window.outerHeight,
  innerWidth: window.innerWidth,
  innerHeight: window.innerHeight,
})
locate_element(
  app_name: "Google Chrome",
  element_description: "the close button on the confirmation modal",
  viewport_bounds: {
    x: screenX,
    y: screenY + outerHeight - innerHeight,
    width: innerWidth,
    height: innerHeight,
  }
)
→ { x: 891, y: 267, coordinate_mode: "viewport" }

How it works

  1. analyze_screenshot launches a headless Chromium browser via Playwright, navigates to the URL, waits for JS to settle, and captures a PNG.

  2. capture_window uses AppleScript to get the window bounds, screencapture -R to grab exactly that region, and optionally sips to crop and scale.

  3. locate_element captures the specified region, asks Ollama to return element coordinates as JSON fractions (0–1), then converts to pixel coordinates.

  4. All tools base64-encode the PNG and POST it to Ollama's /api/generate endpoint.

  5. If Ollama isn't running, the server spawns ollama serve and waits up to 30 seconds for it to become ready.


Rebuilding after changes

npm run build

Then restart Claude Code to reload the server.

Available Tools

3 tools
analyze_screenshotA

Take a screenshot of a URL in a headless browser and analyze it with a local Ollama vision model. Good for public pages. For pages requiring login, use capture_window instead. Use full_page: true to capture content below the fold — the page is sliced into segments and sent as multiple images so no detail is lost.

ParametersJSON Schema
NameRequiredDescriptionDefault
urlYesURL to screenshot (e.g. http://localhost:3004)
modelNoOllama vision model to use (defaults to OLLAMA_MODEL env var, or gemma4:e4b)gemma4:e4b
wait_msNoMilliseconds to wait after page load for JS rendering
questionYesWhat to analyze or look for in the screenshot
full_pageNoCapture the full page height, not just the visible viewport. Long pages are sliced into segments sent as multiple images so detail is preserved at every scroll depth.
max_slicesNoMaximum number of slices when full_page is true. Slices are distributed evenly across the page height. Default 8 covers most pages well.
viewport_widthNoViewport width in pixels
viewport_heightNoViewport height in pixels

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden of behavioral disclosure. It reveals that the tool runs a headless browser, uses a local Ollama vision model, and explains the slicing mechanism for full-page captures to preserve detail. This goes beyond the schema and provides meaningful insight into how the tool behaves, though it omits return format and potential side effects.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is two sentences, front-loaded with the primary purpose, followed by usage guidance and a key behavioral note. Every sentence earns its place with no redundancy or filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description covers the main use case, login-page alternative, and full-page behavior, but it does not explicitly specify what the tool returns (e.g., analysis text, screenshot, or both). Given the absence of an output schema, this is a notable gap for a tool with 8 parameters and a potentially complex response.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the baseline is 3. The description reinforces the full_page parameter's behavior ('sliced into segments and sent as multiple images') but does not materially add meaning beyond what the schema already documents for other parameters.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific action ('Take a screenshot of a URL in a headless browser and analyze it with a local Ollama vision model') and distinguishes the tool from siblings by explicitly contrasting with capture_window for login pages. This makes the tool's purpose unambiguous and clearly differentiated.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives explicit usage context: 'Good for public pages' and directs users to 'use capture_window instead' for login-protected pages. It also advises on full_page usage for content below the fold, covering when to use this tool versus an alternative.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

capture_windowA

Capture a specific app window currently visible on screen and analyze it with a local Ollama vision model. Unlike analyze_screenshot, this sees your real browser with your logged-in session and live state — ideal for testing local apps.

ParametersJSON Schema
NameRequiredDescriptionDefault
cropNoCrop to a sub-region before analysis. All values are fractions 0–1. E.g. bottom half: {x:0, y:0.5, width:1, height:0.5}
modelNoOllama vision model to use (defaults to OLLAMA_MODEL env var, or gemma4:e4b)gemma4:e4b
scaleNoUpscale factor applied before sending to the vision model. Use 2 or 3 for small terminal text or dense UIs.
app_nameYesExact macOS app name, e.g. 'Google Chrome', 'Safari', 'Terminal', 'Cursor'
questionYesWhat to analyze or look for in the screenshot
window_indexNoWhich window to capture if the app has multiple (1 = frontmost)

TDQS

A3.9/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description must carry the full burden. It discloses that the tool captures a live, visible window and uses a local Ollama model, which implies real-state analysis and privacy considerations. However, it does not mention required permissions, whether it brings the window to front, or any side effects, leaving notable behavioral gaps.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is two efficient sentences with the core purpose front-loaded, followed by a single high-value comparison sentence. No wasted words.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

While the description covers purpose and differentiates from a sibling, the tool is complex (6 params, nested object, no output schema). It does not explain the return format (e.g., text from the vision model), prerequisites (e.g., screen recording permission), or the effect of crop/scale parameters. These gaps are significant for an agent unfamiliar with the tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema provides 100% coverage with detailed descriptions for all six parameters, including the nested crop object and model default. The tool description adds no extra parameter semantics, so a baseline of 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's action ('Capture a specific app window currently visible on screen and analyze it with a local Ollama vision model'), specifies the resource (specific app window), and differentiates from the sibling tool analyze_screenshot. The verb is specific and the scope is explicit.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly names an alternative (analyze_screenshot) and contrasts their behavior ('this sees your real browser with your logged-in session and live state'), providing a clear use case ('ideal for testing local apps'). It lacks an explicit 'when not to use' statement, but the contrast implies it.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

locate_elementA

Find a UI element in an app window and return its click coordinates. Uses a local Ollama vision model — Claude never sees the image. For Google Chrome, pass viewport_bounds (computed from javascript_tool: {x: screenX, y: screenY + outerHeight - innerHeight, width: innerWidth, height: innerHeight}) to get viewport-relative coordinates usable directly with the Chrome computer tool. Without viewport_bounds, returns absolute screen coordinates.

ParametersJSON Schema
NameRequiredDescriptionDefault
modelNoOllama vision model to use (defaults to OLLAMA_MODEL env var, or gemma4:e4b)gemma4:e4b
app_nameYesExact macOS app name, e.g. 'Google Chrome', 'Safari', 'Terminal', 'Cursor'
window_indexNoWhich window to capture if the app has multiple (1 = frontmost)
viewport_boundsNoScreen-coordinate bounds of the region to capture (e.g. Chrome's content area). For Chrome: compute as {x: screenX, y: screenY + outerHeight - innerHeight, width: innerWidth, height: innerHeight} using window.screenX/Y/outerWidth/outerHeight/innerWidth/innerHeight from javascript_tool. When provided, returned coordinates are relative to this region's (0,0) — viewport-relative for Chrome.
element_descriptionYesNatural language description of the element to find, e.g. 'the X close button in the top-right of the modal'

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Since annotations are absent, the description carries the behavioral disclosure burden. It reveals key behaviors: the use of a local Ollama vision model ('Claude never sees the image'), privacy preservation, and the coordinate system semantics for viewport_bounds. It does not describe failure handling, permissions, or output format, but the disclosed traits are meaningful and non-obvious.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is three sentences, each earning its place: core purpose, model/privacy, and coordinate behavior with a concrete example. The critical viewport_bounds guidance is front-loaded after the purpose, making the most actionable information easy to find. No filler words or redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Considering the tool's complexity (nested viewport_bounds object, 5 params, no output schema, no annotations), the description covers the essential missing context: coordinate semantics and the crucial Chrome-specific computation. It omits edge cases like element-not-found behavior or return error structure, but for a coordinate-returning tool, the provided detail is sufficient for effective use.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema already documents all 5 parameters with 100% coverage, so the baseline is 3. The description adds significant value by explaining exactly how to compute viewport_bounds for Chrome and why it changes the returned coordinates. This provides meaning beyond the schema's static field definitions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with an explicit, specific verb+resource statement: 'Find a UI element in an app window and return its click coordinates.' This clearly differentiates the tool from siblings like analyze_screenshot (which analyzes an image) and capture_window (which captures a window). The returned artifact (click coordinates) is unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides concrete guidance for the Chrome-specific case, including a detailed formula for viewport_bounds and the consequence of omitting it (absolute vs. viewport-relative coordinates). It does not explicitly name alternative tools or state when not to use this tool, but it offers clear contextual usage instructions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

TDQS

A4.2/5.0
Disambiguation4/5

analyze_screenshot and capture_window both perform capture-and-analyze, but their contexts are clearly differentiated (headless URL vs. live window). locate_element is distinct as it returns coordinates. Minor overlap exists for public pages that could be captured either way.

Naming Consistency5/5

All three tools follow a consistent verb_noun pattern with snake_case: analyze_screenshot, capture_window, locate_element. Predictable and clear.

Tool Count5/5

Three tools is a focused, appropriate scope for a screenshot-vision server—each serves a distinct purpose without redundancy.

Completeness4/5

The set covers the core workflows: capture from URL, capture from live window, and element location. A general 'analyze existing image' tool is missing, but the primary use cases are covered.

Maintenance

ActivitySlowing
ResponsivenessResponsive

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/AVS845/screenshot-vision-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server