The Stash protocol.

How LLMs and agentic tools read Stash captures. This page is the human-readable companion to /llms.txt — the same spec, same precedence rules, just nicer to skim.

Precedence: MCP > XMP > pixel banner. Each is a fallback for the layer above. Protocol version: stash-1.

Three channels, one capture

Every Stash screenshot carries structure in three places so a capture can always be resolved — even after the user pastes it into a web chat and every byte of metadata is stripped.

ChannelSurvivesWhat it carries
Pixel banner Anything an image survives App, window title, appearance, OS version, timestamp, shortID
XMP metadata File-on-disk flows (Drive, email) Full structured payload: annotations, a11y tree summary, dev context
MCP server Local RPC (same machine) Live dossier including full a11y tree and un-summarized fields

Screenshot banner

Rendered at the bottom of every Stash screenshot in a monospace font:

📌 Claude — Settings · dark · macOS 26.4 · 2026-04-12 14:24 · #8FD26F28

When the user drew annotations, a second line appears above the pin:

user focus: blue arrow pointing · red box enclosing

The banner describes shape behavior — never the target. Resolve the target yourself using vision and/or the a11y tree.

XMP payload

On auto-save-to-desktop for developer apps, the JPEG carries an XMP payload under namespace http://stash.app/ns/1.0/. Serialized as a single JSON string under stash:payload:

{
  "protocolVersion": "stash-1",
  "source": "xmp-snapshot",
  "captureId": "8FD26F28-…",
  "mcpURI": "stash://capture/8FD26F28-…",
  "snapshotTimestamp": "2026-04-12T14:24:00Z",
  "appName": "Cursor",
  "bundleID": "com.todesktop.230313mzl4w4u92",
  "windowTitle": "ContextBannerRenderer.swift",
  "appearance": "dark",
  "osVersion": "macOS 26.4",
  "userFocus": [
    { "type": "arrow", "color": "BA0C2F", "behavior": "pointing",
      "from": [120, 340], "to": [420, 300] }
  ],
  "a11yTreeSummary": { /* trimmed: top 3 levels + labelled controls */ },
  "devContext": {
    "activeFilePath": "/Users/x/proj/Foo.swift",
    "selectedText": "let appearance = …",
    "gitBranch": "main"
  }
}

Also tagged with IPTC 2025.1 Iptc4xmpExt:AISystemUsed = "Stash" so conformant tooling can detect AI-assisted captures. Filename convention on save-to-desktop: Stash-YYYY-MM-DD-HHmmss-{shortID}.jpg.

Video bundles

Produced by the Stash screen recorder. A self-describing folder, indexable as one unit:

Recordings/<uuid>/
├── report.md           ← YAML frontmatter + markdown timeline
├── frame_tags.json     ← { "frames": [ … ] } — per-frame app/window/tag
├── llms.txt            ← offline self-description
├── frame_NN.jpg        ← 1-indexed, up to 15 key frames
├── audio.m4a           ← extracted audio when present
└── video.mp4           ← original; generally skip

The report.md opens with machine-readable YAML frontmatter:

---
protocol: stash-1
bundleVersion: 2
captureId: <UUID>
duration: 42.30
frameCount: 12
hasAudio: true
primaryApp: Cursor
mcpURI: stash://bundle/<UUID>
---

MCP server

Stash ships a local Model Context Protocol server on a UNIX domain socket at ~/Library/Application Support/Stash/mcp.sock — line-delimited JSON-RPC 2.0. Local-only by design; the socket is not exposed to the network.

Transport

Stdio MCP clients (Claude Code, Cursor, Codex CLI, etc.) connect via a small bridge binary at ~/.local/bin/stash-mcp that relays stdin/stdout to the socket. The one-line installer at gostash.ai/claude compiles it, signs it, and registers it with every Claude client it finds.

Manual registration by client:

Peer auth

Stash reads the peer's codesign team identifier on connect and silently rejects unknown signers. Built-in allowlist: Anthropic (58LP8PCM82) and Stash itself (VJMJQKCRMC). Extend via Stash → Settings → Privacy → Additional trusted team IDs, or toggle Allow unsigned MCP clients for local experiments.

Tools

ToolPurpose
stash.get_capture(id)Full dossier for a screenshot or video capture
stash.get_bundle(id)Video bundle: report.md, enriched frame_tags, absolute file paths
stash.list_recent(n)Paste-flow fallback; compact summaries newest-first
stash.search(query)Substring match over app / window / text / URL
stash.render_plain(id)Raw JPEG bytes, no banner, no XMP (for evals)

All tools return the MCP-standard {content: [{type: "text", text: "<json>"}]} envelope. render_plain returns {type: "image", data: "<base64>", mimeType: "image/jpeg"}.

Privacy model

Versioning

stash-1 is stable. Additive changes (new fields, new tools) land as v1.1, v1.2 and do not break v1 readers. A breaking change bumps to stash-2. Prefer live MCP data over a frozen XMP snapshot when both are available.

The machine-readable version of this page lives at gostash.ai/llms.txt. Point your agent's config there for a one-shot sync; it's the same spec as above, optimized for plain-text consumption.