llm-video-mcp
by shkyyy18
README.md
# llm-video-mcp
[](https://github.com/shkyyy18/llm-video-mcp/actions/workflows/ci.yml)
[](LICENSE)
**Let any coding agent (Claude Code / Cursor / Kimi Code …) actually *watch* a video — within a token budget.** An MCP server that downloads or ingests a video, extracts scene-aware frames with ffmpeg, transcribes speech with faster-whisper (VAD-gated), builds a shot timeline, and reverse-derives all extraction parameters from your token budget.
*A video link tells your agent nothing, and piping raw frames eats the context window in seconds. Give the agent a token budget instead — llm-video-mcp derives everything else.*
## Install & connect (30 seconds)
Prerequisites: [ffmpeg](https://ffmpeg.org/) on PATH (`winget install ffmpeg` / `brew install ffmpeg` / `sudo apt install ffmpeg`), Python ≥ 3.10.
Add to your MCP host config (e.g. Claude Code / Claude Desktop):
```json
{
"mcpServers": {
"llm-video-mcp": {
"command": "uvx",
"args": ["llm-video-mcp"]
}
}
}
```
Or from a clone: `pip install .` then `llm-video-mcp` (stdio). Then just tell your agent: *"看看这个视频第 2 分钟讲了什么"* — it calls the tools itself.
## What the agent gets (real output)
`analyze_video(path_or_url, token_budget=8000)` on a real 60 s clip (腾讯视频, game-commentary video, faster-whisper `tiny`, zh) — unedited excerpts:
```json
{
"plan": {"token_budget": 8000, "max_frames": 28, "frame_width": 768,
"grid": 4, "scene_threshold": 0.3, "estimated_tokens": 8000},
"frame_count": 7,
"transcript": {"source": "faster-whisper:tiny", "language": "zh",
"text": "…最後是中教材 就打到了…拿下了 Skog 的一個異血…"},
"timeline": {"shot_count": 28, "cuts_per_min": 27.0,
"shots": [{"start_sec": 0.0, "end_sec": 2.0, "motion": "static"},
{"start_sec": 2.0, "end_sec": 2.76, "motion": "static"}, …]}
}
```
Same pipeline on a 10 s synthetic test video at `token_budget=8000`: frames are packed into 2×2 contact sheets (`grid: 4`) and the estimate stays exactly within budget (`estimated_tokens: 8000 ≤ 8000`). Artifacts land in `~/.cache/llm-video-mcp/<content-hash>/`: `frames/*.jpg`, `frames.json`, `transcript.json`, `timeline.json`.
Performance (measured 2026-07-20, CPU only — Intel Core Ultra X7 358H): a 5 min video at `token_budget=8000` analyzes end-to-end in **41 s** (whisper `tiny`) / **63 s** (whisper `base`, model already cached) — scene frames + VAD-gated transcript + shot timeline, `estimated_tokens: 7918 ≤ 8000`.
## Before / after
| Without llm-video-mcp | With llm-video-mcp |
|---|---|
| You paste a video link into the chat. The agent sees a URL string and a title — and summarizes from guesswork. | `analyze_video(path_or_url, token_budget=8000)` downloads the video and returns scene-aware frames, a transcript, and a shot timeline shaped to the budget. |
| You hand-tune `--max-frames 20` and either blow the context window or miss the scene that mattered. | `token_budget` reverse-derives frame count, resolution, contact-sheet packing, and transcript length. `estimated_tokens ≤ token_budget`, unit-tested and monotonic. |
| The video has no speech; the transcriber hallucinates a voiceover anyway. | Whisper is VAD-gated: silent videos honestly report `no_speech` (regression-tested). Subtitle tracks are preferred when present, no model download needed. |
| You want a closer look at one moment, so you re-run the whole extraction. | `get_frames_at(path, timestamp)` pulls frames around that moment only; full analyses are content-hash cached, so repeat calls return instantly. |
## When not to use this tool
- **Sub-second action precision** — frame extraction (`extractor.py:14`) uses ffmpeg scene detection with a 1 frame/sec density floor; a 100 ms moment that is not a scene cut may not be captured. Use `get_frames_at` to target a specific timestamp, or reach for a frame‑accurate tool if every millisecond matters.
- **Real‑time / live streams** — the pipeline downloads or reads a complete file (`yt‑dlp` / `ffmpeg`). It is not a streaming analyzer. Feed it a finished video, not a live feed.
- **Very long videos on CPU** — faster‑whisper transcription (`transcriber.py:81`) is the bottleneck. At the measured throughput (5 min → 41 s @ tiny / 63 s @ base, CPU — see performance note above), a 2‑hour lecture means a multi‑minute wait. Use `get_transcript` for text‑only needs and skip frame extraction, or expect a long first analysis.
- **Frame‑dense visual detail** — the budget controller (`budget.py:85`) deliberately reduces frame count, resolution, and packs frames into contact sheets to fit `token_budget`. Fast‑scrolling code or slides changing every second lose detail. If every frame is critical, a token‑budgeted sampler is the wrong shape.
- **Platform availability drift** — yt‑dlp support for specific sites changes upstream. The supported‑sources table (dated 2026‑07‑20) is a point‑in‑time snapshot; login‑walled platforms other than 抖音/西瓜 (cookie‑bootstrapped) are out of scope.
## The three tools
| Tool | Purpose |
|---|---|
| `analyze_video(path_or_url, token_budget, model?, lang?)` | Full analysis: scene-aware frames + transcript + timeline, shaped to fit `token_budget`. Cached by content hash — repeat calls return instantly. |
| `get_frames_at(path_or_url, timestamp, window?, count?)` | On-demand frames around one moment — incremental viewing without re-analyzing. |
| `get_transcript(path_or_url, start?, end?)` | Text only. Prefers embedded/sidecar subtitles; whisper fallback is VAD-gated so silent videos honestly report `no_speech` instead of hallucinating. |
## Token budget controller (the core differentiator)
You give it a budget; it reverse-derives everything:
```
token_budget ──┬─► max_frames (frame count cap)
├─► frame_width (768 → 512 → 384 → 256)
├─► grid (none → 2×2 → 3×3 contact sheets)
├─► scene_threshold (fewer, more distinct frames when tight)
└─► transcript_max_chars (truncation)
```
Guarantees (unit-tested): `estimated_tokens ≤ token_budget`, and the estimate strictly decreases as the budget tightens. Built-in frame-token estimates for `claude` / `gpt` / `gemini` / `kimi` (`model=` param), overridable with a custom pricing config.
## Supported video sources
Verified end-to-end on 2026-07-20 (yt-dlp 2026.07.04, real URLs, mainland-CN network):
- **Bilibili** — works, no login needed
- **腾讯视频 (v.qq.com)** — works, no login needed
- **优酷 (youku.com)** — works, no login needed
- **微博视频 (weibo.com)** — works, no login needed
- **YouTube** — works **with a proxy** (`export https_proxy=… http_proxy=…`)
- **抖音 / 西瓜视频** — work **after running `llm-video-mcp bootstrap-cookies` once** (see below)
其他平台:请将视频下载为本地文件后使用本地路径输入。Local files are always a first-class input — any container/codec ffmpeg can read.
### `bootstrap-cookies`: fresh cookies without touching your browser
ByteDance platforms (抖音/西瓜) require fresh (non-login) cookies. One command handles it:
```bash
llm-video-mcp bootstrap-cookies
```
It launches a **separate Chrome instance** with its own throwaway `--user-data-dir`, keeps a douyin.com page open for ~90 s to accumulate fresh cookies, closes **only that instance** gracefully (your browser is never touched — graceful close also flushes the cookie DB to disk), and exports `~/.cache/llm-video-mcp/cookies.txt`. Pass it to the tools via `cookies=...`; douyin's `__ac_signature` cookie is bound to the browser UA, so the tools automatically replay the cookies with a User-Agent matching your local Chrome. Verified working 2026-07-20 on both douyin short links and long links.
## vs. claude-real-video
Inspired by [claude-real-video](https://github.com/HUANGCHIHHUNGLeo/claude-real-video) (MIT) — thanks to the author for proving the demand. We re-implemented everything from scratch; differences:
| | claude-real-video | llm-video-mcp |
|---|---|---|
| Agent integration | skill + folder of artifacts | **native MCP server** (3 structured tools) |
| Token economics | manual `--max-frames` guesswork | **`token_budget` → parameters derived automatically** |
| Timeline (shots / pan-zoom / cuts-per-min) | Pro paywall ($19+) | **free, open source** |
| On-demand re-viewing | re-run CLI | `get_frames_at` incremental queries |
## Development
```bash
pip install -e .[dev]
pytest tests -q # synthetic test videos are generated by ffmpeg on the fly
ruff check src tests
```
## Traction so far
Honest numbers, because this project is young:
- **v0.1.0 released 2026-07-20** — the full pipeline (scene frames, VAD-gated transcript, timeline, budget controller) works end-to-end today, verified on real Bilibili / Tencent Video / Youku / Weibo / YouTube / Douyin URLs.
- **The maintainer is still the only contributor. Zero stars, zero external PRs so far.** You would genuinely be among the first.
- GitHub traffic (14 days ending 2026-07-25): **29 unique cloners**, 3 unique visitors — people are cloning it, but nobody has crossed the gap from "cloned" to "contributed" yet.
- The contribution queue is ready: browse the [open good-first-issue list](https://github.com/shkyyy18/llm-video-mcp/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) — each issue is small, has hard acceptance criteria, and is testable offline.
The sibling project [AgentCron](https://github.com/shkyyy18/cc-autopilot) used the same good-first-issue design and received its first 3 external PRs this way; the [first-contribution case study](https://github.com/shkyyy18/cc-autopilot/blob/main/docs/first-contribution-case-study.md) documents what made those tasks approachable.
## Support the project
If llm-video-mcp saved your agent from a hallucinated video summary — or you just want more coding agents that can actually watch things — a star on [GitHub](https://github.com/shkyyy18/llm-video-mcp) helps the next person find it.
Only download videos you have the right to download. MIT licensed.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessSyncing