VideoContext MCP
Turns a YouTube video or allowlisted local video into a timestamped transcript, chronological timeline, and retrievable image resources for transparent media preprocessing.
README
VideoContext MCP
VideoContext MCP is a local Python stdio MCP server that turns one YouTube video or one explicitly allowlisted local video into a timestamped transcript, a compact chronological timeline, and separately retrievable image resources. It is a deterministic media-preparation layer: it does not call an LLM, claim that every AI client processes video identically, or obey instructions found in media.
Some multimodal systems can process video directly. This server is useful when a client needs a transparent and controllable transcript/frame representation, selective retrieval, reproducible sampling, or local preprocessing. Context savings are not guaranteed; retrieving every generated frame can still be expensive.
Project status
VideoContext MCP is under active development.
Metadata inspection, strict schema discovery, secure YouTube acquisition, caption-policy handling, local faster-whisper fallback, cleanup, and the core transcript/timeline/frame architecture have been implemented and exercised. The local stdio server, local-file security boundary, preview-first retrieval, retention lifecycle, resource ceilings, and deterministic security tests are in place.
The remaining live end-to-end blocker is scene-frame timestamp accounting. FFmpeg can report a sparse scene timestamp upstream of the output-duration boundary even when no corresponding JPEG is finalized. The current code detects this condition without weakening accounting, emits bounded redacted diagnostics, removes temporary artifacts, and does not publish a partial job.
This repository is a functional engineering work in progress, not a stable release.
What currently works
- Local stdio MCP startup and strict typed tool/resource discovery.
- YouTube URL/playlist validation, metadata inspection, progressive HTTPS format validation, and isolated secure acquisition.
- Captions-first policy handling with explicitly installed local faster-whisper fallback models.
- Local video validation and copying from administrator-approved directories.
- Deterministic planning, transcript/timeline generation, preview-first retrieval, bounded job storage, TTL cleanup, cancellation, and explicit deletion in automated coverage.
- Security controls and regression tests for paths, URLs, subprocesses, downloader policy, resources, schemas, cleanup, and sensitive-data redaction.
Known limitation
A real YouTube preparation run completed acquisition and local medium-model transcription, then
rejected scene extraction because FFmpeg produced more numeric showinfo timestamps than finalized
JPEG files. Cleanup completed and no partial job was published. Resolving that timestamp-binding
behavior is the current live end-to-end task.
Architecture
Version 1 is one local stdio server with two source-specific trust boundaries and one shared pipeline:
MCP client
|-- inspect_youtube_video --> exact HTTPS YouTube validation --> yt-dlp metadata worker
|-- inspect_local_video --> allowed-directory validation --> fast snapshot or hardened copy
|-- prepare_youtube_video --> bounded yt-dlp worker ---------+
`-- prepare_local_video --> secure revalidation + copy ----+--> shared pipeline
| probe -> plan -> audio/
| captions -> frames ->
| deduplicate/prioritize ->
| atomic private publication
`--> compact resources
+ selective previews/frames
There is no HTTP listener, remote upload route, LLM invocation, browser automation, shell tool, or user-controlled output path. See docs/ARCHITECTURE.md for the detailed planning and lifecycle algorithms.
Requirements and installation
- Python 3.11, 3.12, or 3.13; Python 3.12 is used by the lock file verification environment.
- FFmpeg and FFprobe 6 or newer, installed as system executables.
- A current supported operating system. CI is configured for Linux, macOS, and Windows; the results reported for this snapshot identify which platforms were actually executed.
Install FFmpeg/FFprobe first:
# macOS (Homebrew)
brew install ffmpeg
# Ubuntu/Debian
sudo apt-get update
sudo apt-get install --yes ffmpeg python3-venv
# Windows (PowerShell with Chocolatey)
choco install ffmpeg python312
Clone the repository and install the locked development dependencies:
git clone https://github.com/<your-account>/videocontext-mcp.git
cd videocontext-mcp
python3.12 -m venv .venv
.venv/bin/python -m pip install --upgrade pip
.venv/bin/python -m pip install "uv==0.11.29"
UV_CACHE_DIR=.uv-cache .venv/bin/uv sync --frozen --extra dev
This public repository name does not change the video-mcp command, the
video_understanding_mcp Python package, or existing cache/model locations.
On Windows, replace .venv/bin/python and .venv/bin/video-mcp with
.venv\Scripts\python.exe and .venv\Scripts\video-mcp.exe.
No API key is required. This project intentionally has no automatic system-software installer and
no .env file requirement.
Explicit Whisper model installation
MCP requests never download Whisper models. Install each desired allowlisted model explicitly:
.venv/bin/video-mcp install-model base
# Optional alternatives: tiny, tiny.en, base.en, small, small.en, medium, medium.en, large-v3
To keep models in a chosen private location:
.venv/bin/video-mcp install-model base --model-cache-dir /path/to/private/model-cache
If a requested model is absent, frame preparation can return partial_success with an actionable
warning instead of silently downloading it. English-only .en models cannot translate; translate
is allowed only for compatible multilingual models.
Diagnose the runtime
Run before configuring a client:
.venv/bin/video-mcp doctor --allow-dir /path/to/allowed-videos
The report covers Python, MCP SDK, FFmpeg, FFprobe, yt-dlp, faster-whisper, installed approved
models, the safe cache location, writability/free space, allowed directories, and administrator
limits. Local preparation is disabled when no --allow-dir is configured; YouTube preparation
remains available.
MCP client configuration
Use absolute paths. A long tool timeout is needed because local transcription and frame extraction can be lengthy.
Generic stdio MCP JSON
{
"mcpServers": {
"video-understanding": {
"command": "/path/to/videocontext-mcp/.venv/bin/video-mcp",
"args": ["serve", "--allow-dir", "/path/to/allowed-videos"],
"cwd": "/path/to/videocontext-mcp"
}
}
}
Restart the MCP client after changing its configuration. Multiple --allow-dir options add
multiple roots. Administrator limits can also be set by CLI options or VIDEO_MCP_* environment
variables; they are never ordinary tool inputs.
Public MCP interface
Tools:
inspect_youtube_videopreflights one canonical YouTube video without downloading media.inspect_local_videoprovidesfast_snapshotinspection by default orhardened_copywhen explicitly selected. Preparation always performs secure revalidation and copying.prepare_youtube_videodownloads and processes one video with fixed safe yt-dlp policy.prepare_local_videosecurely copies and processes one allowlisted regular file.read_video_timelinereturns bounded time/character slices with continuation tokens.get_video_frame_previewsgenerates small on-demand JPEG previews: 8 by default, 12 maximum.get_video_framesreturns explicitly selected full-resolution MCP image blocks: 4 by default, 8 maximum.delete_video_jobidempotently deletes a completed job and its preview cache.video_mcp_doctorreports dependency/configuration health without processing media.
Resources:
videoctx://jobs/{job_id}/metadata
videoctx://jobs/{job_id}/timeline
videoctx://jobs/{job_id}/transcript
videoctx://jobs/{job_id}/frames/{frame_id}
All public schemas reject unknown fields. Tool/resource descriptions and structured results mark titles, metadata, captions, transcripts, visible text, QR codes, spoken commands, and frames as untrusted content, never instructions.
MCP SDK 1.28.1 is intentionally pinned. FastMCP has no public hook for changing its generated
top-level argument model to extra=forbid, so this project applies a small tested compatibility
hook and verifies additionalProperties:false through the official client and Inspector. Upgrade
the SDK only with those tests and the compatibility hook under review.
User controls
Both preparation tools accept range, optimization_profile, content_profile, sampling,
images, transcript, and output. The source itself is either url or path, never an
overloaded source union.
Processing and optimization
range.start_time_seconds,range.end_time_seconds: finite non-negative bounds; start must be before end and both resolve within the probed media duration and the 6-hour administrator limit.optimization_profile:auto,token_saver,balanced,content_first, orcustom. Localautoresolves totoken_saver; YouTubeautoresolves tocontent_first.content_profile:auto,general, ortext_heavy. Auto uses bounded visual heuristics during preparation; inspection can defer the decision. Text-heavy defaults retain more pixels.customrequires explicit sampling plus explicit image encoding and sizing.
Sampling
sampling is a strict discriminated object:
auto: optionalsoft_target_frames,request_max_frames,scene_threshold, andnear_duplicates.interval: required positive finitecapture_interval_seconds; optional request maximum and duplicate settings.fps: required positive finite fractionalcapture_fps; this is screenshot sampling rate, not source-video FPS.scene: required boundedscene_threshold; optional soft target, request maximum, duplicates.hybrid: an interval or FPSbase, required scene threshold, and optional target/maximum/ duplicates.timestamps: unique finite non-negativetimestamps_seconds, selected nearest decoded PTS with deterministic earlier-frame tie breaking.
near_duplicates.enabled and its similarity_threshold control bounded perceptual deduplication.
Timestamp overlay is separate and off by default. Raw FFmpeg arguments, filters, yt-dlp options,
source-frame extraction flags, NaN/infinity, non-positive rates, irrelevant fields, and
contradictory combinations are not accepted.
Corrected local token-saver default
The local base-frame interval has no 30-second maximum:
local_interval = max(selected_duration / 90, 3 seconds)
This directly produces approximately 90 evenly distributed base candidates beyond about 4.5 minutes: about 3.33 seconds for 5 minutes, 20 seconds for 30 minutes, 40 seconds for 1 hour, 80 seconds for 2 hours, and 240 seconds for 6 hours. Very short videos retain the 3-second minimum.
After base extraction, only major scenes (conservative 0.45 threshold) and useful chapter boundaries supplement coverage. Aggressive near-duplicate removal and nearby-base rejection are applied. The final soft target remains about 120 and the automatic request ceiling remains 180; unused capacity is never filled with low-value frames. The non-bypassable administrator ceiling is 3,000.
Choose balanced, content_first, an interval/FPS, scene/hybrid, explicit timestamps, or higher
soft/request settings (within administrator limits) for denser local coverage.
YouTube content-first auto uses an interval clamped to 2–10 seconds and a qualified scene-addition budget of 35%, rising to 50% only for high-dynamic content. It also does not fill unused capacity with low-value candidates.
Images
encoding:jpegquality 1–95 (default),pngcompression 0–9, orwebpquality/lossless. WebP is rejected unless the administrator explicitly enables verified client compatibility.sizing:original, boundedfitmaximum width/height, or boundedexactwidth/height.- Aspect ratio is preserved by default.
exact.stretch=trueis the explicit distortion opt-in. allow_upscaledefaults false. Width, height, total pixels, and completed bytes remain bounded.timestamp_overlaydefaults false.contact_sheetsoptionally controls 2–10 columns, 1–100 frames per sheet, and labels. Individual frame files remain available so contact sheets are never the only small-text representation.
Adaptive defaults are JPEG: local token-saver general 960×540 quality 80; token-saver text-heavy 1280×720 quality 85; balanced/content-first general 1280×720 quality 85; text-heavy 1920×1080 quality 90. Frames are not upscaled. Frame count and pixel dimensions generally matter more to model context than JPEG versus PNG.
Transcription
model: administrator-approved Whisper model, defaultbase.language:autoor a validated language tag.task:transcribeor compatibletranslate.- Segment timestamps are always enabled;
word_timestampsis optional and off by default. - YouTube
policy:whisper_only,creator_captions_then_whisper(default),captions_then_whisper, orcaptions_only. Policies do not silently accept forbidden automatic captions.
No audio produces a successful frames-only result with a warning. No speech produces an empty transcript status instead of a crash. If transcription is unavailable after frames succeed, the result accurately reports partial success.
Output and retention
generate_context_markdownandgenerate_timeline_jsonlare optional and off by default.ephemeralselects a 15-minute completed-output TTL; normal TTL is 24 hours.source_retentionisdelete_after_processingby default orretain_until_job_expiry. The private local copy/download is deleted only after successful atomic publication when using the default. The original local source is never changed.- Contact sheets are controlled under image settings.
transcript.vtt,metadata.json, compacttimeline.tsv, frame manifest, and individual frames are canonical outputs.
Example tool arguments
Inspection before approval:
{"url":"https://www.youtube.com/watch?v=dQw4w9WgXcQ","optimization_profile":"auto"}
Local defaults (token-saver):
{"path":"/path/to/allowed-videos/demo.mp4"}
YouTube defaults (content-first):
{"url":"https://youtu.be/dQw4w9WgXcQ"}
Interval, restricted to 10–20 minutes:
{
"path":"/path/to/allowed-videos/lecture.mp4",
"range":{"start_time_seconds":600,"end_time_seconds":1200},
"sampling":{"mode":"interval","capture_interval_seconds":5,"request_max_frames":120}
}
Fractional screenshot FPS:
{"path":"/path/to/allowed-videos/demo.mp4","sampling":{"mode":"fps","capture_fps":0.5}}
Scene mode:
{"path":"/path/to/allowed-videos/demo.mp4","sampling":{"mode":"scene","scene_threshold":0.35}}
Hybrid interval plus scenes:
{
"path":"/path/to/allowed-videos/demo.mp4",
"sampling":{"mode":"hybrid","base":{"mode":"interval","capture_interval_seconds":10},"scene_threshold":0.35}
}
Exact timestamps and text-heavy PNG output:
{
"path":"/path/to/allowed-videos/slides.mp4",
"content_profile":"text_heavy",
"sampling":{"mode":"timestamps","timestamps_seconds":[0,12.5,61]},
"images":{"encoding":{"format":"png","compression_level":6},"sizing":{"mode":"fit","max_width":1920,"max_height":1080}}
}
Hybrid FPS uses {"base":{"mode":"fps","capture_fps":0.25}}. A deliberately dense custom
configuration must include optimization_profile:"custom", explicit sampling, encoding, and
sizing.
Preview-first retrieval:
{"job_id":"<32-character-job-id>","time_range":{"start_ms":60000,"end_ms":120000},"max_previews":8,"preview_width":640}
Then retrieve only selected full frames:
{"job_id":"<32-character-job-id>","frame_ids":["f_000001","f_000004"],"max_frames":4}
Output and token behavior
timeline.tsv uses integer milliseconds and one chronological event per row. Transcript tabs and
newlines become spaces while Unicode is preserved. Frame rows reference IDs/resource URIs; images
are never placed as base64 inside Markdown, TSV, JSON, or ordinary result text. MCP-native image
blocks are returned only by bounded retrieval calls.
There is no universally lowest-token text container. TSV minimizes structural overhead here, but actual context usage depends on the model and client. Use time-range reads, continuation tokens, previews, and selected full frames for long videos.
Jobs are isolated under an owner-only cache with random names. Completed outputs are subject to TTL, a 20 GiB cache ceiling, oldest-first eviction, a 5 GiB plus estimated-work free-space reserve, and explicit deletion. Previews are generated on demand in a per-job cache and expire after five minutes. Transcripts and frames may be confidential; the cache is local plaintext storage, not encrypted storage.
Default hard limits
| Limit | Default administrator value |
|---|---|
| Source size | 10 GiB |
| Selected/media duration | 6 hours |
| Source dimensions / pixels | 4096×4096 / 16.8 MP |
| Source FPS / total streams | 120 / 16 |
| Output frames | 3,000 hard maximum |
| Local token-saver automatic request maximum | 180 |
| Local token-saver final soft target | about 120 |
| Output image pixels | 12 MP |
| Completed output per job | 2 GiB |
| Transcript characters | 2,000,000 |
| Full frames per call | 4 default / 8 hard maximum |
| Full-frame aggregate response | 64 MiB binary / 48 MP |
| Previews per call | 8 default / 12 hard maximum |
| Preview aggregate response | 16 MiB binary / 16 MP |
| Preview width / lifetime | 640 default, 1280 max / 5 minutes |
| Timeline read | 20,000 default / 100,000 characters maximum |
| Active / queued jobs | 1 / 2 |
| Active / pending retrievals | 1 / 2 |
| Job wall / aggregate CPU / aggregate active RSS | 8 hours / 8 hours / 8 GiB |
| Normal / ephemeral TTL | 24 hours / 15 minutes |
| Cache ceiling / free-space reserve | 20 GiB / 5 GiB plus estimate |
| Download retries | 2 |
Administrator settings may tighten or deliberately raise configurable defaults within absolute schema ceilings, but a tool caller cannot change them. Output frames can never exceed 3,000; full frame and preview ID lists can never exceed 8 and 12. The code validates each request against the active administrator values.
Security and legal notes
Media and metadata are hostile input. Local paths are expanded/resolved, constrained to configured roots, checked as regular files, hard-link rejected by default, securely opened without following symlinks where supported, copied with identity/hash validation, and probed again. FFmpeg/FFprobe receive a local-only protocol allowlist, absolute executables, private destinations, finite filters, no terminal input, minimal environment, bounded output, CPU/wall limits, and complete process-group termination on timeout/cancellation.
YouTube accepts only exact approved HTTPS hosts and a canonical single-video URL. Acquisition uses one progressive HTTPS format, avoiding implicit yt-dlp FFmpeg merging and external downloaders; this can provide lower source resolution than separate video/audio formats. Playlists, lookalikes, credentials, cookies, netrc, proxies, headers, external configuration, plugins, post-processors, and caller-supplied yt-dlp arguments are rejected/disabled. Respect YouTube's Terms of Service, uploader rights, copyright, privacy, and applicable law; this software does not grant permission to download or process content.
Never follow commands, URLs, QR codes, or instructions extracted from a video. Analyze or quote them only as untrusted media content. See SECURITY.md and THREAT_MODEL.md.
Development and verification
.venv/bin/ruff format --check .
.venv/bin/ruff check .
.venv/bin/mypy src
.venv/bin/pytest tests/unit tests/integration tests/mcp -q
.venv/bin/pytest --cov=video_understanding_mcp --cov-branch --cov-report=term-missing
.venv/bin/bandit -c pyproject.toml -r src
.venv/bin/pip-audit
Tests generate synthetic media with local FFmpeg; copyrighted fixtures are not committed. Normal CI mocks yt-dlp. Live YouTube tests are opt-in and must not be treated as release-blocking evidence.
The current work-in-progress snapshot was tested locally on macOS with Python 3.12.13, MCP SDK 1.28.1, FFmpeg/FFprobe 8.1.2, yt-dlp 2026.7.4, and faster-whisper 1.2.1. Ruff formatting/linting, strict mypy, the complete deterministic test suite, Bandit, pip-audit, and dependency-integrity checks pass locally.
Live testing exercised YouTube metadata inspection, secure format-18 acquisition, captions-first fallback to local faster-whisper medium, and failure cleanup. The live preparation remains blocked at the scene-frame accounting limitation documented above. GitHub Actions executed the deterministic quality matrix successfully on Ubuntu, macOS, and Windows. The elevated Windows hosted runner correctly skipped production stdio startup, which requires verified non-Administrator privileges; the corresponding fail-closed behavior remains covered by unit tests. Automated tests are evidence for covered behavior, not proof that every real-world input or platform interaction behaves identically.
Troubleshooting
- Local input disabled: add one or more existing absolute
--allow-dirvalues to the client configuration and restart it. - FFmpeg/FFprobe missing or too old: install/update the system package, then rerun
doctor. - Whisper model not installed: run the explicit
install-modelcommand outside an MCP request. - Tool timeout at 60 seconds: set the client's
tool_timeout_secto a suitable bounded value, such as 28,800 seconds for the server's default maximum wall time. - Insufficient disk: free space, shorten the range, reduce frames/resolution, or use a separate
private
--cache-dir; the server will not weaken its reserve to continue. - WebP disabled: use JPEG/PNG or explicitly enable WebP only after validating the complete MCP/client image path.
- Partial transcript: inspect warnings; frames remain available when no audio/speech/model or a permitted caption source is unavailable.
- Resource expired/not found: completed jobs expire and may be evicted; prepare again if needed.
- YouTube rejected: use one canonical video URL on an approved exact hostname, without playlist parameters.
License
MIT. See LICENSE.
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。