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.

Key takeaways

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, zero-padded; hard-capped at 30 per
│                         recording. Start + end bookends always kept;
│                         remaining budget sampled uniformly from
│                         interaction frames, then ambient. Read in
│                         numeric order via frame_tags.json.
├── 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, Claude Desktop, Cursor, Codex CLI, Continue, Windsurf, Zed, Warp, Cline, …) connect via a small bridge binary that relays stdin/stdout to the socket. The bridge ships bundled inside the app at /Applications/Stash.app/Contents/Helpers/stash-mcp — pre-signed as part of Stash.app under Stash's Apple team with Hardened Runtime. There is no compile step and no Apple Developer certificate required. The one-line installer at yourstash.ai/claude verifies the bundled helper exists and is validly signed, then points Claude Code, Claude Desktop, and Cursor at that absolute path. Other clients are manual; see /claude#manual-setup for full per-client snippets.

Note: GUI clients launched from Finder/Dock do not inherit your shell PATH and ~ does not expand reliably — always use the absolute path to stash-mcp in the command field. The bundled path /Applications/Stash.app/Contents/Helpers/stash-mcp is already absolute.

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). The bundled bridge is signed under VJMJQKCRMC, so it connects with no extra setup. Extend the allowlist via Stash → Settings → Privacy → Additional trusted team IDs, or toggle Allow unsigned MCP clients — the mcpAllowUnsignedClients UserDefault. That toggle is for advanced/local use only: enabling it lets any unsigned local process connect to the MCP socket, so it should not be a general recommendation.

One honest caveat: team-ID allowlisting is a speedbump, not a hard boundary. Because the bridge is a pure relay, any local process can launch the trusted bridge and drive it — a confused-deputy weakness. It stops other signed apps from connecting directly; it does not stop arbitrary same-user code. A capability-token handshake is the planned future replacement.

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 yourstash.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.

Frequently asked questions

What is the stash-1 protocol?

The stash-1 protocol is Stash's specification for how LLMs and agentic tools read Stash captures across three channels: the pixel banner rendered into every screenshot, XMP metadata embedded in the saved JPEG, and a local Model Context Protocol (MCP) server. Each channel is a fallback for the layer above.

Which channel is most authoritative — banner, XMP, or MCP?

The MCP server is most authoritative when the original Mac is reachable, because it carries the full live dossier including the un-summarized accessibility tree. XMP is next — it survives file-on-disk flows like email and Drive. The pixel banner is the last-resort channel that survives anything an image survives, including pasted-into-chat captures where every byte of metadata has been stripped.

Can I read Stash captures from a remote machine?

No. The MCP server binds to a UNIX domain socket under the user account and is not exposed to the network by design. To read captures remotely, agents must rely on the XMP payload embedded in the file or the pixel banner.

How does Stash authenticate MCP clients?

Stash reads the connecting peer's codesign team identifier on connect and silently rejects unknown signers. The built-in allowlist trusts Anthropic (team 58LP8PCM82) and Stash itself (team VJMJQKCRMC). The bridge binary ships bundled inside Stash.app, pre-signed under team VJMJQKCRMC, so it connects with no extra setup. Additional team IDs can be added via Settings → Privacy, or the mcpAllowUnsignedClients toggle can bypass the check entirely — but that lets any unsigned local process connect to the socket, so it's for advanced local use only, not a general recommendation. Note that team-ID allowlisting is a speedbump, not a hard boundary: because the bridge is a pure relay, any local process can launch the trusted bridge and drive it (a confused-deputy weakness). It stops other signed apps from connecting directly; it does not stop arbitrary same-user code. A capability-token handshake is the planned future replacement.

Are Stash video bundles indexable by AI tools?

Yes. Each bundle is a self-describing folder containing report.md with YAML frontmatter, frame_tags.json with per-frame app and window metadata, an offline llms.txt, sampled JPEG frames (capped at 30 per recording), and extracted audio. Agents read it as a single unit via stash.get_bundle().

Will the stash-1 protocol ever break?

No. The stash-1 protocol is stable. Additive changes — new fields, new tools — land as v1.1, v1.2 and do not break existing v1 readers. A breaking change would bump to stash-2.

Related reading