Agentic Video

io.github.tlq5lPublished by GitHub account tlq5lv0.3.4Updated Nov 1, 2026

Local video tools for AI agents: contact sheets, transcripts, cuts, loudness and sync checks.

The publisher's own server: agentic.no

VerifiedSTDIODesktop onlyData & AnalyticsMedia & Design

Overview

AI-generated overview

Local stdio MCP server that lets a text-and-image assistant inspect video files: contact sheets, transcripts, cuts, loudness and sync checks.

What you can do
  • Survey a video: metadata, contact sheets with burned timestamps, chunked overviews, dense frame windows and single full-size frames
  • Read and search speech: timestamped transcripts, keyword or semantic search for when a phrase is spoken
  • Find and count: scene cuts and transitions, frame-cadence repeats, separate appearances of an object or on-screen text, repeated action cycles, changes inside a fixed region
  • Analyze audio: onsets, loudness timeline, spectrogram images, and audio/video sync checks against cuts and cues
  • Optional cloud tools: yes/no visual condition watching and sound labelling via Cloudflare Workers AI
When to use it
Useful when an assistant must answer questions about local video or audio files it cannot watch itself: what happens in a clip, when a word is said, how many times something appears, or whether audio and picture are in sync. Also handy for checking rendered or exported videos for sync, loudness and cadence faults.
Requirements
Runs locally as a Python package (PyPI agentic-video, started via uvx or a cloned repo with Python 3.12) over stdio; needs FFmpeg and Git. Optional visual search needs PyTorch and about 605 MB of CLIP weights; optional cloud tools need CLOUDFLARE_ACCOUNT_ID and CLOUDFLARE_API_TOKEN (or AGENTIC_VIDEO_CF_TOKEN_COMMAND). macOS-only features include on-screen text matching via Apple Vision.
Before you install
The optional watch_for and label_sounds tools send video frames or audio to Cloudflare Workers AI; do not use them on private recordings. A maintainer-only trial switch, AGENTIC_VIDEO_SHADOW_CF_ASR, sends audio to Cloudflare after transcription when set to the machine's hostname. Results are cached under a local cache directory, and the project is experimental with no stable API; sampling can miss brief events and transcripts can invent lines.

Installation

In SourceWeft

  1. Open Agentic Video in the dashboard and add it to a workspace.
  2. Enable the server for the chats that should use its tools.

Desktop only via STDIO. STDIO servers start a local process, so they need the SourceWeft desktop host.

Other MCP clients

Follow the launch instructions in the repository.

README

agentic-video

agentic-video lets an AI agent that reads text and images, but not video, work with video files on your own machine. It is a Python library plus a stdio MCP server. The agent asks small, targeted questions ("show me 12 frames from 0:40 to 1:10", "when is 'launch' spoken?") and gets back measurements, transcripts and contact sheets: grids of frames, each with its timestamp burned into the pixels. Everything runs locally with FFmpeg, Whisper and optional local models, except two optional cloud tools (watch_for, label_sounds), which send frames or audio to Cloudflare Workers AI only when you set credentials and call them. An opt-in shadow transcription trial (off by default, owner machines only) also sends audio to Cloudflare when its switch and credentials are set.

See it work: https://agentic-video-gallery.infra-1b7.workers.dev (the gallery: real tasks, including the misses).

Try it in 2 minutes

