Skip to content
edigehPublic

About

MCP server to find, preview and cut public-domain b-roll from the Internet Archive, with a provenance record for every clip

Topics

Resources

Contributing

Stars

51 stars

Watchers

0 watching

Forks

Latest commit

Β 

History

1 Commit

Folders and files

Repository files navigation

🎞️ broll-mcp

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.

CI License: MIT Python 3.12+ MCP No API key


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.

Why

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 copy snap 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.

Features

πŸ”Ž 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.

Quick start

Requirements: Python 3.12+, uv, and ffmpeg / ffprobe built with HTTPS support (brew install ffmpeg, apt install ffmpeg).

Claude Code

claude mcp add broll -- uvx --from git+https://github.com/edigeh/broll-mcp broll-mcp

Claude Desktop, Cursor and other MCP hosts

{
  "mcpServers": {
    "broll": {
      "command": "uvx",
      "args": ["--from", "git+https://github.com/edigeh/broll-mcp", "broll-mcp"],
      "env": { "BROLL_AGENT_MODEL": "claude-opus-5-5" }
    }
  }
}

From a clone

git clone https://github.com/edigeh/broll-mcp && cd broll-mcp
uv sync
uv run broll-mcp          # stdio server; point your host at this command

Try 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'

Tools

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.

Provenance record

// clips/AboutBan1935__0300.00-0308.00__bananas__4ee00c.provenance.json
{
  "schema": "broll-mcp/provenance/1",
  "clip_id": "clp_20261023_4ee00c",
  "clip_sha256": "78e33da7…",
  "source": {
    "identifier": "AboutBan1935",
    "title": "About Bananas",
    "file": "AboutBan1935.mp4",
    "file_md5": "de484b1b573d28be4860f00a593f4416",
    "derived_from": "AboutBan1935.mpeg",
    "collections": ["prelinger"],
    "tier": 1,
    "date": "1935",
    "creator": "Castle Films"
  },
  "license": {
    "licenseurl": "http://creativecommons.org/licenses/publicdomain/",
    "license_label": "Public Domain (CC PD dedication)",
    "commercial_use": "allowed",
    "attribution_required": "no",
    "determined_from": "licenseurl"
  },
  "segment": { "start_s": 300.0, "duration_s": 8.0, "verified_duration_s": 7.975 },
  "retrieval": { "retrieved_at": "2026-10-23T12:28:30Z", "data_node": "dn600301.us.archive.org", "user_agent": "broll-mcp/0.1.0 (…)" },
  "processing": { "ffmpeg": "8.1.1", "audio": false, "reencoded": true },
  "attribution_text": "\"About Bananas\" (1935), Prelinger Archives via Internet Archive, https://archive.org/details/AboutBan1935. License: Public Domain (CC PD dedication)."
}

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.

Configuration

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.

How it works

 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.

Development

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

See CONTRIBUTING.md.

A note on rights

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.

License

MIT for the code. Footage belongs to its respective rights holders under the terms recorded in each clip's provenance file.

About

MCP server to find, preview and cut public-domain b-roll from the Internet Archive, with a provenance record for every clip

Topics

Resources

Contributing

Stars

51 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages