Find, preview and cut public-domain b-roll from the Internet Archive, from inside any MCP agent.
Every clip lands on disk with a provenance record next to it: source, license, exact segment and attribution text.
You: "I need 8 seconds of children eating bananas, ideally black and white."
Agent → search_footage("children eating bananas") 5 ranked items, license + tier on each
Agent → inspect_item("AboutBan1935") scenes with a keyframe image per scene
Agent → extract_clip("AboutBan1935", start_s=300, 8s) verified H.264 MP4 + provenance JSON
Agent → get_provenance(clip_id) "About Bananas" (1935), Prelinger Archives
via Internet Archive · Public Domain
The agent sees the footage before it cuts: inspect_item returns real keyframe images, so a vision model can pick the shot instead of guessing from titles.
archive.org holds decades of public-domain film (Prelinger, NASA, newsreels, stock footage), but using it from an agent is awkward:
- Raw item metadata can exceed 1 MB for one item. That's a huge token cost before you've seen a single frame.
- Licenses differ per item, even inside one collection.
- Clips cut with
-c copysnap to keyframes. An 8 s request came back as 11.35 s.
broll-mcp handles all of this behind four small tools. Responses are compact and cached, and the server applies polite rate limits. Each cut is re-encoded and checked against the requested length.
| 🔎 Brief-driven search | Plain-language briefs become archive.org queries. A two-stage search first requires every term, then matches any term. Local ranking adds collection trust tiers and a black-and-white preference. Results come back 5 per page. |
| 🖼️ Visual inspection | Scene detection runs over HTTPS (PySceneDetect, with ffmpeg as fallback) and returns one keyframe per scene as an MCP image. Items that hold hundreds of clips return a paged clip list with thumbnails. |
| ✂️ Frame-accurate cuts | Cuts are re-encoded to H.264 MP4 with faststart. ffprobe confirms the duration is within ±0.1 s and the decode shows zero errors, and you get 3 check frames back. The same request twice returns the same clip. |
| 📜 Provenance by design | A JSON record sits next to every clip. It holds the source file MD5, the license copied verbatim, commercial_use, the data node, a timestamp and ready-to-paste attribution. Provenance is written before the clip, so a clip never exists without its record. |
| 🤝 Polite to archive.org | Follows archive.org's bot guidance: a descriptive User-Agent, a 1 req/s token bucket, at most 4 media connections, Retry-After support and exponential backoff. |
| 💾 Cache-first | Uses a SQLite WAL cache. Repeating a search, inspection or cut sends 0 upstream requests, and every tool result reports upstream_requests. |
Requirements: Python 3.12+, uv, and ffmpeg / ffprobe built with HTTPS support (brew install ffmpeg, apt install ffmpeg).
claude mcp add broll -- uvx --from git+https://github.com/edigeh/broll-mcp broll-mcp{
"mcpServers": {
"broll": {
"command": "uvx",
"args": ["--from", "git+https://github.com/edigeh/broll-mcp", "broll-mcp"],
"env": { "BROLL_AGENT_MODEL": "claude-opus-5-5" }
}
}
}git clone https://github.com/edigeh/broll-mcp && cd broll-mcp
uv sync
uv run broll-mcp # stdio server; point your host at this commandTry it without an agent using the MCP Inspector:
npx @modelcontextprotocol/inspector --cli uv run broll-mcp \
--method tools/call --tool-name search_footage --tool-arg 'brief=aerial highway 1950s'| Tool | What it does | Upstream cost |
|---|---|---|
search_footage(brief, filters?, cursor?) |
Ranked items with title, year, trust tier, license, runtime, tile image URL and a short why it matched. Filters: max_tier, license (stated · commercial_ok · any), collections, year_from/to, color. |
≤ 4 requests at the default max_tier (2 at tier 1, 6 at tier 3), 0 when cached |
inspect_item(identifier, file?, window?, page_size?, cursor?) |
Scene mode: scenes with timings plus a keyframe image each, a shotlist excerpt and license. Clip-list mode: for items with many separate clips, a paged list with thumbnails. | a few requests, 0 when cached |
extract_clip(identifier, file, start_s, duration_s, label?, audio?, brief?) |
Cuts 1–60 s into BROLL_HOME/clips/, verifies it and writes the provenance JSON. Returns 3 check frames. |
~3 requests, 0 on repeat |
get_provenance(clip_id | identifier, cursor?) |
Reads the record for a clip, or for an item and the clips already cut from it. Local only. | always 0 |
Resource: broll://stats reports request counts by endpoint, cache contents, the active scene detector and the ffmpeg version.
Every tool declares an outputSchema and returns structuredContent. Errors come back as tool results with a stable code, such as NO_RESULTS, ITEM_NOT_FOUND, FILE_NOT_IN_ITEM, RANGE_OUT_OF_BOUNDS, THROTTLED or CUT_VERIFY_FAILED. Each message tells the agent how to recover.
License labels come only from an exact URL table covering PD, CC0, the PD Mark, and CC BY / BY-SA / BY-ND / BY-NC variants for versions 2.0–4.0. A URL outside the table is copied verbatim with the label unrecognized and commercial_use: "unknown"; nothing is guessed.
| Variable | Default | Purpose |
|---|---|---|
BROLL_HOME |
platform data dir (~/Library/Application Support/broll-mcp, ~/.local/share/broll-mcp) |
Cache, keyframes, clips. The server writes nowhere else. |
BROLL_AGENT_MODEL |
unspecified |
Model name sent in the User-Agent, as archive.org asks of AI agents. |
BROLL_CACHE_MAX_MB |
2048 |
Keyframe cache budget; older keyframes are evicted first. |
Collection trust tiers live in BROLL_HOME/tiers.toml. Tier 1 is prelinger and nasa, tier 2 is stock_footage and feature_films, and everything else is tier 3. Edit the file to tune ranking and the max_tier filter.
MCP host ──stdio──► server.py ── jsonschema validation, result shaping, images
│
┌───────────────┼──────────────────┬────────────────────┐
SearchService SceneService ClipService SQLite cache (WAL)
two-stage query PySceneDetect / cut → ffprobe verify searches · items · scenes
+ local ranking ffmpeg, keyframes → provenance → clip clips · provenance · request_log
└───────────────┴─────── ArchiveClient ────────────────┘
UA · 1 req/s bucket · 4 media slots · backoff
│
archive.org (advancedsearch · metadata · download)
The design rests on live archive.org responses captured in research/, which the offline tests also use as fixtures.
uv sync
uv run pytest # 155 offline tests (mock transport, captured metadata)
uv run ruff check . && uv run ruff format --check .
uv run python scripts/e2e_smoke.py /tmp/broll-e2e # live end-to-end over stdio against archive.orgSee CONTRIBUTING.md.
broll-mcp copies license information exactly as archive.org publishes it, and it makes no legal judgement. commercial_use: "unknown" means archive.org states no machine-readable license for that item. Check the source item before you publish footage, especially material that carries only a rights statement.
MIT for the code. Footage belongs to its respective rights holders under the terms recorded in each clip's provenance file.