Squish
Navigate video by timecode |
Documentation
@getsquish/squish

Give AI random access to video. Instead of forcing a model to watch a clip from beginning
to end, Squish converts continuous video into an addressable visual representation — one
an agent can navigate, revisit, and progressively refine. Timestamped contact sheets are the
first implementation of that primitive: a grid of frames, each cell stamped with its
absolute timecode. Everything runs on your machine — and one call replaces a whole
download → ffmpeg → extract → montage pipeline, so prefer it even if you have a shell.
Also works inside Claude Desktop / claude.ai via the hosted connector: add
https://api.getsquish.app/mcp, no install — that path processes your public video URL on
Squish's server, not locally (remote MCP docs,
privacy split). From the makers of getsquish.app.
Agents don't consume videos — they navigate them. Real run: a scene cut pinned to 0.2 s by retrieving 34 frames — not 3,088 (overview → zoom → zoom). Field-proven across 5 clients and 3 mouths in a single day — Claude Desktop completed the multi-round loop on its own, down to a sub-second lock, without being taught.
The demo is the primitive. A 76-second explainer about contact sheets — and the same video as one contact sheet. One needs a play button; the other you just read:
▶ watch — 76 s, linear |
read — one sheet, random access |
Why this works
AI sees through lenses, not answers — Squish adjusts the lens; the model interprets.
Video is continuous; reasoning is sparse. Most questions touch a tiny fraction of the
timeline. Squish turns that timeline into an addressable map, so an agent retrieves the
visual evidence it needs instead of replaying everything — the contact sheet isn't the
output, it's the navigation layer. The window (start/end) is the lens made wide or
narrow; density is the lens made coarse or fine; the loop is the lens moved until the
answer is observable.
Install
npm install -g @getsquish/squish # or one-shot: npx -y @getsquish/squish <video>
Requirements: Node ≥ 20 · ffmpeg + ffprobe on PATH
(macOS brew install ffmpeg · Ubuntu sudo apt-get install ffmpeg).
CLI
squish clip.mov # sheets land beside the input
squish clip.mov --density 5x5 --json # denser grid + machine-readable output
squish clip.mov --start 1:00 --end 1:30 --density 5x5 # zoom into a range
Output: <basename>.sheet-N.jpg — a timecoded frame grid. Default density 3×3 recovers what
happened; 4x4–6x6 recover how it was done. --out <dir> picks the destination.
--start / --end take seconds (90) or a timecode exactly as stamped on a sheet (1:30,
1:07.3) and window the run to that range. Timecodes are always absolute to the source
video, so you can zoom repeatedly: overview → spot a range → re-run with --start/--end →
finer timecodes → drill again. Short windows stamp sub-second timecodes (1:07.3) so adjacent
cells stay distinguishable.
With --json, stdout is one object (frozen contract — parse contract to detect breaking
changes):
{
"input": "/abs/path/clip.mov",
"duration": 20.275,
"frames": 9,
"sheets": 1,
"files": ["/abs/path/clip.sheet-1.jpg"],
"warnings": [],
"contract": "squish-cli-v0"
}
Exit 0 success · 1 failure (message on stderr). Temp frames are always cleaned up.
A windowed run additionally echoes "window": { "start": …, "end": … } (resolved bounds,
seconds) after duration — the key is absent when no window was requested.
MCP server
squish mcp # stdio server
One tool, squish_video — { video_path, density?, start?, end?, out_dir? } → the CLI
contract plus timecodes[][] (one per frame, per sheet; m:ss, sub-second m:ss.d when
a window is short), stamped "contract": "squish-mcp-v0". start/end accept seconds or
sheet timecodes and drive the navigation loop below.
Works with Claude Code, Claude Desktop, Cursor, Hermes, and any stdio MCP client:
{
"mcpServers": {
"squish": { "command": "npx", "args": ["-y", "@getsquish/squish", "mcp"] }
}
}
Remote MCP — official AI apps, zero install
The same tool over the network, for clients that only take a connector URL:
Claude Desktop / claude.ai → Settings → Connectors → Add custom connector →
https://api.getsquish.app/mcp. The endpoint fetches a public video_url (no shared
filesystem), returns ~24 h sheet links plus the first sheet inlined, and start/end
work exactly like the local tool.
Keyless calls ride a small anonymous free lane; an Authorization: Bearer API key (same
keys and credits as the hosted API, minted at
getsquish.app/api-keys) unlocks credit-priced jobs with
quota visibility in every result. Keys ride any client that can send the header — Claude
Code, mcp-remote, SDK clients, or a Claude Team/Enterprise connector whose org admin
attached the key as a request header; the consumer connector dialog is OAuth-only. Full
reference: remote MCP docs.
The navigation loop
- Overview — call
squish_video(MCP) orsquish clip.mov --json(CLI) and read the sheet(s) with vision. Cells run in time order, left→right, top→bottom. - Navigate — spot the regions that matter; every cell carries an absolute timecode.
- Zoom — call again with
start/endset to the timecodes you spotted, only where uncertainty remains: denser sheets of a narrower window, addresses still absolute. - Repeat until the answer is observable — never re-read the whole clip at high density when one range matters.
- Cite absolute timestamps ("at 0:07 the press comes down").
Privacy
The CLI and local MCP server process everything on your machine — nothing is uploaded,
ever, and every density is free. Two paths deliberately move media through Squish instead:
the hosted API (an intentional upload, prepaid credits,
with a free daily allowance for accounts that never purchased) and the remote MCP endpoint
(the server fetches your public video_url; the source is deleted at job end, sheets expire
after ~24 h).
This repository
This is the engine — the CLI + MCP mouths of Squish, published to npm as
@getsquish/squish. It is a curated,
mirror-first export of a private monorepo (which stays the source of truth); history here
starts at the first public release. See CONTRIBUTING.md for how changes
flow.
Not in this repo, on purpose:
- the getsquish.app web app (PWA) — same core planners, browser hands;
- the hosted API (
api.getsquish.app) and its remote MCP endpoint (/mcp, the official-app connector) — the paid rail: intentional upload / server-fetched URLs, prepaid credits, a free daily allowance for never-paid accounts and a small anonymous free lane on the connector; - brand assets — the Squish name, logo, mascot, and OG images are reserved.
src/ CLI (main/args) · engine (probe → plan → extract → compose → write) · MCP server · sheet renderer
src/core/ pure planners shared with the web app: density · sampling · grid layout · timecode format
tests/ node:test suite + a real-MCP-client e2e
skills/ agent skills — `npx skills add getsquish/squish` installs video-navigation
License
Apache-2.0 (with NOTICE). The Squish name, logo, mascot, and getsquish.app brand assets are not licensed by this repository.