Pick one route and follow its steps.

  • Claude Code: brew install ffmpeg uv, then claude mcp add -s user agentic-video -- uvx agentic-video.
  • Claude Desktop: download the .mcpb from the latest release and double-click it (macOS; run brew install ffmpeg first).
  • Claude Code plugin: brew install ffmpeg uv first (the plugin's server runs uvx), then claude plugin marketplace add tlq5l/agentic-video, then claude plugin install agentic-video@agentic-video (docs/claude-code-plugin.md).
  • Other clients: Run the MCP server has the JSON block.

Then paste this first prompt: "Use agentic-video: make a contact sheet of /path/to/video.mp4 and list its cuts"

Install: brew install ffmpeg on macOS, or sudo apt-get install -y ffmpeg on Debian or Ubuntu (see Install for the uv line). Then add it to Claude Code with claude mcp add -s user agentic-video -- uvx agentic-video (or pipx install agentic-video). Other clients: Run the MCP server.

Claude Desktop (one click): download agentic-video-<version>.mcpb from the latest GitHub release and double-click it. It still needs brew install ffmpeg. The first start takes about a minute while Claude Desktop installs the Python packages. macOS only. The bundle does not include local visual search or on-screen text counting; for those, use the git install in the "Optional: visual search (CLIP) and on-screen text" section below. Details: docs/features/desktop-bundle.md.

[A contact sheet made by agentic-video from a 5-second clip: six frames, each with its timestamp burned in]

One contact_sheet call on a 5-second clip: six frames, each stamped with its time, as a single image the agent can read.

In 2 minutes (macOS) your agent can answer "what happens in this video?" and "when is X said?" for a local video file. You get the length and size, a contact sheet image with timestamps, the cuts, a loudness summary and searchable speech. Nothing leaves your machine unless you use the optional cloud tools below or set the shadow transcription trial switch (off by default, owner machines only). Setup for Claude Code:

sh
brew install ffmpeg uvgit clone https://github.com/tlq5l/agentic-video.git && cd agentic-videouv venv --python 3.12 .venv && uv pip install --python .venv/bin/python -r requirements.txtclaude mcp add -s user agentic-video -e PYTHONPATH="$PWD" -- "$PWD/.venv/bin/python" -m src.executor.mcp_server

On Debian or Ubuntu, run sudo apt-get install -y ffmpeg git and curl -LsSf https://astral.sh/uv/install.sh | sh in place of the first line, then open a new shell so uv is on your PATH.

Then ask: "Use agentic-video to review /path/to/video.mp4." Other clients and details: Run the MCP server.

Status: experimental. The author uses it every day; support is best-effort. Issues and pull requests are welcome, but expect slow replies.

What it gives an agent

The MCP server exposes 25 tools (full schemas in src/adapters/mcp_schemas.json):

  • Look: review_video (first look in one call: metadata, contact sheet, cuts, near-black stretches and a loudness summary), probe_video (duration, fps, size, codecs, audio), contact_sheet, overview_chunks (consecutive sheets across a long video), inspect_window / inspect_windows (dense frames for short ranges; read_text on inspect_window adds each frame's on-screen text), frame_at (one exact frame at full size, optionally cropped and enlarged to read small text; read_text: true also returns the on-screen text it reads, Apple Vision on macOS), inspect_moments (frames around given instants), refine_temporal_beam (zoom into candidate windows).
  • Listen: transcribe (timestamped speech, cached; a video with no cached transcript starts a background job, so the first call returns status: in_progress and retry_after_seconds: call the same tool again after that many seconds), search_transcript (find when words are spoken: exact words first, then passages that match by meaning when the optional on-device EmbeddingGemma 2 model is set up; keyword-only without it).
  • Find: detect_cuts (hard cuts and animated scene changes), check_frame_cadence (stretches where frames repeat so the picture updates less often than the frame rate, such as 30 fps padded to 60), build_visual_index + search_visual (local CLIP search for things that are visible, optional; the interval holding the top hit grows from its best frames only over neighbouring frames that score close to the best, up to 8 seconds, so it can be shorter; a span already longer than 8 seconds, such as a held frame or hits merged together, is kept whole, so it can be longer and can include lower-scoring hits). watch_for (cloud: wakes when yes/no questions about the picture start to hold, from sampled frames).
  • Count and track: count_occurrences (separate appearances of an object or on-screen text; direction counts those moving one way across the frame, plus spans too brief to show motion), count_repetitions (cycles of a repeated action), track_region_changes / track_region_events (changes inside one fixed rectangle).
  • Audio and sync: audio_onsets (merges hits closer than min_gap_sec, default 0.035 s), loudness_timeline, spectrogram_window, sync_check (cuts and sound cues against measured audio; each cue gives candidate_onsets_count; verbose: true gives each cue its full candidate_onsets list instead of the count). label_sounds (cloud: names the sounds in a stretch of audio).

Which tools need a Mac, Apple Vision, torch with the CLIP model or Cloudflare credentials, and where each one ran: docs/capabilities.md. Agents may pass inspect_moments moments as bare numbers or {"time": 12.5}, and region as [x, y, width, height]; both are read as the documented form. See docs/features/argument-shapes.md.

There is also a command-line checker for rendered videos: python -m src.loop check <video> measures sync, loudness, onsets, cuts and some picture faults, then writes report.md plus evidence images. python -m src.loop cadence <video> prints the check_frame_cadence result as JSON.

AGENTS.md is a guide for agents on which tool to use when. FEATURES.md maps each feature to its code and tests.

Install (macOS or Linux)

You need FFmpeg, Git and uv. Tested with Python 3.12 on Apple silicon (macOS) and on Debian 12 (x86_64).

macOS (Homebrew):

sh
brew install ffmpeg uvgit clone https://github.com/tlq5l/agentic-video.gitcd agentic-videouv venv --python 3.12 .venvuv pip install --python .venv/bin/python -r requirements.txt pytest jsonschema.venv/bin/python -m pytest -q   # optional: about 3 minutes

Debian or Ubuntu (apt and the uv installer; open a new shell after the uv line so uv is on your PATH):

sh
sudo apt-get install -y ffmpeg gitcurl -LsSf https://astral.sh/uv/install.sh | shgit clone https://github.com/tlq5l/agentic-video.gitcd agentic-videouv venv --python 3.12 .venvuv pip install --python .venv/bin/python -r requirements.txt pytest jsonschema.venv/bin/python -m pytest -q   # optional: about 3 minutes

The test run is optional for users.

To lint before every commit (uvx ruff check ., the core rules in ruff.toml), turn the hook on once per clone: git config core.hooksPath .githooks.

What is macOS-only:

  • On-screen text matching in count_occurrences (text=) uses Apple Vision through pyobjc.
  • The bench-v1 harness uses sandbox-exec; some tests and generators use say. Tests that need a missing tool skip themselves.

The core tools run on Linux. A fresh install of 0.3.1 on Debian 12 (x86_64, ffmpeg 5.1) passed the probe, frame and speech-to-text checks; other distributions and CPUs are untested.

Optional: faster, more accurate speech (whisper.cpp turbo)

By default transcribe uses faster-whisper on the CPU (tiny, base or small; weights are downloaded on first use). On Apple silicon, whisper.cpp with the large-v3-turbo model is much faster and more accurate:

sh
brew install whisper-cppmkdir -p ~/.cache/whisper.cppcurl -L -o ~/.cache/whisper.cpp/ggml-large-v3-turbo-q5_0.bin \  https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-large-v3-turbo-q5_0.bin

Set AGENTIC_VIDEO_WHISPER_CPP_MODEL if you keep the model somewhere else. When it is installed, turbo becomes the default model.

Optional: visual search (CLIP) and on-screen text

sh
uv pip install --python .venv/bin/python -r requirements-visual.txt.venv/bin/python -c 'from huggingface_hub import snapshot_download; snapshot_download(repo_id="openai/clip-vit-base-patch32", revision="3d74acf9a28c67741b2f4f2ea7635f0aaf6f0268", allow_patterns=["*.json", "*.txt", "pytorch_model.bin"])'

This installs PyTorch, Transformers and (on macOS) the Apple Vision bindings, and downloads about 605 MB of CLIP weights. The visual search only loads local files and never calls a paid API. Without these, search_visual and build_visual_index return an error that explains the setup.

Optional: cloud tools (watch_for, label_sounds)

watch_for and label_sounds send video frames or audio to Cloudflare Workers AI (the Clef models). They are optional: the other tools never call Cloudflare, except the off-by-default shadow transcription trial (see the trial switch below). Do not use them on recordings you need to keep private. Set these in the server's environment:

  • CLOUDFLARE_ACCOUNT_ID: your account id.
  • CLOUDFLARE_API_TOKEN: a token with Workers AI access. Or set AGENTIC_VIDEO_CF_TOKEN_COMMAND to a command whose last printed line is the token (for example, wrangler auth token); the command runs at most once every 50 minutes per process, and each detached shadow child keeps its own token cache.

Without them the two tools fail with a message naming what is missing, and nothing is sent.

Trial switch (maintainer only): AGENTIC_VIDEO_SHADOW_CF_ASR set to this machine's hostname SENDS AUDIO TO CLOUDFLARE after every fresh local transcription, to compare it with the local result in a log. The user's transcript never changes. It is off unless that variable names the machine, and it stops by itself after 14 days or 200 uses. Details: docs/features/shadow-cf-asr.md.

Optional: hybrid transcript search (macOS, EmbeddingGemma 2)

search_transcript answers with exact keyword hits first, then passages that match by meaning. The meaning part needs the EmbeddingGemma 2 Core ML model folder and an interpreter with coremltools, tokenizers and numpy. Set AGENTIC_VIDEO_EG2_DIR to the model folder and AGENTIC_VIDEO_EG2_PYTHON to that interpreter, then restart the server. Without them the tool answers with keyword hits only and a note says why. Details: docs/features/transcribe.md.

Run the MCP server

The server speaks MCP over stdio, one JSON message per line:

sh
.venv/bin/python -m src.executor.mcp_server

To add it to an MCP client (Claude Code, Claude Desktop, Cursor and others), use the venv's Python and put the repository on PYTHONPATH, so the server starts from any folder:

json
{  "mcpServers": {    "agentic-video": {      "command": "/absolute/path/to/agentic-video/.venv/bin/python",      "args": ["-m", "src.executor.mcp_server"],      "env": { "PYTHONPATH": "/absolute/path/to/agentic-video" }    }  }}

Give tools absolute video paths. Results are cached under ~/.cache/agentic-video (change it with AGENTIC_VIDEO_CACHE_DIR).

Add it to Claude Code (one command)

Run this from the cloned folder (it uses the current folder's path). -s user makes the tool available in every project, not only this folder:

sh
claude mcp add -s user agentic-video -e PYTHONPATH="$PWD" -- "$PWD/.venv/bin/python" -m src.executor.mcp_server

By default results stay small enough for Claude Code: images are saved as files and the result gives their file_path (the agent opens the file). frame_at and spectrogram_window write PNG; contact sheets and frame grids (contact_sheet, review_video, overview_chunks, inspect_window, inspect_windows, inspect_moments) write JPEG; and a long transcribe result lists the first part of the transcript plus a transcript_path file for the rest. A client that renders images from the result itself can add -e AGENTIC_VIDEO_INLINE_IMAGES=1; results then carry base64 images and can pass 1 MB. Changed: earlier versions included base64 images by default; if you relied on that, set AGENTIC_VIDEO_INLINE_IMAGES=1 to restore it.

Tell your agent to use it (paste into your AGENTS.md or CLAUDE.md)

An always-loaded note works better than hoping the agent finds the tool:

md
## Video files (agentic-video MCP)You cannot watch video. For any .mp4/.mov/.webm question, use the agentic-video MCP tools; never guess from the filename.- First look: `review_video` with an absolute path. It returns metadata, a contact sheet, cuts, near-black stretches and loudness. Open the JPEG at `file_path` to see the frames.- Spoken words: `search_transcript` (when is X said) or `transcribe`. Do this before looking at frames.- A short range: `inspect_window` (start_sec, end_sec). Counting: `count_occurrences`. Sync and audio faults: `sync_check`, `loudness_timeline`.- Sampling can miss brief events and a missing hit is not proof. Quote the burned-in timestamps. Full guide: AGENTS.md in the agentic-video repo.

Five-line example

From the repository folder: a handshake, the video's metadata, and a 6-frame contact sheet (its JPEG path is in file_path):

sh
printf '%s\n' \  '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}' \  '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"probe_video","arguments":{"source":"fixtures/reference_videos/flower.mp4"}}}' \  '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"contact_sheet","arguments":{"source":"fixtures/reference_videos/flower.mp4","samples":6,"cols":3}}}' \  | .venv/bin/python -m src.executor.mcp_server

The same from Python:

python
from src.retrieval.engine import AgenticVideoEngine
engine = AgenticVideoEngine("fixtures/reference_videos/flower.mp4")print(engine.metadata.duration_sec)                  # 5.005print(engine.overview(samples=6, cols=3).file_path)  # contact sheet JPEG

Results so far

These are the author's own small tests, not independent benchmarks. The raw runs are not part of this repository.

  • Planted faults: in promo videos with 20 deliberately planted faults, the checker (python -m src.loop check) found 14 of 20, with 2 false alarms. Gemini in agentic video mode found 4 of 20.
  • bench-v1 (benchmarks/bench_v1): on the author's small benchmark, Claude using these tools beat Gemini's agentic video mode at counting, finding moments and long-video search.
  • LVBench (public long-video benchmark): it scored about 86%. Gemini's published score is 88.6%, so it does not beat Gemini there.

It does not beat Gemini's video understanding in general.

Limits

  • Images come back as files. Image tools return file_path, not an MCP image content block; the base64 copy in structuredContent is off by default (set AGENTIC_VIDEO_INLINE_IMAGES=1 to include it). Many clients show the agent only the JSON text, so the agent has to open the image at file_path itself (for example with its file-reading tool).
  • Sampling can miss things. Contact sheets and visual search look at sampled frames; something brief between samples can be missed, and a missing hit does not prove absence. Visual search runs CLIP on the CPU and is slow on long videos.
  • Transcripts can invent lines. Whisper (especially turbo) can invent lines over music or silence. A line repeated 3 or more times in a row is flagged suspect; a single invented line is not.
  • Cuts can be missed. A hard cut between two similar pictures that scores under the 0.25 threshold can be missed.
  • Counting and sync use heuristics tuned on the author's videos. Treat their output as evidence to check, not as ground truth.
  • The fix loop needs extra tools. python -m src.loop run calls the Codex CLI as its fixer (default model ladder in src/loop.py). The optional --judge jev needs TYPESAFE_API_KEY or OPENROUTER_API_KEY in the environment.
  • bench-v1 needs accounts. Running it needs the Codex CLI, GEMINI_API_KEY in the environment, Node.js and macOS sandbox-exec; see benchmarks/bench_v1/README.md.
  • No stable API. Tool names and outputs may change between commits.

Repository layout

  • src/: the library (media/, transcript/, retrieval/, core/), the MCP server (executor/mcp_server.py), the checker (loop.py) and tool schemas (adapters/).
  • tests/: the pytest suite. evals/adversarial_runner.py holds the failure-injection checks that tests/test_adversarial_failures.py runs.
  • benchmarks/bench_v1/: the blind three-contestant benchmark harness. Its data and answer key live outside the repo.
  • benchmarks/dev/count_dev/: a small development set for the counting tools.
  • scripts/generate_synthetic_benchmark.py: regenerates the synthetic fixture video.
  • fixtures/: small test videos (see Media credits).
  • docs/: architecture notes and per-feature pages.

Media credits

  • fixtures/reference_videos/sample_10s.mp4: a 10-second excerpt, with no audio, of Big Buck Bunny (Sunflower version). (c) 2008 Blender Foundation, www.bigbuckbunny.org / peach.blender.org; Sunflower version by Janus Bager Kristensen, 2013 (bbb3d.renderfarming.net). Licensed under Creative Commons Attribution 3.0.
  • fixtures/reference_videos/flower.mp4: a sample video from MDN Web Docs, released under CC0 (public domain).
  • fixtures/synthetic_bench_60s.mp4 with .srt, .vtt and fixtures/synthetic_ground_truth.json: generated by this repository's scripts/generate_synthetic_benchmark.py. Same license as the code.

License

MIT. See LICENSE.

Source: README.md at commit b426347

Tools

25

count_repetitions

Count completed cycles of a repeated visual action (jumps, claps, bounces, swings) inside one window of at most 180 seconds. Give `region` (fractions of the frame: x, y, w, h) to isolate the moving thing. Repeat the same call while status=in_progress; verify peak badges and ambiguous edges with inspect_window. For separate appearances of a specific symbol or exact on-screen text use count_occurrences; to locate one moment use search_visual. For a longer clip call once per window, cutting at pauses between cycles so none straddles an edge. The count is a rough first pass and can be far off on real footage. Before answering, count cycles by eye with inspect_window or contact_sheet over the window; if your count differs, trust your own.

Parametersfpsmethodregionsourcerequiredend_secstart_secaction_hintmax_period_secmin_period_sec

count_occurrences

Count how many separate times a specific symbol/object or exact on-screen text appears. Use for questions like 'how many separate times does the yellow outlined circle appear (ignore other colours)', 'how many times does the exact word RELEASE appear (not RELEASED)', 'how many times does the green diamond cross the screen'. Give `description` (colour and shape words are enforced, so look-alikes in another colour or shape do not count) and/or `text` (whole-word match by offline OCR). Returns `count` plus one span per continuous appearance (start_sec, end_sec, representative_sec, region, travel, weak); gaps of min_gap_sec or more separate spans. Verify a span with inspect_window at its representative_sec. Not for periodic reps or cycles (use count_repetitions) and not for locating one moment (use search_visual, which ranks frames, is capped at top_k 20 and cannot count). No visual index needed. Repeat the same call while status=in_progress. Lists at most max_spans spans (default 20, maximum 100; larger values are clamped and the result says so); `count` includes any omitted. If 100 or more spans match, the description is too broad: `count` is null, `capped` is true and `spans_found` says how many matched, so narrow the description or window and rerun instead of reporting a number. A min_gap_sec above 0.3 can join separate appearances; then `gaps_merged` is set, and `count_without_merges` too when no `direction` is passed (with a direction, rerun with the default min_gap_sec to see the separate appearances). The count is a rough first pass: it can merge separate appearances that are split by a scene cut, split one appearance into many spans, and count appearances rather than crossings; `direction` (e.g. left_to_right) narrows the count to appearances that travel that way, plus spans too brief to show motion (travel unknown), which count under any direction: check those with inspect_window. The result also carries `contact_sheet`, one labeled image with a tile per liste

Parametersfpstextsourcerequiredend_secdirectionmax_spansstart_secdescriptionmin_gap_seccase_sensitive

probe_video

Read a video's duration, frame rate, resolution, codecs, and whether it has an audio track and a video track. Call this first: duration_sec bounds every start_sec/end_sec you pass to other tools. Audio-only files (.m4a, .mp3, .wav) are accepted: has_video is false and width, height and fps are 0. has_audio tells you whether transcribe, search_transcript and the audio tools (audio_onsets, loudness_timeline, spectrogram_window) can work; the visual tools and sync_check need has_video true. `source` is a local file path or a video URL and is the same in every tool: an x.com / Twitter post with a video, a YouTube link, another site yt-dlp supports, or a direct .mp4 link. A URL is downloaded once (up to 1080p) and cached; the result gives source_url and local_path, and while the download runs it is status=in_progress, so call again.

Parameterssourcerequired

transcribe

Get the full speech transcript as timestamped segments (cached after the first run). Audio-only files (.m4a, .mp3, .wav) work too. To find where a specific word or phrase is spoken, use search_transcript instead of reading it all. If the requested model is not cached, returns another cached transcript with cached_model_size and requested_model_cached=false while one background job runs; with no transcript cached, returns status=in_progress and retry_after_seconds, so repeat the same call after that delay. model_size: tiny, base or small (more accurate, slower on the CPU), or turbo (Whisper large-v3-turbo on the GPU via whisper.cpp: the most accurate and, where installed, the fastest, about 1 minute per hour of audio). Omit model_size to get turbo where it is installed, else tiny. Long audio is decoded in 60-second chunks. Lines repeated 3 or more times in a row carry `suspect` and `notice` names them: speech models often invent such lines over music or silence, though a real chant can repeat too. A line without `suspect` is not verified.

Parameterssourcerequiredmodel_size

search_transcript

Find when a word or phrase is SPOKEN. Audio-only files (.m4a, .mp3, .wav) work too. Default mode=hybrid lists exact keyword matches first (match=exact: the phrase; match=all_terms: every content word in one segment), then passages that match by meaning (match=semantic: a 20 s window, found on this Mac with EmbeddingGemma 2; nothing is sent to the cloud), so a reworded question still finds the moment. mode=lexical gives keyword matches only. When no hit is exact or all_terms, the words were not found in the speech: semantic hits are the closest passages, not proof, so read their text before answering. If semantic search is not set up or not ready, hits are keyword-only and `note` says why. Returns up to top_k timestamped hits (default 5, maximum 50; larger values are clamped and the result says so; `note` says when more keyword matches exist). For text shown on screen use count_occurrences, and for visible things use search_visual. If ASR is needed, starts or joins background transcription and returns status=in_progress with retry_after_seconds instead of waiting; repeat the same call after that delay. A hit on a flagged line carries `suspect` (see transcribe).

Parametersmodequeryrequiredtop_ksourcerequired

build_visual_index

Start or poll construction of the local visual frame index that search_visual needs. Call it once, then repeat the same call (or search_visual, which reports status=index_required or partial) until the status is complete; frames are processed in bounded chunks. count_occurrences, contact_sheet and the inspect tools do not need the index.

Parameterssourcerequired

search_visual

Find WHEN something visible happens: an object, person, action or scene (semantic match between the query and sampled frames). It matches meaning, not letters, so it cannot find on-screen words or numbers (names, scores, jersey or bib numbers): find those with count_occurrences `text`, which reads text in frames, one window of up to 2400 s per call. Returns up to top_k ranked frame hits (default 5, maximum 20; larger values are clamped and the result says so), merged intervals (the one holding rank 1 grows from its best frames only over neighbouring frames that score close to the best, up to 8 seconds, so it can be shorter; a span already longer than 8 seconds, such as a held frame or hits merged together, is kept whole, so it can be longer and can include lower-scoring hits; intervals that overlap it are merged into it), and a contact sheet to verify them. It ranks frames, so it cannot count: to count separate appearances of a symbol or exact on-screen text use count_occurrences, for a repeated action use count_repetitions, for spoken words use search_transcript. Build the local visual index first (build_visual_index), then repeat the same query to poll until complete. Use start_sec/end_sec to search one range at a time.

Parametersqueryrequiredtop_ksourcerequiredend_secstart_sec

contact_sheet

Get ONE grid image of evenly spaced frames, with burned timestamp badges, across the whole video or a start_sec/end_sec range: the cheapest way to see what a video contains before searching. Default 12 samples, at most 150 (larger values are clamped and the result says so). Use it to survey; use search_visual to find a specific thing, overview_chunks to survey a long video as ordered sheets, and inspect_window or inspect_windows for dense consecutive frames of a short span.

Parameterscolssourcerequiredend_secsamplesstart_sec

review_video

First look at a video in ONE call: container metadata (duration, size, fps, codecs, has_audio), a contact sheet image with burned timestamp badges (default 24 samples; `samples` up to 150, larger values are clamped and the result says so), every cut and transition from detect_cuts, every near-black stretch of at least 0.2 s (`black_ranges`: start, end and duration; at most 50 are listed and `black_total` counts them all), and a loudness summary (average and peak level, silence gaps, the strongest band-energy outliers with their times). It runs probe_video, contact_sheet, detect_cuts, the black scan and loudness_timeline for you, so start here instead of calling them one by one. A first call on a 10-minute 1080p clip takes about 35 s (the cuts scan and the black scan share one decode of the file); repeat calls on the same file return from cache. `loudness` is null when the video has no audio track. Use the times it returns to zoom in with inspect_window or spectrogram_window; for a different threshold, window or time range call detect_cuts or loudness_timeline directly. Any step failing fails the whole call with that step's error message.

Parameterssourcerequiredsamples

overview_chunks

Survey a long video as consecutive contact sheets, one per chunk_sec (default 60 s), so every stretch is covered in order. At most max_chunks sheets per call (default and maximum 32): for a longer video raise chunk_sec or page through it with start_sec/end_sec; a call over budget fails and says what chunk_sec would fit. samples_per_chunk defaults to 8 (maximum 150, larger values are clamped). Use contact_sheet instead when one sheet is enough.

Parameterscolssourcerequiredend_secchunk_secstart_secmax_chunkssamples_per_chunk

refine_temporal_beam

Zoom into candidate time windows: splits each window into `fanout` contiguous child ranges and returns one timestamped contact sheet per child, without ranking relevance. Use it after contact_sheet or search_visual has narrowed the answer to a few windows and you want to localize inside them; use inspect_windows for dense consecutive frames instead. Budget: windows x fanout x samples_per_child must be at most max_total_samples (default 96, maximum 300); a call over budget fails and says which of the three to lower.

Parameterscolsfanoutsourcerequiredwindowsrequiredcontext_secalign_to_cutsmax_total_samplessamples_per_childcompact_parent_sheet

track_region_changes

Watch one fixed rectangle of the frame (region: x, y, width, height as fractions 0-1) from start_sec to end_sec (both required) and rank the moments its pixels change most, as candidates to verify with inspect_moments or inspect_window. Use for a scoreboard, counter, indicator light or door. Slow continuous motion ranks no candidates: read steady_change.detected for that. Returns ranked candidates, not a count: for a bounded count of state transitions use track_region_events. Budget: about (end_sec - start_sec) x sample_fps frames, at most max_total_frames (default 600, maximum 2000); a call over budget fails and says the sample_fps that fits. This tool's region uses width/height (count_repetitions uses w/h).

Parametersregionrequiredsourcerequiredend_secrequiredstart_secrequiredsample_fpsframe_widthrobust_sigmamax_candidatesmax_total_framesmin_change_scoremin_separation_sec

track_region_events

Count discrete state changes (transitions) inside one fixed rectangle of the frame (region: x, y, width, height as fractions 0-1) between start_sec and end_sec (both required). Returns event_count plus before/after evidence moments to verify. Use for 'how many times did the light turn on' or 'how often does the counter change'. For ranked change candidates without a count use track_region_changes; for a periodic action use count_repetitions; for a symbol appearing anywhere on screen use count_occurrences. Budget: about (end_sec - start_sec) x sample_fps frames, at most max_total_frames (default 600, maximum 2000); a call over budget fails and says the sample_fps that fits. This tool's region uses width/height (count_repetitions uses w/h).

Parametersregionrequiredsourcerequiredend_secrequiredstart_secrequiredmax_eventssample_fpsframe_widthambiguity_marginchange_thresholdmax_total_framesmin_separation_sec

inspect_moments

Look closely around specific instants: for each moment (timestamp_sec, optional label) returns a contact sheet of frames before, at and after it (default 4 s before and 4 s after, samples_per_moment 9). Use it to verify a hit from search_visual, search_transcript, detect_cuts, audio_onsets or track_region_changes. At most 16 moments per call and max_total_samples frames in all (default 144, maximum 400); a call over budget fails and says what to lower. For one continuous span of frames use inspect_window.

Parameterscolssourcerequiredmomentsrequiredafter_secbefore_secmax_momentsmax_total_samplessamples_per_moment

inspect_windows

Dense consecutive frames (default 4 fps) for several time ranges in one call, with optional context_sec padding and merging of overlapping windows. At most 16 windows and max_total_frames frames in all (default 150, maximum 500); a call over budget fails and says to lower fps or shorten the windows. For one range use inspect_window, for a coarse survey use contact_sheet, for frames around single instants use inspect_moments.

Parametersfpssourcerequiredwindowsrequiredcontext_secmax_windowsmerge_overlapsmax_total_frames

inspect_window

Dense consecutive frames of ONE short window [start_sec, end_sec] (both required) with timestamp overlays, as a contact sheet plus frame list. Default 4 fps, maximum 30 (larger values are clamped). At most about 150 frames and 600 seconds per call: frames = fps x duration, so a 10 s window allows at most 15 fps; a call over budget fails and says the fps that fits. Use it to verify a moment or a count. For a wide overview use contact_sheet; for several ranges use inspect_windows. With read_text true each frame also lists the on-screen text read from it (Apple Vision, macOS only).

Parametersfpssourcerequiredend_secrequiredread_textstart_secrequired

frame_at

Use this when you need one exact frame at full size, or a crop to read small text; for many frames use inspect_window or contact_sheet. Returns one PNG of the frame shown at time_sec, at the source's native resolution, with no timestamp drawn on it. An optional crop (x, y, width, height in source pixels) cuts a rectangle out of that frame, and scale (1 to 4, default 1) enlarges the crop with Lanczos so small text is easier to read. frame_time_sec is the start time of the frame actually decoded: the last frame starting at or before time_sec, so on variable-frame-rate video it can be well before time_sec. A repeat call with the same source, time_sec, crop and scale returns the saved file. With read_text true it also returns the on-screen text read from that frame (Apple Vision, macOS only).

Parameterscropscalesourcerequiredtime_secrequiredread_text

detect_cuts

List scene changes (timestamp_sec, score, kind) in the whole video or a start_sec/end_sec range. Hard cuts and animated changes (fades, dissolves, slides, wipes, zooms) are both returned; `kind` tells them apart: "cut" or "transition". A transition's timestamp is its middle. Scores of the two kinds are not comparable. threshold 0-1 (default 0.25) applies to hard cuts only: raise it to keep only stronger cuts. A hard cut between two similar pictures is found by a second check and listed as a cut with a score below the default threshold; any threshold above 0.25 leaves these out. A change that is over within about 4 frames (a very fast wipe) is listed as a cut, and a few similar-picture cuts still come back as a transition. Film footage with heavy motion can still give false transitions; check a doubtful one with inspect_moments. At most 500 cuts are listed; `total` is the full count and the result says how to see the rest. Use it to segment a video into shots or find edit points; use audio_onsets for sounds and sync_check to compare both.

Parameterssourcerequiredend_secstart_secthreshold

check_frame_cadence

Find stretches where the picture updates less often than the file's frame rate, e.g. a 30 fps capture padded to 60 fps so every frame is doubled (judder). It decodes every frame at 160x90 grey and calls a frame a repeat when it is near-identical to the one before; a range is a stretch of at least 0.5 s where repeats fill at least a quarter of every half second while other frames still change. Each range gives start_sec, end_sec, frames, duplicate_frames and effective_fps (unique frames per second; compare with frame_rate). Runs of repeats longer than 0.5 s are holds (still shots, title cards, stalls) and are not ranges; held_frames counts them (use detect_cuts and the frozen-picture check in `python -m src.loop check` for those). It does not judge whether a range is intended: a 24 fps film in a 60 fps file is one range covering the whole file, and a slow, smooth pan can look like repeats. Look at a range with inspect_window to see what moves there. At most 500 ranges are listed; `total` is the full count. Reads the whole file: about 1 s per 700 frames of 1080p; there are no range arguments.

Parameterssourcerequired

audio_onsets

List non-speech sound attacks (hits, claps, beats) with millisecond timestamps and five-band energy profiles, optionally restricted to a frequency band (band_hz: sub, low_mid, mid, high, air, or [low_hz, high_hz]) and compared with another source (compare_to). min_strength 0-1 (default 0.12): raise it for only strong onsets. min_gap_sec (default 0.035 s) merges onsets within that gap of a group's first onset into one onset at the first time with the group's strongest strength; 0 keeps every raw onset. At most 500 onsets are listed; `total` is the full count and the result says how to see the rest. Needs an audio track (probe_video has_audio); audio-only files (.m4a, .mp3, .wav) work. For level over time use loudness_timeline; for speech use transcribe. To name what a sound is, use label_sounds (cloud).

Parameterssourcerequiredband_hzend_secstart_seccompare_tomin_gap_secmin_strength

loudness_timeline

Measure loudness over time: RMS, peak, band energies, spectral centroid, silence gaps, and band-energy outliers classified as steps, ramps or transients, in fixed windows (window_sec 0.01-600, default 0.25) or beat-length windows (bpm 20-300; give one or the other). At most 500 windows are listed in `windows` (`total` is the full count; `outliers` and `silence_gaps` always cover the whole range); use a larger window_sec or a start_sec/end_sec range for the rest. Ranges needing over 50,000 windows are rejected with the window_sec that fits. Needs an audio track (audio-only files work too). For discrete hits use audio_onsets. `peak_dbfs` (top level, with `peak_sec`, and per window) is the loudest sample on any channel, as ffmpeg reports sample peak; `rms_dbfs` and `band_db` are measured on the mono mix (L+R)/1.414, so on centred stereo they read up to 3 dB above a per-channel meter.

Parametersbpmsourcerequiredend_secstart_secwindow_secignore_ranges

spectrogram_window

Render a log-frequency dB spectrogram PNG for [start_sec, end_sec] (both required, at most 600 s) with absolute timecode badges and optional event marks (time_sec + label). Use a short window (a few seconds) to look at one sound; find candidate times with audio_onsets or loudness_timeline first. Needs an audio track (audio-only files work too).

Parametersmarkssourcerequiredend_secrequiredstart_secrequired

sync_check

Check audio/video sync: compares visual cuts with measured audio onsets, and optionally with intended sound cues (time_sec, label, optional band_hz), within tolerance_ms (0-5000, default 80). Returns per-cut and per-cue matches and unmatched strong onsets (the cuts and unmatched_strong_onsets lists show at most 500 items each; the cues list is never cut short; total_cuts and total_cues are the full counts). Each cue reports candidate_onsets_count, the number of onsets near it; verbose true reports that cue's candidate_onsets list instead of the count. Needs an audio track. To look at the two signals separately use detect_cuts and audio_onsets.

Parametersbpmcuessourcerequiredverbosetolerance_ms

label_sounds

Name the sounds in a stretch of a video's audio: cuts [start_sec, end_sec] (both required) into windows of window_sec (default 5 s, at most 60 windows per call) and returns, per window, the chance of each label plus none_chance, from a cloud model (Clef-omni on Cloudflare Workers AI). THIS SENDS THE AUDIO TO CLOUDFLARE: never use it on personal recordings. The server needs CLOUDFLARE_ACCOUNT_ID and CLOUDFLARE_API_TOKEN (or AGENTIC_VIDEO_CF_TOKEN_COMMAND, a command that prints the token) and the call fails with a message naming what is missing. `labels` (default: 20 common sounds such as dog barking, siren, glass breaking, church bells) is the closed list to choose from, so a sound outside the list is forced onto the nearest label or none: list every sound that may be present. Steady musical chords can be named church bells, so treat top_chance under 0.5 as weak and check a hit with spectrogram_window. top_label is null when none_chance beats every label. It does not search: find candidate times with audio_onsets or loudness_timeline first, then label those ranges. For speech use transcribe or search_transcript. Needs an audio track. No window starts after about 40 s and the call answers within about 50 s: a longer range returns the windows finished in time with complete=false and next_start_sec to continue from.

Parameterslabelssourcerequiredend_secrequiredstart_secrequiredwindow_sec

watch_for

Watch a stretch of a video for yes/no conditions with a cloud model (Clef on Cloudflare Workers AI) and return the moments each one started to hold. One frame every interval_sec (default 5 s, 1 to 600) from [start_sec, end_sec] is asked every condition (1 to 8 yes/no questions about ONE frame, e.g. "Is a person visible?"); a condition wakes at the sample where its chance of yes has been at least cut (default 0.5) on `consecutive` samples in a row (default 2), at most once per cooldown_sec (default 30) per condition. THIS SENDS VIDEO FRAMES TO CLOUDFLARE: never use it on personal recordings. The server needs CLOUDFLARE_ACCOUNT_ID and CLOUDFLARE_API_TOKEN (or AGENTIC_VIDEO_CF_TOKEN_COMMAND, a command that prints the token) and the call fails naming what is missing, before anything is sent. Only the questions you write can wake: a condition nobody asked about is never reported, so list every event you care about. An event must last at least consecutive x interval_sec to be caught (two samples 5 s apart need about 10 s); lower interval_sec or consecutive for shorter ones, which costs more calls. Accuracy on real footage is not yet measured. At most 720 samples per call; the call answers within about 50 s even when Cloudflare is slow: a longer range, or a sample that keeps failing after its retry, returns the finished samples with complete=false and next_start_sec to continue from (the cooldown does not carry over). This is for finding when something starts to be true; to inspect what happened use inspect_window, for scene changes use detect_cuts, and for sounds use audio_onsets. Results are not cached.

Parameterscutsourcerequiredend_secstart_secconditionsrequiredconsecutivecooldown_secinterval_sec

Version history

3
  1. v0.3.4LatestNov 1, 2026
  2. v0.3.3Nov 1, 2026
  3. v0.3.2Oct 31, 2026

Score

90% of the applicable checks met

How complete this listing is, computed from the registry entry, the endpoint and public sources. Not a rating of the server and not a statement that it is safe or working today: reachability is a dated check, and no installs or reviews go into the score.

  • Reached in the last 30 days25 pts
  • Auth declared10 pts

    Declare the credential headers in the registry entry, or keep the endpoint answering the probe so its auth can be classified.

  • Tools listedRequired15 pts
  • DescriptionRequired15 pts
  • Logo10 pts
  • Documentation15 pts
  • License10 pts

Other Agentic integrations