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.
stash-1.
Key takeaways
- Three channels carry capture context: pixel banner (most resilient), XMP metadata (file-on-disk flows), MCP server (live local).
- Resolution precedence is MCP > XMP > pixel banner — clients should prefer the highest-fidelity available source.
- The pixel banner describes shape behavior (arrow pointing, box enclosing) — never the target. Agents resolve targets via vision plus the a11y tree.
- The MCP server runs on a local UNIX domain socket and is never exposed to the network.
- Peer auth is by codesign team-ID allowlist; unknown signers are silently rejected.
- Sensitive fields (a11y tree, selected text, file paths, git branches, terminal CWD) auto-purge 24 hours after capture.
stash-1is stable; only additive changes within v1. Breaking changes bump tostash-2.
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.
| Channel | Survives | What 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
- App name (always).
— windowTitlewhen available.dark/light— system appearance at capture time.- macOS version.
- Capture timestamp to the minute, local time.
#XXXXXXXX— first 8 hex chars of the capture UUID. Call it out in a follow-up prompt or filter withstash.search.
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
| Tool | Purpose |
|---|---|
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
- Everything Stash captures stays on the user's Mac.
- The MCP socket is local-only.
- Sensitive capture data (a11y tree, selected text, file paths, git branches, terminal CWD) is purged 24 hours after capture by default. User-adjustable 1 hour → never.
- Screenshots and basic metadata follow the user's normal history retention.
- Detected secrets (API keys, tokens, private keys) are redacted at capture time and never stored.
- Accessibility tree capture is skipped for password managers and their web UIs.
- Stash servers never see any capture data.
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.
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
- Stash for Claude — install & setup — the operator-facing companion to this spec.
- What is MCP (Model Context Protocol)? — background on the protocol Stash speaks.
- Stash MCP server for Claude Code and Cursor — practical integration walkthrough.
- Screenshot context for AI coding — why captures need metadata, not just pixels.
- /llms.txt — the machine-readable version of this page.