Skip to main content
Glama

Self-hosted, AI-powered long-form → short-form video clipper. Paste a YouTube link or upload a video; Reelsaw transcribes it, asks an LLM for the most clippable moments, then renders vertical 9:16 clips with word-highlight captions and a hook overlay. A hosted service at app.reelsaw.com runs the same code (Free, then $12 / $29 / $59 a month, metered in source minutes).

Pure TypeScript. Any AI vendor via the AI SDK. One-step deploy with Docker Compose.

Features

Pipeline

  • Any AI vendor – language model, transcription model and caption-alignment model are each a provider:model string: OpenAI, Anthropic, Google, Groq, Deepgram, AssemblyAI, Vercel AI Gateway, or any OpenAI-compatible server (Ollama, vLLM, LM Studio, local Whisper servers). Fully local with no API keys is supported.

  • Research-backed clip selection – map → pool → rank: the transcript is chunked at long pauses with 20% overlap (no "lost in the middle" on long videos), every standalone moment is collected per chunk in parallel, candidates are snapped/deduped, then scored on a rubric (hook / value / flow / shareability, plus relevance with a focus) as the median of k samples, and the top 10 get K sliding pairwise head-to-head passes (PRP-Sliding-K; ANALYZE_PAIRWISE_PASSES, default 3 so the top 3 you see first are placed reliably) with positions swapped to cancel judge bias. Cost: at most Σ(n−1−p) pairs × 2 calls, i.e. 48 calls for the top 10 with 3 passes (18 with 1); pairs met twice are judged once. No target count: in auto mode weak picks are dropped by a gate relative to the run, not a fixed number, because pointwise scores do not transfer between judge models: a clip is dropped below max(ANALYZE_MIN_SCORE_FLOOR = 25, median − ANALYZE_SCORE_MAD_K = 1 × MAD) of the pooled rubric totals, and the best clip is always kept (ANALYZE_SCORE_RULE=absolute restores the old fixed 45). The threshold and its median/MAD are in the project's candidates artifact. Candidates whose scoring failed are never compared with rubric totals; they only fill in when fewer than 3 scored clips survive. Optional "at most N" cap, and "Find clips about …" keeps only on-topic moments. Click a clip's score for the breakdown. ANALYZE_JUDGE_MODEL lets a different model score and compare (less self-preference than judging its own hooks). ANALYZE_CONCURRENCY, ANALYZE_SCORE_SAMPLES, ANALYZE_PAIRWISE tune cost (auto: 1 call at a time, 1 sample, no pairwise for a self-hosted judge; 4 / 3 / on for cloud).

  • Clean transcripts – names are fixed from vocabulary hints and a constrained LLM correction, and fillers, repetitions and false starts are cut with jump cuts. Every cut is located by forced alignment and checked by listening to the seam, so karaoke highlights land on the syllable. Each fix shows inline in the transcript with accept/reject, and long pauses can be shortened (details below).

  • Smart 9:16 reframing – UltraFace (1.2 MB ONNX, CPU, ~7 ms/frame via onnxruntime-node) samples each clip at 4 fps and picks a layout per segment (clips are split at scene cuts and face-presence changes, so a picture-in-picture intro can be "fit" and the talking head after it "face"; shot cuts are detected on every clip, and a multi-camera edit that alternates a close-up with a wide shot every second or two changes layout at each cut): face (crop follows the speaker with "heavy tripod" smoothing: dead zone, capped pan speed, instant snap at scene cuts), split (two speakers stacked), stack (slides/screen on top, the webcam tile below — the picture-in-picture box of a Zoom, Teams, Meet or OBS recording, not a face-sized crop that reaches into the slides), or fit (whole frame over a blurred background when there is no face, e.g. graphics and b-roll, or when three or more people share the frame). With two people in a wide shot the crop follows whoever is talking (mouth motion correlated with the audio envelope, minimum 2.5 s per shot), falling back to a split when detection isn't confident. Override per project, per clip or per section, including a hand-set crop. Reference for every layout, its triggers, caveats and fixtures: docs/LAYOUTS.md.

  • Captions – AI-picked key words per clip coloured and enlarged (editable in the editor), readability limits (≈2 short lines, comma/sentence breaks, a line stays up long enough to read: at least 0.7 s, 0.3 s per word and 20 characters per second), ASS karaoke word highlight (any colour), boxed, or outlined; size, words on screen (1 = one word at a time), uppercase; hook overlay with its own duration. Caption and hook positions are free x/y set by dragging on a live 9:16 preview that uses the renderer's crop math and font metrics, with a platform safe-zone overlay. In split and stack sections that last 2.5 s or more, captions sit on the line between the two panes instead of on the lower speaker (a switch turns it off). Project-wide defaults, per-clip overrides. Burned in with ffmpeg + libass.

Editing

  • Workspace and clip editor – source player and clickable transcript on the left, clips streaming in on the right as they render, with score, layout badge, post caption + hashtags copy and download. The editor has a live preview of the source with the clip's framing and captions and a multi-lane timeline (ruler, transcript words, clip range, layout track, layers) with zoom, snapping to words/scene cuts/playhead, nudge buttons, typed timestamps and I/O keys for frame-accurate trims. Split the layout track (⌘/Ctrl+B, the Split button, a double-click or the right-click menu) into sections that are Auto or forced to face/split/stack/fit/centre; drag the picture to re-centre a section; right-click for Set crop… (a box on the source, or crop / crop2 / contentCrop on update_clip); "Use for all clips…" copies one clip's framing to the project (apply_framing). Add, drag and remove transitions; ⌘/Ctrl+Z / ⇧⌘Z undo and redo every edit.

  • Deletes can be undone – deleting a project or a clip (card ⋯, the selection bar, the editor, ⌘K) stops its work and hides it. The toast always offers Undo, Delete forever, and See deletions. Everything restorable lives on Deleted (account menu) as one selectable list: each row says when it was deleted and when it is deleted forever, and a compact bar restores the selection or deletes it forever. Storage still counts until you delete it forever (MCP list_deleted plus purge_project / purge_clip / purge_asset / purge_layer_template; reelsaw purge-project, purge-clip, library purge, templates purge). Minutes, hourly creates and first-video-free are never refunded. Re-picking moments keeps every clip you made or edited (text, bounds, framing, style, intro/outro) and only replaces untouched AI picks, which can be restored too. The same through MCP (delete_clip, delete_clips, restore_project, restore_clip, list_deleted) and the CLI (reelsaw deleted, restore, delete-clip, delete-clips, restore-clip, purge, purge-project, purge-clip).

  • Learns from your decisions – what you accept, reject, cut, correct and edit is kept in a decision log (with the words or clip text it was about, the models that proposed it and a fingerprint of the source video) that outlives deleted projects: cuts you rejected are shown to the filler detector as examples to keep, and an export turns your decisions into eval cases (reelsaw decisions export, export_decisions, the decisions suite). Settings → Learning turns it off or forgets everything; deleting a project (or "Delete forever") can forget what it learned from that project, applied when it is purged, even automatically. It never leaves your instance. See docs/specs/decision-log.md.

  • Command palette and bulk export – ⌘K searches projects and clips with status, length and age, opens a row's actions (open, export, resume, retry failed clips, re-run refine, re-render, download, delete project or clip), takes pasted links (one project each, or opens the existing one) and offers retry-all shortcuts. "Export all" zips every rendered clip with platform-ready metadata: a paste-ready .txt (title, description, tags, caption), a .youtube.json (YouTube Data API v3 videos.insert body, private by default) and a .tiktok.json (Content Posting API post_info, SELF_ONLY) per clip, plus manifest.json, clips.csv (with rendered duration) and a README. Descriptions carry a timestamped link to the exact moment in the parent video.

  • Share links – a clip's ⋯ menu copies a link anyone can download it from without signing in (the project and selection menus do the same for the zip), for schedulers (Buffer, Metricool), Instagram's video_url, Zapier/Make file fields or a person. The link answers with the file itself (a plain 200, no redirect), serves the clip's latest render, lasts 90 days by default (MEDIA_LINK_TTL_DAYS; "Never" where MEDIA_LINK_ALLOW_NEVER), and can be revoked one at a time, per clip or project, or all at once under Settings → Share links. MCP get_media_links, list_media_links, revoke_media_link; reelsaw media-links. See docs/specs/media-links.md.

  • Webhooks – Settings → Webhooks sends signed JSON (Standard Webhooks: webhook-id, webhook-timestamp, webhook-signature) to your URLs on project.created, project.done (every clip with share links and the zip), project.failed, clip.done and clip.failed, so n8n, Zapier, Make or a script can act the moment clips are ready instead of polling. Deliveries commit with the event, are retried 8 times over about 22 hours, and an endpoint that fails for 3 days is turned off with an email; each has a delivery log with the request, the answer and Resend. A project's metadata (create_project) comes back in every webhook for it. MCP create_webhook / delete_webhook (how a Zapier or Make app subscribes), list_webhooks, update_webhook, rotate_webhook_secret, test_webhook, list_webhook_deliveries, resend_webhook_delivery; reelsaw webhooks. See docs/specs/webhooks.md.

  • SRT / VTT in and out – every rendered clip's ⋯ menu offers its captions as .srt or .vtt (the same lines and times as the video, intro included, even when the style has captions off), and "Export all" adds them next to each clip as clips/NN-title.srt and .vtt; clips rendered before this feature need a re-render first. The project's ⋯ menu ("Transcript files…") downloads the transcript the same way (VTT can carry a timestamp before every word) and imports a subtitle file you already have: right after creating a project (only the video is fetched) or on a done or failed one (it cleans the transcript up and picks clips again). Imported cues become segments and their words are spread over the cue by length; the render re-aligns words to the audio. Also GET /api/clips/:id/captions.srt|vtt, GET /api/projects/:id/transcript.srt|vtt (?words=1 for VTT word times), the MCP tools get_clip_captions, get_project_transcript_file and import_transcript, and reelsaw captions, transcript-file and import-transcript.

Library, layers and templates

  • Library – intros and outros, logos, stickers, b-roll, music and sound effects (PNG, JPG, GIF, WebP, SVG, video, audio). Drop many files at once (duplicates are detected), search your items plus a bundled catalog and, with an admin key, Pexels, Pixabay, Freesound, Iconify, Giphy and Klipy. Each clip is rendered as intro + clip + outro in a single encode, fitted to vertical over a blurred background.

  • Per project and per clip – a project follows the default intro/outro, uses none or a specific item; each clip can follow the project, use none or pick its own. An unusable asset is skipped with a plain-language event instead of failing the clip.

  • Layers – one timeline lane for image/video overlays, text and sound (update_clip { layers }, reelsaw clip-layers): position, size, opacity, fonts shipped in the image, several music tracks with fade, loop and sidechain ducking under speech. Right-click a layer on the 9:16 preview to edit, centre, reorder, copy or remove; double-click text to retype it. ⌘C / ⌘V copy and paste layers.

  • Layer templates – save a look once (a logo pop in the first seconds, a follow card at the end, a watermark or a music bed, plus an intro and outro) with timing from the start, the end, the whole clip or a share of its length, then apply it to selected clips or set it on a project so every new clip gets it (list_layer_templates, apply_layer_template, reelsaw templates).

Agents and automation

  • MCP server – /api/mcp (Streamable HTTP) lets Claude Code, Claude Desktop, Cursor or a script create projects, wait for them, read timed transcripts, edit layers and framing, create and re-render clips, manage the library and layer templates, and export the results. On a hosted plan the same tools are available, including Free. Authenticate with an API key from Settings, or with MCP_OAUTH=true so claude.ai and Claude Desktop connectors sign in through the browser.

  • Operator CLI – docker compose exec app reelsaw … inspects and fixes a running instance: status, events, clip, rerender, stuck, recover, logs, cuts, align, hear, flags, storage, retention, deleted, restore, purge, users, approve, make-admin, set-password, library, templates, apply-framing, import-report and more (every command is in Operator CLI).

Hosting and accounts

  • Accounts, approval and quotas – the first account is the admin; SIGNUP_MODE=open|approval|closed decides who gets in, and admins approve, suspend, promote and remove people on the Users page, with usage per account (projects, source minutes, clips, model calls and tokens, stored bytes). Optional per-user quotas, retention of old files, captcha on sign-in, email (approval notices, password reset, verification, and a note when clips finish if you've stepped away) and terms/privacy links. Everyone has their own Project defaults (vocabulary, instructions, language, clip length, pauses, caption style, intro and outro) and API keys.

  • Models and providers at runtime – admins switch the language, transcription and alignment models under Settings → System → Models without a restart: pick a provider, enter its key or server URL there (stored encrypted, never shown again), and choose from live model lists; local Ollama and Speaches models download with a progress bar. Keys and models are tested when saved.

Operations

  • Reliable, resumable, scalable – every stage is a pg-boss job in Postgres that checkpoints its output, so a crashed or restarted worker picks up where it left off; a worker that stops heartbeating (every 15 s) has its job re-queued by pg-boss within about a minute or two, and a reconciler re-enqueues anything orphaned. A long stage saves its parts as they finish (each 3-minute window of Finding moments), so a retry or a timeout on a slow local model continues where it stopped instead of starting over, and a timeout says how far it got (LLM_CALL_TIMEOUT_SECONDS, STAGE_IDLE_TIMEOUT_MINUTES). Run extra worker containers to scale. Every run (first pass, retry, re-render, re-analyze) is recorded with its own timings, and the UI shows elapsed against expected time per stage, learned from your finished projects. The browser gets live updates over a tRPC SSE subscription with automatic reconnect.

  • Storage and install – local volume or s3 (AWS S3, MinIO, Cloudflare R2, Backblaze B2…); superseded renders, expired sources and orphaned files are swept hourly (reelsaw storage reports, reelsaw storage sweep runs it). One-line installers for macOS/Linux (bash) and Windows (PowerShell), and Report an issue saves a zip of a project's data, decisions and logs, with secrets left out.

  • Observable and fail-fast – every service is built in main and self-checks, so the process exits with a readable reason if a model id is wrong, a key is missing, a binary is absent, the bucket is unreachable or the DB is down. LLM_TRACE=logs or langfuse captures every prompt/response (tokens, latency, project/clip/stage), and OTEL_ENABLED=true turns on OTLP traces and metrics for HTTP, Postgres, pg-boss jobs, AI SDK calls, ffmpeg/yt-dlp and each stage, with stage, download and transcription time per second of video and per-proxy download health.

Related MCP server: Smart Clip MCP

Contributing

See CONTRIBUTING.md (principles, tests, sign-off). Roadmap for learning from user decisions: docs/SELF-IMPROVEMENT.md.

Read docs/PRINCIPLES.md first: the consistency, concurrency and UI rules every change follows. Overlay stacking (Select inside a Dialog, z-index, portals) and the mobile viewport (h-dvh, not h-screen) are docs/UI.md. Agent instructions live in AGENTS.md (the only agent file; see the process principles).

Install (no source needed)

Needs Docker (Docker Desktop on macOS and Windows). One command: it looks at the machine (RAM, GPU, Docker's memory, free disk, an Ollama already running and the models it has, the port), proposes a setup, asks three questions in a terminal (local Ollama or a hosted provider, which model, go ahead) and installs Ollama for you (Homebrew, ollama.com or winget) if you pick local and it is missing:

curl -fsSL https://raw.githubusercontent.com/osbytes/reelsaw/main/deploy/install.sh | bash

On Windows (Docker Desktop), in PowerShell:

irm https://raw.githubusercontent.com/osbytes/reelsaw/main/deploy/install.ps1 | iex

WSL2 users can run the bash one-liner as is (with Docker Desktop's WSL integration on).

It writes ~/reelsaw (compose file + .env with generated secrets), pulls the published image and the model, starts everything and waits until http://localhost:3000 answers (or the next free port). Fully local is the default when the machine can carry a useful model: qwen2.5:14b (~9 GB) with 24 GB of RAM or a 12 GB GPU, qwen2.5:7b from 12 GB, gemma3:4b below that, and a good model that Ollama already has is reused instead of downloaded; with an NVIDIA GPU and the container toolkit, Ollama and Speaches run on the GPU (and renders encode with NVENC; see "Using a GPU" below). Hosted providers (OpenAI, Anthropic, Google, Groq, Vercel AI Gateway) are a choice in the same prompt: the key is asked for and never echoed, and Anthropic and Google pair with local Speaches for speech-to-text. Without a terminal, or with REELSAW_YES=1, the proposed answers are taken; REELSAW_AI, REELSAW_LLM, REELSAW_PORT and friends answer in advance and REELSAW_DRY_RUN=1 prints the plan and stops (every option is listed at the top of the script). Manual setup: deploy/README.md. The scripts are deploy/install.sh and deploy/install.ps1; they are served from main (see docs/RELEASING.md).

Quick start (from source)

git clone <this repo> reelsaw && cd reelsaw
cp .env.example .env
# edit .env: set BETTER_AUTH_SECRET, APP_URL, and at least one AI provider key
docker compose up -d --build

Open http://localhost:3000, create an account (it becomes the admin), start a project. Set SIGNUP_MODE=closed or approval afterwards if the instance is public.

Scale workers:

docker compose --profile workers up -d --scale worker=2

Each worker keeps its own scratch copies of the sources and renders it works on. With an S3 store they are a cache: idle copies of finished projects are evicted after SCRATCH_CACHE_HOURS (24), a job short of disk evicts the least recently used ones first, and a worker that still cannot make room hands the job to a worker that has it and stops taking media jobs until its disk recovers (Settings → System → Workers shows "disk full"). A job leases the scratch it is about to write against its volume's live leases before the first byte lands, so two large ingests on one box cannot both pass the free-space check and both run out mid-download: the second waits for the first. Processes sharing a volume (the app and worker replicas of one host) share one ledger; a worker on another host never contends.

Use MinIO for local S3 testing:

docker compose --profile minio up -d
# then in .env: STORAGE_DRIVER=s3 S3_ENDPOINT=http://minio:9000 S3_FORCE_PATH_STYLE=true S3_BUCKET=reelsaw S3_ACCESS_KEY_ID=reelsaw S3_SECRET_ACCESS_KEY=reelsaw123

(Create the bucket once via the MinIO console at http://localhost:9001.)

Model configuration

The language model and the transcription model are independent; mix vendors freely (Anthropic has no speech-to-text, so pair it with Deepgram/OpenAI/AssemblyAI/Groq). Each needs only its own key.

Env

Example values

AI_MODEL

openai:gpt-4o-mini, anthropic:claude-sonnet-4-5, google:gemini-2.5-flash, groq:llama-3.3-70b-versatile, gateway:anthropic/claude-sonnet-4-5, compat:qwen2.5:14b

TRANSCRIPTION_MODEL

openai:whisper-1, deepgram:nova-3, assemblyai:best, groq:whisper-large-v3-turbo

The transcription model must return word timestamps (captions and cuts need them), so openai:gpt-4o-transcribe, which returns none, is not supported.

Fully local (no API keys)

Ollama only serves language models; it has no speech-to-text. So the local stack is Ollama for AI_MODEL plus Speaches (faster-whisper behind an OpenAI-compatible API, word timestamps supported) for TRANSCRIPTION_MODEL. Both ship as a compose profile:

docker compose --profile local-ai up -d            # Speaches; use your native Ollama (macOS: Metal GPU)
docker compose --profile local-ai --profile ollama up -d   # Linux: also run Ollama in a container
ollama pull qwen2.5:14b                            # or: docker compose exec ollama ollama pull qwen2.5:14b

.env:

AI_MODEL=compat:qwen2.5:14b            # qwen2.5:7b or gemma3:4b on less RAM
OPENAI_COMPATIBLE_BASE_URL=http://host.docker.internal:11434/v1   # native Ollama; http://ollama:11434/v1 for the container
OPENAI_COMPATIBLE_API_KEY=ollama
TRANSCRIPTION_MODEL=openai:Systran/faster-whisper-small
OPENAI_BASE_URL=http://speaches:8000/v1
OPENAI_API_KEY=local

Notes: the app requests response_format: json_schema from the compat server (OPENAI_COMPATIBLE_STRUCTURED_OUTPUTS=false to disable) and falls back to lenient parsing when a model ignores it, but small models still pick weak moments and write bland hooks. gemma3 4b works; qwen2.5 14b (the installer's default on 24 GB or more), llama3.1 8b or gemma3 12b are noticeably better. On first boot the app auto-downloads the whisper model into Speaches and verifies the Ollama model is pulled (it exits with ollama pull <model> otherwise). Speaches CPU with faster-whisper-small runs roughly real-time; use the latest-cuda image and faster-whisper-large-v3 on a GPU. The OPENAI_* variables are reused for Speaches because it speaks the OpenAI audio API, which is why the LLM goes through the compat provider instead.

Using a GPU

Three things can use one, each in its own container, so the GPU is a Compose override rather than a different image: docker-compose.gpu.yml reserves every NVIDIA device for speaches, ollama, app and worker. Host side you need the NVIDIA driver (nvidia-smi works) and the NVIDIA Container Toolkit (sudo nvidia-ctk runtime configure --runtime=docker && sudo systemctl restart docker; on Windows, Docker Desktop on WSL2 with the Windows driver only). Then in .env:

# Linux/macOS (colon). On Windows use a semicolon: docker-compose.yml;docker-compose.gpu.yml
COMPOSE_FILE=docker-compose.yml:docker-compose.gpu.yml
SPEACHES_IMAGE=ghcr.io/speaches-ai/speaches:latest-cuda
WHISPER_MODEL=Systran/faster-whisper-large-v3      # or deepdml/faster-whisper-large-v3-turbo-ct2
TRANSCRIPTION_MODEL=openai:Systran/faster-whisper-large-v3

and docker compose --profile local-ai up -d as before. What changes:

  • Transcription is the big one: faster-whisper on CUDA runs large-v3 in a fraction of real time, where small on a CPU was about real time (the transcribe factor in the table below).

  • The language model uses the card when Ollama runs in the ollama container (--profile ollama). A native Ollama (macOS, or Linux on the host) already uses the GPU on its own.

  • Renders encode with h264_nvenc instead of libx264: FFMPEG_ENCODER=auto probes it at boot and keeps libx264 when the card is not there. Filters (crops, blur, captions) still run on the CPU, and render was already the short stage, so expect seconds, not minutes. A hardware encode that fails mid-clip (NVENC session limit, driver reset) is redone with libx264 for that clip.

  • The on-device models (face, text, alignment) stay on the CPU by default. ONNX_EXECUTION_PROVIDER=cuda moves them, but the published image carries no CUDA libraries for onnxruntime, and on Apple silicon CoreML measured slower than the CPU for all three; see docs/CONFIG.md.

Settings → System → Overview and docker compose exec app reelsaw machine show the GPU the app process sees, the encoder in use and why, and where each on-device model runs. Workers (and reelsaw workers) lists every API and worker replica with its specs and what it is processing — needed when the HTTP process is not the one doing the work. macOS has no CUDA in Docker: run Ollama natively for Metal, and leave Speaches on the CPU (or point OPENAI_BASE_URL at a whisper.cpp or MLX server running on the host, which speaks the same API).

What to expect: processing time

Every stage scales with the length of the video. Expected seconds for a stage ≈ video seconds × factor, where the factor depends on the models (local vs vendor) and your machine:

stage

what sets the factor

local CPU (measured¹)

vendor (measured²)

download

your bandwidth and the site

0.015

0.015

transcribe

whisper size on CPU/GPU, or the STT vendor

0.25

0.065

refine (fillers, names)

LLM latency × number of candidate windows

0.5

0.03

find moments

LLM latency × (video minutes ÷ 5 + 1) calls

0.2

0.02–0.07

render

ffmpeg + face tracking per clip, ÷ render concurrency

0.04 (3 clips)

0.05 (3 clips)

Rule of thumb: a vendor stack finishes a video in about a fifth of its length; a local CPU stack takes about as long as the video. Refine runs alongside finding moments, so the stages don't add up exactly. Finding moments saves each part it finishes, so on a long video and a slow model a retry or a timeout continues from the last finished part instead of starting over, and the project shows "N of M parts, about X left".

¹ Apple-silicon laptop, qwen2.5:14b on Ollama + faster-whisper-small on Speaches (CPU), 20-minute podcast: download 15 s, transcribe 4.9 min, refine 10 min, find moments 4 min, render 47 s; 20 min in all. On a local 14B model the disfluency verifier (one call per candidate, ~40 s each) is the bottleneck, not transcription; a smaller model or DISFLUENCY_LLM=false cuts refine to a fraction. ² Same video through the Vercel AI Gateway, whisper-1 + claude-haiku-4.5: download 13 s, transcribe 77 s, refine 31 s, find moments 24 s, render 56 s; 3.4 min in all. Gemini 2.5 Flash took 5 min (long "thinking" outputs), Sonnet 4.5 about 4. Mixing a vendor LLM with a local TRANSCRIPT_ALIGN_MODEL makes render the slowest stage (5–9 min: every cut is re-aligned on the CPU); keep alignment on the same vendor as transcription. Full numbers in eval/bench/results/.

These factors are only the starting point. The app learns from its own finished projects: each stage's expected time is the median over your recent projects that ran with the same models, shown next to the elapsed time on the project page while it runs, in the activity feed when a stage finishes (Transcribe finished in 4:50 (expected ~4:00)), and as a per-stage expected vs actual table under Settings → System → Machine. There is no calibration step; the runs are the calibration.

Switching models without a restart

.env names the default models and keys; an admin can change both at any time under Settings → System → Models. Each of the three pickers (language, transcription, alignment) opens provider first: pick a provider and, if it has no key or server URL yet, enter it right there; its models then appear. Cloud vendors are asked live, a self-hosted Ollama or Speaches shows what is installed and downloads anything else with a progress bar, and the search box filters every provider at once. Saving a key tries it first (the provider's models are listed with it) and a wrong one is refused; saving a model checks it too (key present, server reachable and model installed, transcription returns word timestamps). Keys are stored encrypted in the database with a key derived from BETTER_AUTH_SECRET and never shown again (the UI sees the last four characters); rotating that secret makes stored keys unreadable, and the UI asks for them again. Changes survive restarts and reach worker processes within seconds; a stage already running finishes with the model it started with. "Back to .env" / "Remove" drop an override. Over the API: admin.models.*, admin.providers.*.

Accounts and roles

The first account created on an instance is the admin; everyone else is a user. Admins see the system settings (models, storage, resource meters) and the Users page; users see their own projects and settings only. SIGNUP_MODE decides who gets in:

SIGNUP_MODE

Behaviour

open (default)

Anyone can sign up and use the app at once. Right for a single-user box and for a public instance that already confirms email.

approval

New accounts wait on a "Waiting for approval" page until an admin approves them (Users page, or reelsaw approve <email>). Admins get an email when EMAIL_DRIVER is set. Right for a private box, not a public product.

closed

No sign-ups. (The older ALLOW_SIGNUP=false is still read as closed; use SIGNUP_MODE.)

Every user has Settings → Project defaults (vocabulary, instructions, language, clip length, pauses, caption style…) applied to each new project they or their agents start; the new-project form starts from them and can save its current values back. Admins also see the System section (models, storage, resource meters) and /api/health includes machine numbers only for them (or with HEALTH_DETAILS=true).

Suspending an account (Users page) ends its sessions and refuses its API keys at the next request; removing one deletes its projects, clips and files. Locked out of the password? Sign-in has Forgot password when email is on. Without email, an operator sets a new one from the shell (docker compose exec app reelsaw set-password you@example.com); --must-change makes them pick their own at the next sign-in, without asking for the temporary password (the session is the proof they knew it). To promote an account from the shell: docker compose exec app reelsaw make-admin you@example.com.

Email (EMAIL_DRIVER=smtp|resend, EMAIL_FROM, SMTP_URL or RESEND_API_KEY) turns on approval notices, password reset, the ready note and, with REQUIRE_EMAIL_VERIFICATION=true, verification before the first sign-in. Confirmation and reset links work for 24 hours and only once; an expired link lands on a page that sends a new one. Sign-in has Forgot password? when email is on; choosing a new password signs out other devices. Without email everything else works; approvals are handled on the Users page.

The ready note is one email when projects finish and you have not come back to see them. The first project to finish schedules it (EVENT_PROJECT_DONE_READY_EMAIL_DELAY_MINUTES, default 20, Settings → System → Operations); another project that finishes while it is waiting joins the same note instead of sending its own. Opening the app, or an API or MCP call, counts as being here, so clips you stayed to watch are left out of the note. The first project, and later the 10th, 25th, 50th and 100th, get a line of their own. Nothing is sent when email is off. Each note has a one-click unsubscribe (the List-Unsubscribe header mail apps show, and a link in the footer); people can also turn it off under Settings → Account, with set_email_preferences over MCP, or an operator with reelsaw email-prefs <email> --ready off. Optional mail is split into topics (today ready), each with its own switch and its own unsubscribe link, so later kinds of mail can be turned off one at a time.

Hosting for many people

Everything below is off by default and switched on by environment (deploy/.env.example lists them), so a single-user box stays as it is:

  • Quotas: MAX_PROJECTS_PER_USER, MAX_SOURCE_MINUTES_PER_DAY, MAX_SOURCE_MINUTES_PER_MONTH, MAX_NEW_PROJECTS_PER_HOUR, MAX_STORAGE_MB, MAX_LIVE_JOBS_PER_USER (jobs queued or running from one account's own requests), MAX_RERENDERS_PER_HOUR (re-renders, hand-made clips and re-runs such as re-analyze and refine, per hour), MAX_REQUESTS_PER_MINUTE, MAX_REQUESTS_PER_HOUR and MAX_WEBHOOKS_PER_USER (0 = unlimited), with per-user overrides on the Users page. Storage counts sources, uploads (an upload already copied into a project counts once), rendered clips and intros/outros, and is checked on every way a file arrives. A refusal is a sentence that says what to do ("Monthly limit of 300 minutes reached (312 used); resets on 1 November"). The monthly window is the account's billing period when it has a plan, else the UTC calendar month. On an account with a minutes limit, a link whose site reports its length is refused when the project is created (with the upgrade offer), so no project is made for it. Otherwise the length is checked again before the download and once the file is probed, so an over-long video fails the project with that reason, once, without being fetched again. The first video of a plan with a free allowance is the first one whose length is known.

  • Watermark: WATERMARK_NON_ADMINS (default on) burns the logo (WATERMARK_LOGO_PATH) with WATERMARK_TEXT ("reelsaw.com") under it into the clips of accounts that are not admins; a paid plan removes it, and an admin can switch it per user on the Users page (Limits). Decided at render time, so a re-render after the change applies it. Clips already rendered keep what they were rendered with.

  • Usage (Users → Usage): projects, minutes of source, clips rendered, model calls and tokens (every model call writes a llm_usage row attributed to the project's owner), stored bytes.

  • Retention: SOURCE_RETENTION_DAYS / CLIP_RETENTION_DAYS delete stored files from finished projects (the store object and the local copy); rows and history stay. A clip re-renders from its source; once the source is removed, "fetch again" (Retry on the project) downloads it first. reelsaw retention runs a sweep by hand. With an S3 store, SCRATCH_CACHE_HOURS (24) is how long a worker keeps its local copies of stored files once a project is finished and idle; the bucket copy stays and comes back when a job needs it.

  • History pruning: EVENT_RETENTION_DAYS (90), LLM_USAGE_RETENTION_DAYS (180) and ADMIN_EVENT_RETENTION_DAYS (365) delete old rows in batches once a day (0 keeps everything); a project's run skeleton (created, run started, done) is always kept.

  • Bot protection: CAPTCHA_PROVIDER (Cloudflare Turnstile, reCAPTCHA, hCaptcha) with site and secret keys puts a widget on sign-up, sign-in and password reset.

  • Free-tier abuse (all off unless set; apps/server/src/abuse.ts): MAX_SIGNUPS_PER_IP_PER_DAY caps accounts per network address (behind a proxy, TRUST_PROXY reads CF-Connecting-IP, X-Real-IP, X-Forwarded-For); BLOCK_DISPOSABLE_EMAILS refuses temporary-address domains; one account per real mailbox (john.doe+x@gmail.com is johndoe@gmail.com); MAX_REQUESTS_PER_MINUTE / _HOUR cap changes (API mutations, MCP tool calls) per account, with plan and per-user overrides like the other quotas. An hourly pass flags accounts that share a sign-up address, arrive in bursts or use a temporary domain; the Users page shows the flags ("Flagged only"), reelsaw abuse lists them, and nothing is suspended automatically. Every refusal is a sentence.

  • yt-dlp from a datacentre: YouTube refuses many cloud IPs. YTDLP_PROXY is one URL or an operator-ranked pool (a home Pi, a cheap VPS, a paid proxy). {session} in a URL is replaced per download so a residential gateway can sticky-session one video and rotate the next. Reliability is a circuit breaker on each candidate and never changes the ranking among healthy ones. YTDLP_COOKIES_FILE helps too; a blocked download fails with "upload the file instead". reelsaw ytdlp-proxies shows the pool.

  • Legal: TERMS_URL / PRIVACY_URL appear on the sign-up form.

  • MCP over OAuth (MCP_OAUTH=true): Reelsaw becomes an OAuth 2.1 authorization server for MCP connectors (claude.ai, Claude Desktop): dynamic client registration, sign-in and a consent page in the browser, discovery at /.well-known/oauth-protected-resource/api/mcp (which names the issuer, <APP_URL>/api/auth, whose metadata is at /.well-known/oauth-authorization-server/api/auth). API keys keep working for CLIs. The consent page shows the address the approval returns to, registrations are capped at 20 per network address per day, and each person lists and revokes the apps they allowed under Settings → Account → Connected apps.

  • Client address behind a proxy (TRUST_PROXY, TRUSTED_PROXY_HEADER, TRUSTED_PROXY_HOPS): the sign-up cap and the registration limit key on the client address. With the default x-forwarded-for it is the entry TRUSTED_PROXY_HOPS places from the right (1 behind Caddy, 2 behind Cloudflare and Caddy), so addresses a client prepends do not count; the other headers are read only when selected.

Billing (enterprise edition)

The hosted service at app.reelsaw.com adds plans and billing through Stripe. That code lives in apps/server/src/ee and apps/web/src/ee under its own licence (LICENSING.md), ships in the same image, and runs only with REELSAW_EE=true plus STRIPE_SECRET_KEY and STRIPE_WEBHOOK_SECRET. Nothing else changes for a self-hosted install.

  • A plan is a named set of the limits above (ee/billing/plans.ts); the account's plan sits between the instance setting and the admin's per-user override. Free is a plan too: what an account with no, or a lapsed, subscription gets.

  • Stripe is the source of truth. A webhook (POST /api/billing/webhook) is acknowledged only once its event row and a billing.sync job have committed together; the job rewrites the account's row from Stripe, so events that arrive late, twice or out of order converge. A daily reconcile and reelsaw billing sync repair anything a lost webhook left behind.

  • Settings → Plan and billing shows the plan, the period's usage and the plan grid; Checkout and the Billing Portal are Stripe's pages. get_billing, billing_checkout_url and billing_portal_url do the same over MCP; reelsaw billing <email> shows the row, Stripe's view and the last webhook events.

  • A failed payment keeps the plan for BILLING_GRACE_DAYS (7), then the account is on Free. Admins can grant a plan without Stripe (Users → Limits → Plan, or reelsaw billing set-plan). A lapse or downgrade does not re-watermark clips already rendered; only new renders get the watermark. An account that already has a live Stripe subscription is sent to the Billing Portal to change it, never to a second Checkout.

  • Setting up a Stripe account: pnpm --filter @reelsaw/server stripe:setup --webhook --portal (rebuilds @reelsaw/shared first) creates the products, prices (by lookup key), webhook endpoint and portal configuration from the plan list, idempotently. Developing without a Stripe account: pnpm --filter @reelsaw/server stripe:mock stands in for api.stripe.com (STRIPE_API_BASE=http://host.docker.internal:4242) and test/billing-stack.test.ts drives the whole lifecycle through signed webhooks.

Design and decisions: docs/specs/ee.md.

MCP: drive Reelsaw from an agent

Reelsaw is an MCP server (Model Context Protocol, Streamable HTTP) at /api/mcp. With MCP_OAUTH=true (on at app.reelsaw.com), a client needs only the address: you sign in and approve it in the browser, and Settings → API keys and MCP shows the command and connector address.

claude mcp add --transport http reelsaw https://your-host/api/mcp   # then /mcp in Claude Code to sign in

In claude.ai or Claude Desktop, add a custom connector with the same address.

Scripts, and clients that cannot sign in, use an API key. Create one in Settings → API keys and MCP (shown once) and point the client at it:

claude mcp add --transport http reelsaw https://your-host/api/mcp --header "Authorization: Bearer rs_…"
{ "mcpServers": { "reelsaw": { "type": "http", "url": "https://your-host/api/mcp", "headers": { "Authorization": "Bearer rs_…" } } } }

Key permissions. A key acts as you, with one exception: the instance-administration tools (get_instance_settings, set_instance_settings, list_workers, get_queue, list_failures, get_failure, retry_failures, check_for_update, and instance-wide report_issue) need a key created with Allow instance administration (admin tools over MCP), a checkbox admins see in Settings. Keys are created without it by default, and a key that lacks it is refused with "This key cannot change instance settings". Keys created before permissions existed were given the administration grant when the database upgraded, so they keep working; it shows on the key in Settings, where you can revoke it or replace the key. A key with no permission record is never an administrator. Connectors that sign in through OAuth (MCP_OAUTH=true) never get the admin tools, whoever signs in.

Clients that launch a command instead of calling a URL with a header (Claude Desktop without MCP_OAUTH, Cursor's stdio entries) use reelsaw mcp: the same tools over stdio, forwarded to a running instance. It needs no database or models, only REELSAW_URL (default: the app in the same container) and REELSAW_API_KEY:

{ "mcpServers": { "reelsaw": { "command": "docker", "args": ["exec", "-i", "-e", "REELSAW_API_KEY=rs_…", "reelsaw-app-1", "reelsaw", "mcp"] } } }

Reelsaw is listed on the MCP registry as io.github.osbytes/reelsaw (server.json, published on every release), so registry-aware clients can install the bridge from the image without a running container to exec into:

{ "mcpServers": { "reelsaw": { "command": "docker", "args": ["run", "-i", "--rm", "-e", "REELSAW_URL=https://your-host", "-e", "REELSAW_API_KEY=rs_…", "ghcr.io/osbytes/reelsaw", "reelsaw", "mcp"] } } }

Automation tools can call the same endpoint without an MCP client: it is stateless and answers plain JSON, so a tools/call POST is one HTTP request. docs/n8n has ready-made n8n workflows that clip every new upload on a YouTube channel and, on the signed project.done webhook, queue each clip in Buffer (or upload it to YouTube Shorts), with a polling variant for an n8n that cannot receive webhooks, docs/make builds the same pipeline from Make's own modules (MCP Client and Custom webhook), and integrations/zapier is the source of a Zapier app built on the same tools (instant triggers through create_webhook). To be told when clips are ready instead of waiting, add a webhook (create_webhook, or Settings → Webhooks) and verify each delivery with any Standard Webhooks library:

import { Webhook } from "standardwebhooks";
const event = new Webhook(process.env.REELSAW_WEBHOOK_SECRET).verify(rawBody, request.headers); // throws if forged or stale
if (event.type === "project.done") for (const clip of event.data.clips) console.log(clip.title, clip.shareUrl);

The tools (generated from apps/server/src/mcp-tools.ts; each tool's full description and input schema say what it returns, whether it runs in the background and which neighbour to use instead):

Tool

What it does

list_projects

Your projects, most recently updated first.

get_project

One of your projects: status, settings, effective intro/outro, transcript summary, and every clip (id, title, hook, bounds, score, status, download links); replacedClips lists deleted or replaced clips for restore_clip.

create_project

Start clipping a video from a URL (anything yt-dlp fetches) or a file you uploaded: POST /api/upload with the file as the body, header x-filename and your API key as Authorization: Bearer, then pass the uploadId it returns.

update_project

Rename a project and/or change its saved settings (focus, instructions, clip length, maxClips, caption style, framing, pauses, refineEntities, refineDisfluency, disfluencyLlm, refineScenes); only the fields you pass change.

wait_for_project

Block until the project is done or failed, or timeoutSeconds pass.

get_transcript

The project's transcript as timed lines "[m:ss] text", optionally only a time range.

get_clip_captions

The captions of a clip's last render as an SRT or WebVTT file: the same lines and times as the video (intro included), even when the style has captions off.

get_project_transcript_file

The project transcript as an SRT or WebVTT subtitle file, one cue per transcript segment, in source-video seconds.

import_transcript

Use an SRT or WebVTT file as the project's transcript instead of transcribing: right after create_project (only the video is fetched) or on a done or failed project (it then re-cleans the transcript and picks clips again; accepted and rejected fixes, cuts and corrections on the old transcript are lost for good).

search_clips

Find your clips and projects by words in their title, hook or caption (every word must match; case-insensitive, typo-tolerant; best first).

get_project_events

The project's pipeline history, newest first (the same events the Activity panel shows; live progress ticks are not stored): stage starts and completions, clip renders, retries, failures with reasons.

get_clip

Everything about one clip: text, bounds, scores, emphasis, layers (assets, text, sounds), render diagnostics, video/thumbnail/editor links, and what the next render will use with where each comes from: style { own, effective, from: clip|project|default }, framing { own, effective: { mode, adjust, sections }, from }, bumpers { own, effective } (intro/outro).

update_clip

Edit one clip (3 s-10 min): only the fields you pass change; style, layoutSections and layers replace the whole value (null = project's / none).

create_clip

Add a clip from a time range of the source (needs a transcript).

delete_clip

Delete one clip of a live project; a render in progress stops.

delete_clips

Delete several clips of one live project in one step (the selection bar's Delete); renders in progress stop.

rerender_clip

Queue one clip for rendering again (after rerender: false edits, a failure, or an intro/outro change).

rerender_clips

Queue several clips of one project for rendering again in one step (after edits or failures).

get_media_links

Links anyone can download without an Authorization header, for a scheduler (Buffer, Metricool), Instagram's video_url, Zapier/Make or a person.

list_media_links

Your share links, newest first.

revoke_media_link

Turn share links off now: one (linkId), a clip's (clipId), a project's (projectId) or all of yours (all: true).

list_webhooks

Your webhook endpoints.

create_webhook

Subscribe a URL to events: project.created, project.done (with every clip, share links and the zip), project.failed, clip.done (also re-renders), clip.failed.

update_webhook

Change a webhook's url, events or description, or turn it on or off (enabled).

delete_webhook

Remove a webhook (unsubscribe); nothing more is sent to it and its delivery log goes with it.

rotate_webhook_secret

A new signing secret for a webhook.

test_webhook

Send a webhook.test delivery now and wait for the answer.

list_webhook_deliveries

The delivery log, newest first.

resend_webhook_delivery

Send a delivery again (a new series of attempts, same webhook-id and body, signed afresh).

export_project

A download link for a zip of the project's rendered clips with YouTube/TikTok metadata.

report_issue

A download link for an issue-report zip (the same file Report an issue in the app saves; admins may include instance-wide diagnostics).

retry_project

Resume a failed or stuck project from its furthest completed stage: re-render unfinished clips if there are clips, else pick clips, refine or transcribe again.

refine_project

Re-run refine steps on the existing transcript: entities (name spelling), disfluency (filler and repeat cuts), scenes (shot cuts); all three when steps is omitted.

reanalyze_project

Pick clips again from the existing transcript (needs one), optionally with new settings.

search_assets

Find assets.

add_asset

Add from exactly one source: a public url or an uploadId (video, image, GIF, SVG or sound) becomes one of your library uploads; a search_assets catalog / provider hit (provider + externalId) materializes one instance-shared copy (checksum verified, license recorded; not listed in your library).

update_asset

Rename one of your own library items, replace its tags, or change how long a still image shows (deleted items too; shared catalog and provider items cannot be changed).

delete_asset

Delete one of your uploads: it leaves the library and your defaults and goes to Deleted.

restore_asset

Bring one of your deleted library items back (the id from delete_asset or list_deleted); restoring an item that is not deleted changes nothing.

asset_usage

Where a library item is used: as intro, outro, overlay or music on projects and clips (replaced clips left out), and in layer templates.

set_default_bumper

Make a library item your default intro or outro for projects you create from now on (assetId null clears it).

set_project_bumpers

Set one project's intro and/or outro: "default" (your current default), "none", or a library asset id; a slot left out is untouched, and introSec/outroSec are saved with the project.

set_clip_bumpers

Override the intro and/or outro of up to 200 clips of one project: "inherit" (follow the project), "none", or a library asset id; a slot left out is untouched.

list_layer_templates

Your layer templates (layers that fit any clip length, plus an optional intro / outro), newest first.

save_layer_template

Create a template from exactly one of clipId (that clip's layers: spanning = fill, touching the start = head, touching the end = tail, else anchored to the nearer edge; plus the intro and outro the clip renders with) or layers (each with a timing, see list_layer_templates; [] with bumpers = an intro / outro only template).

update_layer_template

Rename a layer template, replace its layers (the whole list, each with a timing) and/or replace its intro / outro (bumpers; null = it sets neither); a field left out is untouched.

delete_layer_template

Delete a template (it goes to Deleted) and drop it from your project defaults; clips that already got its layers keep them.

restore_layer_template

Bring back a deleted layer template (the id from list_layer_templates deleted or list_deleted).

apply_framing

Make one clip's framing the whole project's, for a recording whose layout never changes (a slide deck with a webcam tile, a fixed camera): the clip's section at at (else its first framed section) gives the mode and its nudge / subject / crop, which become the project's reframe and reframeAdjust; every clip's own sections are cleared (unless keepOverrides) and the finished clips re-render.

apply_layer_template

Write a template onto clips of one project (up to 200 clipIds, or all): its layers fitted to each clip's length, and the intro / outro it sets as each clip's own.

delete_project

Delete one of your projects and all its clips; work in progress stops.

purge_project

Permanently remove a deleted project and its files so the storage is freed (find it with list_deleted or list_projects deleted: true).

restore_project

Bring back a deleted project with its clips and files (find it with list_deleted or list_projects deleted: true); restoring a project that is not deleted changes nothing.

restore_clip

Bring back a deleted clip or one a re-analyze replaced (list_deleted or get_project → replacedClips); it goes to the end of the project's clips.

list_deleted

Restorable deletes (the Deleted page): one newest-first list of projects, clips of live projects, library items and templates.

purge_clip

Permanently remove a deleted clip of a live project and its rendered file so the storage is freed (find it with list_deleted).

purge_asset

Permanently remove an already-deleted library upload and its files so the storage is freed (find it with list_deleted).

purge_layer_template

Permanently remove an already-deleted layer template (find it with list_deleted).

check_for_update

Admins only: is a newer Reelsaw published?

list_releases

Published Reelsaw versions from GitHub (reelsaw.com/releases): tag, date, a short summary and the change notes.

get_instance_settings

Admins only: the instance settings in force (sign-up mode, email verification, captcha, legal links, quotas, upload limits, retention, stall watchdog, update check, health details) and where each comes from.

list_workers

Admins only: every Reelsaw process that has checked in (API, worker, combined app): CPU, memory, disk (free/total MB, -1 = unmeasurable; diskLeasedMB = claimed by jobs in flight), GPU, render slots, queues it listens on, and its jobs.

get_failure

Admins only: one failed project or clip (title, owner email, stage, full recorded reason, when).

get_queue

Admins only: waiting, running and retrying job counts per pipeline stage with when the longest wait began (oldestWaitingAt), plus the jobs currently in flight (project title, attempt, started, and a retrying job's lastError).

list_failures

Admins only: failed projects and clips across the instance (owner email, stage, reason, when).

retry_failures

Admins only: retry matching failed projects and clips through the same lifecycle as retry_project / rerender_clip (never a raw queue poke).

set_instance_settings

Admins only: change instance settings without a restart; every process applies them within seconds and the change is audited.

get_email_preferences

Which optional emails you get, by topic.

set_email_preferences

Turn topics of optional email on or off; omitted topics stay as they are.

get_billing

Hosted instances: your plan and subscription, the billing period, the limits in force with where each comes from (instance, plan, an admin's override), what you have used this period, whether renders carry a watermark, and the plans on offer.

billing_checkout_url

Hosted instances: a Stripe Checkout URL for a plan (or, when the account already pays, the Billing Portal page that confirms the plan change).

list_transcript_flags

The fixes Reelsaw made or suggests in a project's transcript: misheard names, fillers and repetitions to cut, and your own cuts and corrections.

review_transcript_flags

Accept or reject suggested transcript fixes (ids from list_transcript_flags), optionally with your own replacement text for an accepted name fix.

add_transcript_correction

Make captions show "text" instead of the words spoken between startSec and endSec (a misheard name, say); the span must cover whole words.

add_transcript_cut

Remove the words spoken between startSec and endSec from the transcript, so captions and the rendered video skip them; the span must cover whole words.

get_project_defaults

The settings every new project starts with (Settings, Project defaults): language, vocabulary, instructions, focus, clip lengths, pauses, caption look and refine steps.

set_project_defaults

Replace the settings every new project starts with; existing projects keep theirs.

set_project_source_text

Hide or keep text burned into the source video (captions, logos) for the whole project: hide covers it, keep leaves it, always shows it.

update_project_style

Change the caption/hook look for the whole project (font, colors, size, position, words per line).

list_decisions

Your decision log: what you accepted, rejected, cut, corrected, edited, deleted or restored, newest first, kept after a project is deleted so Reelsaw learns from it.

export_decisions

A page of your decision log in export form, oldest first, for evals.

forget_decisions

Delete entries from your decision log so Reelsaw stops learning from them: one project's (projectId, live or already deleted forever) or, without projectId, all of them.

set_learning

Whether Reelsaw records your decisions (accepts, rejects, cuts, clip edits) to learn from (Settings → Learning, on by default).

billing_portal_url

Hosted instances: a Stripe Billing Portal URL to see invoices, change the card or cancel at period end.

get_project / get_clip show each clip's effective intro/outro and where it comes from. They call the same tRPC procedures as the web app, so ownership checks and validation are identical (apps/server/src/mcp.ts; the catalogue both transports advertise is apps/server/src/mcp-tools.ts). The same key also authenticates tRPC (/api/trpc/*), uploads and media downloads, so an agent can fetch the rendered clips. Keys are hashed at rest (@better-auth/api-key); revoke them from Settings.

Operator CLI

docker compose exec app reelsaw <command> (see TROUBLESHOOTING for when to reach for which). Generated from the header of apps/server/src/cli.ts.

Run as docker compose exec app reelsaw <command> (from source: pnpm --filter @reelsaw/server cli <command>).

Command

What it does

reelsaw version [--json]

running version, latest published release, how to upgrade

reelsaw releases [--json] [--limit n]

published versions from GitHub (reelsaw.com/releases / list_releases)

reelsaw users

list accounts: role, status, sign-up date

reelsaw approve <email>

approve a waiting account (when email is off or the admin is locked out)

reelsaw make-admin <email>

promote an account to admin (approves it first if needed)

reelsaw set-password <email> [--must-change]

set an account's password from a prompt (or two stdin lines); --must-change requires a new one at next sign-in. Do not put the secret on the command line

reelsaw retention

run the retention sweep now (SOURCE_RETENTION_DAYS / CLIP_RETENTION_DAYS)

reelsaw deleted [email]

restorable deletes as one list with their purge date. No email: projects across the install. With email: that account (the Deleted page / list_deleted)

reelsaw restore <projectId>

undo a project delete (same as projects.restore and the restore_project tool)

reelsaw restore-clip <clipId>

bring back a deleted clip or one a re-analyze replaced

reelsaw delete-clip <clipId>

delete one clip, restorable like a project (same as clips.delete and delete_clip)

reelsaw delete-clips <projectId> <clipId…>

delete several clips of one project at once (same as the selection bar, clips.deleteMany and delete_clips)

reelsaw purge-clip <clipId>

delete one already-deleted clip forever (same as clips.purge and purge_clip)

reelsaw captions <clipId> [--format srt|vtt] [--words]

print a clip's rendered captions as an SRT or VTT file (same as clips.captions and get_clip_captions)

reelsaw transcript-file <projectId> [--format srt|vtt] [--words]

print a project's transcript as an SRT or VTT file (same as projects.transcriptFile and get_project_transcript_file)

reelsaw import-transcript <projectId> <file.srt|file.vtt> [--language xx]

use a subtitle file as the transcript instead of transcribing (same as projects.importTranscript and import_transcript)

reelsaw update-project <projectId> [--title t] [--set key=value]… [--clear key]…

rename a project or change its settings without re-running anything (same as projects.update and update_project); values are JSON or text

reelsaw source-text <projectId> <hide|keep|always>

hide or keep text burned into the source and re-render finished clips (same as projects.setSourceText and set_project_source_text)

reelsaw update-style <projectId> [--set key=value]… [--keep-overrides] [--no-rerender]

change the caption/hook style of the whole project, clearing per-clip overrides and re-rendering (same as projects.updateStyle and update_project_style); values are JSON or text

reelsaw create-project <url> [--user <email|id>] [--title t] [--set key=value]…

start a project from a link for an account (the given user, else the only account); same as projects.create and create_project; takes the same settings as update-project

reelsaw retry <projectId>

resume a failed or stuck project from its furthest completed stage (same as the Retry button, projects.retry and retry_project)

reelsaw reanalyze <projectId> [--set key=value]…

pick clips again from the existing transcript, optionally with new selection settings; clips made or edited by hand stay (same as projects.reanalyze and reanalyze_project)

reelsaw refine <projectId> [--steps entities,disfluency,scenes] [--vocabulary text]

re-run refine steps on the existing transcript, then re-render (same as projects.refine and refine_project)

reelsaw delete-project <projectId> [--forget-decisions]

delete a project, restorable like a clip (same as projects.delete and delete_project); --forget-decisions forgets its decisions when it is purged

reelsaw export <projectId> [clipId…] --out <file.zip>

write the zip of rendered clips with YouTube / TikTok metadata; the same conditions as the Export button and export_project (every picked clip must belong to the project, at least one rendered)

reelsaw create-clip <projectId> <startSec> <endSec> [--mode exact|ai]

add a clip from a time range of the source: exact keeps the bounds, ai picks the best moment inside them (same as clips.create and create_clip)

reelsaw update-clip <clipId> [--title t] [--hook t] [--caption t] [--start s] [--end s] [--rerender|--no-rerender]

edit one clip; a change burned into the video re-renders it unless told not to (same as clips.update and update_clip)

reelsaw purge [--dry-run]

remove deleted projects and replaced clips past DELETED_RETENTION_DAYS now

reelsaw purge-project <projectId> [--forget-decisions]

delete one already-deleted project forever (frees storage; minutes stay counted); its decisions stay in the decision log unless --forget-decisions

reelsaw decisions <email> [--kind k] [--project id]

the account's decision log: what it accepted, rejected, cut and edited (Settings → Learning / list_decisions)

reelsaw decisions export <email> [--out f.jsonl] [--since iso]

the decision log as JSON lines, for evals (export_decisions; harvest-decisions.ts, the decisions suite)

reelsaw decisions forget <email> [--project id]

delete the account's decisions, or one project's (forget_decisions)

reelsaw decisions learning <email> on|off

record the account's decisions or stop (Settings → Learning / set_learning)

reelsaw decisions backfill [--dry-run]

write decisions still held in transcripts from before the log existed

reelsaw media-links <email> [--project id] [--json] [--active]

the account's share links: target, state, expiry, last use (Settings → Share links / list_media_links)

reelsaw media-links create <projectId> [clipId…] [--kind clip|thumb|srt|vtt|export] [--ttl days] [--label text]

a share link per clip (or one for the zip) that needs no sign-in (get_media_links); reuses the active one unless --ttl or --label

reelsaw media-links revoke <linkId> | --project <id> | --clip <id> | --all <email>

turn share links off now (revoke_media_link)

reelsaw webhooks <email> [--json]

the account's webhook endpoints: masked URL, events, status, last delivery (Settings → Webhooks / list_webhooks)

reelsaw webhooks deliveries <endpointId> [--status failed] [--json]

an endpoint's delivery log (list_webhook_deliveries)

reelsaw webhooks resend <deliveryId> | test <endpointId> | enable <endpointId> | disable <endpointId>

send one again, send a test now, or turn an endpoint on / off (resend_webhook_delivery, test_webhook, update_webhook)

reelsaw machine [--json]

this box: cpus, memory, disk, GPU, ffmpeg encoder in use and why, where each on-device model runs (same as Settings → System → Overview)

reelsaw ytdlp-proxies [--json]

yt-dlp egress pool: operator priority vs circuit (reliability), never URLs

reelsaw workers [--json]

every process that has checked in: role, specs, last seen, current jobs (same as Settings → System → Workers)

reelsaw queue [--json]

waiting / running / retrying jobs per stage, plus what is in flight (same as Settings → System → Queue)

reelsaw failure <projectId|clipId> [--json]

one failed project or clip: owner, stage, the full recorded reason (same as clicking a row under Settings → System → Queue → Failed work)

reelsaw retry-failures [--stage s] [--q text] [--since 24h] [--json]

retry failed projects and clips matching a reason after a platform fix (same as Settings → System → Queue → Retry all matching)

reelsaw status <projectId>

project, refine steps, clips, live jobs, last error

reelsaw events <projectId> [n=30]

recent pipeline events

reelsaw clip <clipId>

everything about one clip incl. render diagnostics

reelsaw faces <clipId> [a-b] [--json]

face boxes per sampled frame + split speaker tracks

reelsaw rerender <clipId…>

queue a re-render of one or several clips; a clip that never got its AI title is prepared first (same as the UI buttons, clips.rerender / rerenderMany and rerender_clip / rerender_clips)

reelsaw import-report <zip> [--user <email>] [--source <file>] [--no-download] [--allow-any-url]

rehydrate an issue report as a new project (dev only; the zip's contents are untrusted data, see its README)

reelsaw report [projectId] [--out file.zip] [--instance] [--description text] [--user email]

write an issue-report zip (same as Report an issue); --instance adds unfinished work, failed jobs, usage and error logs across every account (operator)

reelsaw cuts <clipId> [--save <dir>] [--fixture <dir>]

dry-run the disfluency cut search for a clip and print every candidate verdict; can save the removed and context audio, and export each flagged span as an offline regression case for pnpm eval:cuts

reelsaw align <projectId> <start> <end>

word timestamps from the align model for a source window

reelsaw hear <clipId> [start] [dur]

transcribe a range of the RENDERED clip (what viewers hear)

reelsaw flags <projectId> [from] [to]

transcript flags (entities / disfluencies / collapsed loops) with status

reelsaw storage [sweep [--dry-run] | reclaim <MB> [--dry-run]]

disk use: unique, scratch-only, scratch copies of stored files (S3), leased to jobs in flight, duplicate-source, expired-but-present bytes; sweep removes leftover files nothing points at and evicts idle cache copies (the reconciler's hourly sweep); reclaim evicts least-recently-used copies until that many MB are freed (what a job short of disk does on its own)

reelsaw stuck

every unfinished project/clip: age, last event, live jobs, problems

reelsaw recover <clipId|projectId>

clip: re-run the right step (prepare or render); failed project: retry it; any other project: put its lost jobs back

reelsaw logs <id> [n=60] [--since 2h] [--level debug] [--module render] [--raw]

log lines mentioning a project/clip/job/request id from LOG_FILE (survives redeploys)

reelsaw log-level [<level|module=level,...>|reset] [--for 15m]

show the log levels in force, or set them for a while (e.g. render=debug); all processes follow within ~5 s

reelsaw scenes <projectId> [--store]

re-detect shot cuts (adaptive); --store saves them for layout/snapping/overlays

reelsaw library list|search|add|update|rename|delete|restore|purge|usage|default|reindex [--user <email>]

the library: intro/outro videos and images, logos, stickers, GIFs, SVGs, music and sound effects. list shows your files, search looks in your library, the Reelsaw catalog (default) or an enabled provider (pexels, pixabay, freesound, iconify, openverse, giphy, klipy); add takes a file, a link or a search hit (license and credit are recorded); update sets tags, rename the name, default the intro or outro for new projects. Acts as the given user, else the only account on this install. reindex rebuilds the asset reference table ("used by", purge safety) for every project, to completion

reelsaw catalog status|refresh

the Reelsaw catalog manifest in force (version, source, last check); refresh asks CATALOG_URL now

reelsaw project-bumpers <projectId> [--intro default|none|<id>] [--outro …] [--intro-sec n] [--outro-sec n] [--rerender]

a project's intro/outro: the library default, none or a specific item

reelsaw clip-bumpers <projectId> <clipId…> [--intro inherit|none|<id>] [--outro …] [--intro-sec n] [--outro-sec n] [--rerender]

a clip's own choice (inherit the project's, none or an item); both act as the project's owner; --rerender re-renders only the affected clips

reelsaw clip-layers <projectId> <clipId> [--set '<json array>' | --add <assetId> --from <sec> --to <sec> [--x f] [--y f] [--w f] [--opacity f] [--loop|--no-loop] | --add-text <text> --from <sec> --to <sec> [--x f] [--y f] [--size f] [--color #RRGGBB] [--font <family>] [--box] [--opacity f] | --add-sound <assetId> --from <sec> [--to <sec>] [--volume 0-1] [--offset <sec>] [--fade <sec>] [--duck|--no-duck] [--loop|--no-loop] | --remove <layerId> | --clear] [--rerender]

everything laid over or played under a clip, one list (source seconds, x/y/w/size as fractions of the 9:16 frame); a sound of 8 s or less, is a one-shot at full level, anything else music (ducked, faded, looped); with no change flag it lists them

reelsaw apply-framing <clipId> [--at <sec>] [--keep-overrides] [--no-rerender]

make one clip's framing (its section at --at, else its first framed one: mode plus nudge / subject / crop) the project's default for every clip, clearing the others' own sections and re-rendering the finished ones (same as the editor's "Use for all clips" and apply_framing)

reelsaw templates list | show <id> | save <projectId> <clipId> <name…> | rename <id> <name…> | set <id> [--layers '<json array>'] [--intro keep|none|<id>] [--outro …] [--intro-sec n] [--outro-sec n] | delete <id> | restore <id> | purge <id> | apply <templateId> <projectId> [clipId…] [--add] [--rerender]

layer templates: layers whose timing fits any clip length (head / tail / fill / relative), plus an optional intro / outro (keep = clips keep their own); save builds one from a clip's layers, intro and outro, apply writes them onto the clips of a project (all of them without clip ids; --add keeps the clip's own layers, default replaces; the intro / outro it sets replaces the clip's either way); --rerender re-renders the finished ones. Same procedures as the Templates page and the MCP tools

reelsaw overlays <projectId> [--store]

scan the source for burned-in captions / watermarks; --store saves them (renders then hide them)

reelsaw usage [--days 30]

per account: projects, source minutes, clips, model calls / tokens / reported cost, minutes transcribed, stored bytes (same numbers as Users → Usage)

reelsaw limits <email> [--watermark always|never|follow]

an account's quotas: instance value, plan, override, in force, in use now; --watermark sets the per-user override (same as Users → Limits)

reelsaw email-prefs <email> [--<topic> on|off]

an account's optional mail by topic (ready: the note when projects finish; same as their Settings → Account and the email's unsubscribe link)

reelsaw abuse [--run]

accounts the hourly abuse pass flagged (shared sign-up address, bursts, temporary domains); --run recomputes the flags now

reelsaw billing <email> | sync [<email>] | set-plan <email> <plan|none>

hosted (REELSAW_EE): an account's plan, subscription, Stripe's view and its last webhook events; sync re-reads Stripe for one account or every subscription (the daily reconcile, by hand); set-plan grants a plan without Stripe (none removes it)

reelsaw settings [KEY=value…] [--clear KEY…]

instance settings in force (sign-up mode, quotas, retention, …): .env default, admin override, effective; KEY=value changes one without a restart, --clear KEY goes back to .env (same as Settings → System and set_instance_settings)

reelsaw mcp

the MCP server over stdio for clients that launch a command: same tools as /api/mcp, forwarded to REELSAW_URL with REELSAW_API_KEY (needs no database; see src/mcp-stdio.ts)

Settings and precedence

Where a value comes from, from the weakest source to the strongest (a later link overrides an earlier one):

What

Chain

Models (language, transcription, alignment)

.env (AI_MODEL, TRANSCRIPTION_MODEL, TRANSCRIPT_ALIGN_MODEL) then the admin's runtime override (Settings → System → Models, stored in instance_settings). A stage already running keeps the model it started with.

Provider keys and URLs

.env then the encrypted keys admins enter in Settings.

Instance settings (sign-up mode, email verification, captcha, legal links, quotas, upload limits, refine-stage gates, retention, stall watchdog, ready-email delay, update check, health details)

.env then the admin's runtime override (Settings → System, reelsaw settings, set_instance_settings; stored in instance_settings, applied by every process within seconds). An override whose precondition goes away (email verification without an email driver) is ignored and shown as such.

Refine gates (TRANSCRIPT_CORRECTION, DISFLUENCY_REMOVAL, DISFLUENCY_LLM, REFINE_SCENES = the on-screen text scan; shot cuts are always detected)

.env then the instance setting (Settings → System → Pipeline). TRANSCRIPT_CORRECTION and REFINE_SCENES default on, the disfluency gates off. That value is a ceiling: when off, every project skips the step. When on, each project's refineEntities / refineDisfluency / disfluencyLlm / refineScenes (and the user's Project defaults) decides: name fixes and the text scan run unless turned off, filler removal only when turned on.

Other stage knobs (TRANSCRIPT_BIAS, VAD, ALIGNER, FACE_DETECTION, TEXT_DETECTION, concurrency)

.env only. No UI or per-project override exists.

A project's settings

built-in defaults, then the user's Project defaults, then what the create form or agent sent. The result is frozen into projects.settings; later changes (updateStyle, re-analyze with new settings) patch that row.

A clip's style

the project's style, then the clip's own override (clips.style). updateStyle clears clip overrides unless told to keep them.

Layer template

the user's Project defaults layerTemplateId, then the project's setting (frozen into projects.settings at creation), then each clip the project creates from then on gets the template's layers and the intro / outro it sets (as the clip's own choice). Existing clips never change with the setting; apply_layer_template / the selection bar's Template… / reelsaw templates apply do those.

Framing

auto (face detection decides), then the project's reframe with its reframeAdjust (a nudge, subject or crop; apply_framing sets both from one clip), then the clip's layoutOverride, then a section of the clip (layoutSections[i], mode and adjustment).

Quotas

.env MAX_* (0 = unlimited), then the instance setting an admin saved under Settings → System → Limits, then what the account's plan grants (hosted, REELSAW_EE), then a per-user limit set on the Users page. The Users page says which applies.

Watermark

An admin's per-user choice (Users → Limits), else the plan (a paid plan removes it), else WATERMARK_NON_ADMINS for everyone who is not an admin.

Share links

.env MEDIA_LINK_TTL_DAYS (default 90) and MEDIA_LINK_ALLOW_NEVER (default true; hosted sets false), then the instance setting (Settings → System → Limits). A link may ask for less, never more. MEDIA_LINKS_REDIRECT (.env only, S3) answers with a redirect to a presigned URL instead of the file.

Webhooks

MAX_WEBHOOKS_PER_USER follows the quota chain (default 10). ALLOW_PRIVATE_WEBHOOK_URLS (default false: public https only) and WEBHOOK_DELIVERY_RETENTION_DAYS (default 14) are instance settings; WEBHOOK_CONCURRENCY is .env only.

Learning (the decision log)

On for every account; the user's Settings → Learning switch (or set_learning) turns recording off. DECISION_RETENTION_DAYS (instance setting, default 0 = keep) prunes old decisions; a purge keeps a project's decisions unless the user ticked "Also forget what Reelsaw learned from it" when deleting it (honoured by every purge, the retention sweep included) or in the Delete forever dialog.

Two things to know:

  • An env / instance refine gate wins. DISFLUENCY_REMOVAL=false (or the matching Pipeline setting) skips filler removal for everyone even when a project asks for it; the new-project form disables that toggle and says so. TRANSCRIPT_BIAS=false still silently ignores vocabulary hints (.env only). If a project setting seems to do nothing, check the gate first (TROUBLESHOOTING, "A project or clip setting has no effect").

  • Existing projects keep their frozen settings. Changing your Project defaults affects new projects only.

Code: apps/server/src/pipeline/settings.ts (project), resolveStyle in packages/shared/src/index.ts (style), apps/server/src/pipeline/render.ts (framing), apps/server/src/ai/models.ts (models), apps/server/src/quota.ts (quotas).

Architecture

apps/web      React 19 + Vite + Tailwind + TanStack Query + tRPC client + Better Auth client
apps/server   Hono + tRPC + Better Auth + Drizzle (Postgres) + pg-boss workers + ffmpeg/yt-dlp
packages/shared  zod schemas and types shared by both

Server composition (apps/server/src/services.ts): createServices() builds config → media tools → ai → storage → database (+migrations) → events → queue → auth, checking each one. The resulting Services object is injected into the tRPC context, the HTTP app and every pipeline stage (StageContext), so nothing reaches for a global. Boot logs one boot step ok line per step (JSON, or pretty on a terminal) with the step name and its duration in ms: config, data dir, resources, media tools, LLM call capture, AI models, the on-device detectors, storage, database, model settings, event bus, job queue, email, auth. Steps whose feature is switched off say so in their name (face detector (disabled)). A failing step stops the process with exit code 78 and a message that names the step and the fix, for example a transcription model that returns no word timestamps or a model server that is unreachable. Some steps depend on the process's role: a process with RUN_WORKERS=false only warns when free disk is under MIN_FREE_DISK_MB (so a full disk cannot lock out the admin UI that frees it) and skips the face, text and voice detectors; a process with RUN_HTTP=false skips the mail server check. The AI-models step checks the environment's models; once the database is up the effective models (after an admin's runtime override) are checked too.

Pipeline (each box is a pg-boss queue; each stage emits events and enqueues the next):

project.ingest ─▶ project.transcribe ─┬▶ refine.entities   ─┐
 yt-dlp / upload   audio → chunks       ├▶ refine.disfluency ─┼▶ project.analyze ─▶ clip.render (×N)
 ffprobe           parallel transcribe  └▶ refine.scenes     ─┘  LLM picks moments    layout + captions
 store source      checkpoint per chunk    (fan-out, fan-in       snap to sentences    cuts + crossfades
                                            on the transcript row)                     finalize project

The three refine jobs are independent: each writes its result under a row lock on the transcript (entity flags win over overlapping disfluency flags), and the last one to finish enqueues analyze. A refine step that exhausts its retries is marked failed and skipped; it never blocks the project. "Re-run refine" re-queues any subset on the existing transcript, e.g. after editing the vocabulary. Chunk transcription runs TRANSCRIBE_CONCURRENCY calls in parallel (auto: 1 for a self-hosted whisper, 3 for cloud vendors).

Durability model:

  • pgboss.job is the source of truth for "what should run". projects.status / clips.status is the source of truth for "what happened".

  • Each stage checks for existing output first (source file in storage, transcript chunk rows, clip rows, rendered clip in storage) and skips work that is done.

  • Queues use policy: exclusive + singletonKey so a stage can never run twice concurrently for the same project/clip.

  • heartbeatSeconds (15 s) on every queue: if a worker dies, pg-boss notices the missed heartbeat on its next supervisor pass (every 60 s), fails the job and retries it elsewhere after a 15 s delay that doubles per retry. Budget a minute or two, not seconds; each interruption uses one retry.

  • On boot and every 5 minutes a reconciler re-enqueues any in-progress project or clip that has no live job.

  • Manual Retry resumes from the furthest completed stage; Re-analyze keeps the transcript and picks new clips.

Development

Live-reload loop in Docker (source mounted, no image rebuilds; rebuild the dev image only when dependencies change):

docker compose -f docker-compose.yml -f docker-compose.dev.yml --profile local-ai up -d app
open http://localhost:5173      # Vite + HMR; /api and /media proxied to the server (tsx watch)
docker compose exec app reelsaw stuck

Models for running outside Docker: pnpm models:download (face, on-screen text, voice activity, forced alignment; ~105 MB).

pnpm install
docker compose up -d db
cp .env.example .env   # set DATABASE_URL=postgres://reelsaw:reelsaw@localhost:5432/reelsaw
pnpm dev               # server on :3000 (tsx watch), web on :5173 (vite, proxies /api and /media)

Requires ffmpeg, ffprobe and yt-dlp on your PATH for local dev. Schema changes: edit apps/server/src/db/schema.ts, then pnpm db:generate; migrations run automatically on boot.

Most tests run against the local stack (they create and delete their own users and data); CI runs only the ones that need no stack, so run the ones you touched against yours:

pnpm --filter @reelsaw/server test

Releases and distribution

Every image is built by the release workflow with an SBOM and SLSA provenance attached, and a GitHub artifact attestation for its digest. Verify what you are about to run:

gh attestation verify oci://ghcr.io/osbytes/reelsaw:latest --owner osbytes
docker buildx imagetools inspect ghcr.io/osbytes/reelsaw:latest --format '{{ json .SBOM }}' | head -c 400

Users install from a published image. The runtime image contains only the minified server bundle (apps/server/scripts/bundle.mjs), the web build and production dependencies, which scripts/check-image.sh <image> verifies.

  • Tag vX.Y.Z → .github/workflows/release.yml builds amd64 + arm64, checks the image, pushes to GHCR (and Docker Hub when vars.DOCKERHUB_IMAGE + DOCKERHUB_USERNAME/DOCKERHUB_TOKEN are set) and attaches reelsaw-X.Y.Z.zip (the deploy/ folder, image pinned) to the release.

  • Before tagging: build locally, run scripts/check-image.sh reelsaw:latest, and boot deploy/ in a scratch directory with only a .env (see the spec's verification section).

  • Versions and changelogs come from changesets: every PR adds one (pnpm changeset), a "Release" PR bumps the version, merging it pushes the tag. Full flow, versioning policy, pre-releases and hotfixes: docs/RELEASING.md.

Issue reports

Report an issue (project ⋯ menu, a failed clip, the account menu) downloads a zip with the project's data, pipeline decisions (stage_artifacts), logs, system info and recent errors, and the user's description. Secrets never go in. Admins can tick Instance-wide diagnostics to include unfinished work, failed jobs, usage and error logs across every account — for queue, disk or machine problems, not only their own projects. To reproduce one locally:

docker compose cp ~/Downloads/reelsaw-report-….zip app:/data/tmp/report.zip
docker compose exec app reelsaw import-report /data/tmp/report.zip [--user you@example.com]

Everything in a report except its README and manifest is untrusted data; see TROUBLESHOOTING before letting an agent work on one. Spec: docs/specs/distribution-and-issue-reports.md.

Environment reference

Every variable, with its default and meaning, is in docs/CONFIG.md (generated from apps/server/src/config.ts; pnpm docs:gen refreshes it). .env.example and deploy/.env.example are generated from the same source.

Observability

Health. GET /api/health is a readiness check, not a ping. It answers 200 {"ok": true, "checks": {…}} when Postgres answers, the event listener is connected, the job queue is started and (in a process that runs workers) its worker heartbeat is fresh; each check has a 2 s timeout. Otherwise it answers 503 {"ok": false, "checks": {…}} with a short reason per failing check (db, events, queue, workers), and the log has the detail. The Docker HEALTHCHECK and any load balancer pointed at it restart or drain a process that is up but cannot work.

Errors and product analytics (optional). SENTRY_DSN turns on Sentry: the server reports every error or fatal log line that carries an error, tagged with the line's ids (5xx responses, unexpected MCP tool errors, a job's final failed attempt, a job the reconciler gave up on, failed emails and billing syncs, process crashes; a retry still to come is a warning); the web app reports render errors and unhandled rejections, but not the 4xx refusals it already shows the person. The worker heartbeat (heartbeat-<WORKER_ID or role>), the reconciler and the daily billing reconcile check in as Sentry cron monitors, so a stopped worker or a loop that no longer runs raises an alert. SENTRY_REPLAYS_ON_ERROR_SAMPLE_RATE (default 0) keeps a fully masked replay of the moments before a browser error. POSTHOG_KEY (and POSTHOG_HOST, EU cloud or self-hosted) turns on PostHog: the server sends sign-ups, approvals, email verification, project created / done / failed / cancelled, clip done / failed, jobs a person asked for, quota refusals, clip downloads and exports, MCP tool calls (tool, outcome, client name), and billing and abuse-flag events, each with the surface it came through (web, api, mcp, cli); the web app sends page views and a handful of product events (lib/telemetry.ts). Event properties are ids, counts, durations and names, never a title, an error message or other free text; people are identified by user id, role and plan only, with no autocapture and no session recording. Both are off unless set, the browser gets the public keys from system.status, and the SDK chunks are only requested when on. Prompts, transcripts and media never go through either (apps/server/src/telemetry.ts). Release images upload their source maps to Sentry when the release workflow has SENTRY_AUTH_TOKEN (.github/workflows/release.yml); no map ships in the image.

OTEL_ENABLED=true
OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4318   # OTLP/HTTP; traces + metrics
OTEL_SERVICE_NAME=reelsaw

Sentry's OTLP endpoint takes traces and logs (…/integration/otlp, plus OTEL_EXPORTER_OTLP_HEADERS). Each pino line that passes its level is exported to /v1/logs, with the active trace id attached; prompt, transcript and address fields stay in the local log. Sentry does not ingest OTLP metrics. With SENTRY_DSN set and OTEL_METRICS_ENABLED left on, the server sends Node runtime gauges (memory, CPU, event loop) and the metrics below through the DSN instead. Set OTEL_METRICS_ENABLED=false to keep traces and logs and drop those metrics.

Metrics. Durations are seconds. Attributes are bounded sets (stage, proxy id, model, outcome), never a project, clip or user id. Work grows with the video, so the stage, download and transcription numbers each come with a realtime factor: wall seconds per second of media (0.25 = a 1-hour video takes 15 minutes), comparable across a 30-second clip and a 2-hour talk.

Metric

Unit

Attributes

What it answers

reelsaw.pipeline.stage.duration

s

stage, outcome (ok, error, superseded, gone), encoder on render

How long each stage job takes and how often it fails

reelsaw.pipeline.media.duration

s

stage, encoder

Source length per finished job (clip length for prepare and render)

reelsaw.pipeline.realtime_factor

1

stage, encoder

Stage seconds per second of media; render excludes the wait for a slot

reelsaw.ytdlp.download.duration

s

proxy, outcome, fallback

One link download through the pool, fallbacks and waits included

reelsaw.ytdlp.download.realtime_factor

1

proxy

Download seconds per second of video, by the proxy that served it

reelsaw.ytdlp.download.throughput

By/s

proxy

Source bytes per second, by proxy

reelsaw.quota.minutes_precheck_skipped

{download}

reason (unannounced, failed)

Links downloaded under a minutes limit before their length was known, so the limit was checked only on the file

reelsaw.ytdlp.attempts

{attempt}

proxy, outcome (ok, proxy, content, other, abort, busy, circuit_open), panic

Which candidate was tried and why it failed: proxy faults count against the exit, content against the video

reelsaw.ytdlp.attempt.duration

s

proxy, outcome

Wall time of one yt-dlp process through one candidate

reelsaw.ytdlp.proxy.circuit.changes

{change}

proxy, state

Circuit opens (cooling), half-opens and closes

reelsaw.ytdlp.proxy.healthy / .in_flight / .success_rate

gauge

proxy

Circuit closed (1) or cooling (0), running downloads, last-20 success rate

reelsaw.stt.duration / .audio.duration / .realtime_factor

s, s, 1

provider, model, outcome

Transcription time, audio per call, seconds per audio second

gen_ai.client.operation.duration

s

gen_ai.request.model, reelsaw.stage, reelsaw.call, outcome

Language model latency per pipeline step

gen_ai.client.token.usage

{token}

gen_ai.request.model, gen_ai.token.type, reelsaw.stage, reelsaw.call

Tokens per call

reelsaw.llm.cost

USD

gen_ai.request.model, reelsaw.stage

Provider-reported cost (AI Gateway)

reelsaw.render.slots

gauge

state (running, limit)

Renders running in a worker and the limit CPU and memory allow

reelsaw.worker.jobs.active

gauge

queue

Jobs a worker process is running

reelsaw.span.duration

s

span, status

Every withSpan: each stage and each exec ffmpeg, exec ffprobe, exec yt-dlp

Over OTLP, the auto-instrumentation adds http.server.request.duration, and pg-boss adds messaging.process.duration and pgboss.queue.jobs (backlog by queue and state). A proxy dashboard: reelsaw.ytdlp.download.realtime_factor p50/p95 by proxy, reelsaw.ytdlp.attempts rate by proxy and outcome, and reelsaw.ytdlp.proxy.healthy by proxy.

Spans: inbound HTTP named by route (POST /api/trpc/projects.create; /api/health excluded), Postgres queries, outbound fetch to AI vendors, pipeline.<stage> per job with reelsaw.project_id / reelsaw.clip_id / reelsaw.attempt, exec ffmpeg|ffprobe|yt-dlp, and the AI SDK's own ai.generateText / ai.transcribe spans (inputs/outputs not recorded). pg-boss propagates trace context from send() into the worker span, so one trace follows a project from the create mutation through every stage. A client span with no parent (an S3 read or a query no request or job is around, such as queue polling) is not recorded unless OTEL_TRACES_SAMPLER is set.

The browser exports its own spans as reelsaw-web (page load, navigation, fetch, and live subscriptions; not clicks) and sends traceparent on API calls, including the subscription stream, so a click and the server work it starts are one trace. OTEL_TRACES_SAMPLE_RATE (default 1) is the fraction kept, on the browser and the server together. The Sentry browser SDK does not trace; those spans would be a second copy.

Entity-aware transcription

Whisper mishears names ("Copia" → "Copious"). Three layers fix that, all optional:

  1. Vocabulary hints (TRANSCRIPT_BIAS=true): proper nouns pulled from the channel name, title, description, tags and chapters, plus anything typed into "Names & terms" on the project form, are passed to the transcriber (prompt for OpenAI/Groq/Speaches, keyterm for Deepgram, wordBoost for AssemblyAI). Biggest win: in our eval it halves WER and lifts entity recall from 64% to 95% at zero cost.

  2. Constrained LLM correction (TRANSCRIPT_CORRECTION=true): transcript spans that sound like a known entity become candidates; the language model confirms each one in context with a confidence score. Only candidates can change, so the model cannot rewrite the transcript, and word timestamps are kept.

  3. Review in the UI: corrections at or above TRANSCRIPT_AUTO_FIX_CONFIDENCE (0.85) are applied automatically and shown as "auto-fixed"; the rest appear under the transcript line with the confidence and accept/reject buttons. Applying decisions re-renders only the clips whose captions overlap the change.

Language: the filler/repeat rules, the sound-alike name search and the CTC cut aligner are English-only. For a transcript detected as any other language those steps are skipped with a plain-language event ("not available for de") instead of running blind; the language-model checks and the whisper-based cut search still work.

Disfluencies

Fillers ("um"), repetitions ("the the") and false starts ("your pay actual pay dates") are detected after transcription (DISFLUENCY_REMOVAL=true): a rule pass with near-certain confidence, then a model pass (DISFLUENCY_LLM=true) that rewrites each window cleanly and is diffed against the original so it can only ever delete. A model deletion is kept only when every removed word is a filler or is restated within the next few words; repairs land as pending flags for review. Flags appear inline in the transcript (struck through once applied) with the same accept/reject popover (over MCP: list_transcript_flags, review_transcript_flags, add_transcript_correction, add_transcript_cut). Captions drop accepted disfluencies, and they are always cut from the audio/video with jump cuts. Long pauses are shortened too (project setting Pauses: natural 0.6 s, tight 0.35 s, or keep), detected with Silero VAD on the clip audio. Cut boundaries are not taken from the chunk-level ASR timestamps (they drift by up to a word): every cut is first located by CTC forced alignment (wav2vec2, exact word boundaries at 20 ms from the audio plus the known words, ~0.2 s on CPU) and verified with one transcription of the seam; only if that fails is it re-aligned by transcribing windows around it (TRANSCRIPT_ALIGN_MODEL) twice with different offsets (word timings shift with the window), boundaries snap to the quietest 5 ms frame beside the neighbouring words, and a candidate search keeps the first seam that passes verification: both neighbours heard back to back, nothing left between them, and the deleted words gone (or, when the recogniser fills them back in from context, the removed audio on its own is heard as those words). Unverifiable cuts are skipped and listed on the clip. Seams get a 20 ms equal-power crossfade (acrossfade) with length-compensated trims so audio and video stay in sync. Substitution repairs between function words ("into between", "a the") are caught by a rule; legitimate compounds ("out of", "up to") are excluded.

Eval

Regression evals live in one runner with pluggable suites (selection, framing, asr-loops, scenes, pauses, alignment, speakers, overlays, cuts, transcription, disfluency, decisions, selection-model); fixtures are small and in git, large media is fetched into a gitignored cache, baselines are versioned and a run fails on regressions. See docs/EVALS.md, including how to turn a new bug into a case.

pnpm eval                 # all suites
pnpm eval cuts framing    # some suites
pnpm eval --offline       # skip suites that need models
pnpm eval --update        # accept new numbers as the baseline

The transcription suite synthesizes its cases with macOS text-to-speech so the ground truth is exact (eval/generate-audio.sh), and reports WER, entity recall and false entity insertions per model × vocabulary bias × LLM correction.

Results on the bundled cases (43 entity mentions, Speaches on CPU, qwen2.5:14b as judge):

model

vocab bias

LLM correction

WER

entity recall

false insertions

faster-whisper-small

no

no

9.0%

63%

0

faster-whisper-small

no

yes

6.6%

79%

0

faster-whisper-small

yes

no

4.3%

95%

0

faster-whisper-small

yes

yes

4.3%

95%

0

faster-whisper-medium

yes

no

3.7%

95%

0

Takeaway: vocabulary bias is the lever; the model size buys a little WER; the correction pass is the safety net for names the hints did not cover (and the review UI catches the rest).

Testing without AI keys

scripts/mock-ai.mjs is a tiny OpenAI-compatible server (chat completions + transcription with word timestamps). Point the app at it to exercise the whole pipeline offline:

OPENAI_BASE_URL=http://localhost:4545/v1 OPENAI_API_KEY=mock AI_MODEL=openai:mock TRANSCRIPTION_MODEL=openai:whisper-1

Troubleshooting

Start with docker compose exec app reelsaw status <projectId>; see docs/TROUBLESHOOTING.md for the CLI, structured logs, diagnostics and evals, including a "setting has no effect" checklist.

Roadmap

See docs/RESEARCH.md for the competitor audit and research review behind these priorities. Top items still open:

  • Punch-in zooms on the emphasis words.

  • Active speaker: more labelled two-way conversation cases; a learned audio-visual model (Light-ASD ONNX) if the synchrony heuristic plateaus. Saliency fallback when no face is visible.

  • Direct publishing to YouTube Shorts / TikTok / Instagram.

License

Reelsaw is free software under the GNU Affero General Public License v3.0. You can run it, change it and host it; if you offer a modified version as a network service you must make your modifications available under the same licence. The hosted service's additions (billing) live in apps/server/src/ee and apps/web/src/ee under the Reelsaw Enterprise License and load only with REELSAW_EE=true; see LICENSING.md. Contributions need a DCO sign-off (git commit -s), see CONTRIBUTING.md.

Copyright (C) 2026 osbytes

Available Tools

84 tools
add_assetAdd library assetA

Add from exactly one source: a public url or an uploadId (video, image, GIF, SVG or sound) becomes one of your library uploads; a search_assets catalog / provider hit (provider + externalId) materializes one instance-shared copy (checksum verified, license recorded; not listed in your library). Returns { id, name, kind, mime, durationSec, duplicate, restored, owner } (the same file twice returns the existing item). setDefault on an upload makes it your default intro/outro for NEW projects only.

NameRequiredDescriptionDefault
urlNohttp(s) link to a video (MP4/MOV/WebM), image (PNG/JPG/WebP/GIF/SVG) or sound (MP3/M4A/AAC/WAV/OGG/FLAC); the server downloads it into your library (max 50 MB, 60 s)
nameNoLibrary name (url or uploadId; default: the file name). update_asset changes it later
tagsNoSearch tags (lowercase words; max 20)
colorNoiconify hits only: icon color as hex, e.g. #ffffff (default white)
providerNoA search_assets hit's provider (catalog or an enabled provider), together with externalId; materializes the instance-shared file instead of adding an upload
uploadIdNoAn existing upload id (from POST /api/upload) instead of a url
externalIdNoA search_assets hit's externalId, together with provider
setDefaultNoAlso make it your default intro or outro for new projects
imageSecondsNoHow long a still image shows as an intro/outro (1-10 s)

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden and does most of it: idempotency ('the same file twice returns the existing item'), checksum verification and license recording for provider hits, the fact that provider materializations are not listed in your library, and the forward-only effect of setDefault. It omits permission/auth requirements and any rate-limit or cost context for provider materialization.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three sentences, front-loaded with the constraint that matters most (exactly one source) and ending with the caveat about setDefault. The dense parentheticals are efficient rather than redundant, though the packed first sentence takes effort to parse.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Nine parameters with 100% schema coverage and no output schema, and the description still specifies the return shape ({ id, name, kind, mime, durationSec, duplicate, restored, owner }) plus the meaning of the duplicate/restored flags via the idempotency note. An agent has everything needed to call and interpret it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so baseline is 3, but the description adds semantics the schema cannot express: that url, uploadId, and provider+externalId are mutually exclusive source selectors, and that setDefault applies only to future projects. That is genuine value beyond the field-level docs.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The opening sentence gives a specific verb (add) and enumerates the three mutually exclusive source modes that produce distinct outcomes: an upload in your library, or an instance-shared copy materialized from a search_assets hit. It names the sibling tools (search_assets, update_asset) that bracket this operation, so an agent can place it without opening any schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

'Add from exactly one source' states the mutual-exclusion rule and then maps each available input (public url, uploadId, provider+externalId) to its use case, which is real routing guidance. It also scopes setDefault to NEW projects only. It stops short of saying when not to use this tool (e.g. restore_asset for previously deleted items), so it is not fully exhaustive.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

add_transcript_correctionCorrect transcript wordsA

Make captions show "text" instead of the words spoken between startSec and endSec (a misheard name, say); the span must cover whole words. Returns { ok, flagId, rerendered, hint }. Undo it by passing the flagId to review_transcript_flags with status rejected.

NameRequiredDescriptionDefault
idYesProject id (from list_projects, search_clips or create_project)
textYesWhat the captions should say for that span
endSecYesEnd of the words to correct, seconds
rerenderNoRe-render finished clips whose captions overlap the span (default true)
startSecYesStart of the words to correct, seconds from the start of the source video

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries full weight and does well: it discloses the return shape { ok, flagId, rerendered, hint }, which implies a review flag is created, and explains how to reverse the change. Missing are permission requirements, whether the source transcript or existing renders are otherwise altered, and what `hint` means.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three compact clauses: the effect first, the constraint second, the return and undo last. Nothing is padded and the most important information is front-loaded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a mutation tool with no annotations and no output schema, the description supplies both the response fields and the reversal path, which is exactly the gap structured data leaves. It could go one step further on permission or review-workflow impact, but an agent can call it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is high; the description still adds meaning by tying startSec/endSec to "the words spoken" and clarifying that text is what the captions should read, reinforcing the whole-word constraint. No redundant restatement.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Names the concrete effect (captions show "text" instead of the spoken words) plus the exact span parameters, so it is unmistakable what resource is mutated. The contrast with review_transcript_flags (undo path) and add_transcript_cut clarifies its niche.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives a motivating case ("a misheard name, say"), a hard precondition ("the span must cover whole words"), and an explicit undo route via review_transcript_flags with status rejected. It stops short of saying when to prefer add_transcript_cut or list/flags-review workflows instead.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

add_transcript_cutCut transcript wordsA

Remove the words spoken between startSec and endSec from the transcript, so captions and the rendered video skip them; the span must cover whole words. Returns { ok, flagId, rerendered, hint }. Undo it by passing the flagId to review_transcript_flags with status rejected.

NameRequiredDescriptionDefault
idYesProject id (from list_projects, search_clips or create_project)
endSecYesEnd of the words to cut, seconds
rerenderNoRe-render finished clips whose captions overlap the span (default true)
startSecYesStart of the words to cut, seconds from the start of the source video

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden and does well: it discloses that the operation mutates the transcript, affects captions and rendered video, requires whole-word spans, returns specific fields, and can be reverted via review_transcript_flags. It omits failure behavior if the span does not cover whole words and any permission/auth context.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three compact sentences, front-loaded with the action and effect, followed by return shape and undo guidance. Every sentence adds useful information with no repetition.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a mutation tool with no annotations and no output schema, the description is largely complete: it explains the effect, return fields, and reversal path. The main gap is what happens when the span fails the whole-word requirement and whether there are other prerequisites.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3. The description adds a meaningful constraint beyond the schema by stating that startSec and endSec must cover whole words, which affects how the agent should choose those values.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource: 'Remove the words spoken between startSec and endSec from the transcript', and immediately explains the downstream effect on captions and rendered video. It also distinguishes its undo path from review_transcript_flags, making the operation identifiable against transcript-related siblings.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives an explicit reversal route: 'Undo it by passing the flagId to review_transcript_flags with status rejected.' It also states the constraint that the span must cover whole words. It does not, however, compare itself to add_transcript_correction or say when to cut versus correct.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

apply_framingApply framing to the projectA
Destructive

Make one clip's framing the whole project's, for a recording whose layout never changes (a slide deck with a webcam tile, a fixed camera): the clip's section at at (else its first framed section) gives the mode and its nudge / subject / crop, which become the project's reframe and reframeAdjust; every clip's own sections are cleared (unless keepOverrides) and the finished clips re-render. New clips get it too. Returns { reframe, adjust, cleared, rerendered }.

NameRequiredDescriptionDefault
atNoSeconds from the start of the source: which section of the clip to copy (default: its first section that is not plain automatic)
clipIdYesClip whose framing to copy (from get_project or search_clips)
rerenderNoRe-render the finished clips now (default true)
keepOverridesNoLeave other clips' own sections in place; they still win over the project's framing (default false: clear them)

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond destructiveHint=true, it discloses exactly what gets destroyed: every clip's own sections are cleared unless keepOverrides, and finished clips are re-rendered. It also notes forward behavior ("New clips get it too") and the response shape, which annotations alone would never convey.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

One front-loaded sentence that leads with the action and the applicability constraint, followed by the return shape. It is dense with parentheticals and nested clauses, but no sentence is filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a destructive mutation with no output schema, it covers the source of truth, the clear/keep behavior, the re-render default, the effect on future clips, and the returned fields. An agent has everything needed to call it correctly and know the consequences.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3; the description earns above that by explaining the semantics of `at` (the clip's section at that point, else its first framed section) and how keepOverrides interacts with the project framing (other clips' sections "still win"). It adds meaning rather than restating field descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource: copy one clip's framing (mode, nudge, subject, crop) and make it the project's reframe/reframeAdjust. It also scopes the operation to recordings with a fixed layout, which separates it from per-clip tools like rerender_clip or update_clip.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives a concrete when-to-use condition ("a recording whose layout never changes (a slide deck with a webcam tile, a fixed camera)"), which implicitly excludes variable-layout projects. It does not name an alternative tool for the case where layouts vary, so the routing is clear but not exhaustive.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

apply_layer_templateApply layer template to clipsA
Destructive

Write a template onto clips of one project (up to 200 clipIds, or all): its layers fitted to each clip's length, and the intro / outro it sets as each clip's own. Not linked afterwards. mode replace (default) swaps the clips' layers, add keeps them and adds. rerender true re-renders finished clips now, else the next render shows them. update_project settings.layerTemplateId only affects clips created later. Returns { updated, rerendered, skipped: [{ clipId, reason }], layersSkipped, bumpers: slots set }

NameRequiredDescriptionDefault
modeNo"replace" (default) = the template becomes the clip's layers; "add" = its layers join the ones the clip already hasreplace
clipIdsNoClip ids of that project (get_project), 1-200; omitted = every clip of the project
rerenderNotrue = re-render the affected finished clips now; false = the layers show on their next render
projectIdYesProject the clips belong to (list_projects)
templateIdYesLayer template id (list_layer_templates)

TDQS

A4.1/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations supply only destructiveHint=true; the description earns credit by disclosing that replace swaps (destroys) existing clip layers, that templates are not linked afterwards, that rerender controls whether finished clips update now or later, and what the response contains including skipped clipIds with reasons. It does not mention permission requirements or whether the call is synchronous/queued, which for a destructive batch mutation would be useful.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the core action and scope, then parameters, then return shape — a sensible order with no filler sentences. The prose is over-compressed in places ("the intro / outro it sets as each clip's own", "bumpers: slots set"), which costs a little readability but not much length.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description usefully summarizes the return payload (updated, rerendered, skipped with reasons, layersSkipped, bumpers), and it covers every parameter's behavioral consequence plus the non-linkage semantics. Remaining gaps are peripheral: sync vs async execution, partial-failure semantics beyond the skipped list, and permission prerequisites for a destructive batch write.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so mode, clipIds, rerender, projectId and templateId are already fully documented in the schema, and the description largely restates them. The only genuinely additive semantics is the caveat about layerTemplateId on update_project affecting only later-created clips. Baseline 3 applies when the schema does the heavy lifting.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

"Write a template onto clips of one project" names a specific verb (write/apply) and resource (layer template → clips), and adds scope (up to 200 clipIds, or all). "Not linked afterwards" separates it from the template-management siblings (save_layer_template, update_layer_template) that would otherwise look similar. An agent can distinguish this from every sibling without opening a schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives the operative decision rules: mode replace vs add, rerender true/false, and clipIds omitted = every clip. The note that "update_project settings.layerTemplateId only affects clips created later" implicitly tells the agent this is the tool for retroactive application, which is a strong when-to-use signal. It stops short of explicitly naming an alternative tool for the settings-only path.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

asset_usageWhere a library asset is usedA
Read-only

Where a library item is used: as intro, outro, overlay or music on projects and clips (replaced clips left out), and in layer templates. Returns { projects: [{ id, title, slots, clips: [{ id, index, title, slots, via, overlays, music }] }], templates: [{ id, name, layers }] }. A deleted asset's layer is skipped when a template is applied. Check before deleting one.

NameRequiredDescriptionDefault
assetIdYesLibrary asset id (from search_assets)

TDQS

A4.4/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Goes well beyond readOnlyHint=true by disclosing non-obvious semantics: replaced clips are excluded, and a deleted asset's layer is skipped when a template is applied. These behavioral details materially affect how an agent interprets results before a destructive action.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the core purpose, followed by the return shape and then the actionable caveat. The inline return-shape block is dense but justified since no output schema exists; nothing is redundant.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description supplies the full response shape (projects with nested clips/slots/via/overlays/music, and templates with layers) and the caveats that affect interpretation. An agent has everything needed to call it and read the result correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

There is a single parameter, and the schema already documents it fully (id from search_assets), so baseline applies. The description adds no syntax, format, or sourcing guidance beyond what the schema states.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific resource (a library item) and precisely what is reported: its use as intro, outro, overlay or music on projects and clips, plus layer templates. This is clearly distinguishable from siblings like get_clip or search_assets, which retrieve content rather than reverse-lookup usage.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The closing instruction 'Check before deleting one' gives an explicit trigger for calling this tool, implicitly routing agents here before delete_asset/purge_asset. It does not explicitly name those siblings or state when-not to use it, so it stops short of a full routing rule.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

billing_checkout_urlCheckout URLA

Hosted instances: a Stripe Checkout URL for a plan (or, when the account already pays, the Billing Portal page that confirms the plan change). Returns { url, kind: "checkout" | "change" }; a person must open it in a browser, an agent cannot pay. Plans and intervals come from get_billing.

NameRequiredDescriptionDefault
planYesPlan name from get_billing (e.g. creator, pro)
intervalNoBilling interval: month (default) or yearmonth

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden and does well: it discloses the return shape and the two possible `kind` outcomes, and warns that payment requires a human in a browser. It never says whether calling it creates a Stripe session as a side effect or is idempotent, which matters for a tool that provisions checkout state.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three dense clauses with no filler, front-loaded with the 'Hosted instances' scope qualifier. The nested parenthetical and 'Returns { url, kind: ... }' shorthand make it slightly harder to parse than it needs to be, but nothing is wasted.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema exists, and the description compensates by naming the returned fields and the `kind` discriminator. Parameters, hosting scope, human-in-the-loop requirement, and the alternate return path are all covered, so an agent knows what it will get and what to do with it.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and the schema already documents both `plan` (from get_billing) and `interval` (month/year with default), so the description repeats rather than extends that. It adds nothing about format constraints, casing, or invalid-plan behavior beyond the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States exactly what is returned: a Stripe Checkout URL for a plan, or the Billing Portal page when the account already pays. It explicitly distinguishes itself from get_billing, which supplies the plan/interval values rather than the URL.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Clear context: 'Plans and intervals come from get_billing' routes the agent to the sibling that supplies inputs, and 'a person must open it in a browser, an agent cannot pay' sets a hard usage constraint. The 'Hosted instances' prefix implies self-hosted installs are unsupported, but that exclusion is left implicit.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

billing_portal_urlBilling portal URLA

Hosted instances: a Stripe Billing Portal URL to see invoices, change the card or cancel at period end. Returns { url }; a person must open it in a browser. On a self-hosted Reelsaw this answers that billing is not enabled.

NameRequiredDescriptionDefault

No parameters

TDQS

A4.1/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden and does well: it discloses that the call is non-mutating (returns a URL rather than acting), that a human must open it in a browser, and that self-hosted deployments get a 'billing not enabled' outcome instead of a URL. It omits whether the URL is user-scoped, expiring, or requires admin auth, which leaves some behavioral gaps.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three tight clauses: what it returns and what it is for, the human-in-the-loop requirement, and the self-hosted fallback. Nothing is wasted and the primary purpose is front-loaded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a zero-parameter tool with no output schema, the description explains the return payload ({ url }) and the human browser step, plus the self-hosted degenerate case. Lifetime/expiry of the URL and any auth prerequisites are the only notable omissions.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes zero parameters, so per the baseline this scores 4. The description adds no parameter information but none is needed.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific artifact (a Stripe Billing Portal URL) and its concrete uses (see invoices, change card, cancel at period end), which clearly separates it from a checkout URL or a billing status read. It does not name the nearest siblings (billing_checkout_url, get_billing) explicitly, so the differentiation is inferred rather than stated.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives a clear usage condition split by deployment: on hosted instances it is the way to manage billing, while on a self-hosted Reelsaw it simply reports that billing is not enabled. There is no explicit routing to alternatives, but the hosted/self-hosted framing supplies usable context for when the tool applies.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

check_for_updateCheck for updateA
Read-only

Admins only: is a newer Reelsaw published? Returns { current, latest: { version, url, publishedAt }, available, checkedAt, error, upgrade: { command, note, docsUrl } }; available is null when the running build is not a release (from source). The server checks every few hours; refresh: true asks GitHub again now.

NameRequiredDescriptionDefault
refreshNotrue = ask GitHub now instead of returning the last answer (at most once a minute)

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only declare readOnlyHint=true, and the description adds substantial behavior: the full return shape, the fact that available is null for non-release/source builds, the cached polling cadence, and what refresh does. There is no contradiction with the read-only annotation.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two dense sentences with no wasted framing. The admin restriction is front-loaded, and the return shape is stated compactly because no output schema exists to carry it.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a one-parameter, read-only diagnostic tool with no output schema, the description covers audience, return values, the key edge case, caching behavior, and refresh semantics. Nothing important for correct invocation appears to be missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and already documents refresh, default false, and the once-per-minute limit. The description adds useful context by explaining why refresh exists: the server otherwise returns the last cached check from its every-few-hours polling.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource: checking whether a newer Reelsaw release is published. It also names the admin-only audience and distinguishes this operational/update check from the many project, clip, and bumper tools in the sibling list.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It clearly says this is for admins and explains that the server normally checks every few hours, while refresh: true forces an immediate GitHub check. It does not name alternatives or explicit when-not-to-use conditions, but the intended context is clear.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

create_clipCreate clipA

Add a clip from a time range of the source (needs a transcript). mode exact keeps your bounds (3 s-10 min); mode ai lets the model pick the best moment inside them (≥8 s). Returns the new clip summary { id, status: "preparing", queued: "prepare", hint }; preparation (title, hook, scores) is queued and the render follows by itself (about a minute), so poll get_clip until done. To change an existing clip use update_clip instead.

NameRequiredDescriptionDefault
modeNoexact = these bounds (default); ai = best moment inside themexact
endSecYesRange end, seconds
startSecYesRange start, seconds from the start of the source video
projectIdYesProject id (from list_projects, search_clips or create_project)

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the whole burden and does so well: it discloses that the call is asynchronous, that preparation is queued, that rendering follows automatically and takes about a minute, and that the caller must poll get_clip. It does not cover failure modes or what happens if the range exceeds the stated limits.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Every sentence earns its place and the core purpose comes first, but the single dense paragraph with stacked parentheticals ("(title, hook, scores)", "(about a minute)") makes it heavier to parse than necessary. No wasted claims, just tight packing.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

There is no output schema, and the description compensates by giving the exact return shape { id, status, queued, hint } plus the follow-up workflow via get_clip. Combined with the mode limits and the transcript prerequisite, an agent has everything needed to call this correctly on the first try.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description adds real constraints the schema lacks: a 3 s–10 min bound in exact mode and a ≥8 s minimum in ai mode. It does not add anything for projectId, startSec, or endSec beyond what the schema already states.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Opens with a specific verb and resource ("Add a clip from a time range of the source") and immediately adds the prerequisite that the source needs a transcript. It distinguishes itself from update_clip and get_clip by name within the same paragraph.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Spells out the two operating modes with their selection criteria (exact keeps your bounds, ai lets the model pick the best moment) and names the alternative for mutation ("To change an existing clip use update_clip instead"). The transcript prerequisite further tells the agent when this call will not work.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

create_projectCreate projectA

Start clipping a video from a URL (anything yt-dlp fetches) or a file you uploaded: POST /api/upload with the file as the body, header x-filename and your API key as Authorization: Bearer, then pass the uploadId it returns. Returns { id, title, status: "queued", next } at once; the pipeline (download, transcribe, pick, render) runs in the background for minutes, so follow with wait_for_project(id). Intro/outro default to your library defaults unless introAssetId/outroAssetId say otherwise.

NameRequiredDescriptionDefault
titleNoProject title (default: the video's own title)
introSecNoSeconds an intro image shows (1-10)
metadataNoYour own key/value strings (≤20 keys, 1 KB), returned by get_project and sent in every webhook for this project, e.g. { runId: "n8n-123" }
outroSecNoSeconds an outro image shows (1-10)
settingsNoProject settings; omit for the defaults (automatic clip count, 20–60 s clips, natural pauses, name fixes and the on-screen text scan on, filler removal off). Set refineDisfluency / disfluencyLlm true to opt into filler removal, refineEntities / refineScenes false to skip those steps (instance Pipeline gates must be on).
uploadIdNoId returned by POST /api/upload (the file body, x-filename header, API key as Bearer); give this or sourceUrl
sourceUrlNohttp(s) URL of the video; give this or uploadId
introAssetIdNoLibrary asset id (search_assets with source library) for the intro of every clip; null = no intro; omitted = your default intro
outroAssetIdNoSame for the outro

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations exist, so the description carries the burden and does well: it discloses the asynchronous pipeline (download, transcribe, pick, render) running for minutes, the immediate return shape with status 'queued', and the required follow-up call. It could be stronger by mentioning error conditions or whether creation consumes quota, but the core behavioral profile is clearly disclosed.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The opening action and source options are front-loaded, and the sentence is dense with useful detail. However, it is a single long run-on sentence that could be split for easier scanning.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 9-parameter, nested, no-output-schema tool, the description provides the crucial return shape and async follow-up guidance, which compensates for the missing output schema. It does not cover error behavior or quota implications, but nothing essential for correct invocation is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the baseline is 3. The description reiterates the uploadId flow and intro/outro default semantics, but those are already documented in the schema; it adds little parameter-level meaning beyond what the schema provides.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (start clipping) and resource (project), plus the two source modes it accepts. It also names the follow-up sibling wait_for_project, so an agent can distinguish it from read/update siblings without opening schemas.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly says how to start (URL or uploaded file via POST /api/upload with uploadId) and what to do after (follow with wait_for_project(id)). It also states the intro/outro default behavior versus supplying introAssetId/outroAssetId, so the invocation path is fully prescribed.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

create_webhookCreate webhookA

Subscribe a URL to events: project.created, project.done (with every clip, share links and the zip), project.failed, clip.done (also re-renders), clip.failed. This is how a Zapier or Make app subscribes (delete_webhook unsubscribes). Returns { endpoint, secret }: the whsec_ signing secret is shown only here; verify deliveries with any Standard Webhooks library. The URL must be public https unless the admin allows private networks.

NameRequiredDescriptionDefault
urlYesWhere to POST (https)
eventsYesEvents to send
descriptionNoYour label, e.g. "n8n clips workflow"

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden and does it well: it discloses the return shape { endpoint, secret }, the one-time-only nature of the whsec_ secret (an irreversible behavior), how to verify deliveries, and the public-https / private-network constraint. These are exactly the facts an agent needs before firing a mutation it cannot undo.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Four dense sentences with zero filler, front-loaded with the verb and the event list, then the return contract, then the URL constraint. The event enumeration is long but is load-bearing information, not padding.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema exists, yet the description documents the response object and the secret's one-time visibility, covers the https requirement, and names the verification path. Nothing essential for a correct first call is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3; the description goes beyond the enum names by explaining what each event actually delivers ('project.done (with every clip, share links and the zip)', 'clip.done (also re-renders)'). It does not add anything for the optional 'description' label, but the schema already covers that adequately.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Subscribe a URL to events') and enumerates the exact event types it accepts. It also names the sibling it is not ('delete_webhook unsubscribes'), so an agent can place it in the webhook lifecycle without opening another schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

'This is how a Zapier or Make app subscribes' plus the parenthetical pointing at delete_webhook as the inverse gives clear context and one named alternative. It does not explicitly contrast with update_webhook or rotate_webhook_secret, which are the closest ambiguous siblings for an agent that already has a webhook registered.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

delete_assetDelete library assetA
Destructive

Delete one of your uploads: it leaves the library and your defaults and goes to Deleted. Projects and clips already using it keep rendering with it. Undo with restore_asset (list_deleted says how long it is kept); check asset_usage first. Returns { ok }.

NameRequiredDescriptionDefault
assetIdYesLibrary asset id (from search_assets)

TDQS

A4.5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With only destructiveHint=true available, the description carries the burden well: it discloses soft-delete semantics (moves to Deleted), the retention window is discoverable via list_deleted, the key non-obvious side effect that existing projects/clips keep rendering with the asset, and the return shape { ok }.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three tight clauses, each earning its place: what it does, what survives, how to undo/verify, and what comes back. The behavioral constraint is front-loaded ahead of the recovery advice.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a single-parameter mutation with no output schema, the description covers effect, reversibility, retention, downstream render impact, and the return value. Nothing an agent needs in order to call this safely is absent.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

A single required parameter with 100% schema description coverage (including its provenance, 'from search_assets'), so the schema already does the work. The description adds only an indirect hint that the asset must be one of the caller's own uploads, not syntax or sourcing detail beyond the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource ('Delete one of your uploads') and immediately clarifies the scope: it leaves the library and defaults but goes to 'Deleted' rather than being destroyed. This implicitly separates it from purge_asset while explicitly naming restore_asset and asset_usage, so an agent can place it among the delete/purge siblings.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives real procedural guidance: 'check asset_usage first' establishes a precondition, and 'Undo with restore_asset (list_deleted says how long it is kept)' points to the recovery path. What's missing is an explicit exclusion — it never says to use purge_asset when permanent removal is intended.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

delete_clipDelete clipA
DestructiveIdempotent

Delete one clip of a live project; a render in progress stops. It shows up on Deleted (list_deleted). Reversible: restore_clip brings it back for DELETED_RETENTION_DAYS (7 days by default). Storage still counts until purge_clip. For several clips of one project use delete_clips. Returns { changed, stoppedRender (a render in flight was stopped), renderQueued: false, restorable, storedBytes }.

NameRequiredDescriptionDefault
idYesClip id (from get_project or search_clips)

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare destructiveHint=true and idempotentHint=true, but the description adds substantial behavior the annotations do not: a render in progress stops, the item becomes visible in Deleted, deletion is reversible for DELETED_RETENTION_DAYS (7 days default), and storage still counts until purge_clip. It also describes the return payload fields, which is especially valuable because no output schema exists.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is dense and front-loaded: purpose first, then lifecycle consequences, then the bulk alternative, then return fields. Every sentence carries operational information and there is no filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a destructive single-item delete with no output schema, the description covers the full operational picture: what happens to an in-flight render, where the item goes, how long it is restorable, when storage is actually freed, the relevant sibling tools, and the return object fields. Nothing essential is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and the single id parameter is fully documented in the schema ('Clip id (from get_project or search_clips)'). The description does not add any further meaning about the id parameter, so the baseline of 3 for full schema coverage is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb and resource ('Delete one clip of a live project') and immediately distinguishes scope from the bulk sibling ('For several clips of one project use delete_clips'). An agent can tell exactly what this tool does and how it differs from delete_clips, purge_clip, and restore_clip.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It explicitly names the alternative for bulk deletion ('use delete_clips') and explains the lifecycle context: the clip appears in list_deleted, is restorable via restore_clip for the retention period, and only stops counting toward storage after purge_clip. That gives clear when-to-use and when-to-use-something-else guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

delete_clipsDelete clipsA
DestructiveIdempotent

Delete several clips of one live project in one step (the selection bar's Delete); renders in progress stop. Each shows up on Deleted (list_deleted) and restore_clip brings it back for DELETED_RETENTION_DAYS. Already-deleted ids are skipped. Storage still counts until purge_clip. Returns { deleted, skipped, stoppedRenders, renderQueued: false, restorable, storedBytes }.

NameRequiredDescriptionDefault
clipIdsYesClip ids of that project (get_project), 1-200
projectIdYesProject id (from list_projects, search_clips or create_project)

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Goes well past destructiveHint/idempotentHint: in-progress renders are stopped, deletions land on Deleted and are restorable for DELETED_RETENTION_DAYS, skipped ids are ignored (explaining the idempotency claim), and storage continues to be billed until purge_clip. It also documents the response payload, which no output schema provides.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the action and scope, then lifecycle, then return shape; no filler sentences. The trailing return-field enumeration is dense but pays for itself since there is no output schema.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Covers action, side effects, recovery window, purge relationship, retry semantics and the full return object, compensating for the absent output schema. Nothing needed to invoke or interpret the call correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so baseline is 3, but the description adds real semantics: all clipIds must belong to a single live project, and ids already in the deleted state are silently skipped rather than erroring — behavior an agent needs to interpret results.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource with scope: 'Delete several clips of one live project in one step', and disambiguates from the singular sibling by calling out the batch/selection-bar semantics. An agent can distinguish it from delete_clip, delete_project and delete_asset without opening any schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Names the recovery path (list_deleted, restore_clip, DELETED_RETENTION_DAYS) and the true-removal alternative (purge_clip), and notes that already-deleted ids are skipped so retries are safe. It does not explicitly say 'use delete_clip for a single clip', so one routing decision is left implicit.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

delete_layer_templateDelete layer templateA
DestructiveIdempotent

Delete a template (it goes to Deleted) and drop it from your project defaults; clips that already got its layers keep them. Undo with restore_layer_template (list_deleted says how long it is kept). Returns { ok, clearedDefault } (it was your default and no longer is)

NameRequiredDescriptionDefault
templateIdYesLayer template id (list_layer_templates)

TDQS

A4.5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Although annotations already declare destructiveHint=true and idempotentHint=true, the description adds substantial behavioral context: deletion is soft (goes to Deleted), it clears the template from project defaults, existing clips retain their layers, and the return value includes whether a default was cleared. It also names the restore path and the retention reference.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is compact and front-loaded: the delete behavior, default-clearing effect, existing-clip behavior, undo path, retention reference, and return shape all appear in a few tight clauses with no filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a single-parameter destructive tool with no output schema, the description covers the important effects: what happens to the template, project defaults, existing clips, how to undo, where retention is documented, and what the return value contains. Nothing essential is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% and the only parameter's schema already explains that it is a layer template id available via list_layer_templates. The description adds no additional parameter semantics beyond what the schema provides, so the baseline of 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb and resource ('Delete a template') and immediately distinguishes this soft delete from permanent removal by saying it goes to Deleted and can be undone. An agent can tell this apart from purge_layer_template without inspecting either schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives clear recovery guidance ('Undo with restore_layer_template') and points to list_deleted for retention duration. It does not explicitly compare against purge_layer_template, but the soft-delete semantics strongly imply when this tool is appropriate rather than permanent purge.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

delete_projectDelete projectA
DestructiveIdempotent

Delete one of your projects and all its clips; work in progress stops. Reversible: restore_project brings it back for DELETED_RETENTION_DAYS (7 days by default). Storage still counts until purge_project (or the retention sweep) removes it for good; minutes already used stay counted. Returns { changed, restorable, storedBytes, forgetDecisions, message }.

NameRequiredDescriptionDefault
idYesProject id (from list_projects, search_clips or create_project)
forgetDecisionsNoForget what Reelsaw learned from it (its decision log entries) when it is deleted for good, by purge_project or the retention sweep; restoring it cancels that. Default false keeps them

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already flag destructiveHint and idempotentHint, but the description goes well beyond them: it discloses the soft-delete/retention model, that storage keeps counting until purge, that used minutes are not refunded, and the exact return payload. Nothing an agent needs to predict the consequences is missing.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three dense sentences, front-loaded with the destructive effect and scope, then reversibility window, then the residual-cost caveat, then the return shape. Every clause carries information an agent would otherwise have to guess.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

There is no output schema, and the description compensates by naming the returned fields ({ changed, restorable, storedBytes, forgetDecisions, message }). Combined with the retention and billing caveats, this is complete for a two-parameter destructive mutation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and both parameters (id, forgetDecisions) are documented there, so the schema does the heavy lifting. The description mentions forbidDecisions only as a return field and adds no syntax or constraint detail about either input parameter, so the baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ("Delete one of your projects and all its clips") and immediately scopes the effect: work in progress stops and clips go with the project. It is clearly distinguishable from the sibling purge_project, which the description names as the permanent-removal path.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly routes the agent: restore_project is the undo path within DELETED_RETENTION_DAYS (7 days default), and purge_project (or the retention sweep) is what actually frees the data. It also warns that minutes already used stay counted, which is decision-relevant before calling.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

delete_webhookDelete webhookA
Destructive

Remove a webhook (unsubscribe); nothing more is sent to it and its delivery log goes with it. Returns { ok, message }.

NameRequiredDescriptionDefault
idYesWebhook id (from list_webhooks)

TDQS

A3.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare destructiveHint=true, and the description goes beyond that by disclosing exactly what is destroyed: future deliveries stop and the delivery log is removed with it. It also names the return shape { ok, message }. It stops short of saying whether the deletion is reversible or what permission is required.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two compact clauses: the action with its consequence front-loaded, then the return contract. No filler and nothing repeated from the structured fields.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a one-parameter destructive tool with annotations and full schema coverage, this is nearly complete: mutation effect, collateral data loss, and return value are all covered. The remaining gap is whether the operation is permanent or has any restore path.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Only one parameter, and the schema already documents it at 100% coverage ("Webhook id (from list_webhooks)"). The description adds nothing about the id's format or sourcing, so the baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ("Remove a webhook") and immediately qualifies scope with "(unsubscribe)." It is unmistakably distinct from create_webhook, update_webhook, and list_webhooks, though it never names a sibling to route the agent.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

There is no when-to-use / when-not-to-use guidance and no alternative named (e.g., update_webhook to disable delivery without deleting). The word "unsubscribe" only implies the use case rather than stating conditions or prerequisites.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

export_decisionsExport decisionsA
Read-only

A page of your decision log in export form, oldest first, for evals. Returns { items (one decision each, the same objects reelsaw decisions export writes as JSON lines), cases: { disfluency: [eval cases in eval/fixtures/disfluency/cases.json shape, split dev/heldout by project] }, nextCursor }. Pass nextCursor back until it is null.

NameRequiredDescriptionDefault
kindNoOnly this kind of decision
limitNoPage size (1-1000, default 200)
sinceNoOnly decisions made at or after this ISO time
cursorNonextCursor from the previous page

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description adds substantial behavioral detail beyond the readOnlyHint annotation: it describes the return shape, the oldest-first ordering, eval-case grouping, and cursor-based pagination. It does not mention auth or rate-limit behavior, but those are less critical for a read-only export.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with the core purpose and then efficiently covers return shape and pagination in a few dense but purposeful sentences. Every sentence contributes useful information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description carries the burden of explaining returns, and it does so by specifying items, cases, and nextCursor. It also covers ordering and pagination, making the definition complete for an agent to call it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3. The description still adds meaningful semantics by explaining that the returned nextCursor is fed back into the cursor parameter until null, which clarifies the pagination contract beyond the schema field description.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly identifies the resource and output mode: a paginated export of the decision log, ordered oldest first, intended for evals. It does not explicitly distinguish itself from the sibling list_decisions tool, so it is clear but misses full sibling differentiation.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives clear usage context by saying the export is 'for evals' and explains the pagination loop ('Pass nextCursor back until it is null'). It does not state when not to use this tool or explicitly point to list_decisions for ordinary browsing.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

export_projectExport clipsA
Read-only

A download link for a zip of the project's rendered clips with YouTube/TikTok metadata. Same conditions as the app's Export: the project must be live and at least one picked clip rendered, else the answer says why (not found, or nothing rendered yet). Returns { url, clips (rendered, in the zip), notRendered (picked but not finished, left out) }; the link needs the same Authorization header as this server.

NameRequiredDescriptionDefault
idYesProject id (from list_projects, search_clips or create_project)
clipIdsNoOnly these clips (max 200; must be rendered clips of the project); omitted = every rendered clip

TDQS

A4.4/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Goes well beyond readOnlyHint: discloses that the returned link requires the same Authorization header as this server, that unrendered clips are silently excluded (notRendered), and what the error responses mean. That is exactly the kind of non-obvious auth/partial-result behavior an agent cannot infer from annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the outcome (a zip download link), then preconditions, then return shape. Dense but every clause carries information; only the nested parentheticals slightly slow parsing.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description supplies the exact return shape ({ url, clips, notRendered }) and the mechanism for consuming the link (Authorization header). Combined with precondition and error behavior, an agent has everything needed to call and use this correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and both parameters (id, clipIds with max 200 and rendered-only constraint) are already documented in the schema. The description mentions rendered vs picked clips but adds no syntax or format detail for the parameters themselves, so baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('a download link for a zip of the project's rendered clips with YouTube/TikTok metadata'), which no sibling tool duplicates — delete_project, get_project, rerender_clips all do different things. An agent can immediately tell this is the export/download operation.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives concrete preconditions ('the project must be live and at least one picked clip rendered') and explains failure outcomes. It does not name an alternative sibling for the case where nothing is rendered (e.g. rerender_clips), so it stops just short of explicit alternative routing.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

forget_decisionsForget decisionsA
DestructiveIdempotent

Delete entries from your decision log so Reelsaw stops learning from them: one project's (projectId, live or already deleted forever) or, without projectId, all of them. Cannot be undone. Returns { forgotten, message }.

NameRequiredDescriptionDefault
projectIdNoOnly this project's decisions; omitted = everything

TDQS

A3.9/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare destructiveHint=true and idempotentHint=true, so the safety profile is known. The description adds important context: deletions cannot be undone, affect learning behavior, and return { forgotten, message }.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is short and front-loads the destructive operation and its learning consequence. The parenthetical phrase 'projectId, live or already deleted forever' is slightly awkward but overall the sentence is efficient.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a one-parameter destructive tool with no output schema, it covers the essential scope, irreversibility, learning effect, and return shape. It could be more complete by relating explicitly to set_learning or non-destructive decision-log tools.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, and the one optional projectId is already documented. The description reinforces that omitting projectId deletes everything, but adds little syntax or format detail beyond the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource: delete entries from the decision log, with the consequence that Reelsaw stops learning from them. This clearly distinguishes it from sibling read/export tools like list_decisions and export_decisions.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explains project scoping well: projectId limits deletion to one project, omission deletes all. However, it gives no explicit guidance on when to use this versus related tools such as set_learning, list_decisions, or export_decisions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_billingBillingA
Read-only

Hosted instances: your plan and subscription, the billing period, the limits in force with where each comes from (instance, plan, an admin's override), what you have used this period, whether renders carry a watermark, and the plans on offer. Returns { enabled, plan, reason, subscription, period, limits, limitSource, usage, watermark, canManage, plans }. On a self-hosted Reelsaw this answers that billing is not enabled.

NameRequiredDescriptionDefault

No parameters

TDQS

A4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The readOnlyHint=true annotation covers the safety profile, and the description adds real context beyond it: what the response contains, that limits carry a source (instance, plan, admin override), and that self-hosted deployments return an explicit 'not enabled' answer rather than an error. It stops short of stating permission requirements or what canManage implies for the caller.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the hosted-instance scope, which is good, but the first sentence's prose list and the second sentence's return-key list largely restate each other (plan, subscription, period, limits, limitSource, usage, watermark, plans appear twice). The self-hosted note is the one non-redundant sentence and should not be buried at the end.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

There is no output schema, so the description carries the return-value burden and does so by enumerating the response keys plus the top-level enabled/reason/canManage fields. With zero parameters and a read-only annotation, the remaining gaps (auth requirements, what canManage gates) are minor for a status-reading tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes no parameters, so there is nothing for the description to disambiguate beyond confirming it acts on the current instance without arguments. Baseline applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description names the resource (billing/subscription for a hosted instance) and enumerates exactly what it reports: plan, billing period, effective limits with provenance, period usage, watermark status, and available plans. It also explicitly carves out the self-hosted case, so an agent can tell immediately whether this tool applies to the running instance.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage context is implied by the hosting distinction ('Hosted instances…', 'On a self-hosted Reelsaw this answers that billing is not enabled'), which is genuinely useful for deciding whether it's worth calling. However, it never names the obvious alternative, billing_checkout_url, nor states the condition that selects one over the other (e.g., view status here, obtain a payment URL there).

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_clipGet clipA
Read-only

Everything about one clip: text, bounds, scores, emphasis, layers (assets, text, sounds), render diagnostics, video/thumbnail/editor links, and what the next render will use with where each comes from: style { own, effective, from: clip|project|default }, framing { own, effective: { mode, adjust, sections }, from }, bumpers { own, effective } (intro/outro). Returns the clip object. get_project lists all clips in brief; poll this after an edit to see the render finish (status done).

NameRequiredDescriptionDefault
idYesClip id (from get_project or search_clips)

TDQS

A4.1/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true, so the safety profile is covered; the description adds genuine context beyond that by promising 'Returns the clip object' and explaining that the response includes both 'own' and 'effective' values with their source (clip|project|default). That inheritance behavior is the kind of thing an agent cannot infer from structured fields.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Purpose is front-loaded and the usage hint is short and actionable. The opening sentence is a long enumeration of return fields that borders on run-on, but each item tells the agent something exists in the response, so little of it is wasted.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

There is no output schema, so the description's enumeration of returned fields and the own/effective/from resolution rules are essential and present. It also explains the render-status polling pattern. What is missing is minor (e.g., what happens with an invalid or stale id).

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and the single parameter already carries a description ('Clip id (from get_project or search_clips)'). The description adds nothing about the id format, so the schema does the work and the baseline of 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Everything about one clip') and then enumerates exactly what that means (text, bounds, scores, emphasis, layers, render diagnostics, links). It explicitly distinguishes itself from get_project ('lists all clips in brief'), so an agent can choose between them without opening either schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Names a concrete alternative (get_project for the brief list) and gives an explicit operational trigger: 'poll this after an edit to see the render finish (status done).' That is clear when-to-use guidance, though it does not cover when to prefer get_clip_captions or search_clips instead.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_clip_captionsGet clip captionsA
Read-only

The captions of a clip's last render as an SRT or WebVTT file: the same lines and times as the video (intro included), even when the style has captions off. Returns the file text; VTT with words: true adds karaoke word timestamps. A clip rendered before caption files existed answers that it needs a re-render (rerender_clip).

NameRequiredDescriptionDefault
idYesClip id (from get_project or search_clips)
wordsNoVTT only: add a <timestamp> before every word after the first in each cue
formatNosrt (default) or vttsrt

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond readOnlyHint=true, the description discloses that captions mirror the rendered video's lines and times including the intro, that they exist even when the style has captions off, that the return value is file text, and that a clip rendered before caption files returns a message pointing at rerender_clip. That stale-render behavior is exactly the kind of non-obvious trait the description must carry.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three sentences, front-loaded with what the tool returns, then parameter nuance, then the failure case. Dense but every sentence earns its place; slight compression could improve readability.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

There is no output schema, but the description states the return value is file text and explains what that text contains. Combined with the stale-render case, an agent has everything needed to call it correctly and interpret the result.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so baseline is 3, but the description adds meaningful plain-language semantics: words: true yields karaoke-style word timestamps and only applies to VTT. That clarifies the interaction between the two optional parameters beyond what the schema states.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description names a specific verb+resource (fetch the captions of a clip's last render) and pins down the exact artifact form (SRT or WebVTT file text). The phrase 'a clip's last render' distinguishes it from project-level transcript siblings such as get_transcript and get_project_transcript_file.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It clearly states the context in which this tool applies (captions of the last render, intro included, regardless of style settings) and routes the stale-render case explicitly to rerender_clip. It doesn't explicitly contrast with get_clip or get_transcript, but the trigger conditions are clear.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_email_preferencesEmail preferencesB
Read-only

Which optional emails you get, by topic. Returns { emailEnabled, emailPreferences: { ready } }; ready is the note sent when projects finish while you are away. emailEnabled false means this instance sends no mail at all.

NameRequiredDescriptionDefault

No parameters

TDQS

B3.4/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

readOnlyHint=true already tells the agent this is a safe read, and the parameterless schema confirms nothing is mutated. The description does add real behavioral value by naming the returned fields and explaining what `ready` means, but it stops short of saying whether preferences are per-instance or per-user, or what an empty result implies.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three tight sentences with the core answer front-loaded and the return shape immediately after. No filler, though the field walkthrough is slightly terse and could be grouped more cleanly.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description carries the burden of explaining the return value and does so, covering both emailEnabled and the ready note. For a trivial parameterless read tool that is close to sufficient; only the relationship to the setter tool is left implicit.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Zero parameters, so the baseline is 4. The description uses its space on output field semantics instead, which the schema cannot cover, so nothing is lost by the absence of parameter discussion.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description identifies the resource concretely: which optional emails the instance sends, broken down by topic, with the top-level return shape given. The verb is implied (a read/query) rather than stated outright, and the sibling set_email_preferences is never named, so the agent must infer the read-vs-write split itself.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

There is no statement of when to call this versus set_email_preferences or get_instance_settings. The note that emailEnabled false means no mail is sent is interpretive context, not a usage condition, so an agent gets no routing guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_failureGet a failed project or clipA
Read-only

Admins only: one failed project or clip (title, owner email, stage, full recorded reason, when). A clip whose project also failed is not returned — retry the project. Returns the item or null if it is gone (retried, deleted). Hosted non-admins are refused.

NameRequiredDescriptionDefault
idYesProject id or clip id
kindYesproject = the whole pipeline stopped; clip = one clip failed to prepare or render

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only declare readOnlyHint=true, and the description adds substantial context beyond that: admin-only authorization, refusal behavior for hosted non-admins, a null return when the item is gone (retried or deleted), and the clip/project deduplication rule. These are exactly the behavioral traits an agent cannot infer from structured fields.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three compact sentences, no filler. The admin restriction leads, the return payload and the clip-suppression rule follow, and the null case closes it out — front-loaded and dense with information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema exists, yet the description enumerates the returned fields, covers the null result, and documents the authorization boundary. For a two-parameter read tool, an agent has everything needed to call it and interpret the response.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so both parameters are already documented, including the kind enum. The description still contributes meaning by tying kind to a behavioral consequence (clip suppression when its parent project failed), which the schema does not convey. Baseline 3 plus that added semantics merits a 4.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Names a specific verb+resource (retrieve one failed project or clip) and immediately enumerates what comes back: title, owner email, stage, full recorded reason, and timestamp. That field list plus the admin restriction makes it unmistakable against siblings like list_failures or get_project.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives concrete routing advice: a clip whose project also failed is not returned, so retry the project instead. That is an explicit alternative path. It stops short of stating when to prefer this over list_failures for enumeration, which is the only real gap.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_instance_settingsInstance settingsA
Read-only

Admins only: the instance settings in force (sign-up mode, email verification, captcha, legal links, quotas, upload limits, retention, stall watchdog, update check, health details) and where each comes from. Returns { values, env, override, source: { KEY: "env" | "db" }, ignored: { KEY: reason }, groups, help, updatedAt, updatedBy }; secrets are masked.

NameRequiredDescriptionDefault

No parameters

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true, so the description isn't burdened with safety claims. It still adds real value by disclosing that secrets are masked and that provenance (env vs db) and ignored keys are part of the result — traits an agent needs to interpret the output correctly.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the purpose and audience, then the return shape. The long parenthetical setting list is dense but earns its place by telling the agent what is covered before it calls.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema present, the description fully compensates by spelling out the return object's fields (values, env, override, source, ignored, groups, help, updatedAt/updatedBy). Nothing needed to call or interpret it is absent.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes zero parameters, so the baseline is 4 by rule. No parameter meaning is missing because there is nothing to document.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('the instance settings in force') and enumerates the domains covered (sign-up mode, quotas, retention, etc.) plus provenance. An agent can distinguish it from the mutation sibling set_instance_settings without opening a schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

'Admins only' gives a clear access prerequisite, and the read-only framing implies inspection usage. But it never names set_instance_settings as the write alternative or says when to prefer this tool over sibling lookups like check_for_update or get_billing.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_projectGet projectA
Read-only

One of your projects: status, settings, effective intro/outro, transcript summary, and every clip (id, title, hook, bounds, score, status, download links); replacedClips lists deleted or replaced clips for restore_clip. Returns the project object. For one clip's full detail use get_clip, for the words get_transcript, for the pipeline history get_project_events.

NameRequiredDescriptionDefault
idYesProject id (from list_projects, search_clips or create_project)

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only declare readOnlyHint=true, so the description carries the burden of describing the return payload and does so richly, including the non-obvious replacedClips field and its link to restore_clip. It stops short of pagination, size limits, or error behavior, which keeps it from a 5.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with what the tool returns, followed by one routing sentence. It is dense but each clause (clip fields, replacedClips, sibling routing) earns its place; only the 'Returns the project object' sentence is slightly redundant with the opening enumeration.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description must convey the response shape, and it does so field-by-field, including the otherwise undiscoverable replacedClips semantics. Combined with the sibling routing, an agent has everything needed to call and interpret this tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

There is a single parameter with 100% schema description coverage, and the schema already states the id and where to obtain it (list_projects, search_clips, create_project). The description adds nothing about the id beyond what the schema provides, so the baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Names a specific verb and resource and enumerates exactly what the project payload contains (status, settings, intro/outro, transcript summary, clips with their fields). It also names the sibling tools it is not (get_clip, get_transcript, get_project_events), so an agent can distinguish it without opening schemas.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly routes the agent to alternatives: get_clip for one clip's detail, get_transcript for words, get_project_events for pipeline history. It also states the purpose of replacedClips (restore_clip), giving a clear when-to-use signal for a downstream tool.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_project_defaultsGet project defaultsA
Read-only

The settings every new project starts with (Settings, Project defaults): language, vocabulary, instructions, focus, clip lengths, pauses, caption look and refine steps. Returns { projectDefaults }; keys you never set are absent and use the built-in value. Change them with set_project_defaults.

NameRequiredDescriptionDefault

No parameters

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

readOnlyHint is already declared by annotations, but the description adds real behavioral detail beyond that: the response shape '{ projectDefaults }' and the crucial semantics that unset keys are absent and fall back to built-in values. This is exactly the kind of return-value subtlety annotations cannot convey, though nothing is said about permissions or rate limits (low relevance for a read).

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, no padding: the identity/scope of the resource comes first, the return shape and absent-key semantics second, the handoff to set_project_defaults last. Every clause carries information an agent would otherwise have to guess.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a zero-parameter read tool with no output schema, the description supplies what is missing from structured data: the returned key name, the absent-key fallback rule, and the companion mutation tool. Nothing an agent needs in order to call it correctly is absent.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes no parameters, so the baseline is 4. The description correctly implies no inputs are needed and spends its words on the return payload instead.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States the specific resource ('the settings every new project starts with') and enumerates the fields it covers (language, vocabulary, instructions, focus, clip lengths, pauses, captions, refine steps). The closing sentence names its mutation counterpart set_project_defaults, so the agent can distinguish it from the rewrite tool without inspecting schemas.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Clearly frames the tool as the read side of project defaults and explicitly routes mutation to set_project_defaults. It does not state when-not conditions (e.g., when get_project would be preferred for a specific project's settings), but for a zero-argument getter the intended context is unambiguous.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_project_eventsProject activityA
Read-only

The project's pipeline history, newest first (the same events the Activity panel shows; live progress ticks are not stored): stage starts and completions, clip renders, retries, failures with reasons. Returns [{ at, type, ...details }]. Use it to find out why something failed or is slow; get_project shows the current state.

NameRequiredDescriptionDefault
idYesProject id (from list_projects, search_clips or create_project)
limitNoMost events to return (1-200, default 50)

TDQS

A4.1/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With readOnlyHint already declaring safety, the description adds genuinely new behavioral context: live progress ticks are not stored (so polling will not surface in-flight state), the order is newest-first, and the return shape is given. It stops short of saying what a truncated result looks like or how far back history goes.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two dense sentences, front-loaded with what the tool is and immediately followed by the qualifier about unstored ticks and the return shape. Every clause carries information, though the parenthetical and clause list make it slightly harder to scan than a purely linear phrasing.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema exists, but the description supplies the return shape and event taxonomy, which is the key missing piece. The only real gap is pagination/truncation semantics for limit, which matters for an agent that may need older events.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so both id and limit are already fully documented in the schema, including the 1-200 range and default. The description adds only the ordering guarantee, not any meaning for the parameters themselves, so baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific resource (the project's pipeline history) with a concrete scope and ordering (newest first), plus the exact event kinds returned. It explicitly contrasts with get_project's current-state view, so an agent can pick between them without opening a schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives an explicit use case ("find out why something failed or is slow") and names the alternative (get_project) with the condition that selects it. It does not address the other overlapping siblings such as get_failure or get_queue, which also surface failure details, so the routing is clear but not exhaustive.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_project_transcript_fileGet transcript fileA
Read-only

The project transcript as an SRT or WebVTT subtitle file, one cue per transcript segment, in source-video seconds. Returns the file text; VTT with words: true adds word timestamps. Use get_transcript to read it as plain timed lines instead.

NameRequiredDescriptionDefault
idYesProject id (from list_projects, search_clips or create_project)
wordsNoVTT only: add a <timestamp> before every word after the first in each cue
formatNosrt (default) or vttsrt

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

readOnlyHint=true already covers the safety profile, and the description adds real value on top: returns file text (not a parsed structure), timing is in source-video seconds, and words:true only affects VTT word timestamps. It doesn't mention failure behavior when no transcript exists, which is the one behavioral gap for a retrieval tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, front-loaded with the output format and segmentation, then the words/format behavior, then the sibling pointer. Nothing is redundant with the schema or annotations.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description does the job of describing what comes back (file text, one cue per segment, source-video seconds). Complete for the happy path, though it never says what happens if the project has no transcript or is still processing, which matters in a pipeline with wait_for_project and reanalyze_project siblings.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description adds semantics the schema lacks: that cue timing is expressed in source-video seconds, and that the words flag produces a per-word <timestamp> marker in the cue rather than a separate field. That clarifies intent beyond the enum/default documentation.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource (get the project transcript file) and pins the output form: SRT or WebVTT subtitle file, one cue per segment, in source-video seconds. It explicitly distinguishes itself from the closely named sibling get_transcript by contrasting file-based subtitles with plain timed lines.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

'Use get_transcript to read it as plain timed lines instead' names the alternative and the condition that selects it, so an agent can route between the two transcript tools without opening either schema. The subtitle-file vs timed-lines distinction is the exact decision an agent faces here.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_queueQueue summaryA
Read-only

Admins only: waiting, running and retrying job counts per pipeline stage with when the longest wait began (oldestWaitingAt), plus the jobs currently in flight (project title, attempt, started, and a retrying job's lastError). Returns { queues, active, truncated, error? }. Failed work is list_failures, not this. Hosted non-admins are refused.

NameRequiredDescriptionDefault

No parameters

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond readOnlyHint=true, it discloses authorization behavior (non-admins refused on hosted) and a truncation flag in the response, plus what error may appear. That is meaningful operational context an agent cannot infer from the annotations. It does not state rate limits or how partial results are ordered/filled when truncated, which keeps it off a 5.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three sentences, with the admin gate and the metric list front-loaded before the return shape and the list_failures exclusion. Dense but essentially waste-free; the parenthetical field enumeration is the only slightly heavy part.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description carries the return-value burden and does so by naming the top-level shape { queues, active, truncated, error? } and the key fields (project title, attempt, started, lastError). Inner structure of queue entries and truncation behavior are only sketched, which is the remaining gap for a read-only summary tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes zero parameters, so there is nothing for the description to disambiguate; baseline is 4. The description correctly does not invent arguments and instead spends its words on scope and return shape.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific resource (queue) and what it reports — waiting/running/retrying counts per pipeline stage plus in-flight jobs — in concrete terms. It explicitly distinguishes itself from the sibling list_failures ("Failed work is list_failures, not this"), so an agent can pick between them without opening schemas.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives a hard prerequisite ("Admins only", hosted non-admins are refused) and routes the failed-job case to list_failures. It stops short of naming other adjacent alternatives (e.g. list_workers, list_failures sibling set) or the situations that make this the right call vs. a project-status tool, but the usage context is clear.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_transcriptGet transcriptA
Read-only

The project's transcript as timed lines "[m:ss] text", optionally only a time range. Returns text; output longer than maxChars is cut with a note saying which fromSec to continue from. Use it to pick your own moments for create_clip.

NameRequiredDescriptionDefault
idYesProject id (from list_projects, search_clips or create_project)
toSecNoEnd of the range, seconds
fromSecNoStart of the range, seconds from the start of the video
maxCharsNoCut the output at this many characters (1000-200000, default 60000)

TDQS

A4.1/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true, so safe read is covered. The description adds valuable behavior: the truncation policy ('output longer than maxChars is cut with a note saying which fromSec to continue from') and the return format, which goes beyond the annotation's safety hint.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three focused sentences with no waste, front-loading the output format and then covering the truncation behavior and the recommended use case. Slightly dense but well-structured.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a read-only transcript tool with no output schema, the description covers the return format, truncation behavior, and downstream use case. It is nearly complete, though it could mention whether the range is clamped to video length or how missing projects are handled.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already fully documents all 4 parameters. The description mentions the time range and maxChars truncation, but adds no syntax or format details beyond what the schema provides. Baseline 3 is appropriate when the schema does the heavy lifting.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource ('transcript') and describes the exact output format ('[m:ss] text' timed lines). It also distinguishes it from siblings by naming create_clip as the downstream use case.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly tells the agent 'Use it to pick your own moments for create_clip', giving a clear use case. However, it doesn't state when NOT to use it or name an alternative for retrieving transcript text in a different way.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

import_transcriptImport transcriptA
Destructive

Use an SRT or WebVTT file as the project's transcript instead of transcribing: right after create_project (only the video is fetched) or on a done or failed project (it then re-cleans the transcript and picks clips again; accepted and rejected fixes, cuts and corrections on the old transcript are lost for good). Give exactly one of text, uploadId or url. Returns { format, cues, words, warnings, message }; follow with wait_for_project.

NameRequiredDescriptionDefault
idYesProject id (from list_projects, search_clips or create_project)
urlNohttp(s) link to a public .srt/.vtt file
textNoThe contents of the .srt or .vtt file
formatNoauto (default) decides from the WEBVTT headerauto
languageNoLanguage code of the text, e.g. en, es, ja (default: the existing transcript's, else unknown)
uploadIdNoUpload id of an .srt/.vtt file sent to POST /api/upload

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only flag destructiveHint=true; the description supplies the actual consequence: the transcript is re-cleaned, clips are picked again, and 'accepted and rejected fixes, cuts and corrections on the old transcript are lost for good'. That is exactly the irreversibility context an agent needs before a destructive call. It stops short of noting auth requirements or size/rate constraints, so a 4 rather than 5.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three sentences, front-loaded with the purpose, then the source constraint, then the return shape and next step — no filler. The first sentence is long with nested parentheticals, which slightly slows parsing but every clause carries distinct information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 6-parameter destructive tool with no output schema, the description covers applicability states, the mutually exclusive source inputs, the destructive consequence, the return shape, and the required follow-up call. Nothing an agent needs to invoke it safely and correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline would be 3, but the description adds a constraint the schema does not encode: exactly one of text, uploadId or url must be supplied (the schema only requires id). That meaningfully reduces malformed calls beyond what the field descriptions convey.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Use an SRT or WebVTT file as the project's transcript') and explicitly contrasts it with the default path ('instead of transcribing'), which distinguishes it from siblings like reanalyze_project or refine_project. An agent can identify the operation without opening the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Names concrete lifecycle states where it applies ('right after create_project (only the video is fetched)' and 'on a done or failed project'), and prescribes the follow-up call ('follow with wait_for_project'). It also gives the mutual-exclusion rule ('Give exactly one of text, uploadId or url'), leaving nothing to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_decisionsList decisionsA
Read-only

Your decision log: what you accepted, rejected, cut, corrected, edited, deleted or restored, newest first, kept after a project is deleted so Reelsaw learns from it. Returns { summary: { enabled, total, projects, byKind }, items: [{ id, kind, explicit, projectId, clipId, before, after, context, models, source, createdAt }], nextCursor, total }. kinds are fix.* (transcript fixes) and clip.*; clip.replaced (explicit false) is a pick a re-analyze replaced.

NameRequiredDescriptionDefault
kindNoOnly this kind of decision
limitNoPage size (1-200, default 50)
cursorNonextCursor from the previous page
projectIdNoOnly decisions about this project (it may already be deleted)

TDQS

A3.9/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only declare readOnlyHint=true, while the description adds meaningful behavior: decisions are returned newest first, are retained after project deletion so the system can learn from them, and include summary/item/pagination fields. It also explains the special meaning of clip.replaced and explicit=false. These details go well beyond the annotation, though permission or rate-limit behavior is not discussed.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with the core purpose and then moves efficiently into return shape and kind semantics. It is fairly dense but each part contributes useful information, with only mild run-on density in the first sentence.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a read-only list tool with no output schema, the description supplies the return structure, ordering, pagination field, retained-after-deletion behavior, and kind semantics. Combined with full schema coverage and readOnlyHint, it gives an agent enough context to call and interpret the tool correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is already 100%, so the baseline is 3, but the description adds useful semantic grouping for kind values as fix.* transcript fixes versus clip.* decisions, and clarifies the meaning of clip.replaced with explicit=false. It also notes that projectId may refer to an already-deleted project, reinforcing the schema's wording.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states a specific verb and resource: it lists the user's decision log, including accepted/rejected/cut/corrected/edited/deleted/restored decisions, newest first. It is specific enough to distinguish this from generic listing tools, though it does not explicitly name sibling tools such as export_decisions or forget_decisions.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is implied as viewing the decision history, but the description gives no explicit when-to-use guidance and does not contrast this tool with nearby alternatives like export_decisions, forget_decisions, or set_learning. An agent can infer the purpose, but routing between decision-related siblings is left to the agent.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_deletedList deleted itemsA
Read-only

Restorable deletes (the Deleted page): one newest-first list of projects, clips of live projects, library items and templates. Returns { items: [{ kind, id, title, deletedAt, purgeAt, storedBytes, canPurge, … }], total, truncated } (max 200) plus keepDays / libraryDays / templateDays. purgeAt null = kept until deleted forever. restore_project / restore_clip / restore_asset / restore_layer_template bring them back; purge_project / purge_clip / purge_asset / purge_layer_template remove them for good.

NameRequiredDescriptionDefault

No parameters

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations declare readOnlyHint=true and the description is fully consistent, adding behavior the annotation can't: max 200 items with a truncated flag, retention windows (keepDays/libraryDays/templateDays), and that purgeAt null means kept until deleted forever. This is exactly the extra context the annotation bar asks for.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the scope and the newest-first ordering, then the return shape, then the sibling routing. Dense and nearly waste-free, though the item-shape enumeration and the four sibling name pairs make it a long single paragraph that could be split for scanability.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema exists, so the description carries the full burden of describing the return value and does so precisely (items fields, total, truncated, max 200, retention fields, purgeAt semantics). An agent has everything needed to call it and interpret the result.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Zero parameters, so the baseline is 4; the description has no parameter semantics to add and none are needed. It redirects that space into return-shape detail instead.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('one newest-first list of projects, clips of live projects, library items and templates'), plus the scope 'Restorable deletes (the Deleted page)'. That scope qualifier cleanly separates it from list_projects, search_clips and the restore_*/purge_* siblings.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly routes the agent: restore_project/restore_clip/restore_asset/restore_layer_template bring items back, purge_* removes them permanently, implying this tool is the read-only discovery step before either. It doesn't state an explicit 'do not use for X' exclusion, which keeps it from a 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_failuresList failed workA
Read-only

Admins only: failed projects and clips across the instance (owner email, stage, reason, when). A failed project's clips are not listed separately — retry the project. Returns { items, nextCursor, total, limit, truncated }. Filter with q (substring of the error), stage, since (ISO time). Hosted non-admins are refused.

NameRequiredDescriptionDefault
qNoCase-insensitive substring of the recorded error (e.g. disk, encoder, model)
limitNoPage size, default 50
sinceNoISO timestamp; only failures updated at or after this time
stageNoPipeline stage that failed
cursorNonextCursor from the previous page

TDQS

A4.1/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Goes well beyond the readOnlyHint annotation: it discloses the admin-only permission requirement, the hosted-tier refusal behavior, the aggregation semantics (child clips are collapsed into the parent project failure), and the truncation/nextCursor pagination behavior. These are exactly the traits an agent needs before calling.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Dense and front-loaded: access restriction first, then scope, then the clip-aggregation caveat, then return shape, then filters. Slightly crammed, but nearly every clause carries information an agent needs.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

There is no output schema, yet the description names the return keys ({ items, nextCursor, total, limit, truncated }) and explains pagination via cursor. Combined with the auth and filtering notes, an agent has enough to call this correctly without inspecting anything further.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so all five parameters are already documented with examples and formats. The description only restates q, stage, and since at a high level and says nothing about limit or cursor, so it adds marginal value over the schema — baseline 3.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('failed projects and clips across the instance') plus the fields returned (owner email, stage, reason, when). It is clearly distinct from get_failure (single item) and retry_failures in intent, though it never names a sibling explicitly.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly scopes the audience ('Admins only', 'Hosted non-admins are refused') and gives a concrete routing rule: a failed project's clips are not listed separately, so retry the project instead. It also tells the agent which filters to apply (q, stage, since), though it doesn't contrast against get_failure or retry_failures by name.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_layer_templatesList layer templatesA
Read-only

Your layer templates (layers that fit any clip length, plus an optional intro / outro), newest first. Returns { items: [{ id, name, layerCount, layers, bumpers, updatedAt }], nextCursor, total, deleted } (nextCursor goes in cursor; deleted = restorable). A layer has the clip-layer fields plus timing { mode, start, end, minSec, maxSec }: head = seconds after the start; tail = seconds before the end (end 0 = the very end); fill = whole clip; relative = fractions 0-1 of the length, held between minSec and maxSec

NameRequiredDescriptionDefault
limitNoTemplates per page (1-500, default 100)
cursorNonextCursor from the previous page; omit for the first page

TDQS

A4.4/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With readOnlyHint already declaring safety, the description adds substantial behavior: newest-first ordering, pagination via nextCursor, a deleted field meaning restorable items, and detailed semantics for layer timing modes. This goes well beyond what annotations provide and gives an agent operational context for interpreting results.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with the tool's purpose and has no filler, but it is a single dense run-on sentence with many nested clauses. The information density is justified because there is no output schema, though readability could be improved with structural separation.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

There is no output schema, so the description must carry return-shape information, and it does: it names the item fields, explains nextCursor/total/deleted, and defines the timing object used by layers. For a read-only listing tool with only two simple parameters, this is complete enough for an agent to call and interpret it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3. The description adds meaningful semantics beyond the schema by explaining that nextCursor goes into cursor when paging, which clarifies how the pagination parameter is meant to be used.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb-and-resource pair: it lists the caller's layer templates, defines them as reusable layers plus optional intro/outro, and states the default ordering (newest first). That clearly separates it from sibling mutation tools like save_layer_template, update_layer_template, apply_layer_template, and delete_layer_template.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is implied by the verb 'Your layer templates' and by the pagination details, but there is no explicit when-to-use guidance, prerequisite note, or alternative mentioned. An agent can infer this is the listing/read tool, but the description does not route it against alternatives such as get_clip or apply_layer_template.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_projectsList projectsA
Read-only

Your projects, most recently updated first. Returns { projects: [{ id, title, status, stage, clips: "n/m rendered", url }], total, nextCursor }; pass nextCursor as cursor for the next page. deleted: true lists restorable deletes (storedBytes still count until purge_project). For every kind of restorable delete (projects, clips, library, templates) use list_deleted. Use get_project for one project's clips, search_clips to find by words.

NameRequiredDescriptionDefault
limitNoProjects per page (1-100, default 24)
cursorNonextCursor from the previous page; omit for the first page
deletedNotrue = only deleted projects that restore_project can still bring back

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With readOnlyHint already declaring safety, the description still adds real behavior: result ordering, the exact response envelope, the nextCursor pagination loop, and that deleted items' storedBytes still count until purge_project. That last point is non-obvious mutation-adjacent context an agent would otherwise miss.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Densely packed, front-loaded with scope and ordering, and every clause carries information (response shape, pagination, deleted semantics, alternatives). It is a single long block rather than broken up, and the return-shape serialization is slightly verbose, but nothing is filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema exists, and the description supplies the full response envelope, pagination mechanics, deleted-mode caveat, and sibling disambiguation. For a three-param read-only list tool, an agent has everything needed to call it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description earns extra: it explains that nextCursor must be passed as cursor for the next page (the pagination idiom) and clarifies what deleted:true really means (restorable deletes whose bytes still bill). It adds marginal but useful meaning beyond the schema strings.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States the resource ('Your projects') and ordering ('most recently updated first') and implicitly the list verb, while naming the siblings it is not (get_project for a single project, search_clips for word search, list_deleted for other delete kinds). An agent can distinguish it from every sibling without opening a schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit routing: 'Use get_project for one project's clips, search_clips to find by words', and 'For every kind of restorable delete (projects, clips, library, templates) use list_deleted'. That covers when to use this versus alternatives, including the subtlety that deleted=true only lists projects.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_releasesList releasesA
Read-only

Published Reelsaw versions from GitHub (reelsaw.com/releases): tag, date, a short summary and the change notes. Returns { releases, total, windowDays, commits, commitDays, releaseDays, truncated, fetchedAt, sourceUrl, error }; pass tag for one version. Cached for an hour.

NameRequiredDescriptionDefault
tagNoOnly this tag or version (v0.19.1 or 0.19.1)
limitNoHow many releases to return (1-100, default 20, newest first)

TDQS

A3.9/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only declare readOnlyHint=true, so the description usefully adds that results are 'Cached for an hour', names the upstream source URL, and discloses envelope fields including truncated and error signals. It doesn't discuss rate limits or behaviour when the upstream fetch fails, but the caching and error-field disclosure go meaningfully beyond the annotation.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two compact sentences, resource and source front-loaded, with the response envelope listed inline rather than prose-padded. The key enumeration is dense but earns its place given the absence of an output schema.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description takes on the return-shape burden and does so by enumerating the envelope fields including truncated and error. Caching and source are covered; the only gap is how limit and tag interact, which is minor for a read-only listing tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so both parameters are already documented in the schema, and the description only adds the passing notion that a tag returns a single version rather than a list. The limit parameter and the interaction between tag and limit are not addressed, so the description does not materially exceed the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource ('Published Reelsaw versions from GitHub') and immediately enumerates the fields returned (tag, date, summary, change notes), so the agent knows exactly what this yields. It is clearly distinct from app-update siblings like check_for_update because it names the release source and URL.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The clause 'pass tag for one version' gives implicit guidance on how to narrow the call, and 'newest first' in the schema implies the default listing behaviour. However, there is no explicit when-to-use/when-not guidance and no comparison to the nearest sibling, check_for_update, which an agent could plausibly confuse with this tool.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_transcript_flagsList transcript fixesA
Read-only

The fixes Reelsaw made or suggests in a project's transcript: misheard names, fillers and repetitions to cut, and your own cuts and corrections. Returns { total, flags: [{ id, kind, subkind, status, start, end, heard, suggested, replacement, confidence }] }; status pending waits for your decision. Use the ids with review_transcript_flags.

NameRequiredDescriptionDefault
idYesProject id (from list_projects, search_clips or create_project)
limitNoMost fixes to return (1-500, default 100)
statusNoOnly fixes in this state; omitted = all (pending = waiting for review, auto = applied automatically)

TDQS

A4.1/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With readOnlyHint already declaring the safety profile, the description adds real value: it discloses the return shape, the meaning of the flags' content ('misheard names, fillers and repetitions to cut, and your own cuts and corrections'), and the status semantics that drive the next action. It stops short of describing ordering, pagination/truncation behavior at the limit, or what an empty result looks like.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the resource and its contents, then the return shape, then the actionable next step — no filler sentences. The inline brace-notation return schema is dense but earns its place since no output schema exists.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a read-only, three-parameter listing tool with no output schema, the description supplies the return structure, the flag taxonomy, and the workflow link to review_transcript_flags — enough to call it correctly. Remaining gaps (result ordering, behavior when limit truncates) are minor for a listing operation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, and the enum descriptions already define pending and auto ('waiting for review', 'applied automatically'), so the description's 'status pending waits for your decision' largely restates structured data. The description adds indirect context about what the ids are for, but no new constraint or format details beyond the schema, so baseline 3 is warranted.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('the fixes Reelsaw made or suggests in a project's transcript') and enumerates the categories of flags returned, which distinguishes it from get_transcript and from the mutating sibling review_transcript_flags. An agent can tell at a glance that this is a read-only enumeration of reviewable corrections.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly routes the agent forward: 'Use the ids with review_transcript_flags', and explains that 'status pending waits for your decision', which frames the review workflow. It does not state when to prefer get_transcript or add_transcript_cut/add_transcript_correction instead, so the exclusion side is implied rather than spelled out.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_webhook_deliveriesList webhook deliveriesA
Read-only

The delivery log, newest first. Returns { items: [{ id, endpointId, eventId, type, projectId, status (pending | sending | delivered | failed | cancelled), attempt, lastStatusCode, lastError, nextAttemptAt, deliveredAt }], nextCursor, total }. Failed deliveries were retried 8 times over about 22 h.

NameRequiredDescriptionDefault
limitNoDeliveries per page (1-100)
cursorNonextCursor from the previous page
statusNoOnly deliveries in this state
projectIdNoOnly deliveries about this project
endpointIdNoOnly this webhook's deliveries (from list_webhooks)

TDQS

A3.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true, and the description adds useful behavioral context: newest-first ordering, the full return shape, status values, and retry behavior for failed deliveries. It does not cover permissions or rate limits, but for a read-only list tool this is strong added transparency.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with the core purpose and then provides return details and retry context. The inline return object is dense but justified because there is no output schema, and every sentence contributes useful information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description fully documents the return structure, ordering, status values, pagination fields, and retry behavior. Combined with the complete input schema and readOnly annotation, an agent has enough information to call and interpret the tool correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents all five parameters. The description does not add meaning beyond the schema for limit, cursor, status, projectId, or endpointId, so the baseline of 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description identifies the resource as a webhook delivery log and states the ordering (newest first), so the agent can tell it is a list operation over delivery records. It does not explicitly differentiate from sibling tools such as list_webhooks or list_failures, which keeps it from a 5.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

There is no explicit guidance on when to use this tool versus alternatives like list_webhooks or list_failures. The retry note is contextual but does not tell the agent when this tool is the right choice.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_webhooksList webhooksA
Read-only

Your webhook endpoints. Returns [{ id, url (masked), description, events, status (active | failing | off), disabledReason, failingSince, lastDelivery }]. Reelsaw POSTs signed JSON (Standard Webhooks headers webhook-id, webhook-timestamp, webhook-signature) to each when a subscribed event happens.

NameRequiredDescriptionDefault

No parameters

TDQS

A3.9/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true, so the safety profile is covered; the description nonetheless adds real behavioral context the annotations do not: URLs are masked, status is a three-valued state (active | failing | off) with accompanying disabledReason/failingSince/lastDelivery fields, and deliveries are signed JSON using Standard Webhooks headers. That tells the agent both what a 'failing' endpoint looks like and how delivery works.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two tight sentences: the resource identity first, then the exact response fields, then delivery behavior. No filler, no restatement of the title, and the field list is given in a form an agent can parse directly.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema and no parameters, the description carries the whole burden of describing the return value, and it does so thoroughly, including edge-state fields like disabledReason and lastDelivery. Minor gaps remain: ordering, whether all endpoints are returned or truncated, and per-endpoint event subscription semantics are unstated.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes zero parameters, so the schema has nothing to explain and the baseline is 4. The description does not need to add argument semantics, and it correctly spends its words on the return payload instead.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The opening line 'Your webhook endpoints' plus an explicit return-shape enumeration makes clear this is a read of configured webhook endpoints. It is distinguishable from delete_webhook/update_webhook by the read framing and from list_webhook_deliveries by returning endpoint objects (id, url, events, status) rather than deliveries. It never names a sibling explicitly, so differentiation is inferential rather than stated.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is implied: you call this to inspect the webhooks currently registered on the account. There is no explicit 'use this when...' or 'do not use this for delivery history, use list_webhook_deliveries instead' statement, and no mention of prerequisites such as authentication or scoping. An agent can guess correctly, but the description does not route it.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_workersList workersA
Read-only

Admins only: every Reelsaw process that has checked in (API, worker, combined app): CPU, memory, disk (free/total MB, -1 = unmeasurable; diskLeasedMB = claimed by jobs in flight), GPU, render slots, queues it listens on, and its jobs. A worker out of disk drops the media queues from queues until it recovers. paused = what it was short of (memory, CPU, disk), null when it has room. Returns { items, live, stale }; stale = no check-in for 45 seconds. Hosted non-admins are refused.

NameRequiredDescriptionDefault

No parameters

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Consistent with readOnlyHint=true and adds substantial context beyond annotations: authorization gating, the 45-second staleness threshold, the -1 unmeasurable sentinel, diskLeasedMB semantics, the out-of-disk queue-drop behavior, and the meaning of `paused` including when it is null. This is exactly the operational detail the annotations cannot carry.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the access gate, then the payload contents, then the return shape — a sensible order. It is a single dense run-on with many parentheticals, so it is heavier than ideal, but nearly every clause carries semantics an agent needs (sentinels, staleness, queue dropping).

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

There is no output schema, so the description carries the return contract itself and does: { items, live, stale } with the staleness definition spelled out. Combined with field-level semantics and the auth gate, an agent has everything needed to call and interpret this tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes zero parameters, so the baseline is 4 and there is nothing for the description to disambiguate. The description correctly spends no space on argument semantics.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (list) and a precisely scoped resource: every Reelsaw process that has checked in, enumerated as API, worker, combined app. The metric inventory (CPU, memory, disk, GPU, render slots, queues, jobs) makes it unmistakable against siblings like get_queue or get_instance_settings.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives a clear access precondition up front ('Admins only' ... 'Hosted non-admins are refused'), which tells the agent when the call will fail. It does not name an alternative sibling for non-admin monitoring or for queue-level vs fleet-level inspection, so it falls short of explicit when/when-not routing.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

purge_assetDelete library item foreverA
DestructiveIdempotent

Permanently remove an already-deleted library upload and its files so the storage is freed (find it with list_deleted). Refused while a project, clip or live template still names it. An item that is not deleted is refused: call delete_asset first. Returns { storedBytes, message }.

NameRequiredDescriptionDefault
assetIdYesLibrary asset id (from list_deleted)

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare destructiveHint=true and idempotentHint=true, so safety is covered structurally. The description still adds non-obvious behavior: refusal when an item is referenced, the prerequisite deletion state, and the return payload { storedBytes, message }. Not mentioning any permission/auth requirement is the only mild gap.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Four short sentences, each carrying distinct information: the action, the discovery pointer, the refusal rule, the prerequisite, and the return shape. The irreversible action is front-loaded with no filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With only one parameter and no output schema, the description covers everything an agent needs: prerequisites, refusal conditions, and even the return fields. Nothing required for correct invocation is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and the sole parameter already documents 'Library asset id (from list_deleted)'. The description points to list_deleted for discovery but adds no format, length, or other semantics beyond the schema, so the baseline of 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (permanently remove) and resource (an already-deleted library upload and its files), plus the goal (free storage). The two-stage distinction from delete_asset is made explicit, so the agent can tell it apart from the sibling delete tools without reading schemas.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Names the discovery tool (list_deleted) and states the precondition that the item must already be deleted, with the alternative to call delete_asset first. It also states explicit refusal conditions (referenced by a project, clip or live template), covering both when and when-not to use it.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

purge_clipDelete clip foreverA
DestructiveIdempotent

Permanently remove a deleted clip of a live project and its rendered file so the storage is freed (find it with list_deleted). A clip that is not deleted is refused: call delete_clip first. Clips of a deleted project go with purge_project. Returns { storedBytes, message }.

NameRequiredDescriptionDefault
idYesClip id (from list_deleted)

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare destructiveHint and idempotentHint, but the description adds real value beyond them: the refusal behavior for non-deleted clips, the fact the rendered file is removed and storage freed, and the return shape { storedBytes, message }. There is no output schema, so naming the return fields is a meaningful supplement.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Four short sentences, front-loaded with the destructive action, then prerequisites, then scope alternative, then return value. Nothing is redundant or padded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a one-parameter destructive tool with no output schema, the description covers what it destroys, the required precondition, the per-project alternative, the refusal case, and the response fields. An agent has everything needed to call it correctly and safely.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and the single parameter's schema description already says 'Clip id (from list_deleted)'. The description repeats that provenance but adds no format, syntax, or constraint detail beyond the schema, so baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Permanently remove a deleted clip ... and its rendered file') plus the outcome (storage freed). It is clearly distinguished from sibling mutations like delete_clip (soft delete) and purge_project (project-scoped purge).

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly states the prerequisite (clip must already be deleted; call delete_clip first), how to locate the target (list_deleted), and the alternative for a different scope (purge_project for deleted projects). No inference required.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

purge_layer_templateDelete layer template foreverA
DestructiveIdempotent

Permanently remove an already-deleted layer template (find it with list_deleted). Templates hold no media files; this only drops the row. A template that is not deleted is refused: call delete_layer_template first. Returns { ok, message }.

NameRequiredDescriptionDefault
templateIdYesLayer template id (list_layer_templates)

TDQS

A4.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare destructiveHint and idempotentHint, so the bar is lower, yet the description still adds real context: the action is irreversible, templates hold no media files and only the row is dropped, and non-deleted targets are refused. It also states the return shape { ok, message }. Slightly short of 5 only because it doesn't say what happens to any references or downstream state after the row disappears.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three tight sentences: effect, scope-of-damage, precondition, return value — each load-bearing, with the find-it hint attached to the operation it modifies. No filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a single-parameter destructive tool with no output schema, everything an agent needs is present: how to find the target, the sequencing requirement, the blast radius, the refusal behavior, and the return payload.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so baseline is 3. The description earns an extra point because the schema's own hint for templateId points to list_layer_templates, which lists non-deleted templates that this tool would refuse; the description corrects that by directing the agent to list_deleted instead.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (permanently remove) and resource (an already-deleted layer template), and immediately distinguishes itself from delete_layer_template, the sibling that performs the soft delete. Against a large sibling set containing delete_/purge_/restore_ variants, an agent can pick this one without opening a schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit preconditions and routing: locate the record with list_deleted, and if the template is not yet deleted the call is refused so delete_layer_template must be run first. This is exactly the when/when-not/alternative triad the dimension asks for.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

purge_projectDelete project foreverA
DestructiveIdempotent

Permanently remove a deleted project and its files so the storage is freed (find it with list_deleted or list_projects deleted: true). Minutes, hourly creates and first-video-free are not refunded. A project that is not deleted is refused: call delete_project first. What you decided about it (accepted and rejected fixes, clip edits) stays in your decision log so Reelsaw keeps learning from it, unless forgetDecisions is true. Returns { storedBytes, forgottenDecisions, message }.

NameRequiredDescriptionDefault
idYesProject id (from list_projects with deleted: true)
forgetDecisionsNoAlso forget what Reelsaw learned from this project (its entries in the decision log); default false keeps them

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the destructiveHint/idempotentHint annotations, it discloses what is irrecoverable, that billing artifacts (minutes, hourly creates, first-video-free) are not refunded, and that decision-log entries survive unless forgetDecisions is set. That is exactly the side-effect detail annotations cannot express.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the destructive action and its scope, then preconditions, then side effects, then return shape. Dense and mostly waste-free, though the parenthetical and the return-value sentence pack several ideas into one long line.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description supplies the return shape ({ storedBytes, forgottenDecisions, message }), the refusal case, and the non-refund policy, which covers everything needed to invoke a destructive purge correctly. Nothing material is left undocumented for a 2-parameter tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so baseline is 3; the description adds real meaning by framing forgetDecisions as a learning-retention tradeoff ('Reelsaw keeps learning from it, unless forgetDecisions is true') rather than just a flag. The id parameter's provenance is reinforced from both the schema and the guidance in the usage sentence.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('permanently remove a deleted project and its files') plus the purpose (freeing storage), which cleanly separates it from delete_project (soft delete) and from purge_clip/purge_asset siblings. An agent can tell what it destroys without opening the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives explicit preconditions ('A project that is not deleted is refused: call delete_project first') and names the discovery tools to obtain the id (list_deleted or list_projects with deleted: true). It also states the condition under which the optional forgetDecisions behavior applies, leaving no routing inference to the agent.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

reanalyze_projectRe-analyze projectA

Pick clips again from the existing transcript (needs one), optionally with new settings. Clips you created or edited are kept; untouched AI picks are replaced and restorable with restore_clip for a while. Returns { kept, replaced } at once; the picking runs in the background, so follow with wait_for_project. To only save settings use update_project; to resume a failure use retry_project.

NameRequiredDescriptionDefault
idYesProject id (from list_projects, search_clips or create_project)
settingsNoProject settings; omit for the defaults (automatic clip count, 20–60 s clips, natural pauses, name fixes and the on-screen text scan on, filler removal off). Set refineDisfluency / disfluencyLlm true to opt into filler removal, refineEntities / refineScenes false to skip those steps (instance Pipeline gates must be on).

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full behavioral burden and does so well: it discloses preservation of created/edited clips, replacement of untouched AI picks, restorability via restore_clip 'for a while,' the immediate return shape { kept, replaced }, and the background picking behavior requiring wait_for_project.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is dense but front-loaded: purpose, preservation rules, return behavior, async follow-up, and alternatives are all covered in a few sentences with no wasted text.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a two-parameter tool with a deeply nested settings object and no output schema, the description supplies the key missing context: what is kept vs replaced, that picking is asynchronous, how to wait for completion, and which sibling tools cover adjacent operations.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the detailed settings fields are already documented in the input schema. The description only says 'optionally with new settings' and adds no parameter-level meaning beyond that, which is the expected baseline when the schema does the heavy lifting.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource: pick clips again from the existing transcript, optionally with new settings. It distinguishes this from update_project and retry_project explicitly, so an agent can select the correct tool without opening schemas.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives clear when-to-use context (needs a transcript, optionally new settings) and names the alternative tools for adjacent operations: update_project for saving settings only, and retry_project for resuming a failure. Exclusions are explicit.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

refine_projectRefine projectA
Destructive

Re-run refine steps on the existing transcript: entities (name spelling), disfluency (filler and repeat cuts), scenes (shot cuts); all three when steps is omitted. Optionally set the vocabulary (names and terms, comma separated; empty string clears it) first. Fixes you accepted or rejected may be replaced. Clips re-render afterwards. Returns { ok, steps }; then wait_for_project.

NameRequiredDescriptionDefault
idYesProject id (from list_projects, search_clips or create_project)
stepsNoWhich refine steps to re-run; default all
vocabularyNoNames and terms the transcript should spell this way; empty string clears it

TDQS

A4.3/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Even though destructiveHint=true is annotated, the description adds substantive consequences beyond it: accepted/rejected fixes may be replaced, clips re-render afterwards, and vocabulary can be cleared with an empty string. It also discloses the return shape ({ ok, steps }), which the tool has no output schema for.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Dense but front-loaded: the core action and its variants come first, then side effects, then the return value and follow-up. No sentence is filler, and semicolon-separated clauses keep it compact.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a three-parameter mutation with no output schema, the description covers the step semantics, vocabulary lifecycle, destructive consequences, re-render side effect, return shape, and the correct follow-up call. An agent has everything needed to invoke it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the schema already documents id, steps and vocabulary. The description adds only marginal meaning, notably the comma-separated format for vocabulary and the default-all behavior for steps, both of which the schema largely covers already.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (re-run) and resource (refine steps on the existing transcript), and enumerates the three step types with parenthetical clarification of what each does. It does not distinguish itself from close siblings like reanalyze_project or retry_project, which is the only gap.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives clear operating context: steps default to all three when omitted, and vocabulary can optionally be set 'first'. It also routes the agent's next action ('then wait_for_project'), but never states when to prefer this over reanalyze_project or retry_project.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

report_issueReport an issueA
Read-only

A download link for an issue-report zip (the same file Report an issue in the app saves; admins may include instance-wide diagnostics). Returns { url, filename }; GET the url with the same Authorization header as this server. Admins may set instance true to include unfinished work, failed jobs, usage and error logs across every account, for queue, disk or machine problems that are not just one project. On hosted instances with telemetry, non-admins cannot download a zip.

NameRequiredDescriptionDefault
logsNoInclude this project's log lines (default true)
sourceNoInclude the source video file (default false)
systemNoInclude versions, machine resources, queue counts and recent errors (default true; ignored when not allowed)
clipIdsNoClips the problem shows up in (from get_project)
allClipsNoInclude every rendered clip (default false)
instanceNoAdmins only: include unfinished work, failed jobs, usage and error logs across every account
previewsNoInclude thumbnails and the selected clips' videos (default true)
projectIdNoProject the report is about (from list_projects); omit for an account- or instance-level report
descriptionYesWhat went wrong (a sentence is enough)

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only say readOnlyHint=true, which the description is consistent with (nothing server-side is mutated; a zip is generated for download). Beyond that, it discloses the two facts an agent would otherwise get wrong: the response is a URL that must be fetched with the same Authorization header, and admin/hosted-telemetry access restrictions apply. That is real behavioral context beyond the annotation.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The return value and the auth step are front-loaded, which is the right priority, and every sentence carries information (URL handling, admin scope, hosted restriction). The opening sentence stacks a parenthetical and a subordinate clause, making it denser than necessary, but there is no filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 9-parameter tool with no output schema, the description covers the two things an agent cannot infer: the return object shape and the required follow-up GET with an auth header. It also closes the permission gap on `instance` and `description`-only callers. Nothing needed to invoke it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description goes further on the highest-risk parameter: it explains what `instance` is for (unfinished work, failed jobs, logs across every account) and when that scope is appropriate, adding the queue/disk/machine diagnosis rationale the schema lacks. The remaining eight parameters are left entirely to the schema, which is acceptable at full coverage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states exactly what the tool produces: a download link for an issue-report zip, identified as the same file the in-app 'Report an issue' action saves. It also names the return shape { url, filename }, so an agent knows this is a diagnostics-packaging tool rather than a generic export; no sibling tool in the list does anything comparable.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives concrete selection guidance for the key fork: set instance true only for queue, disk or machine problems that are not just one project, as opposed to a project-scoped report. It also flags the precondition that non-admins on hosted instances with telemetry cannot download the zip. What's missing is an explicit trigger phrase for the base case (e.g. 'use when a user reports a bug'), though that is strongly implied.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

rerender_clipRe-render clipA
Idempotent

Queue one clip for rendering again (after rerender: false edits, a failure, or an intro/outro change). Async: the new video replaces the old when done (usually under a minute); poll get_clip. Returns { step: render | prepare, renderQueued, hint } (prepare = it never got its AI title, which runs first and renders afterwards). For several clips of a project use rerender_clips.

NameRequiredDescriptionDefault
idYesClip id, not a project id (from get_project or search_clips)

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only declare idempotentHint, so the description carries real weight and does: async semantics, that the old video is replaced on completion, typical latency, the follow-up poll via get_clip, and the two-step return shape. The 'prepare' branch is explained as a clip that never received its AI title, which is non-obvious behavior an agent could otherwise misread.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the action, then behavior, then the sibling hand-off. Dense but nearly every clause carries information; the nested parenthetical defining 'prepare' is the one spot that costs readability for its value.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema exists, yet the description documents the return object and the meaning of each field, plus the async polling path and the mutation's effect on prior output. Nothing needed to invoke or interpret the call is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, and the schema already states the clip-vs-project-id distinction. The description reinforces the clip scope but adds no new syntax or format detail beyond the schema; a 4 reflects that the surrounding context (where the id comes from, what the call does with it) is made actionable.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Queue one clip for rendering again') plus the trigger conditions, and explicitly distinguishes itself from the plural sibling rerender_clips. An agent can route between the two without opening either schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Names the exact situations that call for it (rerender:false edits, a failure, an intro/outro change) and explicitly routes multi-clip work to rerender_clips. When-to-use and when-to-use-something-else are both present.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

rerender_clipsRe-render clipsA
Idempotent

Queue several clips of one project for rendering again in one step (after edits or failures). Finished and failed clips are queued, clips still rendering are skipped; each new video replaces the old when done. Returns { requested, rerendered, skipped, renderQueued, hint }; poll get_project or wait_for_project. For a single clip rerender_clip is enough.

NameRequiredDescriptionDefault
clipIdsYesClip ids of that project (get_project), 1-200
projectIdYesProject id (from list_projects, search_clips or create_project)

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only declare idempotentHint=true, so the description carries the real burden and delivers: it discloses exactly which clips get queued (finished and failed), which are skipped (rendering), and replacement semantics ('each new video replaces the old when done'). It also names the return keys and the polling path, which is genuinely useful behavior an agent could not infer.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the action and scope, then flows into queue/skip semantics, return shape, polling, and the alternative. Every sentence earns its place with no filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema exists, but the description explains the return contract ({ requested, rerendered, skipped, renderQueued, hint }) and the polling follow-up, which is exactly what an agent needs for an async batch operation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% with both params documented, including source tools for projectId and id/range for clipIds. The description adds no further parameter detail beyond what the schema already provides, so the baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (queue for re-rendering), resource (several clips of one project), and scope (one step, after edits or failures). An agent can immediately distinguish it from rerender_clip, which is explicitly named.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives explicit when-to-use conditions (after edits or failures), when-not behavior (still-rendering clips are skipped), and names the alternative 'rerender_clip' for the single-clip case. Nothing is left to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

resend_webhook_deliveryResend webhook deliveryA

Send a delivery again (a new series of attempts, same webhook-id and body, signed afresh). The webhook must be on. Returns { ok, deliveryId, message }.

NameRequiredDescriptionDefault
deliveryIdYesDelivery id (from list_webhook_deliveries)

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description details the internal mechanism of the resend, specifying that it creates a 'new series of attempts,' uses the 'same webhook-id and body,' and signs 'afresh.' This is high-value behavioral context.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The definition is highly concise. It packs the action, the constraints (webhook status), and the technical details of the resend process into one information-dense sentence.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the absence of formal annotations or output schema, the description is exceptionally complete, detailing the operation, preconditions, and the expected return structure.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The description adds critical context to the deliveryId parameter by stating its origin ('from list_webhook_deliveries'), which assists the agent in understanding the required input source.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The tool's purpose is stated clearly: to send a delivery again. It is highly specific and distinct from sibling tools like list_webhook_deliveries or create_webhook.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It provides a critical usage requirement that the webhook must be on. While it doesn't list formal exclusions, this condition acts as a vital operational guideline for the agent.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

restore_assetRestore library assetA
Idempotent

Bring one of your deleted library items back (the id from delete_asset or list_deleted); restoring an item that is not deleted changes nothing. It does not become a default again. Returns { ok }.

NameRequiredDescriptionDefault
assetIdYesLibrary asset id (from delete_asset)

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the idempotentHint annotation, it discloses two non-obvious behaviors: the call is a silent no-op on non-deleted items, and the restored asset does not regain default status. It also documents the return shape ({ ok }) despite no output schema. Not covered: permissions/auth requirements or whether the restore window is limited.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two compact sentences, action and source of the id front-loaded, with the edge case and return value in a trailing clause. No filler or restatement of the tool name.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a single-parameter mutation with no output schema, the description covers invocation, the id source, the no-op case, and the response shape. Missing only peripheral context such as whether a purged asset can ever be restored, which matters given purge_asset exists nearby.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description adds a source the schema omits: the schema says only 'from delete_asset', while the description also points to list_deleted as a way to obtain the assetId.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (restore) on a specific resource (deleted library item/asset), and the resource name distinguishes it from restore_project and restore_clip in the sibling list. An agent can identify the correct tool without opening a schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly says the id comes from delete_asset or list_deleted, which tells the agent both the precondition and the prerequisite call sequence. It also covers the no-op case (restoring a non-deleted item changes nothing), though it never names explicit alternatives such as restore_project/restore_clip for other resource types.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

restore_clipRestore clipA
Idempotent

Bring back a deleted clip or one a re-analyze replaced (list_deleted or get_project → replacedClips); it goes to the end of the project's clips. Returns { changed, status, needsRerender }; needsRerender means it never finished, so call rerender_clip.

NameRequiredDescriptionDefault
idYesClip id (from list_deleted or get_project → replacedClips)

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only declare idempotentHint, so the description carries most of the burden and delivers: the restored clip is appended to the end of the project's clips, and the return shape { changed, status, needsRerender } is explained with actionable meaning for needsRerender. It does not mention permissions, limits on how long after deletion a restore works, or behavior for purged clips.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, front-loaded with the core action and eligibility, then the return value and follow-up. No filler; every clause carries information the agent needs.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema exists, yet the description documents the returned fields and their interpretation, plus the required follow-up call — everything needed to invoke and act on the result for a single-parameter tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Only one parameter, and schema description coverage is 100% with the schema already stating the id comes from list_deleted or get_project → replacedClips. The description repeats that provenance without adding new syntax or constraints, so the baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource (restore a deleted/replaced clip) and scopes which clips qualify — deleted ones or those swapped out by re-analysis — which separates it from delete_clip, purge_clip and restore_project in the sibling list.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly names where to get the id (list_deleted or get_project → replacedClips) and gives the conditional next step (call rerender_clip when needsRerender). Lacks an explicit exclusion, e.g. that purged clips cannot be restored via this tool (purge_clip is a sibling), so it is strong but not complete routing guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

restore_layer_templateRestore layer templateA
Idempotent

Bring back a deleted layer template (the id from list_layer_templates deleted or list_deleted). It does not become a default again. Returns the template.

NameRequiredDescriptionDefault
templateIdYesLayer template id (list_layer_templates)

TDQS

A3.6/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The only annotation is idempotentHint=true; the description supplements this with two real behavioral facts: the template does not regain default status, and the call returns the template. It does not cover error behavior when the id exists but is not deleted, name-collision handling, or permission requirements, so disclosure is useful but incomplete.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three short sentences, front-loaded with the action and followed by provenance and the important caveat. The parenthetical is a little awkwardly worded ('list_layer_templates deleted or list_deleted'), but nothing is wasted.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a one-parameter, non-destructive restore with a thin annotation set and no output schema, the description covers action, id source, a key state caveat, and the return value. Remaining gaps are minor edge-case behaviors rather than anything needed to invoke it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With a single parameter already fully documented in the schema, the baseline is 3. The description's note about where the id comes from mirrors the schema's own 'Layer template id (list_layer_templates)' hint rather than adding new meaning, and it does not clarify format or validation.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses a clear verb-plus-resource combination ('Bring back a deleted layer template') that tells an agent exactly what the operation does. It does not, however, explicitly distinguish itself from the sibling purge_layer_template, which is the permanent-removal counterpart an agent might confuse it with.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives the precondition ('a deleted layer template'), points to both places to obtain the required id ('list_layer_templates deleted or list_deleted'), and states a key post-condition. It stops short of naming alternatives or exclusions (for example, when to prefer purge over restore), so it is clear context rather than full when/when-not guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

restore_projectRestore projectA
Idempotent

Bring back a deleted project with its clips and files (find it with list_deleted or list_projects deleted: true); restoring a project that is not deleted changes nothing. Refused if the account is already at its live project cap. A project that was processing comes back failed: retry_project resumes it. Returns { changed, status }.

NameRequiredDescriptionDefault
idYesProject id (from list_projects with deleted: true)

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With only idempotentHint in annotations, the description carries the burden well: it discloses the no-op on non-deleted projects, the account-cap refusal, the processing→failed outcome, and the exact return shape { changed, status }. These are behavioral traits not derivable from structured fields.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three compact sentences, each earning its place: action, lookup+no-op, refusal, failure path, and return value. Front-loaded with the primary action and no filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a single-parameter mutation tool with no output schema, the description covers preconditions (must be deleted, cap limit), edge outcomes (processing→failed), and the return payload. Nothing an agent needs to invoke it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and the id parameter is already documented, so the baseline is met. The description corroborates the source of the id ('from list_deleted or list_projects deleted: true'), adding a small locating hint but little beyond the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource ('Bring back a deleted project') and adds scope ('with its clips and files'), which clearly separates it from sibling restores like restore_clip and restore_asset. An agent can identify the operation without opening the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly names where to locate the target ('find it with list_deleted or list_projects deleted: true'), states the no-op case for non-deleted projects, the refusal case at the live project cap, and routes to retry_project for projects that come back failed. When/when-not/alternatives are all covered.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

retry_failuresRetry failed workA

Admins only: retry matching failed projects and clips through the same lifecycle as retry_project / rerender_clip (never a raw queue poke). Each item is its own transaction so live-job caps still apply. Cap 50 per call. Returns { retried, skipped, nextCursor }. Writes an admin event failures.retried. Hosted non-admins are refused.

NameRequiredDescriptionDefault
qNoSame error substring as list_failures
idsNoRetry these rows instead of the current filter; omitted = every match of q/stage/since
limitNoMax items to retry this call, default 50
sinceNoSame since filter as list_failures
stageNoSame stage filter as list_failures

TDQS

A4.5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full behavioral burden and does so well. It discloses admin-only access, per-item transactions so live-job caps still apply, the 50-item cap, the response shape, the admin event write, and that non-admins are refused.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with the admin-only constraint and action, then quickly covers lifecycle behavior, caps, return shape, event logging, and refusal behavior. Every sentence contributes useful context without waste.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Although there is no output schema, the description supplies the return object shape. Combined with full schema parameter coverage and explicit auth, lifecycle, transaction, cap, and event behavior, it is complete enough for an agent to call this mutation tool correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the input schema already documents q, ids, limit, since, and stage. The description reinforces the overall filtering model and cap but does not add parameter-level meaning beyond what the schema provides.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource: retrying matching failed projects and clips. It distinguishes itself from sibling lifecycle operations by saying it goes through the same lifecycle as retry_project / rerender_clip and is never a raw queue poke.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives clear usage constraints: admins only, hosted non-admins are refused, and each call is capped at 50 items. It also ties its q/stage/since filters to list_failures, but it does not explicitly state when to choose this bulk retry tool over the single-item retry_project or rerender_clip alternatives.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

retry_projectRetry projectA

Resume a failed or stuck project from its furthest completed stage: re-render unfinished clips if there are clips, else pick clips, refine or transcribe again. Finished clips, the transcript and the source are kept. Returns { queue } (the stage restarted); then wait_for_project. To pick different clips use reanalyze_project instead.

NameRequiredDescriptionDefault
idYesProject id (from list_projects, search_clips or create_project)

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden and does well: it discloses what is preserved (finished clips, transcript, source), what gets redone (re-render unfinished clips, else pick/refine/transcribe), and the return shape { queue }. It does not mention permissions, cost, or idempotency of repeated retries, which keeps it short of a 5.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the action and scope, then behavior, return value, and alternative. Dense but nearly every clause carries information; the branch enumeration ('re-render unfinished clips ... else pick clips, refine or transcribe again') is slightly heavy for a one-line description.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

There is no output schema, so the description supplies the return value itself ({ queue }, the restarted stage) plus the follow-up tool. Combined with the stated preservation semantics, an agent has everything needed to call and chain this tool correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Only one parameter (id) and schema coverage is 100%, so the schema already documents its origin. The description adds no param-level detail, but with a single fully described parameter the baseline is 4.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (resume/retry) and resource (a failed or stuck project), and pins the starting point precisely ('from its furthest completed stage'). It also names the sibling it is not (reanalyze_project), so an agent can distinguish it without opening a schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives an explicit trigger condition ('failed or stuck project'), an explicit alternative with its condition ('To pick different clips use reanalyze_project instead'), and a follow-up step ('then wait_for_project'). Nothing about routing is left to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

review_transcript_flagsReview transcript fixesA

Accept or reject suggested transcript fixes (ids from list_transcript_flags), optionally with your own replacement text for an accepted name fix. The transcript is rebuilt; with rerender true (default) clips whose captions overlap a change are re-rendered. Returns { changed, rerendered, needsRerender, flags: [{ id, status, replacement }], hint }.

NameRequiredDescriptionDefault
idYesProject id (from list_projects, search_clips or create_project)
rerenderNoRe-render the finished clips whose captions overlap a change (default true); false only saves, re-render later with rerender_clips
decisionsYesOne decision per fix

TDQS

A4.1/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full load, and it does disclose real side effects: the transcript is rebuilt and overlapping captions are re-rendered by default. It omits permissions/credits/re-render cost and reversibility, which matters for a mutation tool, but the core blast radius is stated.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three front-loaded sentences covering purpose, side effect, and return shape with no filler. The trailing return-shape enumeration is dense but justified by the absence of an output schema.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema exists, so spelling out { changed, rerendered, needsRerender, flags, hint } is valuable, and the rerender side effect is disclosed. What remains missing for a mutation with zero annotations is precondition/authorization context and whether re-rendering consumes credits or is undoable.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3; the schema already documents id, rerender (including the rerender_clips fallback) and the decisions array. The description adds one genuinely new nuance, that replacement text applies to an accepted *name* fix, but otherwise restates schema content.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb pair (accept/reject) and resource (suggested transcript fixes), and ties the id space to the sibling list_transcript_flags, so it is unmistakably distinct from add_transcript_correction or import_transcript. The optional replacement behavior is also named inline.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The parenthetical '(ids from list_transcript_flags)' establishes the required prior step, and the description clarifies that replacement text only applies to accepted name fixes. It does not explicitly exclude alternatives or say when to skip this tool in favor of add_transcript_correction, so it falls short of full when/when-not guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

rotate_webhook_secretRotate webhook secretA
Destructive

A new signing secret for a webhook. Deliveries are signed with both the new and the old one for 24 h, so the receiver can switch; refused while a rotation is still open. Returns { secret, previousStopsAt, message }.

NameRequiredDescriptionDefault
idYesWebhook id (from list_webhooks)

TDQS

A3.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The annotation only says destructiveHint=true; the description adds real substance beyond that by disclosing the 24-hour dual-signing grace period so receivers can switch, the refusal condition for overlapping rotations, and the exact response shape including previousStopsAt. The only gap is the absence of any permission or permanence statement about what happens to the old secret after the window closes.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three tight sentences, front-loaded with the outcome, then the behavioral caveat, then the return shape; no filler. The first sentence is a noun fragment rather than a verb phrase, which slightly weakens the opening but costs no length.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a one-parameter mutation with no output schema, the description covers the essential behavioral contract: grace period, refusal condition, and returned fields. It is close to complete; only authorization requirements and post-window invalidation of the old secret are left unstated.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

There is a single required parameter with 100% schema description coverage, and the schema already explains that it is the webhook id sourced from list_webhooks. The description adds nothing further about the id, which is acceptable given the schema does the work — the baseline of 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description identifies the exact resource and outcome: a new signing secret is generated for a webhook, and it explains the dual-signing window that makes this a rotation rather than a plain read. It is missing an explicit verb in the opening fragment, but combined with the name/title the action is unambiguous and clearly distinct from update_webhook or create_webhook.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It supplies one concrete precondition — the call is 'refused while a rotation is still open' — which tells the agent when the tool will fail. However, it never states when an agent should rotate versus simply updating the webhook, nor does it name update_webhook as the alternative, so the routing guidance is implied rather than explicit.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

save_layer_templateSave layer templateA

Create a template from exactly one of clipId (that clip's layers: spanning = fill, touching the start = head, touching the end = tail, else anchored to the nearer edge; plus the intro and outro the clip renders with) or layers (each with a timing, see list_layer_templates; [] with bumpers = an intro / outro only template). Returns the template { id, name, layerCount, layers, bumpers }. Use apply_layer_template to put it on clips

NameRequiredDescriptionDefault
nameYesTemplate name (1-120 chars)
clipIdNoBuild the template from this clip's layers (get_project or search_clips). Pass clipId or layers, not both
layersNoOr the layers themselves, each with a timing: { kind: asset|text|audio, id, timing: { mode: head|tail|fill|relative, start, end, minSec, maxSec }, ...the layer's own fields (assetId, x, y, w / text, size, color / volume, duck, fade, loop) }
bumpersNoWith layers: the intro / outro the template puts on clips ({ intro, introSec, outro, outroSec }; a slot left out = clips keep their own, null = none, an id = that video or image). With clipId the clip's intro and outro are saved too

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the burden and does well: it explains how clipId layers are translated into timing modes (spanning=fill, touching start=head, touching end=tail, else nearest edge), that intro/outro rendered with the clip are saved too, and the returned object shape. It omits any notes on permissions or limits, but discloses core behavior beyond the schema.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the primary action and the exclusive-or constraint, then edge cases, then the return shape and the sibling hand-off. It is dense and slightly run-on but every clause carries information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with deeply nested layer schemas and no output schema, the description supplies the return shape { id, name, layerCount, layers, bumpers } and the clipId translation rules. Cross-references to list_layer_templates, list_bumpers, and search_assets cover the remaining lookup steps.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description adds genuine meaning: the clipId-to-timing-mode mapping rules are not derivable from the schema, and the empty-layers-plus-bumpers case is a semantic edge case. Bumpers wording largely repeats the schema's own description.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ("Create a template") and immediately names the two mutually exclusive sources (clipId vs layers). It is easy to distinguish from siblings like apply_layer_template, list_layer_templates, and update_layer_template.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives explicit routing ("Use apply_layer_template to put it on clips") and clarifies the exclusive-or between clipId and layers, plus the edge case of [] with bumpers for an intro/outro-only template. It does not explicitly contrast with update_layer_template, so it stops just short of a 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_assetsSearch library, catalog and providersA
Read-only

Find assets. source "library" = your own uploads (name and tags; empty q lists all); omitted or "catalog" = the Reelsaw catalog (CC0, works offline); or a provider an admin enabled. Returns { provider, items: [{ provider, externalId, name, kind, license, durationSec }], nextCursor }. Adds nothing: a catalog / provider hit's provider + externalId go to add_asset.

NameRequiredDescriptionDefault
qNoWhat to look for, e.g. "whoosh", "arrow", "city at night"; empty lists everything in the source
kindNoOnly this kind of result
limitNoResults per page (1-60, default 24)
cursorNonextCursor from the previous page; omit for the first page
sourceNolibrary, catalog (default), pexels, pixabay, freesound, iconify, openverse, giphy or klipy
deletedNolibrary only: list your deleted items instead (their ids go to restore_asset)
licenseNofreesound only: cc0 (default) or by (CC0 and CC-BY, attribution is stored)

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true, so the safety profile is covered; the description adds real value with the return shape (provider, items, nextCursor), the offline/catalog behavior, and the fact that this tool adds nothing itself. It stops short of disclosing auth requirements or provider-enablement nuance, but it is well beyond the annotation baseline.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Dense and front-loaded: purpose first, then source semantics, then return shape, then the add_asset handoff. It is slightly telegraphic ('Adds nothing', parentheticals) and could be marginally smoother, but nearly every clause earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 7-param, read-only search tool with no output schema, the description supplies the return envelope and pagination token, covers all source modes, and links to the downstream add_asset call. Remaining gaps (kind/limit filtering details, license attribution nuance) are covered by the rich schema, so the description is essentially complete for correct invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3; the description adds semantics the schema does not, notably source default behavior, the library name-and-tag matching scope, offline catalog behavior, and the role of provider + externalId in add_asset. The deleted and license params are only lightly touched, but the core source semantics are clarified.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource ('Find assets') and immediately distinguishes three source modes (own uploads, catalog, admin-enabled provider) plus the terminal action of routing hits to add_asset. An agent can tell this apart from add_asset and unrelated siblings without opening the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly defines when each source applies: library = own uploads, omitted/'catalog' = Reelsaw catalog (CC0, works offline), otherwise an admin-enabled provider. It also states the follow-up path ('a catalog / provider hit's provider + externalId go to add_asset'), removing ambiguity about the search-then-add workflow.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_clipsSearch clipsA
Read-only

Find your clips and projects by words in their title, hook or caption (every word must match; case-insensitive, typo-tolerant; best first). Returns { projects (max 8), clips (max limit) with ids, bounds and links, truncated? }. Use list_projects to browse instead.

NameRequiredDescriptionDefault
qYesWords to find, e.g. "budget tips"
limitNoMost clips to return (1-50, default 15)

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only declare readOnlyHint=true, so the description carries matching and ranking behavior: every word must match, case-insensitive, typo-tolerant, best-first ordering. It also discloses the return envelope (projects max 8, clips max limit, ids/bounds/links, truncated flag) despite no output schema. It stops short of error or rate-limit behavior, keeping it out of top marks.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three tight sentences with no filler: what it searches, what it returns, and the alternative tool, in that priority order. Every clause earns its place and the core purpose is front-loaded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema exists, and the description compensates by outlining the result shape including the truncation flag and per-entity caps. Combined with full schema coverage on both parameters and the explicit sibling alternative, an agent has everything needed to call it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description adds genuine semantics for q: implicit AND across every word, case-insensitive, typo-tolerant matching, and it clarifies that limit caps only the clips portion of the result. That is meaning beyond the schema's terse 'Words to find' example.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (Find) applied to specific resources (clips and projects) and names the searched fields (title, hook, caption). An agent can immediately distinguish search_clips from list_projects, the browsing sibling, without opening either schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides a concrete routing rule — 'Use list_projects to browse instead' — which contrasts searching by keyword versus browsing. It is clear context with an explicit alternative, though it doesn't cover edge cases like empty results or when to prefer get_clip over search for a known id.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

set_clip_bumpersSet clips' intro/outroA

Override the intro and/or outro of up to 200 clips of one project: "inherit" (follow the project), "none", or a library asset id; a slot left out is untouched. rerender: true re-renders only these clips. Returns { updated, rerendered }.

NameRequiredDescriptionDefault
introNo"inherit" (alias "default") = follow the project, "none" = no bumper on this clip, or a library asset id
outroNo"inherit" (alias "default") = follow the project, "none" = no bumper on this clip, or a library asset id
clipIdsYesClip ids of that project (get_project), 1-200 per call
introSecNoSeconds an intro image shows on these clips (1-10)
outroSecNoSeconds an outro image shows on these clips (1-10)
rerenderYestrue = re-render only these clips now; false = apply on their next render
projectIdYesProject the clips belong to (list_projects)

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden and does well: it discloses the partial-update semantics (omitted slots untouched), the meaning of each value mode, the rerender side effect ('re-renders only these clips'), and the return shape ({ updated, rerendered }) despite having no output schema. It omits permission requirements and what happens to clips that keep an inherited bumper.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two dense, front-loaded sentences with no filler: value modes and scope come first, then the rerender caveat, then the return shape. Every clause carries information an agent needs.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 7-parameter mutation with no annotations and no output schema, the description covers the critical gaps: operation scope, partial-update behavior, side effects, and the return shape. It stops short of stating prerequisites (e.g. that clipIds must belong to projectId, or permission needs).

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so 3 is the baseline, but the description adds value beyond it with the 200-clip cap, the slot-omission rule, and the scoping of 'rerender: true' to only the affected clips. The enum value semantics largely duplicate the schema's own parameter descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description gives a specific verb (override) and resource (intro/outro of clips) plus a hard scope bound ('up to 200 clips of one project'), which implicitly separates it from the project-level sibling set_project_bumpers. An agent can tell what this tool mutates and on which entities without opening the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is implied by the clip-level scope and the partial-update note ('a slot left out is untouched'), but no sibling is ever named and there is no explicit 'use set_project_bumpers instead when...' routing. The agent must infer the clip-vs-project split from the description alone.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

set_default_bumperSet default intro/outroA
Idempotent

Make a library item your default intro or outro for projects you create from now on (assetId null clears it). Existing projects keep theirs; to change one use set_project_bumpers. Returns { ok }.

NameRequiredDescriptionDefault
slotYesWhich default to set
assetIdYesLibrary asset id (search_assets with source library), or null for no default

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With only idempotentHint in annotations, the description carries real weight and delivers: the forward-looking scope of the change, the fact that existing projects are unaffected, the clearing behavior, and the return shape { ok }. It doesn't cover permission/auth requirements or confirm whether setting a default replaces any prior default, so it stops short of fully describing side effects.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three tight clauses: what it does, the scope caveat, and the alternative tool, all front-loaded with zero filler. The return note is one token and earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a two-param, non-destructive default-setting tool with a fully documented schema, the description covers scope, clearing, the sibling alternative, and the return value. Nothing an agent needs to invoke it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% — slot's enum and assetId's null branch are both documented in the schema, including the search_assets hint. The description's 'assetId null clears it' only restates the schema, so the baseline 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (set default), resource (library item as intro/outro) and an explicit scope (projects created from now on), which cleanly separates it from set_project_bumpers. An agent can distinguish this from the per-project variant without opening either schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly names when this applies ('projects you create from now on') and when it does not ('existing projects keep theirs'), then routes the alternative case to set_project_bumpers. The null case for clearing is also stated inline.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

set_email_preferencesSet email preferencesA
Idempotent

Turn topics of optional email on or off; omitted topics stay as they are. ready = the note sent when projects finish while you are away (Settings → Account, or the link in the email). Mail you need (password reset, verification) is not affected. Returns { ok, emailPreferences }.

NameRequiredDescriptionDefault
readyNothe note sent when projects finish while you are away: true to get it, false to stop it

TDQS

A3.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only declare idempotentHint, so the description carries most of the burden and does add real behavior: partial-update semantics ('omitted topics stay as they are') and a scope boundary that essential mail (password reset, verification) is unaffected. It also discloses the return shape, which no output schema provides; the main gap is that it doesn't say how to discover which optional topics exist.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the core action and the partial-update rule, and every clause (ready meaning, unaffected essential mail, return shape) carries information. The parenthetical about Settings → Account is slightly tangential and the sentence fragments make it a bit choppy, but nothing is padding.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a one-parameter, idempotent mutation with no output schema, the description covers what changes, what is preserved, what is out of scope, and the return shape — enough to call it correctly. The remaining hole is discoverability of topics and the read counterpart, which the description never mentions.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% for the single parameter, and the description's 'ready = the note sent when projects finish while you are away' essentially restates the schema's own definition rather than adding new meaning. With one well-documented parameter, baseline 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description gives a specific verb and resource: turning optional-email topics on or off, with a clear scope restriction that only optional mail is touched. It never names the sibling get_email_preferences, so the read/write pairing with that tool must be inferred from the name alone.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is implied rather than stated: call it to toggle a topic, and omitted topics are left untouched (useful partial-update guidance). It offers no explicit when-to-use or when-not guidance and never points at get_email_preferences for reading current state, though the Settings → Account remark hints at where this is surfaced.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

set_instance_settingsChange instance settingsA
DestructiveIdempotent

Admins only: change instance settings without a restart; every process applies them within seconds and the change is audited. Pass only the keys to change; null returns a key to the environment's value; an empty string clears an optional one (URLs, captcha). Validated as a whole (a captcha provider needs both keys; email verification needs an email driver). Returns the same state as get_instance_settings.

NameRequiredDescriptionDefault
TERMS_URLNoTerms of service link shown on the sign-up form; empty = none.
CATALOG_URLNoWhere the Reelsaw catalog manifest (sounds, icons, stickers) is fetched from, hourly; the starter pack bundled with Reelsaw is used until a newer one arrives.
PRIVACY_URLNoPrivacy policy link shown on the sign-up form; empty = none.
SIGNUP_MODENoWho may sign up: open (anyone, right for a public instance with email confirmation), approval (new accounts wait for an admin; a private box) or closed (nobody). The first account is always the admin.
UPDATE_CHECKNoAsk GitHub for the latest release and tell admins when a newer Reelsaw exists. Only the request leaves the machine.
GIPHY_API_KEYNoGiphy API key: enables GIFs and stickers in Browse. Stored encrypted; empty = provider hidden.
KLIPY_API_KEYNoKlipy API key: enables GIF search in Browse (experimental). Stored encrypted; empty = provider hidden.
MAX_UPLOAD_MBNoLargest accepted file upload, in MB.
REFINE_SCENESNoScan the video for on-screen text (burned-in captions, watermarks, slides) during refine. On by default; off closes the ceiling (projects opt out via refineScenes). Shot cuts are always detected.
STALL_MINUTESNoA running job with no progress for this long is reported as stalled (log and UI).
DISFLUENCY_LLMNoModel pass on top of rule-based disfluency detection. Off by default. Ignored when disfluency removal is off. Turn on so projects may opt in via disfluencyLlm.
HEALTH_DETAILSNoInclude machine numbers in GET /api/health for anonymous callers (private networks only). Admins always get them.
MAX_STORAGE_MBNoMB of sources, uploads, renders and intros/outros an account may keep (deleted items count until they are deleted forever). 0 = unlimited.
PEXELS_API_KEYNoPexels API key (free): enables stock photos and portrait videos in Browse. Stored encrypted; empty = provider hidden.
WATERMARK_TEXTNoThe watermark text, e.g. your site name.
ICONIFY_API_URLNoIconify API used for icon search; the public one by default, or your own self-hosted instance (https).
PIXABAY_API_KEYNoPixabay API key (free): enables photos, illustrations, vectors and videos in Browse. Stored encrypted; empty = provider hidden.
CAPTCHA_PROVIDERNoBot protection on sign-up, sign-in and password reset: cloudflare-turnstile, google-recaptcha or hcaptcha; empty = off. Needs both keys.
CAPTCHA_SITE_KEYNoPublic captcha key the browser widget uses; empty = unset.
FREESOUND_API_KEYNoFreesound API key (free): enables sound effects in Browse. Stored encrypted; empty = provider hidden.
OPENVERSE_ENABLEDNoShow Openverse (Creative Commons images and audio, no key) in Browse. Off by default until you have checked its limits for your use.
CAPTCHA_SECRET_KEYNoSecret captcha key the server verifies with; stored encrypted, never shown again; empty = unset.
DISFLUENCY_REMOVALNoDetect fillers, repetitions and false starts after transcription. Off by default; turn on so projects may opt in via refineDisfluency.
MAX_SOURCE_FILE_MBNoLargest file a link source may download (yt-dlp --max-filesize), in MB.
CLIP_RETENTION_DAYSNoDelete rendered clip files this many days after rendering; the clip stays and can be re-rendered. 0 = keep forever.
MEDIA_LINK_TTL_DAYSNoShare links last this many days unless the owner picks less.
SCRATCH_CACHE_HOURSNoWith an S3 store, drop a worker's local copies of the sources and rendered clips of finished projects after this many idle hours; a job pulls them back from the bucket when it needs them, and a worker short of disk drops the least recently used ones first. 0 = keep them on the worker. The local store needs nothing: scratch and store are the same file.
STEP_RETENTION_DAYSNoDrop the saved checkpoints (finding-moments windows) of a finished project this many days after it last changed; a Retry sooner than that reuses them. 0 = keep forever.
EVENT_RETENTION_DAYSNoPrune project events older than this; each project keeps its run skeleton. 0 = keep forever.
WATERMARK_NON_ADMINSNoBurn a small watermark into the clips of accounts that are not admins. A paid plan removes it, and an admin can switch it per user on the Users page.
MAX_PROJECTS_PER_USERNoProjects an account may keep. 0 = unlimited. Admins can override per user on the Users page.
MAX_REQUESTS_PER_HOURNoChanges an account may make per hour. 0 = unlimited.
MAX_WEBHOOKS_PER_USERNoWebhook endpoints an account may add. 0 = unlimited. Admins can override per user on the Users page.
SOURCE_RETENTION_DAYSNoDelete stored source videos this many days after upload; the project stays and Retry re-fetches. 0 = keep forever.
TRANSCRIPT_CORRECTIONNoAfter transcription, fix misheard names with the language model. On by default; off closes the ceiling so no project runs it (projects opt out via refineEntities).
DELETED_RETENTION_DAYSNoDeleted projects and replaced clips stay restorable this long, then rows and files go. 0 = keep forever.
MAX_LIVE_JOBS_PER_USERNoQueued plus running jobs an account's own requests may add to. 0 = unlimited.
MAX_RERENDERS_PER_HOURNoRe-renders, hand-made clips and re-runs (re-analyze, refine) an account may ask for per hour. 0 = unlimited.
MEDIA_LINK_ALLOW_NEVERNoOwners may make a share link that never expires (they can still revoke it).
BLOCK_DISPOSABLE_EMAILSNoRefuse sign-ups from known temporary-address domains (a vendored list of about 120k domains).
DECISION_RETENTION_DAYSNoPrune users' recorded decisions (accepts, rejects, edits Reelsaw learns from) older than this. They outlive deleted projects until then. 0 = keep forever.
MAX_REQUESTS_PER_MINUTENoChanges (API mutations, MCP tool calls) an account may make per minute. 0 = unlimited. Admins can override per user on the Users page.
LLM_CALL_TIMEOUT_SECONDSNoLongest one model call may take (all its HTTP attempts together) before its step is stopped and retried. 0 = auto: 120 s for a hosted model, 600 s for a self-hosted one, and for a self-hosted transcription at least twice the chunk's audio length. Time spent waiting for a provider slot does not count. A slow CPU-only model may need more.
LLM_USAGE_RETENTION_DAYSNoPrune model and transcription usage rows older than this. 0 = keep forever.
ALLOW_PRIVATE_SOURCE_URLSNoLet link sources point at LAN or loopback hosts (a NAS). Off, they are blocked (SSRF guard).
MAX_NEW_PROJECTS_PER_HOURNoProjects an account may create per hour. 0 = unlimited.
MEDIA_LINK_RETENTION_DAYSNoDelete revoked and expired share links this many days after they stopped working. 0 = keep forever.
ADMIN_EVENT_RETENTION_DAYSNoPrune the admin event log after this many days. 0 = keep forever.
ALLOW_PRIVATE_WEBHOOK_URLSNoLet webhook URLs point at LAN or loopback hosts (a self-hosted n8n next to Reelsaw), over http too. Off, they must be public https (SSRF guard).
MAX_SIGNUPS_PER_IP_PER_DAYNoAccounts one network address may create per day. 0 = unlimited. Needs TRUST_PROXY behind a reverse proxy.
MAX_SOURCE_MINUTES_PER_DAYNoMinutes of source video an account may add per day (UTC). 0 = unlimited.
REQUIRE_EMAIL_VERIFICATIONNoNew users must click the verification link before they can sign in. The link works for 24 hours and is single-use; an expired one offers a new send. Needs an email driver in .env.
STAGE_IDLE_TIMEOUT_MINUTESNoA stage made of checkpointed steps (finding moments) is stopped when no step has finished for this long; Retry continues where it stopped. 0 = auto: 1.5 times the model call timeout.
MAX_SOURCE_MINUTES_PER_MONTHNoMinutes of source video an account may add per period: its billing period when it has a plan, else the calendar month (UTC). 0 = unlimited.
WEBHOOK_DELIVERY_RETENTION_DAYSNoDelete webhook delivery log rows older than this. 0 = keep forever.
EVENT_PROJECT_DONE_READY_EMAIL_DELAY_MINUTESNoMinutes to wait after the first project in a batch finishes before emailing that the clips are ready, so several projects land in one note. Sent only when an email driver is set and the account has not come back since those projects finished. A project that finishes while a note is already waiting joins that note.

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Goes well beyond the annotations by disclosing that changes take effect within seconds across processes without a restart, that the change is audited, and that validation happens as a whole so one bad key rejects the batch. It does not explain the destructiveHint=true consequence (e.g. that clearing a URL or key silently disables a feature) or whether a rejected batch leaves any state changed, and no rollback path is described.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Roughly sixty words across four front-loaded sentences: permissions and effect first, then the update convention, then validation rules, then return shape. No sentence is redundant with the schema and nothing is buried.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given 56 parameters and no output schema, the description handles the cross-cutting semantics well and points to get_instance_settings for the returned state. However, for a destructive, audited bulk mutation it omits what an agent most needs at this complexity: whether a failed whole-batch validation changes nothing, and whether there is any way to revert an accidental reset to defaults.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is already 100%, so the per-key meanings are documented. The description still adds real meaning the schema cannot: the tri-state convention (omit / null / empty string) and cross-field validation constraints such as a captcha provider needing both keys and email verification needing an email driver.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('change instance settings') plus scope ('Admins only') and effect ('without a restart'). It also names the sibling get_instance_settings as the read counterpart whose state is returned, so an agent can tell the pair apart without opening either schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives concrete invocation rules: pass only the keys to change, use null to reset to the environment value, use an empty string to clear optional values. It does not explicitly exclude per-user settings handled by siblings such as set_email_preferences or set_learning, so the when-not guidance is implied rather than stated.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

set_learningTurn learning on or offA
Idempotent

Whether Reelsaw records your decisions (accepts, rejects, cuts, clip edits) to learn from (Settings → Learning, on by default). Off stops recording; what is already kept stays until forget_decisions. Returns { enabled }.

NameRequiredDescriptionDefault
enabledYestrue records decisions, false stops

TDQS

A4.1/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Goes well beyond the lone idempotentHint annotation: discloses the default ('on by default'), the exact scope of what is recorded (accepts, rejects, cuts, clip edits), the fact that disabling is non-destructive, and the retention rule until forget_decisions. The return shape is also given. Missing only auth/permission notes, which keeps it from a 5.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three short clauses, front-loaded with the state semantics before the retention caveat and the return shape. Slightly fragmentary opening, but no sentence is wasted.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a one-boolean, idempotent settings toggle this covers everything an agent needs: default state, effect of each value, data-retention consequence, the relevant alternative tool, and the response shape. Nothing material is left to guess.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and the single boolean is already documented, so the baseline is 3; the description earns a step up by enumerating the decision types that get recorded and clarifying that 'off' merely stops new recording rather than deleting anything.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

Names the resource (Reelsaw decision recording / Learning setting) and the effect of both states, so an agent can tell it apart from the nearby forget_decisions and list_decisions siblings. It is phrased as a fragment ('Whether Reelsaw records...') rather than a verb+object statement, which costs a bit of crispness but not comprehension.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly states the consequence of each state ('Off stops recording') and points to the sibling to use for actually removing data ('stays until forget_decisions'), which is a real routing decision. It does not spell out an explicit when-to-use trigger, but the toggle semantics make the selection obvious.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

set_project_bumpersSet a project's intro/outroA

Set one project's intro and/or outro: "default" (your current default), "none", or a library asset id; a slot left out is untouched, and introSec/outroSec are saved with the project. Clips with their own choice (set_clip_bumpers) keep it. Applies on each clip's next render; rerender: true re-renders finished clips now. Returns { rerendered }. set_default_bumper changes future projects instead.

NameRequiredDescriptionDefault
introNo"default" = your current default intro/outro, "none" = no bumper, or a library asset id (see list_bumpers)
outroNo"default" = your current default intro/outro, "none" = no bumper, or a library asset id (see list_bumpers)
introSecNoSeconds an intro image shows in this project (1-10); videos keep their own length. Saved with the project
outroSecNoSeconds an outro image shows in this project (1-10). Saved with the project
rerenderYestrue = re-render the project's finished clips now; false = apply on their next render
projectIdYesProject id (list_projects or create_project)

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden and does so well: it discloses override precedence (clip-level choices survive), timing semantics ('Applies on each clip's next render'), the effect of rerender:true (re-renders finished clips now), that a slot left out is untouched, and the return shape ({ rerendered }).

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Dense and front-loaded: the core action and value semantics come first, then precedence, then effect timing and the sibling routing. The colon/semicolon chain makes it slightly run-on, but every clause carries information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a six-parameter mutation tool with no annotations and no output schema, the description covers what is mutated, what is left alone, when changes take effect, override precedence, the return value, and the alternative tools. Nothing needed to call it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description adds real semantics beyond the schema by explaining that omitted slots are untouched and that introSec/outroSec are persisted with the project while clip-level choices override them. The enum-value meanings largely duplicate the schema text.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Opens with a specific verb and resource scope ('Set one project's intro and/or outro'), immediately distinguishing project-level bumper config from the clip-level and default-level siblings. An agent can tell this apart from set_clip_bumpers and set_default_bumper without opening any schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Names both adjacent tools and their scopes: clip-specific choices win ('Clips with their own choice (set_clip_bumpers) keep it') and set_default_bumper 'changes future projects instead'. It gives clear context for selection, though it never states an explicit 'use this when' rule or exclusion beyond the scope contrast.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

set_project_defaultsSet project defaultsA
Idempotent

Replace the settings every new project starts with; existing projects keep theirs. Send the whole set you want: keys left out are cleared, {} clears all (read get_project_defaults first). Intro and outro change through set_default_bumper. Returns { ok, projectDefaults, hint }.

NameRequiredDescriptionDefault
defaultsYesThe complete set of defaults, e.g. { language: "en", maxClips: 5, pauses: "tight" }

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the idempotent annotation, the description discloses the destructive replacement semantics: omitted keys are cleared, {} clears all, and existing projects are unaffected. It also names the return shape ({ ok, projectDefaults, hint }), which is valuable with no output schema.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two compact sentences front-load the replacement behavior, then cover clearing semantics, a prerequisite, an alternative tool, and the return value with no filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a single nested-object parameter with full schema documentation and no output schema, the description supplies the missing replacement semantics, prerequisite, sibling routing, and return shape. Nothing essential for correct invocation is omitted.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the nested property definitions already explain individual setting keys. The description adds the crucial whole-object replacement rule ('Send the whole set you want: keys left out are cleared, {} clears all'), which the schema does not make explicit, though it does not discuss every parameter individually.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource ('Replace the settings every new project starts with') and immediately scopes it: existing projects keep theirs. It distinguishes this tool from the closely related get_project_defaults and set_default_bumper by name.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It explicitly says to read get_project_defaults first, explains that intro and outro belong to set_default_bumper, and clarifies the alternative behavior for existing projects. That is direct when-to-use and when-not-to-use guidance rather than implied context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

set_project_source_textSet source text handlingA
Idempotent

Hide or keep text burned into the source video (captions, logos) for the whole project: hide covers it, keep leaves it, always shows it. Re-renders every finished clip so the set agrees. Returns { changed, needsRerender, rerendered, skipped, hint }; rerendered is how many clips were queued, so follow with wait_for_project; skipped clips were still preparing or rendering and need rerender_clips once done (needsRerender is true).

NameRequiredDescriptionDefault
idYesProject id (from list_projects, search_clips or create_project)
sourceTextYeshide = cover on-screen text in the source, keep = leave it, always = always show it

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only declare idempotency, yet the description discloses the key side effect (re-renders every finished clip) and the meaning of each return field (changed, needsRerender, rerendered, skipped), plus the skipped-clip edge case and remediation. This is disclosure well beyond the structured fields.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Single dense paragraph that front-loads the action and mode semantics before the return-value explanation. Efficient, though slightly crowded by packing enum restatement and return fields together.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema exists, but the description explains the returned object's fields and next steps, covering everything an agent needs to call and correctly follow up on this mutation. Complete for a 2-param idempotent setter.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and the enum is documented in-schema, so the description doesn't need to repeat it, but it reinforces the three mode meanings ('hide covers it, keep leaves it, always shows it') in workflow terms. Marginal added value over an already-complete schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb ('Hide or keep') and resource ('text burned into the source video') scoped to 'the whole project', with the enum behaviors spelled out. An agent can distinguish this from clip-level or rendering siblings without opening the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explains the three modes and, importantly, tells the agent to follow with wait_for_project and to call rerender_clips once skipped clips finish. It lacks an explicit when-not-to-use statement versus rerender_clips/apply_framing, but the follow-up routing is clear.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

test_webhookTest webhookA

Send a webhook.test delivery now and wait for the answer. Returns { ok, code, message, response (first 2 KB of what the receiver said) }.

NameRequiredDescriptionDefault
idYesWebhook id (from list_webhooks)

TDQS

A3.6/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Without annotations, the description carries the full behavioral burden. It usefully discloses that the call waits for a response and returns ok, code, message, and the first 2 KB of the receiver response, but it omits timeout behavior, external side effects, and permission requirements.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two compact sentences with the action front-loaded and the return shape immediately following. There is no redundant or filler text.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple one-parameter test tool with no output schema, the description is largely complete: it explains the action, the blocking wait, and the returned fields. Minor gaps remain around failure/timeout behavior and side effects.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, and the single id parameter is fully documented in the schema as coming from list_webhooks. The description adds no parameter details beyond that, so the baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource: send a webhook.test delivery and wait for the answer. It is clearly a webhook-testing tool, though it does not explicitly contrast with the sibling resend_webhook_delivery.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The word "test" and "now" imply this is for verifying a webhook endpoint, but the description gives no explicit when-to-use guidance, prerequisites, or alternatives. Usage is understandable but left to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

update_assetUpdate library assetA
Idempotent

Rename one of your own library items, replace its tags, or change how long a still image shows (deleted items too; shared catalog and provider items cannot be changed). Only labels change; nothing re-renders, except that a new image length applies on each project's next render. Returns { id, name, tags, durationSec, previousName }.

NameRequiredDescriptionDefault
nameNoNew name (1-120 chars)
tagsNoReplaces the tags (lowercase words; max 20); [] clears them
assetIdYesLibrary asset id (from search_assets)
imageSecondsNoSeconds a still image shows as an intro/outro (1-10); images only

TDQS

A4.5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the idempotentHint annotation, the description discloses the limited mutation surface: only labels change and nothing re-renders except image duration on the next render. It also states scope exclusions and specifies the return shape, giving the agent a strong behavioral picture.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is compact and front-loaded: it names the operations, states scope constraints, explains render behavior, and gives the return shape without wasted prose.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Although there is no output schema, the description supplies the return fields. Combined with the rich input schema and clear scope/side-effect disclosure, it is complete enough for an agent to invoke the tool correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the schema already documents name, tags, assetId, and imageSeconds in detail. The description adds conceptual mapping for the operations and some behavioral context, but does not add syntax or format detail beyond the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states specific operations (rename, replace tags, change still-image duration) on a clearly identified resource (own library items). It also distinguishes editable assets from shared catalog/provider items, which siblings like add_asset or delete_asset do not address.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives clear scope: own library items can be updated, deleted items can be updated, but shared catalog and provider items cannot. It does not explicitly name sibling alternatives such as search_assets for finding IDs or delete_asset for removal, so it falls short of full routing guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

update_clipUpdate clipA

Edit one clip (3 s-10 min): only the fields you pass change; style, layoutSections and layers replace the whole value (null = project's / none). Title, caption and hashtags apply at once; bounds, hook, framing, style, emphasis, layers and intro/outro are burned in and re-render (~1 min) unless rerender is false. Returns the clip summary plus { changed, needsRerender, rerendered, hint }; poll get_clip until done.

NameRequiredDescriptionDefault
idYesClip id (from get_project or search_clips)
hookNoOn-screen opening line (max 80 chars)
styleNoCaption/hook style for this clip on top of the project's; replaces the clip's override; null = follow the project
titleNoClip title (1-120 chars)
endSecNoNew end, seconds from the start of the source; clip must stay ≥3 s
layersNoEverything laid over or played under the clip, one list (max 30): kind asset = a library image/GIF/video (search_assets with source library; logos, stickers, b-roll), kind text = a line of text, kind audio = music or a sound effect (a library audio asset with a window in source seconds, volume, ducking, fade, loop). Replaces the whole list; null or [] = none. get_clip returns it
captionNoPost caption, not burned in (max 300 chars)
emphasisNoWords the captions colour (max 30); replaces the list
hashtagsNoHashtags without # (max 20); replaces the list
rerenderNoRe-render when a burned-in field changed (default true, same as the app); false = keep the old video until rerender_clip
startSecNoNew start, seconds from the start of the source video
layoutOverrideNoFraming for the whole clip; null = automatic
layoutSectionsNoFraming per time range (max 50), source seconds; replaces all sections; null or [] = automatic

TDQS

A4.4/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden and does: it discloses destructive replacement semantics for three fields, the ~1 min per-field render cost, which fields are burned in vs applied instantly, and the async completion protocol (poll get_clip). It even states the return payload. Nothing material about side effects or timing is left hidden.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Four dense clauses, each doing distinct work — scope, merge/replace rules, render cost, return protocol — with the most actionable constraint (partial update) front-loaded. Zero filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 13-parameter mutation with no annotations and no output schema, the description covers update semantics, side-effect cost, the deferred-render escape hatch, and the exact return shape plus polling instruction. An agent could invoke it correctly without opening the schema for semantics.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so baseline is 3. The description earns above that by adding semantics the schema cannot express: the instant-vs-burned-in partition of fields, null meaning 'follow the project'/none, and the fact that emphasis/layers replace rather than merge.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

Opens with a specific verb+resource ('Edit one clip') plus a hard scope constraint (3 s–10 min), which immediately separates it from create_clip, delete_clip and rerender_clip. It never explicitly names a sibling it is not, so the differentiation is inferred rather than stated.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives clear operating context: partial-update semantics ('only the fields you pass change'), the replacement rule for style/layoutSections/layers, and the rerender:false path that defers work to rerender_clip. It stops short of an explicit 'don't use this when…' exclusion or a named alternative for non-clip editing (update_project).

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

update_layer_templateUpdate layer templateA
Idempotent

Rename a layer template, replace its layers (the whole list, each with a timing) and/or replace its intro / outro (bumpers; null = it sets neither); a field left out is untouched. Clips that already got the template keep what they have; apply_layer_template again to refresh them. Returns the template.

NameRequiredDescriptionDefault
nameNoTemplate name (1-120 chars)
layersNoThe template's layers, each with a timing (max 30)
bumpersNo
templateIdYesLayer template id (list_layer_templates)

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the idempotent annotation, the description discloses important mutation semantics: omitted fields are untouched, layers are replaced as a whole list, bumpers use null to mean neither intro nor outro, and existing clips retain their current template until re-applied. It also states the return value.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is compact and front-loaded, covering update behavior, propagation effects, and return value without redundant restatement. Every sentence adds operational meaning.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the complex nested schema, the description sufficiently explains the update model, replacement semantics, clip-refresh behavior, and return value. It does not cover every nested layer field, but those are well documented in the schema and do not need to be repeated.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema has 75% description coverage, and the description adds meaningful semantics beyond field-level schema text: whole-list replacement for layers, partial-update behavior for omitted fields, and null-vs-omitted behavior for bumpers. It does not fully restate all nested layer properties, but the schema already covers those in detail.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses a specific verb/resource pair ('Rename a layer template') and enumerates the exact updateable aspects: name, layers, and bumpers. It also distinguishes this from applying a template by explaining that existing clips are not refreshed unless apply_layer_template is called again.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description makes clear this is a partial update of an existing layer template, with omitted fields untouched and layers treated as a whole-list replacement. It also gives clear follow-up guidance for refreshing already-applied clips, though it does not explicitly compare against save_layer_template or other sibling update paths.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

update_projectUpdate projectA
Idempotent

Rename a project and/or change its saved settings (focus, instructions, clip length, maxClips, caption style, framing, pauses, refineEntities, refineDisfluency, disfluencyLlm, refineScenes); only the fields you pass change. Does not re-pick clips or re-render: reanalyze_project picks again with the new selection settings, refine_project re-runs refine steps, rerender_clips applies style changes. Returns { changed, needsReanalyze, needsRefine, needsRerender, hint }.

NameRequiredDescriptionDefault
idYesProject id (from list_projects, search_clips or create_project)
clearNoOptional settings to reset to their default, e.g. maxClips (back to automatic) or focus
titleNoNew title
metadataNoReplace the project's metadata (the whole map; {} clears it)
settingsNoSettings to change; keys left out keep their value. Intro/outro go through set_project_bumpers

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With only idempotentHint=true in annotations, the description carries most of the burden and does well: it discloses partial-update semantics, that no clips are re-picked or re-rendered, and the exact return shape including the needsReanalyze/needsRefine/needsRerender follow-up flags — valuable since no output schema exists. It omits permissions/auth and what happens on invalid or conflicting values (e.g. maxClipSeconds below minClipSeconds).

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the action and the merge rule, followed by the sibling routing and return shape — good ordering. The long settings enumeration in the first sentence partially duplicates the schema and is the one part that does not fully earn its space.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a mutation tool with a deeply nested settings object and no output schema, the description supplies the two things an agent most needs: the merge semantics and the downstream-action flags. Minor gaps remain around validation errors and the bumpers/clear/metadata paths, which are only covered in the schema.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% with rich per-field descriptions, so the baseline is 3. The description's parenthetical list of settings mirrors what the schema already documents and does not mention the 'clear' or 'metadata' parameters, so it adds little beyond structured data.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource pair ('rename a project and/or change its saved settings') and enumerates the settings domains covered. An agent can distinguish it from update_project_style, set_project_bumpers, and update_clip without opening any schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly states the key constraint ('only the fields you pass change') and draws hard boundaries against three named alternatives: reanalyze_project for re-picking, refine_project for refine steps, rerender_clips for applying style. That is when-to-use and when-not-to-use in two sentences.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

update_project_styleUpdate project styleA
DestructiveIdempotent

Change the caption/hook look for the whole project (font, colors, size, position, words per line). Clears per-clip style overrides unless keepOverrides, and re-renders every finished clip unless rerender is false. Returns { changed, needsRerender, rerendered, skipped, hint }; rerendered is how many clips were queued, skipped how many were still preparing or rendering (they need rerender_clips once done). For one clip use update_clip { style }.

NameRequiredDescriptionDefault
idYesProject id (from list_projects, search_clips or create_project)
styleYesThe style fields to change; fields left out keep their value
rerenderNoRe-render finished clips now (default true); false saves the style and the next render shows it
keepOverridesNoKeep clips' own style overrides instead of clearing them (default false)

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations declare destructiveHint=true and idempotentHint=true, and the description concretizes the destruction: per-clip style overrides are cleared unless keepOverrides is set, and every finished clip is re-rendered unless rerender is false. It also discloses the return payload semantics (changed, needsRerender, rerendered, skipped) and the follow-up action for skipped clips, going well beyond the annotation flags.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the core action and scope, then side effects, then return shape, then sibling routing – a logical order with no filler. The opening sentence is dense with parenthetical examples, which is efficient but slightly heavy; nothing is redundant, however.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a destructive, nested-object mutation with no output schema, the description covers the side effects, the shape and meaning of the return object, and the follow-up path for skipped clips. An agent has everything needed to invoke it correctly and interpret the result.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description adds real meaning beyond the schema by explaining the consequence of rerender=false (style saved, applied on next render) and the difference between rerendered and skipped counts. The keepOverrides consequence (preserving per-clip overrides) is also spelled out rather than merely labeled.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ("Change the caption/hook look for the whole project") and enumerates the dimensions affected (font, colors, size, position, words per line). It explicitly scopes the operation to the whole project and names the sibling tool to use for a single clip, so it is distinguishable from update_clip at a glance.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The final sentence routes the agent to update_clip { style } for one-clip changes, and the description explains what happens to clips that are still preparing or rendering (they need rerender_clips once done). It gives clear context but does not state prerequisites or when-not-to-use conditions beyond the single-clip case.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

update_webhookUpdate webhookA
Idempotent

Change a webhook's url, events or description, or turn it on or off (enabled). Turning it on clears a failure streak; turning it off cancels deliveries waiting to be sent (resend_webhook_delivery brings them back). Returns { endpoint, changed, message }.

NameRequiredDescriptionDefault
idYesWebhook id (from list_webhooks)
urlNoNew URL to POST to (https)
eventsNoReplace the events it gets
enabledNoTurn it on (true) or off (false)
descriptionNoNew label; null clears it

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only declare idempotentHint, so the description carries the real burden and does so well: enabling clears a failure streak, disabling cancels in-flight deliveries, and the recovery path is named. It also discloses the return shape { endpoint, changed, message }, which no output schema provides.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three tight sentences, front-loaded with the mutable surface, then the side effects, then the return shape. Every clause earns its place; no restatement of the name or title.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema exists, yet the description supplies the return shape and the destructive side effect of disabling, which is the highest-risk thing an agent could do here. Nothing needed to invoke this correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% so the baseline is 3, but the description adds semantics the schema does not: 'enabled' has the side effect of clearing a failure streak, and events is a replace rather than a merge. The null-clears-it behavior for description is already in the schema, so no extra credit there.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (change/update) and resource (webhook) and enumerates exactly which fields are mutable: url, events, description, enabled. That field-level enumeration distinguishes it cleanly from create_webhook, delete_webhook, and rotate_webhook_secret without needing to name them.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Makes clear this is the partial-update path and routes the agent to resend_webhook_delivery for restoring canceled deliveries, which is genuine when-to-use guidance. It stops short of explicit when-not guidance (e.g., use create_webhook to add a new endpoint, use test_webhook to verify), so it lands just below the top.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

wait_for_projectWait for projectA
Read-only

Block until the project is done or failed, or timeoutSeconds pass. Returns { id, status, stage, stageProgress, stageDetail ({ done, total, etaSec } while a stage works in parts, else null), error, clips: [{ id, title, status }] }; on timeout the same with hint "Still running": call it again. Use get_project instead for a non-blocking look with full clip details.

NameRequiredDescriptionDefault
idYesProject id (from create_project or list_projects)
timeoutSecondsNoLongest to wait, seconds (1-120, default 60)

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Goes well beyond the readOnlyHint annotation by disclosing that the call blocks, what the timeout produces, and how to continue after a timeout. It also enumerates the return payload including stageProgress semantics ("else null") and the partial-stage etaSec, which is real behavioral context an agent cannot get from annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the blocking behavior and the timeout escape hatch before the return shape. The dense parenthetical type listing for stageDetail is slightly harder to parse than prose or a shallow field list, but every clause carries information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema exists, so the description carries the return-value burden and does so thoroughly (id, status, stage, stageProgress, stageDetail, error, clips). For a two-parameter long-poll read tool, nothing needed to invoke or interpret it is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and documents both id (source: create_project/list_projects) and timeoutSeconds (1-120, default 60). The description only alludes to timeoutSeconds passing, adding no syntax or semantics beyond the schema, so the baseline applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource plus the blocking scope: "Block until the project is done or failed, or timeoutSeconds pass." It also names the contrasting sibling (get_project) so an agent can distinguish blocking vs non-blocking reads immediately.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives explicit when/when-not guidance: use this to wait, use get_project "instead for a non-blocking look with full clip details." It even covers the post-timeout path — "on timeout the same with hint 'Still running': call it again" — leaving no retry inference to the agent.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 3 tool updatesv0.32.0
    • Addedget_email_preferences
    • Addedset_email_preferences
    • Changedset_instance_settings1 field changed
      • addedInput schema / properties / EVENT_PROJECT_DONE_READY_EMAIL_DELAY_MINUTES
        Added value: +{
        +  "anyOf": [
        +    {
        +      "maximum": 180,
        +      "minimum": 1,
        +      "type": "integer"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "description": "Minutes to wait after the first project in a batch finishes before emailing that the clips are ready, so several projects land in one note. Sent only when an email driver is set and the account has not come back since those projects finished. A project that finishes while a note is already waiting joins that note."
        +}
  2. 14 tool updatesv0.31.0
    • Changedcreate_project1 field changed
      • addedInput schema / properties / metadata
        Added value: +{
        +  "additionalProperties": {
        +    "maxLength": 500,
        +    "type": "string"
        +  },
        +  "description": "Your own key/value strings (≤20 keys, 1 KB), returned by get_project and sent in every webhook for this project, e.g. { runId: \"n8n-123\" }",
        +  "propertyNames": {
        +    "pattern": "^[A-Za-z0-9_.-]{1,40}$",
        +    "type": "string"
        +  },
        +  "type": "object"
        +}
    • Addedcreate_webhook
    • Addeddelete_webhook
    • Addedget_media_links
    • Addedlist_media_links
    • Addedlist_webhook_deliveries
    • Addedlist_webhooks
    • Addedresend_webhook_delivery
    • Addedrevoke_media_link
    • Addedrotate_webhook_secret
    • Changedset_instance_settings6 fields changed
      • addedInput schema / properties / ALLOW_PRIVATE_WEBHOOK_URLS
        Added value: +{
        +  "description": "Let webhook URLs point at LAN or loopback hosts (a self-hosted n8n next to Reelsaw), over http too. Off, they must be public https (SSRF guard).",
        +  "type": [
        +    "boolean",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / MAX_WEBHOOKS_PER_USER
        Added value: +{
        +  "anyOf": [
        +    {
        +      "maximum": 9007199254740991,
        +      "minimum": 0,
        +      "type": "integer"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "description": "Webhook endpoints an account may add. 0 = unlimited. Admins can override per user on the Users page."
        +}
      • addedInput schema / properties / MEDIA_LINK_ALLOW_NEVER
        Added value: +{
        +  "description": "Owners may make a share link that never expires (they can still revoke it).",
        +  "type": [
        +    "boolean",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / MEDIA_LINK_RETENTION_DAYS
        Added value: +{
        +  "anyOf": [
        +    {
        +      "maximum": 9007199254740991,
        +      "minimum": 0,
        +      "type": "integer"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "description": "Delete revoked and expired share links this many days after they stopped working. 0 = keep forever."
        +}
      • addedInput schema / properties / MEDIA_LINK_TTL_DAYS
        Added value: +{
        +  "anyOf": [
        +    {
        +      "maximum": 3650,
        +      "minimum": 1,
        +      "type": "integer"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "description": "Share links last this many days unless the owner picks less."
        +}
      • addedInput schema / properties / WEBHOOK_DELIVERY_RETENTION_DAYS
        Added value: +{
        +  "anyOf": [
        +    {
        +      "maximum": 9007199254740991,
        +      "minimum": 0,
        +      "type": "integer"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "description": "Delete webhook delivery log rows older than this. 0 = keep forever."
        +}
    • Addedtest_webhook
    • Changedupdate_project1 field changed
      • addedInput schema / properties / metadata
        Added value: +{
        +  "additionalProperties": {
        +    "maxLength": 500,
        +    "type": "string"
        +  },
        +  "description": "Replace the project's metadata (the whole map; {} clears it)",
        +  "propertyNames": {
        +    "pattern": "^[A-Za-z0-9_.-]{1,40}$",
        +    "type": "string"
        +  },
        +  "type": "object"
        +}
    • Addedupdate_webhook
  3. 14 tool updatesv0.28.1
    • Changedcreate_project1 field changed
      • addedInput schema / properties / settings / properties / captionSeam
        Added value: +{
        +  "description": "In split and stack sections, put captions on the line between the two panes instead of at captionY (default true)",
        +  "type": "boolean"
        +}
    • Changeddelete_project1 field changed
      • addedInput schema / properties / forgetDecisions
        Added value: +{
        +  "default": false,
        +  "description": "Forget what Reelsaw learned from it (its decision log entries) when it is deleted for good, by purge_project or the retention sweep; restoring it cancels that. Default false keeps them",
        +  "type": "boolean"
        +}
    • Addedexport_decisions
    • Addedforget_decisions
    • Addedlist_decisions
    • Changedpurge_project1 field changed
      • addedInput schema / properties / forgetDecisions
        Added value: +{
        +  "default": false,
        +  "description": "Also forget what Reelsaw learned from this project (its entries in the decision log); default false keeps them",
        +  "type": "boolean"
        +}
    • Changedreanalyze_project1 field changed
      • addedInput schema / properties / settings / properties / captionSeam
        Added value: +{
        +  "description": "In split and stack sections, put captions on the line between the two panes instead of at captionY (default true)",
        +  "type": "boolean"
        +}
    • Changedsave_layer_template1 field changed
      • addedInput schema / properties / bumpers
        Added value: +{
        +  "description": "With layers: the intro / outro the template puts on clips ({ intro, introSec, outro, outroSec }; a slot left out = clips keep their own, null = none, an id = that video or image). With clipId the clip's intro and outro are saved too",
        +  "properties": {
        +    "intro": {
        +      "anyOf": [
        +        {
        +          "maxLength": 100,
        +          "minLength": 1,
        +          "type": "string"
        +        },
        +        {
        +          "type": "null"
        +        }
        +      ],
        +      "description": "Library video or image id (list_bumpers) for the intro; null = no intro; left out = clips keep their own"
        +    },
        +    "introSec": {
        +      "description": "Seconds an image intro/outro shows (1-10); videos keep their own length",
        +      "maximum": 10,
        +      "minimum": 1,
        +      "type": "number"
        +    },
        +    "outro": {
        +      "anyOf": [
        +        {
        +          "maxLength": 100,
        +          "minLength": 1,
        +          "type": "string"
        +        },
        +        {
        +          "type": "null"
        +        }
        +      ],
        +      "description": "Library video or image id (list_bumpers) for the outro; null = no outro; left out = clips keep their own"
        +    },
        +    "outroSec": {
        +      "description": "Seconds an image intro/outro shows (1-10); videos keep their own length",
        +      "maximum": 10,
        +      "minimum": 1,
        +      "type": "number"
        +    }
        +  },
        +  "type": "object"
        +}
    • Changedset_instance_settings2 fields changed
      • addedInput schema / properties / DECISION_RETENTION_DAYS
        Added value: +{
        +  "anyOf": [
        +    {
        +      "maximum": 9007199254740991,
        +      "minimum": 0,
        +      "type": "integer"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "description": "Prune users' recorded decisions (accepts, rejects, edits Reelsaw learns from) older than this. They outlive deleted projects until then. 0 = keep forever."
        +}
      • changedInput schema / properties / LLM_CALL_TIMEOUT_SECONDS / description
        Previous value: -"Longest one model call may take (all its HTTP attempts together) before its step is stopped and retried. 0 = auto: 120 s for a hosted model, 600 s for a self-hosted one. A slow CPU-only model may need more."New value: +"Longest one model call may take (all its HTTP attempts together) before its step is stopped and retried. 0 = auto: 120 s for a hosted model, 600 s for a self-hosted one, and for a self-hosted transcription at least twice the chunk's audio length. Time spent waiting for a provider slot does not count. A slow CPU-only model may need more."
    • Addedset_learning
    • Changedupdate_clip1 field changed
      • changedInput schema / properties / style / anyOf
        Previous value: -[
        -  {
        -    "properties": {
        -      "captionSize": {
        -        "description": "Caption size multiplier (0.5-2, default 1)",
        -        "maximum": 2,
        -        "minimum": 0.5,
        -        "type": "number"
        -      },
        -      "captionX": {
        -        "description": "Caption centre, horizontal, as a fraction of the 9:16 frame (0-1)",
        -        "maximum": 1,
        -        "minimum": 0,
        -        "type": "number"
        -      },
        -      "captionY": {
        -        "description": "Caption centre, vertical (default 0.72), as a fraction of the 9:16 frame (0-1)",
        -        "maximum": 1,
        -        "minimum": 0,
        -        "type": "number"
        -      },
        -      "captions": {
        -        "description": "Caption look: karaoke (active word highlighted), box, plain, or none (no captions)",
        -        "enum": [
        -          "karaoke",
        -          "box",
        -          "plain",
        -          "none"
        -        ],
        -        "type": "string"
        -      },
        -      "emphasis": {
        -        "description": "Colour the clip's key words (default true)",
        -        "type": "boolean"
        -      },
        -      "emphasisColor": {
        -        "description": "Colour of emphasised words, #RRGGBB (default #39FF14)",
        -        "pattern": "^#[0-9a-fA-F]{6}$",
        -        "type": "string"
        -      },
        -      "highlightColor": {
        -        "description": "Karaoke active-word colour, #RRGGBB (default #FFD700)",
        -        "pattern": "^#[0-9a-fA-F]{6}$",
        -        "type": "string"
        -      },
        -      "hookSeconds": {
        -        "description": "How long the hook shows, seconds (0-15, default 3.5)",
        -        "maximum": 15,
        -        "minimum": 0,
        -        "type": "number"
        -      },
        -      "hookX": {
        -        "description": "Hook centre, horizontal, as a fraction of the 9:16 frame (0-1)",
        -        "maximum": 1,
        -        "minimum": 0,
        -        "type": "number"
        -      },
        -      "hookY": {
        -        "description": "Hook centre, vertical (default 0.16), as a fraction of the 9:16 frame (0-1)",
        -        "maximum": 1,
        -        "minimum": 0,
        -        "type": "number"
        -      },
        -      "showHook": {
        -        "description": "Show the hook text at the start of the clip (default true)",
        -        "type": "boolean"
        -      },
        -      "uppercase": {
        -        "description": "Captions in capitals",
        -        "type": "boolean"
        -      },
        -      "wordsPerLine": {
        -        "description": "Max caption words on screen at once (1-8, default 4)",
        -        "maximum": 8,
        -        "minimum": 1,
        -        "type": "integer"
        -      }
        -    },
        -    "type": "object"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]New value: +[
        +  {
        +    "properties": {
        +      "captionSeam": {
        +        "description": "In split and stack sections, put captions on the line between the two panes instead of at captionY (default true)",
        +        "type": "boolean"
        +      },
        +      "captionSize": {
        +        "description": "Caption size multiplier (0.5-2, default 1)",
        +        "maximum": 2,
        +        "minimum": 0.5,
        +        "type": "number"
        +      },
        +      "captionX": {
        +        "description": "Caption centre, horizontal, as a fraction of the 9:16 frame (0-1)",
        +        "maximum": 1,
        +        "minimum": 0,
        +        "type": "number"
        +      },
        +      "captionY": {
        +        "description": "Caption centre, vertical (default 0.72), as a fraction of the 9:16 frame (0-1)",
        +        "maximum": 1,
        +        "minimum": 0,
        +        "type": "number"
        +      },
        +      "captions": {
        +        "description": "Caption look: karaoke (active word highlighted), box, plain, or none (no captions)",
        +        "enum": [
        +          "karaoke",
        +          "box",
        +          "plain",
        +          "none"
        +        ],
        +        "type": "string"
        +      },
        +      "emphasis": {
        +        "description": "Colour the clip's key words (default true)",
        +        "type": "boolean"
        +      },
        +      "emphasisColor": {
        +        "description": "Colour of emphasised words, #RRGGBB (default #39FF14)",
        +        "pattern": "^#[0-9a-fA-F]{6}$",
        +        "type": "string"
        +      },
        +      "highlightColor": {
        +        "description": "Karaoke active-word colour, #RRGGBB (default #FFD700)",
        +        "pattern": "^#[0-9a-fA-F]{6}$",
        +        "type": "string"
        +      },
        +      "hookSeconds": {
        +        "description": "How long the hook shows, seconds (0-15, default 3.5)",
        +        "maximum": 15,
        +        "minimum": 0,
        +        "type": "number"
        +      },
        +      "hookX": {
        +        "description": "Hook centre, horizontal, as a fraction of the 9:16 frame (0-1)",
        +        "maximum": 1,
        +        "minimum": 0,
        +        "type": "number"
        +      },
        +      "hookY": {
        +        "description": "Hook centre, vertical (default 0.16), as a fraction of the 9:16 frame (0-1)",
        +        "maximum": 1,
        +        "minimum": 0,
        +        "type": "number"
        +      },
        +      "showHook": {
        +        "description": "Show the hook text at the start of the clip (default true)",
        +        "type": "boolean"
        +      },
        +      "uppercase": {
        +        "description": "Captions in capitals",
        +        "type": "boolean"
        +      },
        +      "wordsPerLine": {
        +        "description": "Max caption words on screen at once (1-8, default 4)",
        +        "maximum": 8,
        +        "minimum": 1,
        +        "type": "integer"
        +      }
        +    },
        +    "type": "object"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
    • Changedupdate_layer_template1 field changed
      • addedInput schema / properties / bumpers
        Added value: +{
        +  "anyOf": [
        +    {
        +      "description": "Intro / outro the template puts on clips, as each clip's own choice",
        +      "properties": {
        +        "intro": {
        +          "anyOf": [
        +            {
        +              "maxLength": 100,
        +              "minLength": 1,
        +              "type": "string"
        +            },
        +            {
        +              "type": "null"
        +            }
        +          ],
        +          "description": "Library video or image id (list_bumpers) for the intro; null = no intro; left out = clips keep their own"
        +        },
        +        "introSec": {
        +          "description": "Seconds an image intro/outro shows (1-10); videos keep their own length",
        +          "maximum": 10,
        +          "minimum": 1,
        +          "type": "number"
        +        },
        +        "outro": {
        +          "anyOf": [
        +            {
        +              "maxLength": 100,
        +              "minLength": 1,
        +              "type": "string"
        +            },
        +            {
        +              "type": "null"
        +            }
        +          ],
        +          "description": "Library video or image id (list_bumpers) for the outro; null = no outro; left out = clips keep their own"
        +        },
        +        "outroSec": {
        +          "description": "Seconds an image intro/outro shows (1-10); videos keep their own length",
        +          "maximum": 10,
        +          "minimum": 1,
        +          "type": "number"
        +        }
        +      },
        +      "type": "object"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ]
        +}
    • Changedupdate_project1 field changed
      • addedInput schema / properties / settings / properties / captionSeam
        Added value: +{
        +  "description": "In split and stack sections, put captions on the line between the two panes instead of at captionY (default true)",
        +  "type": "boolean"
        +}
    • Changedupdate_project_style1 field changed
      • addedInput schema / properties / style / properties / captionSeam
        Added value: +{
        +  "description": "In split and stack sections, put captions on the line between the two panes instead of at captionY (default true)",
        +  "type": "boolean"
        +}
  4. 27 tool updatesv0.26.3
    • Addedadd_transcript_correction
    • Addedadd_transcript_cut
    • Addedbilling_portal_url
    • Changedcreate_project3 fields changed
      • changedInput schema / properties / sourceUrl / description
        Previous value: -"http(s) URL of the video"New value: +"http(s) URL of the video; give this or uploadId"
      • addedInput schema / properties / uploadId
        Added value: +{
        +  "description": "Id returned by POST /api/upload (the file body, x-filename header, API key as Bearer); give this or sourceUrl",
        +  "maxLength": 100,
        +  "minLength": 1,
        +  "type": "string"
        +}
      • removedInput schema / required
        Removed value: -[
        -  "sourceUrl"
        -]
    • Addeddelete_asset
    • Addeddelete_clips
    • Addeddelete_layer_template
    • Addedget_project_defaults
    • Addedlist_deleted
    • Changedlist_layer_templates2 fields changed
      • addedInput schema / properties / cursor
        Added value: +{
        +  "description": "nextCursor from the previous page; omit for the first page",
        +  "maxLength": 300,
        +  "type": "string"
        +}
      • addedInput schema / properties / limit
        Added value: +{
        +  "default": 100,
        +  "description": "Templates per page (1-500, default 100)",
        +  "maximum": 500,
        +  "minimum": 1,
        +  "type": "integer"
        +}
    • Addedlist_transcript_flags
    • Addedpurge_asset
    • Addedpurge_clip
    • Addedpurge_layer_template
    • Addedpurge_project
    • Addedrefine_project
    • Removedremove_asset
    • Removedremove_layer_template
    • Changedrestore_asset1 field changed
      • changedInput schema / properties / assetId / description
        Previous value: -"Library asset id (from remove_asset)"New value: +"Library asset id (from delete_asset)"
    • Changedrestore_clip1 field changed
      • changedInput schema / properties / id / description
        Previous value: -"Clip id (from get_project → replacedClips)"New value: +"Clip id (from list_deleted or get_project → replacedClips)"
    • Addedreview_transcript_flags
    • Changedsearch_assets2 fields changed
      • addedInput schema / properties / deleted
        Added value: +{
        +  "description": "library only: list your deleted items instead (their ids go to restore_asset)",
        +  "type": "boolean"
        +}
      • removedInput schema / properties / removed
        Removed value: -{
        -  "description": "library only: list the items removed in the last 30 days instead (their ids go to restore_asset)",
        -  "type": "boolean"
        -}
    • Changedset_instance_settings6 fields changed
      • addedInput schema / properties / LLM_CALL_TIMEOUT_SECONDS
        Added value: +{
        +  "anyOf": [
        +    {
        +      "maximum": 86400,
        +      "minimum": 0,
        +      "type": "integer"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "description": "Longest one model call may take (all its HTTP attempts together) before its step is stopped and retried. 0 = auto: 120 s for a hosted model, 600 s for a self-hosted one. A slow CPU-only model may need more."
        +}
      • changedInput schema / properties / MAX_RERENDERS_PER_HOUR / description
        Previous value: -"Re-renders and hand-made clips an account may ask for per hour. 0 = unlimited."New value: +"Re-renders, hand-made clips and re-runs (re-analyze, refine) an account may ask for per hour. 0 = unlimited."
      • changedInput schema / properties / MAX_STORAGE_MB / description
        Previous value: -"MB of sources, uploads, renders and intros/outros an account may keep. 0 = unlimited."New value: +"MB of sources, uploads, renders and intros/outros an account may keep (deleted items count until they are deleted forever). 0 = unlimited."
      • addedInput schema / properties / SCRATCH_CACHE_HOURS
        Added value: +{
        +  "anyOf": [
        +    {
        +      "maximum": 9007199254740991,
        +      "minimum": 0,
        +      "type": "integer"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "description": "With an S3 store, drop a worker's local copies of the sources and rendered clips of finished projects after this many idle hours; a job pulls them back from the bucket when it needs them, and a worker short of disk drops the least recently used ones first. 0 = keep them on the worker. The local store needs nothing: scratch and store are the same file."
        +}
      • addedInput schema / properties / STAGE_IDLE_TIMEOUT_MINUTES
        Added value: +{
        +  "anyOf": [
        +    {
        +      "maximum": 1440,
        +      "minimum": 0,
        +      "type": "integer"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "description": "A stage made of checkpointed steps (finding moments) is stopped when no step has finished for this long; Retry continues where it stopped. 0 = auto: 1.5 times the model call timeout."
        +}
      • addedInput schema / properties / STEP_RETENTION_DAYS
        Added value: +{
        +  "anyOf": [
        +    {
        +      "maximum": 9007199254740991,
        +      "minimum": 0,
        +      "type": "integer"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "description": "Drop the saved checkpoints (finding-moments windows) of a finished project this many days after it last changed; a Retry sooner than that reuses them. 0 = keep forever."
        +}
    • Addedset_project_defaults
    • Addedset_project_source_text
    • Changedupdate_clip1 field changed
      • changedInput schema / properties / rerender / description
        Previous value: -"Re-render when a burned-in field changed (default true); false = keep the old video until rerender_clip"New value: +"Re-render when a burned-in field changed (default true, same as the app); false = keep the old video until rerender_clip"
    • Addedupdate_project_style
  5. 13 tool updatesv0.23.0
    • Addedapply_framing
    • Addedapply_layer_template
    • Changedcreate_project5 fields changed
      • changedInput schema / properties / settings / description
        Previous value: -"Project settings; omit for the defaults (automatic clip count, 20–60 s clips, natural pauses, refine steps off). Set refineEntities / refineDisfluency / disfluencyLlm / refineScenes true to opt into polish (instance Pipeline gates must be on)."New value: +"Project settings; omit for the defaults (automatic clip count, 20–60 s clips, natural pauses, name fixes and the on-screen text scan on, filler removal off). Set refineDisfluency / disfluencyLlm true to opt into filler removal, refineEntities / refineScenes false to skip those steps (instance Pipeline gates must be on)."
      • addedInput schema / properties / settings / properties / layerTemplateId
        Added value: +{
        +  "anyOf": [
        +    {
        +      "maxLength": 100,
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "description": "Layer template id (list_layer_templates) applied to each new clip of this project; null = none. Existing clips: apply_layer_template"
        +}
      • changedInput schema / properties / settings / properties / refineEntities / description
        Previous value: -"Fix misheard names after transcription (LLM). Off by default; on runs that refine step."New value: +"Fix misheard names after transcription (LLM). On by default; off skips that refine step."
      • changedInput schema / properties / settings / properties / refineScenes / description
        Previous value: -"Detect scene cuts and on-screen text. Off by default; on runs that refine step."New value: +"Scan the video for on-screen text (burned-in captions, watermarks, slides). On by default; off skips that refine step. Shot cuts are always detected."
      • addedInput schema / properties / settings / properties / reframeAdjust
        Added value: +{
        +  "anyOf": [
        +    {
        +      "properties": {
        +        "contentCrop": {
        +          "description": "Stack only: the part of the frame shown on top instead of the whole frame or the detected content region",
        +          "properties": {
        +            "x1": {
        +              "description": "Left edge, fraction of the source width",
        +              "maximum": 1,
        +              "minimum": 0,
        +              "type": "number"
        +            },
        +            "x2": {
        +              "description": "Right edge, fraction of the source width",
        +              "maximum": 1,
        +              "minimum": 0,
        +              "type": "number"
        +            },
        +            "y1": {
        +              "description": "Top edge, fraction of the source height",
        +              "maximum": 1,
        +              "minimum": 0,
        +              "type": "number"
        +            },
        +            "y2": {
        +              "description": "Bottom edge, fraction of the source height",
        +              "maximum": 1,
        +              "minimum": 0,
        +              "type": "number"
        +            }
        +          },
        +          "required": [
        +            "x1",
        +            "y1",
        +            "x2",
        +            "y2"
        +          ],
        +          "type": "object"
        +        },
        +        "crop": {
        +          "description": "Show exactly this part of the source in the (top / only) pane, as fractions of the source size; replaces the automatic crop, the offset and the subject. A shape unlike the pane's is fitted over a blurred copy",
        +          "properties": {
        +            "x1": {
        +              "description": "Left edge, fraction of the source width",
        +              "maximum": 1,
        +              "minimum": 0,
        +              "type": "number"
        +            },
        +            "x2": {
        +              "description": "Right edge, fraction of the source width",
        +              "maximum": 1,
        +              "minimum": 0,
        +              "type": "number"
        +            },
        +            "y1": {
        +              "description": "Top edge, fraction of the source height",
        +              "maximum": 1,
        +              "minimum": 0,
        +              "type": "number"
        +            },
        +            "y2": {
        +              "description": "Bottom edge, fraction of the source height",
        +              "maximum": 1,
        +              "minimum": 0,
        +              "type": "number"
        +            }
        +          },
        +          "required": [
        +            "x1",
        +            "y1",
        +            "x2",
        +            "y2"
        +          ],
        +          "type": "object"
        +        },
        +        "crop2": {
        +          "description": "Split only: a fixed crop for the bottom pane",
        +          "properties": {
        +            "x1": {
        +              "description": "Left edge, fraction of the source width",
        +              "maximum": 1,
        +              "minimum": 0,
        +              "type": "number"
        +            },
        +            "x2": {
        +              "description": "Right edge, fraction of the source width",
        +              "maximum": 1,
        +              "minimum": 0,
        +              "type": "number"
        +            },
        +            "y1": {
        +              "description": "Top edge, fraction of the source height",
        +              "maximum": 1,
        +              "minimum": 0,
        +              "type": "number"
        +            },
        +            "y2": {
        +              "description": "Bottom edge, fraction of the source height",
        +              "maximum": 1,
        +              "minimum": 0,
        +              "type": "number"
        +            }
        +          },
        +          "required": [
        +            "x1",
        +            "y1",
        +            "x2",
        +            "y2"
        +          ],
        +          "type": "object"
        +        },
        +        "offsetX": {
        +          "description": "Shift the (top) crop sideways by this fraction of the source width (-0.5 to 0.5), relative to the face it follows",
        +          "maximum": 0.5,
        +          "minimum": -0.5,
        +          "type": "number"
        +        },
        +        "offsetX2": {
        +          "description": "Split only: shift the bottom crop sideways (-0.5 to 0.5)",
        +          "maximum": 0.5,
        +          "minimum": -0.5,
        +          "type": "number"
        +        },
        +        "offsetY": {
        +          "description": "Shift the (top) crop up or down by this fraction of the source height; only matters where the crop is shorter than the frame (split, stack)",
        +          "maximum": 0.5,
        +          "minimum": -0.5,
        +          "type": "number"
        +        },
        +        "offsetY2": {
        +          "description": "Split only: shift the bottom crop up or down (-0.5 to 0.5)",
        +          "maximum": 0.5,
        +          "minimum": -0.5,
        +          "type": "number"
        +        },
        +        "subjectX": {
        +          "description": "Follow the person at this x (0-1 of the source width) in the top/only pane; the offset still applies on top",
        +          "maximum": 1,
        +          "minimum": 0,
        +          "type": "number"
        +        },
        +        "subjectX2": {
        +          "description": "Split only: follow the person at this x in the bottom pane",
        +          "maximum": 1,
        +          "minimum": 0,
        +          "type": "number"
        +        }
        +      },
        +      "type": "object"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "description": "Applied with reframe to clips without their own sections: offsetX/Y, offsetX2/Y2, subjectX/X2, crop, crop2, contentCrop as on a clip's section; null = none. apply_framing sets it from one clip"
        +}
    • Addedlist_layer_templates
    • Addedlist_releases
    • Changedreanalyze_project5 fields changed
      • changedInput schema / properties / settings / description
        Previous value: -"Project settings; omit for the defaults (automatic clip count, 20–60 s clips, natural pauses, refine steps off). Set refineEntities / refineDisfluency / disfluencyLlm / refineScenes true to opt into polish (instance Pipeline gates must be on)."New value: +"Project settings; omit for the defaults (automatic clip count, 20–60 s clips, natural pauses, name fixes and the on-screen text scan on, filler removal off). Set refineDisfluency / disfluencyLlm true to opt into filler removal, refineEntities / refineScenes false to skip those steps (instance Pipeline gates must be on)."
      • addedInput schema / properties / settings / properties / layerTemplateId
        Added value: +{
        +  "anyOf": [
        +    {
        +      "maxLength": 100,
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "description": "Layer template id (list_layer_templates) applied to each new clip of this project; null = none. Existing clips: apply_layer_template"
        +}
      • changedInput schema / properties / settings / properties / refineEntities / description
        Previous value: -"Fix misheard names after transcription (LLM). Off by default; on runs that refine step."New value: +"Fix misheard names after transcription (LLM). On by default; off skips that refine step."
      • changedInput schema / properties / settings / properties / refineScenes / description
        Previous value: -"Detect scene cuts and on-screen text. Off by default; on runs that refine step."New value: +"Scan the video for on-screen text (burned-in captions, watermarks, slides). On by default; off skips that refine step. Shot cuts are always detected."
      • addedInput schema / properties / settings / properties / reframeAdjust
        Added value: +{
        +  "anyOf": [
        +    {
        +      "properties": {
        +        "contentCrop": {
        +          "description": "Stack only: the part of the frame shown on top instead of the whole frame or the detected content region",
        +          "properties": {
        +            "x1": {
        +              "description": "Left edge, fraction of the source width",
        +              "maximum": 1,
        +              "minimum": 0,
        +              "type": "number"
        +            },
        +            "x2": {
        +              "description": "Right edge, fraction of the source width",
        +              "maximum": 1,
        +              "minimum": 0,
        +              "type": "number"
        +            },
        +            "y1": {
        +              "description": "Top edge, fraction of the source height",
        +              "maximum": 1,
        +              "minimum": 0,
        +              "type": "number"
        +            },
        +            "y2": {
        +              "description": "Bottom edge, fraction of the source height",
        +              "maximum": 1,
        +              "minimum": 0,
        +              "type": "number"
        +            }
        +          },
        +          "required": [
        +            "x1",
        +            "y1",
        +            "x2",
        +            "y2"
        +          ],
        +          "type": "object"
        +        },
        +        "crop": {
        +          "description": "Show exactly this part of the source in the (top / only) pane, as fractions of the source size; replaces the automatic crop, the offset and the subject. A shape unlike the pane's is fitted over a blurred copy",
        +          "properties": {
        +            "x1": {
        +              "description": "Left edge, fraction of the source width",
        +              "maximum": 1,
        +              "minimum": 0,
        +              "type": "number"
        +            },
        +            "x2": {
        +              "description": "Right edge, fraction of the source width",
        +              "maximum": 1,
        +              "minimum": 0,
        +              "type": "number"
        +            },
        +            "y1": {
        +              "description": "Top edge, fraction of the source height",
        +              "maximum": 1,
        +              "minimum": 0,
        +              "type": "number"
        +            },
        +            "y2": {
        +              "description": "Bottom edge, fraction of the source height",
        +              "maximum": 1,
        +              "minimum": 0,
        +              "type": "number"
        +            }
        +          },
        +          "required": [
        +            "x1",
        +            "y1",
        +            "x2",
        +            "y2"
        +          ],
        +          "type": "object"
        +        },
        +        "crop2": {
        +          "description": "Split only: a fixed crop for the bottom pane",
        +          "properties": {
        +            "x1": {
        +              "description": "Left edge, fraction of the source width",
        +              "maximum": 1,
        +              "minimum": 0,
        +              "type": "number"
        +            },
        +            "x2": {
        +              "description": "Right edge, fraction of the source width",
        +              "maximum": 1,
        +              "minimum": 0,
        +              "type": "number"
        +            },
        +            "y1": {
        +              "description": "Top edge, fraction of the source height",
        +              "maximum": 1,
        +              "minimum": 0,
        +              "type": "number"
        +            },
        +            "y2": {
        +              "description": "Bottom edge, fraction of the source height",
        +              "maximum": 1,
        +              "minimum": 0,
        +              "type": "number"
        +            }
        +          },
        +          "required": [
        +            "x1",
        +            "y1",
        +            "x2",
        +            "y2"
        +          ],
        +          "type": "object"
        +        },
        +        "offsetX": {
        +          "description": "Shift the (top) crop sideways by this fraction of the source width (-0.5 to 0.5), relative to the face it follows",
        +          "maximum": 0.5,
        +          "minimum": -0.5,
        +          "type": "number"
        +        },
        +        "offsetX2": {
        +          "description": "Split only: shift the bottom crop sideways (-0.5 to 0.5)",
        +          "maximum": 0.5,
        +          "minimum": -0.5,
        +          "type": "number"
        +        },
        +        "offsetY": {
        +          "description": "Shift the (top) crop up or down by this fraction of the source height; only matters where the crop is shorter than the frame (split, stack)",
        +          "maximum": 0.5,
        +          "minimum": -0.5,
        +          "type": "number"
        +        },
        +        "offsetY2": {
        +          "description": "Split only: shift the bottom crop up or down (-0.5 to 0.5)",
        +          "maximum": 0.5,
        +          "minimum": -0.5,
        +          "type": "number"
        +        },
        +        "subjectX": {
        +          "description": "Follow the person at this x (0-1 of the source width) in the top/only pane; the offset still applies on top",
        +          "maximum": 1,
        +          "minimum": 0,
        +          "type": "number"
        +        },
        +        "subjectX2": {
        +          "description": "Split only: follow the person at this x in the bottom pane",
        +          "maximum": 1,
        +          "minimum": 0,
        +          "type": "number"
        +        }
        +      },
        +      "type": "object"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "description": "Applied with reframe to clips without their own sections: offsetX/Y, offsetX2/Y2, subjectX/X2, crop, crop2, contentCrop as on a clip's section; null = none. apply_framing sets it from one clip"
        +}
    • Addedremove_layer_template
    • Addedrestore_layer_template
    • Addedsave_layer_template
    • Changedset_instance_settings2 fields changed
      • changedInput schema / properties / REFINE_SCENES / description
        Previous value: -"Detect scene cuts and on-screen text during refine. Off by default; turn on so projects may opt in via refineScenes."New value: +"Scan the video for on-screen text (burned-in captions, watermarks, slides) during refine. On by default; off closes the ceiling (projects opt out via refineScenes). Shot cuts are always detected."
      • changedInput schema / properties / TRANSCRIPT_CORRECTION / description
        Previous value: -"After transcription, fix misheard names with the language model. Off by default (ceiling closed); turn on so projects may opt in via refineEntities."New value: +"After transcription, fix misheard names with the language model. On by default; off closes the ceiling so no project runs it (projects opt out via refineEntities)."
    • Changedupdate_clip1 field changed
      • changedInput schema / properties / layoutSections / anyOf
        Previous value: -[
        -  {
        -    "items": {
        -      "properties": {
        -        "end": {
        -          "description": "Section end, seconds from the start of the source video",
        -          "minimum": 0,
        -          "type": "number"
        -        },
        -        "mode": {
        -          "description": "Framing for this section (same modes as the project's reframe, without none)",
        -          "enum": [
        -            "auto",
        -            "face",
        -            "split",
        -            "stack",
        -            "fit",
        -            "center"
        -          ],
        -          "type": "string"
        -        },
        -        "offsetX": {
        -          "description": "Shift the (top) crop sideways by this fraction of the source width (-0.5 to 0.5), relative to the face it follows",
        -          "maximum": 0.5,
        -          "minimum": -0.5,
        -          "type": "number"
        -        },
        -        "offsetX2": {
        -          "description": "Split only: shift the bottom crop sideways (-0.5 to 0.5)",
        -          "maximum": 0.5,
        -          "minimum": -0.5,
        -          "type": "number"
        -        },
        -        "offsetY": {
        -          "description": "Shift the (top) crop up or down by this fraction of the source height; only matters where the crop is shorter than the frame (split, stack)",
        -          "maximum": 0.5,
        -          "minimum": -0.5,
        -          "type": "number"
        -        },
        -        "offsetY2": {
        -          "description": "Split only: shift the bottom crop up or down (-0.5 to 0.5)",
        -          "maximum": 0.5,
        -          "minimum": -0.5,
        -          "type": "number"
        -        },
        -        "start": {
        -          "description": "Section start, seconds from the start of the source video",
        -          "minimum": 0,
        -          "type": "number"
        -        },
        -        "subjectX": {
        -          "description": "Follow the person at this x (0-1 of the source width) in the top/only pane; the offset still applies on top",
        -          "maximum": 1,
        -          "minimum": 0,
        -          "type": "number"
        -        },
        -        "subjectX2": {
        -          "description": "Split only: follow the person at this x in the bottom pane",
        -          "maximum": 1,
        -          "minimum": 0,
        -          "type": "number"
        -        }
        -      },
        -      "required": [
        -        "start",
        -        "end",
        -        "mode"
        -      ],
        -      "type": "object"
        -    },
        -    "maxItems": 50,
        -    "type": "array"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]New value: +[
        +  {
        +    "items": {
        +      "properties": {
        +        "contentCrop": {
        +          "description": "Stack only: the part of the frame shown on top instead of the whole frame or the detected content region",
        +          "properties": {
        +            "x1": {
        +              "description": "Left edge, fraction of the source width",
        +              "maximum": 1,
        +              "minimum": 0,
        +              "type": "number"
        +            },
        +            "x2": {
        +              "description": "Right edge, fraction of the source width",
        +              "maximum": 1,
        +              "minimum": 0,
        +              "type": "number"
        +            },
        +            "y1": {
        +              "description": "Top edge, fraction of the source height",
        +              "maximum": 1,
        +              "minimum": 0,
        +              "type": "number"
        +            },
        +            "y2": {
        +              "description": "Bottom edge, fraction of the source height",
        +              "maximum": 1,
        +              "minimum": 0,
        +              "type": "number"
        +            }
        +          },
        +          "required": [
        +            "x1",
        +            "y1",
        +            "x2",
        +            "y2"
        +          ],
        +          "type": "object"
        +        },
        +        "crop": {
        +          "description": "Show exactly this part of the source in the (top / only) pane, as fractions of the source size; replaces the automatic crop, the offset and the subject. A shape unlike the pane's is fitted over a blurred copy",
        +          "properties": {
        +            "x1": {
        +              "description": "Left edge, fraction of the source width",
        +              "maximum": 1,
        +              "minimum": 0,
        +              "type": "number"
        +            },
        +            "x2": {
        +              "description": "Right edge, fraction of the source width",
        +              "maximum": 1,
        +              "minimum": 0,
        +              "type": "number"
        +            },
        +            "y1": {
        +              "description": "Top edge, fraction of the source height",
        +              "maximum": 1,
        +              "minimum": 0,
        +              "type": "number"
        +            },
        +            "y2": {
        +              "description": "Bottom edge, fraction of the source height",
        +              "maximum": 1,
        +              "minimum": 0,
        +              "type": "number"
        +            }
        +          },
        +          "required": [
        +            "x1",
        +            "y1",
        +            "x2",
        +            "y2"
        +          ],
        +          "type": "object"
        +        },
        +        "crop2": {
        +          "description": "Split only: a fixed crop for the bottom pane",
        +          "properties": {
        +            "x1": {
        +              "description": "Left edge, fraction of the source width",
        +              "maximum": 1,
        +              "minimum": 0,
        +              "type": "number"
        +            },
        +            "x2": {
        +              "description": "Right edge, fraction of the source width",
        +              "maximum": 1,
        +              "minimum": 0,
        +              "type": "number"
        +            },
        +            "y1": {
        +              "description": "Top edge, fraction of the source height",
        +              "maximum": 1,
        +              "minimum": 0,
        +              "type": "number"
        +            },
        +            "y2": {
        +              "description": "Bottom edge, fraction of the source height",
        +              "maximum": 1,
        +              "minimum": 0,
        +              "type": "number"
        +            }
        +          },
        +          "required": [
        +            "x1",
        +            "y1",
        +            "x2",
        +            "y2"
        +          ],
        +          "type": "object"
        +        },
        +        "end": {
        +          "description": "Section end, seconds from the start of the source video",
        +          "minimum": 0,
        +          "type": "number"
        +        },
        +        "mode": {
        +          "description": "Framing for this section (same modes as the project's reframe, without none)",
        +          "enum": [
        +            "auto",
        +            "face",
        +            "split",
        +            "stack",
        +            "fit",
        +            "center"
        +          ],
        +          "type": "string"
        +        },
        +        "offsetX": {
        +          "description": "Shift the (top) crop sideways by this fraction of the source width (-0.5 to 0.5), relative to the face it follows",
        +          "maximum": 0.5,
        +          "minimum": -0.5,
        +          "type": "number"
        +        },
        +        "offsetX2": {
        +          "description": "Split only: shift the bottom crop sideways (-0.5 to 0.5)",
        +          "maximum": 0.5,
        +          "minimum": -0.5,
        +          "type": "number"
        +        },
        +        "offsetY": {
        +          "description": "Shift the (top) crop up or down by this fraction of the source height; only matters where the crop is shorter than the frame (split, stack)",
        +          "maximum": 0.5,
        +          "minimum": -0.5,
        +          "type": "number"
        +        },
        +        "offsetY2": {
        +          "description": "Split only: shift the bottom crop up or down (-0.5 to 0.5)",
        +          "maximum": 0.5,
        +          "minimum": -0.5,
        +          "type": "number"
        +        },
        +        "start": {
        +          "description": "Section start, seconds from the start of the source video",
        +          "minimum": 0,
        +          "type": "number"
        +        },
        +        "subjectX": {
        +          "description": "Follow the person at this x (0-1 of the source width) in the top/only pane; the offset still applies on top",
        +          "maximum": 1,
        +          "minimum": 0,
        +          "type": "number"
        +        },
        +        "subjectX2": {
        +          "description": "Split only: follow the person at this x in the bottom pane",
        +          "maximum": 1,
        +          "minimum": 0,
        +          "type": "number"
        +        }
        +      },
        +      "required": [
        +        "start",
        +        "end",
        +        "mode"
        +      ],
        +      "type": "object"
        +    },
        +    "maxItems": 50,
        +    "type": "array"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
    • Addedupdate_layer_template
    • Changedupdate_project6 fields changed
      • changedInput schema / properties / clear / items / enum
        Previous value: -[
        -  "maxClips",
        -  "focus",
        -  "instructions",
        -  "language",
        -  "vocabulary"
        -]New value: +[
        +  "maxClips",
        +  "focus",
        +  "instructions",
        +  "language",
        +  "vocabulary",
        +  "reframeAdjust"
        +]
      • changedInput schema / properties / clear / maxItems
        Previous value: -5New value: +6
      • addedInput schema / properties / settings / properties / layerTemplateId
        Added value: +{
        +  "anyOf": [
        +    {
        +      "maxLength": 100,
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "description": "Layer template id (list_layer_templates) applied to each new clip of this project; null = none. Existing clips: apply_layer_template"
        +}
      • changedInput schema / properties / settings / properties / refineEntities / description
        Previous value: -"Fix misheard names after transcription (LLM). Off by default; on runs that refine step."New value: +"Fix misheard names after transcription (LLM). On by default; off skips that refine step."
      • changedInput schema / properties / settings / properties / refineScenes / description
        Previous value: -"Detect scene cuts and on-screen text. Off by default; on runs that refine step."New value: +"Scan the video for on-screen text (burned-in captions, watermarks, slides). On by default; off skips that refine step. Shot cuts are always detected."
      • addedInput schema / properties / settings / properties / reframeAdjust
        Added value: +{
        +  "anyOf": [
        +    {
        +      "properties": {
        +        "contentCrop": {
        +          "description": "Stack only: the part of the frame shown on top instead of the whole frame or the detected content region",
        +          "properties": {
        +            "x1": {
        +              "description": "Left edge, fraction of the source width",
        +              "maximum": 1,
        +              "minimum": 0,
        +              "type": "number"
        +            },
        +            "x2": {
        +              "description": "Right edge, fraction of the source width",
        +              "maximum": 1,
        +              "minimum": 0,
        +              "type": "number"
        +            },
        +            "y1": {
        +              "description": "Top edge, fraction of the source height",
        +              "maximum": 1,
        +              "minimum": 0,
        +              "type": "number"
        +            },
        +            "y2": {
        +              "description": "Bottom edge, fraction of the source height",
        +              "maximum": 1,
        +              "minimum": 0,
        +              "type": "number"
        +            }
        +          },
        +          "required": [
        +            "x1",
        +            "y1",
        +            "x2",
        +            "y2"
        +          ],
        +          "type": "object"
        +        },
        +        "crop": {
        +          "description": "Show exactly this part of the source in the (top / only) pane, as fractions of the source size; replaces the automatic crop, the offset and the subject. A shape unlike the pane's is fitted over a blurred copy",
        +          "properties": {
        +            "x1": {
        +              "description": "Left edge, fraction of the source width",
        +              "maximum": 1,
        +              "minimum": 0,
        +              "type": "number"
        +            },
        +            "x2": {
        +              "description": "Right edge, fraction of the source width",
        +              "maximum": 1,
        +              "minimum": 0,
        +              "type": "number"
        +            },
        +            "y1": {
        +              "description": "Top edge, fraction of the source height",
        +              "maximum": 1,
        +              "minimum": 0,
        +              "type": "number"
        +            },
        +            "y2": {
        +              "description": "Bottom edge, fraction of the source height",
        +              "maximum": 1,
        +              "minimum": 0,
        +              "type": "number"
        +            }
        +          },
        +          "required": [
        +            "x1",
        +            "y1",
        +            "x2",
        +            "y2"
        +          ],
        +          "type": "object"
        +        },
        +        "crop2": {
        +          "description": "Split only: a fixed crop for the bottom pane",
        +          "properties": {
        +            "x1": {
        +              "description": "Left edge, fraction of the source width",
        +              "maximum": 1,
        +              "minimum": 0,
        +              "type": "number"
        +            },
        +            "x2": {
        +              "description": "Right edge, fraction of the source width",
        +              "maximum": 1,
        +              "minimum": 0,
        +              "type": "number"
        +            },
        +            "y1": {
        +              "description": "Top edge, fraction of the source height",
        +              "maximum": 1,
        +              "minimum": 0,
        +              "type": "number"
        +            },
        +            "y2": {
        +              "description": "Bottom edge, fraction of the source height",
        +              "maximum": 1,
        +              "minimum": 0,
        +              "type": "number"
        +            }
        +          },
        +          "required": [
        +            "x1",
        +            "y1",
        +            "x2",
        +            "y2"
        +          ],
        +          "type": "object"
        +        },
        +        "offsetX": {
        +          "description": "Shift the (top) crop sideways by this fraction of the source width (-0.5 to 0.5), relative to the face it follows",
        +          "maximum": 0.5,
        +          "minimum": -0.5,
        +          "type": "number"
        +        },
        +        "offsetX2": {
        +          "description": "Split only: shift the bottom crop sideways (-0.5 to 0.5)",
        +          "maximum": 0.5,
        +          "minimum": -0.5,
        +          "type": "number"
        +        },
        +        "offsetY": {
        +          "description": "Shift the (top) crop up or down by this fraction of the source height; only matters where the crop is shorter than the frame (split, stack)",
        +          "maximum": 0.5,
        +          "minimum": -0.5,
        +          "type": "number"
        +        },
        +        "offsetY2": {
        +          "description": "Split only: shift the bottom crop up or down (-0.5 to 0.5)",
        +          "maximum": 0.5,
        +          "minimum": -0.5,
        +          "type": "number"
        +        },
        +        "subjectX": {
        +          "description": "Follow the person at this x (0-1 of the source width) in the top/only pane; the offset still applies on top",
        +          "maximum": 1,
        +          "minimum": 0,
        +          "type": "number"
        +        },
        +        "subjectX2": {
        +          "description": "Split only: follow the person at this x in the bottom pane",
        +          "maximum": 1,
        +          "minimum": 0,
        +          "type": "number"
        +        }
        +      },
        +      "type": "object"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "description": "Applied with reframe to clips without their own sections: offsetX/Y, offsetX2/Y2, subjectX/X2, crop, crop2, contentCrop as on a clip's section; null = none. apply_framing sets it from one clip"
        +}
  6. 18 tool updatesv0.19.1
    • Addedadd_asset
    • Removedadd_bumper
    • Addedasset_usage
    • Removedbumper_usage
    • Changedcreate_project3 fields changed
      • changedInput schema / properties / introAssetId / description
        Previous value: -"Library asset id (list_bumpers) for the intro of every clip; null = no intro; omitted = your default intro"New value: +"Library asset id (search_assets with source library) for the intro of every clip; null = no intro; omitted = your default intro"
      • changedInput schema / properties / settings / properties / introAssetId / description
        Previous value: -"Library asset id of the intro (list_bumpers); null = none"New value: +"Library asset id of the intro (search_assets with source library); null = none"
      • changedInput schema / properties / settings / properties / outroAssetId / description
        Previous value: -"Library asset id of the outro (list_bumpers); null = none"New value: +"Library asset id of the outro (search_assets with source library); null = none"
    • Removedlist_bumpers
    • Changedreanalyze_project2 fields changed
      • changedInput schema / properties / settings / properties / introAssetId / description
        Previous value: -"Library asset id of the intro (list_bumpers); null = none"New value: +"Library asset id of the intro (search_assets with source library); null = none"
      • changedInput schema / properties / settings / properties / outroAssetId / description
        Previous value: -"Library asset id of the outro (list_bumpers); null = none"New value: +"Library asset id of the outro (search_assets with source library); null = none"
    • Addedremove_asset
    • Removedremove_bumper
    • Removedrename_bumper
    • Addedrestore_asset
    • Removedrestore_bumper
    • Addedsearch_assets
    • Changedset_default_bumper1 field changed
      • changedInput schema / properties / assetId / description
        Previous value: -"Library asset id (list_bumpers), or null for no default"New value: +"Library asset id (search_assets with source library), or null for no default"
    • Changedset_instance_settings10 fields changed
      • addedInput schema / properties / CATALOG_URL
        Added value: +{
        +  "anyOf": [
        +    {
        +      "format": "uri",
        +      "maxLength": 500,
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "description": "Where the Reelsaw catalog manifest (sounds, icons, stickers) is fetched from, hourly; the starter pack bundled with Reelsaw is used until a newer one arrives."
        +}
      • addedInput schema / properties / FREESOUND_API_KEY
        Added value: +{
        +  "anyOf": [
        +    {
        +      "maxLength": 200,
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "description": "Freesound API key (free): enables sound effects in Browse. Stored encrypted; empty = provider hidden."
        +}
      • addedInput schema / properties / GIPHY_API_KEY
        Added value: +{
        +  "anyOf": [
        +    {
        +      "maxLength": 200,
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "description": "Giphy API key: enables GIFs and stickers in Browse. Stored encrypted; empty = provider hidden."
        +}
      • addedInput schema / properties / ICONIFY_API_URL
        Added value: +{
        +  "anyOf": [
        +    {
        +      "format": "uri",
        +      "maxLength": 500,
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "description": "Iconify API used for icon search; the public one by default, or your own self-hosted instance (https)."
        +}
      • addedInput schema / properties / KLIPY_API_KEY
        Added value: +{
        +  "anyOf": [
        +    {
        +      "maxLength": 200,
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "description": "Klipy API key: enables GIF search in Browse (experimental). Stored encrypted; empty = provider hidden."
        +}
      • addedInput schema / properties / OPENVERSE_ENABLED
        Added value: +{
        +  "description": "Show Openverse (Creative Commons images and audio, no key) in Browse. Off by default until you have checked its limits for your use.",
        +  "type": [
        +    "boolean",
        +    "null"
        +  ]
        +}
      • addedInput schema / properties / PEXELS_API_KEY
        Added value: +{
        +  "anyOf": [
        +    {
        +      "maxLength": 200,
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "description": "Pexels API key (free): enables stock photos and portrait videos in Browse. Stored encrypted; empty = provider hidden."
        +}
      • addedInput schema / properties / PIXABAY_API_KEY
        Added value: +{
        +  "anyOf": [
        +    {
        +      "maxLength": 200,
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "description": "Pixabay API key (free): enables photos, illustrations, vectors and videos in Browse. Stored encrypted; empty = provider hidden."
        +}
      • changedInput schema / properties / REQUIRE_EMAIL_VERIFICATION / description
        Previous value: -"New users must click the verification link before they can sign in. Needs an email driver in .env."New value: +"New users must click the verification link before they can sign in. The link works for 24 hours and is single-use; an expired one offers a new send. Needs an email driver in .env."
      • changedInput schema / properties / SIGNUP_MODE / description
        Previous value: -"Who may sign up: open (anyone), approval (new accounts wait for an admin) or closed (nobody). The first account is always the admin."New value: +"Who may sign up: open (anyone, right for a public instance with email confirmation), approval (new accounts wait for an admin; a private box) or closed (nobody). The first account is always the admin."
    • Addedupdate_asset
    • Changedupdate_clip3 fields changed
      • removedInput schema / properties / audio
        Removed value: -{
        -  "anyOf": [
        -    {
        -      "items": {
        -        "properties": {
        -          "assetId": {
        -            "description": "Library audio asset id (list_bumpers, kind audio); the same asset can be used more than once",
        -            "maxLength": 100,
        -            "minLength": 1,
        -            "type": "string"
        -          },
        -          "duck": {
        -            "description": "Lower the music while someone speaks (default true)",
        -            "type": "boolean"
        -          },
        -          "end": {
        -            "description": "Music stops after this second of the source video",
        -            "minimum": 0,
        -            "type": "number"
        -          },
        -          "fade": {
        -            "description": "Fade in and out, seconds (default 0.5)",
        -            "maximum": 5,
        -            "minimum": 0,
        -            "type": "number"
        -          },
        -          "id": {
        -            "description": "Stable id for this track (any short string; keep it when editing)",
        -            "maxLength": 40,
        -            "minLength": 1,
        -            "type": "string"
        -          },
        -          "loop": {
        -            "description": "Repeat the track when it is shorter than the window (default true)",
        -            "type": "boolean"
        -          },
        -          "offset": {
        -            "description": "Seconds into the track to start from (default 0)",
        -            "minimum": 0,
        -            "type": "number"
        -          },
        -          "start": {
        -            "description": "Music starts at this second of the source video",
        -            "minimum": 0,
        -            "type": "number"
        -          },
        -          "volume": {
        -            "description": "Music level 0-1 (default 0.6) on a track normalised to -20 LUFS, before ducking",
        -            "maximum": 1,
        -            "minimum": 0,
        -            "type": "number"
        -          }
        -        },
        -        "required": [
        -          "id",
        -          "assetId",
        -          "start",
        -          "end"
        -        ],
        -        "type": "object"
        -      },
        -      "maxItems": 10,
        -      "type": "array"
        -    },
        -    {
        -      "type": "null"
        -    }
        -  ],
        -  "description": "Background music tracks under parts of the clip (max 10; the same asset may repeat): library audio assets (list_bumpers, kind audio), each with a window in source seconds, volume, ducking under speech, fades. Replaces the list; null or [] = none"
        -}
      • addedInput schema / properties / layers
        Added value: +{
        +  "anyOf": [
        +    {
        +      "items": {
        +        "oneOf": [
        +          {
        +            "properties": {
        +              "assetId": {
        +                "description": "Library asset id (search_assets with source library): an image, an animation or a video",
        +                "maxLength": 100,
        +                "minLength": 1,
        +                "type": "string"
        +              },
        +              "end": {
        +                "description": "Hidden after this second of the source video",
        +                "minimum": 0,
        +                "type": "number"
        +              },
        +              "id": {
        +                "description": "Stable id for this overlay (any short string; keep it when editing)",
        +                "maxLength": 40,
        +                "minLength": 1,
        +                "type": "string"
        +              },
        +              "kind": {
        +                "const": "asset",
        +                "type": "string"
        +              },
        +              "loop": {
        +                "description": "Repeat a video / animation while its window is open (default: on for GIF and animated WebP, off for videos)",
        +                "type": "boolean"
        +              },
        +              "opacity": {
        +                "description": "0.1-1, default 1",
        +                "maximum": 1,
        +                "minimum": 0.1,
        +                "type": "number"
        +              },
        +              "start": {
        +                "description": "Shown from this second of the source video",
        +                "minimum": 0,
        +                "type": "number"
        +              },
        +              "w": {
        +                "description": "asset: width, fraction of the output width (0.05-1); the height follows the file. Default 0.3",
        +                "maximum": 1,
        +                "minimum": 0.05,
        +                "type": "number"
        +              },
        +              "x": {
        +                "description": "Centre, fraction of the output width (0-1)",
        +                "maximum": 1,
        +                "minimum": 0,
        +                "type": "number"
        +              },
        +              "y": {
        +                "description": "Centre, fraction of the output height (0-1)",
        +                "maximum": 1,
        +                "minimum": 0,
        +                "type": "number"
        +              }
        +            },
        +            "required": [
        +              "id",
        +              "assetId",
        +              "start",
        +              "end",
        +              "x",
        +              "y",
        +              "kind"
        +            ],
        +            "type": "object"
        +          },
        +          {
        +            "properties": {
        +              "box": {
        +                "description": "text: dark plate behind the text (default: outlined text, no plate)",
        +                "type": "boolean"
        +              },
        +              "color": {
        +                "description": "text: colour as #RRGGBB, default white",
        +                "pattern": "^#[0-9a-fA-F]{6}$",
        +                "type": "string"
        +              },
        +              "end": {
        +                "description": "Hidden after this second of the source video",
        +                "minimum": 0,
        +                "type": "number"
        +              },
        +              "font": {
        +                "description": "text: font family from TEXT_FONTS (default DejaVu Sans, the caption font)",
        +                "maxLength": 60,
        +                "minLength": 1,
        +                "type": "string"
        +              },
        +              "id": {
        +                "description": "Stable id for this overlay (any short string; keep it when editing)",
        +                "maxLength": 40,
        +                "minLength": 1,
        +                "type": "string"
        +              },
        +              "kind": {
        +                "const": "text",
        +                "type": "string"
        +              },
        +              "opacity": {
        +                "description": "0.1-1, default 1",
        +                "maximum": 1,
        +                "minimum": 0.1,
        +                "type": "number"
        +              },
        +              "size": {
        +                "description": "text: font size as a fraction of the output height (0.02-0.12), default 0.045",
        +                "maximum": 0.12,
        +                "minimum": 0.02,
        +                "type": "number"
        +              },
        +              "start": {
        +                "description": "Shown from this second of the source video",
        +                "minimum": 0,
        +                "type": "number"
        +              },
        +              "text": {
        +                "description": "The line to show (1-120 chars)",
        +                "maxLength": 120,
        +                "minLength": 1,
        +                "type": "string"
        +              },
        +              "x": {
        +                "description": "Centre, fraction of the output width (0-1)",
        +                "maximum": 1,
        +                "minimum": 0,
        +                "type": "number"
        +              },
        +              "y": {
        +                "description": "Centre, fraction of the output height (0-1)",
        +                "maximum": 1,
        +                "minimum": 0,
        +                "type": "number"
        +              }
        +            },
        +            "required": [
        +              "id",
        +              "text",
        +              "start",
        +              "end",
        +              "x",
        +              "y",
        +              "kind"
        +            ],
        +            "type": "object"
        +          },
        +          {
        +            "properties": {
        +              "assetId": {
        +                "description": "Library audio asset id (search_assets with source library, kind audio); the same asset can be used more than once",
        +                "maxLength": 100,
        +                "minLength": 1,
        +                "type": "string"
        +              },
        +              "duck": {
        +                "description": "Lower the music while someone speaks (default true)",
        +                "type": "boolean"
        +              },
        +              "end": {
        +                "description": "Music stops after this second of the source video",
        +                "minimum": 0,
        +                "type": "number"
        +              },
        +              "fade": {
        +                "description": "Fade in and out, seconds (default 0.5)",
        +                "maximum": 5,
        +                "minimum": 0,
        +                "type": "number"
        +              },
        +              "id": {
        +                "description": "Stable id for this track (any short string; keep it when editing)",
        +                "maxLength": 40,
        +                "minLength": 1,
        +                "type": "string"
        +              },
        +              "kind": {
        +                "const": "audio",
        +                "type": "string"
        +              },
        +              "loop": {
        +                "description": "Repeat the track when it is shorter than the window (default true)",
        +                "type": "boolean"
        +              },
        +              "offset": {
        +                "description": "Seconds into the track to start from (default 0)",
        +                "minimum": 0,
        +                "type": "number"
        +              },
        +              "start": {
        +                "description": "Music starts at this second of the source video",
        +                "minimum": 0,
        +                "type": "number"
        +              },
        +              "volume": {
        +                "description": "Music level 0-1 (default 0.6) on a track normalised to -20 LUFS, before ducking",
        +                "maximum": 1,
        +                "minimum": 0,
        +                "type": "number"
        +              }
        +            },
        +            "required": [
        +              "id",
        +              "assetId",
        +              "start",
        +              "end",
        +              "kind"
        +            ],
        +            "type": "object"
        +          }
        +        ]
        +      },
        +      "maxItems": 30,
        +      "type": "array"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "description": "Everything laid over or played under the clip, one list (max 30): kind asset = a library image/GIF/video (search_assets with source library; logos, stickers, b-roll), kind text = a line of text, kind audio = music or a sound effect (a library audio asset with a window in source seconds, volume, ducking, fade, loop). Replaces the whole list; null or [] = none. get_clip returns it"
        +}
      • removedInput schema / properties / overlays
        Removed value: -{
        -  "anyOf": [
        -    {
        -      "items": {
        -        "properties": {
        -          "assetId": {
        -            "description": "asset: library asset id (list_bumpers), an image or a video",
        -            "maxLength": 100,
        -            "minLength": 1,
        -            "type": "string"
        -          },
        -          "box": {
        -            "description": "text: dark plate behind the text (default: outlined text, no plate)",
        -            "type": "boolean"
        -          },
        -          "color": {
        -            "description": "text: colour as #RRGGBB, default white",
        -            "pattern": "^#[0-9a-fA-F]{6}$",
        -            "type": "string"
        -          },
        -          "end": {
        -            "description": "Hidden after this second of the source video",
        -            "minimum": 0,
        -            "type": "number"
        -          },
        -          "font": {
        -            "description": "text: font family from TEXT_FONTS (default DejaVu Sans, the caption font)",
        -            "maxLength": 60,
        -            "minLength": 1,
        -            "type": "string"
        -          },
        -          "id": {
        -            "description": "Stable id for this overlay (any short string; keep it when editing)",
        -            "maxLength": 40,
        -            "minLength": 1,
        -            "type": "string"
        -          },
        -          "kind": {
        -            "description": "asset (default): a library image or video; text: a line of text",
        -            "enum": [
        -              "asset",
        -              "text"
        -            ],
        -            "type": "string"
        -          },
        -          "opacity": {
        -            "description": "0.1-1, default 1",
        -            "maximum": 1,
        -            "minimum": 0.1,
        -            "type": "number"
        -          },
        -          "size": {
        -            "description": "text: font size as a fraction of the output height (0.02-0.12), default 0.045",
        -            "maximum": 0.12,
        -            "minimum": 0.02,
        -            "type": "number"
        -          },
        -          "start": {
        -            "description": "Shown from this second of the source video",
        -            "minimum": 0,
        -            "type": "number"
        -          },
        -          "text": {
        -            "description": "text: the line to show (1-120 chars)",
        -            "maxLength": 120,
        -            "minLength": 1,
        -            "type": "string"
        -          },
        -          "w": {
        -            "description": "asset: width, fraction of the output width (0.05-1); the height follows the file. Default 0.3",
        -            "maximum": 1,
        -            "minimum": 0.05,
        -            "type": "number"
        -          },
        -          "x": {
        -            "description": "Centre, fraction of the output width (0-1)",
        -            "maximum": 1,
        -            "minimum": 0,
        -            "type": "number"
        -          },
        -          "y": {
        -            "description": "Centre, fraction of the output height (0-1)",
        -            "maximum": 1,
        -            "minimum": 0,
        -            "type": "number"
        -          }
        -        },
        -        "required": [
        -          "id",
        -          "start",
        -          "end",
        -          "x",
        -          "y"
        -        ],
        -        "type": "object"
        -      },
        -      "maxItems": 20,
        -      "type": "array"
        -    },
        -    {
        -      "type": "null"
        -    }
        -  ],
        -  "description": "Things laid over the clip for a time range (max 20): kind asset = a library image/video (list_bumpers; logos, stickers, b-roll), kind text = a line of text (size, color, box). Replaces the list; null or [] = none"
        -}
    • Changedupdate_project2 fields changed
      • changedInput schema / properties / settings / properties / introAssetId / description
        Previous value: -"Library asset id of the intro (list_bumpers); null = none"New value: +"Library asset id of the intro (search_assets with source library); null = none"
      • changedInput schema / properties / settings / properties / outroAssetId / description
        Previous value: -"Library asset id of the outro (list_bumpers); null = none"New value: +"Library asset id of the outro (search_assets with source library); null = none"
  7. 18 tool updatesv0.17.0
    • Addedbilling_checkout_url
    • Addedcheck_for_update
    • Changedcreate_project5 fields changed
      • changedInput schema / properties / settings / description
        Previous value: -"Project settings; omit for the defaults (automatic clip count, 20–60 s clips, natural pauses)."New value: +"Project settings; omit for the defaults (automatic clip count, 20–60 s clips, natural pauses, refine steps off). Set refineEntities / refineDisfluency / disfluencyLlm / refineScenes true to opt into polish (instance Pipeline gates must be on)."
      • addedInput schema / properties / settings / properties / disfluencyLlm
        Added value: +{
        +  "description": "Model pass on top of rule-based disfluency detection. Off by default (rules only when disfluency is on).",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / settings / properties / refineDisfluency
        Added value: +{
        +  "description": "Find fillers, repetitions and false starts. Off by default; on runs that refine step.",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / settings / properties / refineEntities
        Added value: +{
        +  "description": "Fix misheard names after transcription (LLM). Off by default; on runs that refine step.",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / settings / properties / refineScenes
        Added value: +{
        +  "description": "Detect scene cuts and on-screen text. Off by default; on runs that refine step.",
        +  "type": "boolean"
        +}
    • Addedget_billing
    • Addedget_clip_captions
    • Addedget_failure
    • Addedget_instance_settings
    • Addedget_project_transcript_file
    • Addedget_queue
    • Addedimport_transcript
    • Addedlist_failures
    • Addedlist_workers
    • Changedreanalyze_project5 fields changed
      • changedInput schema / properties / settings / description
        Previous value: -"Project settings; omit for the defaults (automatic clip count, 20–60 s clips, natural pauses)."New value: +"Project settings; omit for the defaults (automatic clip count, 20–60 s clips, natural pauses, refine steps off). Set refineEntities / refineDisfluency / disfluencyLlm / refineScenes true to opt into polish (instance Pipeline gates must be on)."
      • addedInput schema / properties / settings / properties / disfluencyLlm
        Added value: +{
        +  "description": "Model pass on top of rule-based disfluency detection. Off by default (rules only when disfluency is on).",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / settings / properties / refineDisfluency
        Added value: +{
        +  "description": "Find fillers, repetitions and false starts. Off by default; on runs that refine step.",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / settings / properties / refineEntities
        Added value: +{
        +  "description": "Fix misheard names after transcription (LLM). Off by default; on runs that refine step.",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / settings / properties / refineScenes
        Added value: +{
        +  "description": "Detect scene cuts and on-screen text. Off by default; on runs that refine step.",
        +  "type": "boolean"
        +}
    • Addedreport_issue
    • Addedretry_failures
    • Addedset_instance_settings
    • Changedupdate_clip3 fields changed
      • addedInput schema / properties / audio
        Added value: +{
        +  "anyOf": [
        +    {
        +      "items": {
        +        "properties": {
        +          "assetId": {
        +            "description": "Library audio asset id (list_bumpers, kind audio); the same asset can be used more than once",
        +            "maxLength": 100,
        +            "minLength": 1,
        +            "type": "string"
        +          },
        +          "duck": {
        +            "description": "Lower the music while someone speaks (default true)",
        +            "type": "boolean"
        +          },
        +          "end": {
        +            "description": "Music stops after this second of the source video",
        +            "minimum": 0,
        +            "type": "number"
        +          },
        +          "fade": {
        +            "description": "Fade in and out, seconds (default 0.5)",
        +            "maximum": 5,
        +            "minimum": 0,
        +            "type": "number"
        +          },
        +          "id": {
        +            "description": "Stable id for this track (any short string; keep it when editing)",
        +            "maxLength": 40,
        +            "minLength": 1,
        +            "type": "string"
        +          },
        +          "loop": {
        +            "description": "Repeat the track when it is shorter than the window (default true)",
        +            "type": "boolean"
        +          },
        +          "offset": {
        +            "description": "Seconds into the track to start from (default 0)",
        +            "minimum": 0,
        +            "type": "number"
        +          },
        +          "start": {
        +            "description": "Music starts at this second of the source video",
        +            "minimum": 0,
        +            "type": "number"
        +          },
        +          "volume": {
        +            "description": "Music level 0-1 (default 0.6) on a track normalised to -20 LUFS, before ducking",
        +            "maximum": 1,
        +            "minimum": 0,
        +            "type": "number"
        +          }
        +        },
        +        "required": [
        +          "id",
        +          "assetId",
        +          "start",
        +          "end"
        +        ],
        +        "type": "object"
        +      },
        +      "maxItems": 10,
        +      "type": "array"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "description": "Background music tracks under parts of the clip (max 10; the same asset may repeat): library audio assets (list_bumpers, kind audio), each with a window in source seconds, volume, ducking under speech, fades. Replaces the list; null or [] = none"
        +}
      • changedInput schema / properties / layoutSections / anyOf
        Previous value: -[
        -  {
        -    "items": {
        -      "properties": {
        -        "end": {
        -          "description": "Section end, seconds from the start of the source video",
        -          "minimum": 0,
        -          "type": "number"
        -        },
        -        "mode": {
        -          "description": "Framing for this section (same modes as the project's reframe, without none)",
        -          "enum": [
        -            "auto",
        -            "face",
        -            "split",
        -            "stack",
        -            "fit",
        -            "center"
        -          ],
        -          "type": "string"
        -        },
        -        "offsetX": {
        -          "description": "Shift the crop sideways by this fraction of the source width (-0.5 to 0.5)",
        -          "maximum": 0.5,
        -          "minimum": -0.5,
        -          "type": "number"
        -        },
        -        "start": {
        -          "description": "Section start, seconds from the start of the source video",
        -          "minimum": 0,
        -          "type": "number"
        -        }
        -      },
        -      "required": [
        -        "start",
        -        "end",
        -        "mode"
        -      ],
        -      "type": "object"
        -    },
        -    "maxItems": 50,
        -    "type": "array"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]New value: +[
        +  {
        +    "items": {
        +      "properties": {
        +        "end": {
        +          "description": "Section end, seconds from the start of the source video",
        +          "minimum": 0,
        +          "type": "number"
        +        },
        +        "mode": {
        +          "description": "Framing for this section (same modes as the project's reframe, without none)",
        +          "enum": [
        +            "auto",
        +            "face",
        +            "split",
        +            "stack",
        +            "fit",
        +            "center"
        +          ],
        +          "type": "string"
        +        },
        +        "offsetX": {
        +          "description": "Shift the (top) crop sideways by this fraction of the source width (-0.5 to 0.5), relative to the face it follows",
        +          "maximum": 0.5,
        +          "minimum": -0.5,
        +          "type": "number"
        +        },
        +        "offsetX2": {
        +          "description": "Split only: shift the bottom crop sideways (-0.5 to 0.5)",
        +          "maximum": 0.5,
        +          "minimum": -0.5,
        +          "type": "number"
        +        },
        +        "offsetY": {
        +          "description": "Shift the (top) crop up or down by this fraction of the source height; only matters where the crop is shorter than the frame (split, stack)",
        +          "maximum": 0.5,
        +          "minimum": -0.5,
        +          "type": "number"
        +        },
        +        "offsetY2": {
        +          "description": "Split only: shift the bottom crop up or down (-0.5 to 0.5)",
        +          "maximum": 0.5,
        +          "minimum": -0.5,
        +          "type": "number"
        +        },
        +        "start": {
        +          "description": "Section start, seconds from the start of the source video",
        +          "minimum": 0,
        +          "type": "number"
        +        },
        +        "subjectX": {
        +          "description": "Follow the person at this x (0-1 of the source width) in the top/only pane; the offset still applies on top",
        +          "maximum": 1,
        +          "minimum": 0,
        +          "type": "number"
        +        },
        +        "subjectX2": {
        +          "description": "Split only: follow the person at this x in the bottom pane",
        +          "maximum": 1,
        +          "minimum": 0,
        +          "type": "number"
        +        }
        +      },
        +      "required": [
        +        "start",
        +        "end",
        +        "mode"
        +      ],
        +      "type": "object"
        +    },
        +    "maxItems": 50,
        +    "type": "array"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • addedInput schema / properties / overlays
        Added value: +{
        +  "anyOf": [
        +    {
        +      "items": {
        +        "properties": {
        +          "assetId": {
        +            "description": "asset: library asset id (list_bumpers), an image or a video",
        +            "maxLength": 100,
        +            "minLength": 1,
        +            "type": "string"
        +          },
        +          "box": {
        +            "description": "text: dark plate behind the text (default: outlined text, no plate)",
        +            "type": "boolean"
        +          },
        +          "color": {
        +            "description": "text: colour as #RRGGBB, default white",
        +            "pattern": "^#[0-9a-fA-F]{6}$",
        +            "type": "string"
        +          },
        +          "end": {
        +            "description": "Hidden after this second of the source video",
        +            "minimum": 0,
        +            "type": "number"
        +          },
        +          "font": {
        +            "description": "text: font family from TEXT_FONTS (default DejaVu Sans, the caption font)",
        +            "maxLength": 60,
        +            "minLength": 1,
        +            "type": "string"
        +          },
        +          "id": {
        +            "description": "Stable id for this overlay (any short string; keep it when editing)",
        +            "maxLength": 40,
        +            "minLength": 1,
        +            "type": "string"
        +          },
        +          "kind": {
        +            "description": "asset (default): a library image or video; text: a line of text",
        +            "enum": [
        +              "asset",
        +              "text"
        +            ],
        +            "type": "string"
        +          },
        +          "opacity": {
        +            "description": "0.1-1, default 1",
        +            "maximum": 1,
        +            "minimum": 0.1,
        +            "type": "number"
        +          },
        +          "size": {
        +            "description": "text: font size as a fraction of the output height (0.02-0.12), default 0.045",
        +            "maximum": 0.12,
        +            "minimum": 0.02,
        +            "type": "number"
        +          },
        +          "start": {
        +            "description": "Shown from this second of the source video",
        +            "minimum": 0,
        +            "type": "number"
        +          },
        +          "text": {
        +            "description": "text: the line to show (1-120 chars)",
        +            "maxLength": 120,
        +            "minLength": 1,
        +            "type": "string"
        +          },
        +          "w": {
        +            "description": "asset: width, fraction of the output width (0.05-1); the height follows the file. Default 0.3",
        +            "maximum": 1,
        +            "minimum": 0.05,
        +            "type": "number"
        +          },
        +          "x": {
        +            "description": "Centre, fraction of the output width (0-1)",
        +            "maximum": 1,
        +            "minimum": 0,
        +            "type": "number"
        +          },
        +          "y": {
        +            "description": "Centre, fraction of the output height (0-1)",
        +            "maximum": 1,
        +            "minimum": 0,
        +            "type": "number"
        +          }
        +        },
        +        "required": [
        +          "id",
        +          "start",
        +          "end",
        +          "x",
        +          "y"
        +        ],
        +        "type": "object"
        +      },
        +      "maxItems": 20,
        +      "type": "array"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "description": "Things laid over the clip for a time range (max 20): kind asset = a library image/video (list_bumpers; logos, stickers, b-roll), kind text = a line of text (size, color, box). Replaces the list; null or [] = none"
        +}
    • Changedupdate_project4 fields changed
      • addedInput schema / properties / settings / properties / disfluencyLlm
        Added value: +{
        +  "description": "Model pass on top of rule-based disfluency detection. Off by default (rules only when disfluency is on).",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / settings / properties / refineDisfluency
        Added value: +{
        +  "description": "Find fillers, repetitions and false starts. Off by default; on runs that refine step.",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / settings / properties / refineEntities
        Added value: +{
        +  "description": "Fix misheard names after transcription (LLM). Off by default; on runs that refine step.",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / settings / properties / refineScenes
        Added value: +{
        +  "description": "Detect scene cuts and on-screen text. Off by default; on runs that refine step.",
        +  "type": "boolean"
        +}
  8. 28 tool updatesv0.7.0
    • Changedadd_bumper1 field changed
      • addedInput schema / properties / name / description
        Added value: +"Library name (url only; default: the file name). rename_bumper changes it later"
    • Changedbumper_usage1 field changed
      • addedInput schema / properties / assetId / description
        Added value: +"Library asset id (from list_bumpers)"
    • Changedcreate_clip4 fields changed
      • addedInput schema / properties / endSec / description
        Added value: +"Range end, seconds"
      • addedInput schema / properties / mode / description
        Added value: +"exact = these bounds (default); ai = best moment inside them"
      • addedInput schema / properties / projectId / description
        Added value: +"Project id (from list_projects, search_clips or create_project)"
      • addedInput schema / properties / startSec / description
        Added value: +"Range start, seconds from the start of the source video"
    • Changedcreate_project33 fields changed
      • changedInput schema / properties / introAssetId / description
        Previous value: -"Library asset id for the intro of every clip; null = no intro; omitted = your default intro"New value: +"Library asset id (list_bumpers) for the intro of every clip; null = no intro; omitted = your default intro"
      • changedInput schema / properties / introSec / description
        Previous value: -"Seconds an intro image shows"New value: +"Seconds an intro image shows (1-10)"
      • addedInput schema / properties / outroSec / description
        Added value: +"Seconds an outro image shows (1-10)"
      • addedInput schema / properties / settings / properties / captionPosition / description
        Added value: +"Deprecated: use captionY"
      • addedInput schema / properties / settings / properties / captionSize / description
        Added value: +"Caption size multiplier (0.5-2, default 1)"
      • addedInput schema / properties / settings / properties / captionX / description
        Added value: +"Caption centre, horizontal, as a fraction of the 9:16 frame (0-1)"
      • addedInput schema / properties / settings / properties / captionY / description
        Added value: +"Caption centre, vertical (default 0.72), as a fraction of the 9:16 frame (0-1)"
      • addedInput schema / properties / settings / properties / captions / description
        Added value: +"Caption look: karaoke (active word highlighted), box, plain, or none (no captions)"
      • addedInput schema / properties / settings / properties / emphasis / description
        Added value: +"Colour the clip's key words (default true)"
      • addedInput schema / properties / settings / properties / emphasisColor / description
        Added value: +"Colour of emphasised words, #RRGGBB (default #39FF14)"
      • addedInput schema / properties / settings / properties / focus / description
        Added value: +"Only pick clips about this topic (max 300 chars)"
      • addedInput schema / properties / settings / properties / highlightColor / description
        Added value: +"Karaoke active-word colour, #RRGGBB (default #FFD700)"
      • addedInput schema / properties / settings / properties / hookSeconds / description
        Added value: +"How long the hook shows, seconds (0-15, default 3.5)"
      • addedInput schema / properties / settings / properties / hookX / description
        Added value: +"Hook centre, horizontal, as a fraction of the 9:16 frame (0-1)"
      • addedInput schema / properties / settings / properties / hookY / description
        Added value: +"Hook centre, vertical (default 0.16), as a fraction of the 9:16 frame (0-1)"
      • addedInput schema / properties / settings / properties / instructions / description
        Added value: +"Free-text guidance for picking and titling clips (max 2000 chars)"
      • addedInput schema / properties / settings / properties / introAssetId / description
        Added value: +"Library asset id of the intro (list_bumpers); null = none"
      • addedInput schema / properties / settings / properties / introSec / description
        Added value: +"Seconds an image intro/outro shows (1-10); videos keep their own length"
      • addedInput schema / properties / settings / properties / language / description
        Added value: +"Spoken language code (e.g. en); unset = detected"
      • addedInput schema / properties / settings / properties / maxClipSeconds / description
        Added value: +"Longest clip, seconds (10-300, default 60; must be above minClipSeconds)"
      • addedInput schema / properties / settings / properties / maxClips / description
        Added value: +"Most clips to pick (1-50); unset = every moment that stands on its own"
      • addedInput schema / properties / settings / properties / minClipSeconds / description
        Added value: +"Shortest clip, seconds (5-180, default 20)"
      • addedInput schema / properties / settings / properties / outroAssetId / description
        Added value: +"Library asset id of the outro (list_bumpers); null = none"
      • addedInput schema / properties / settings / properties / outroSec / description
        Added value: +"Seconds an image intro/outro shows (1-10); videos keep their own length"
      • addedInput schema / properties / settings / properties / pauses / description
        Added value: +"Long pauses inside clips: natural (cut to 0.6 s, default), tight (0.35 s) or off"
      • addedInput schema / properties / settings / properties / reframe / description
        Added value: +"Framing: auto (detect per section), face (follow the speaker), split (two speakers stacked), stack (full frame over a face crop), fit (whole frame on blur), center (static crop), none (original aspect)"
      • addedInput schema / properties / settings / properties / showHook / description
        Added value: +"Show the hook text at the start of the clip (default true)"
      • addedInput schema / properties / settings / properties / sourceText / description
        Added value: +"Text already in the video: hide (blur captions/logos except in slide decks, default), always (blur even then), keep"
      • addedInput schema / properties / settings / properties / uppercase / description
        Added value: +"Captions in capitals"
      • addedInput schema / properties / settings / properties / vocabulary / description
        Added value: +"Names, brands and jargon to spell correctly, comma separated (max 1000 chars)"
      • addedInput schema / properties / settings / properties / wordsPerLine / description
        Added value: +"Max caption words on screen at once (1-8, default 4)"
      • addedInput schema / properties / sourceUrl / description
        Added value: +"http(s) URL of the video"
      • addedInput schema / properties / title / description
        Added value: +"Project title (default: the video's own title)"
    • Addeddelete_clip
    • Changeddelete_project1 field changed
      • addedInput schema / properties / id / description
        Added value: +"Project id (from list_projects, search_clips or create_project)"
    • Changedexport_project2 fields changed
      • addedInput schema / properties / clipIds / description
        Added value: +"Only these clips (max 200; must be rendered clips of the project); omitted = every rendered clip"
      • addedInput schema / properties / id / description
        Added value: +"Project id (from list_projects, search_clips or create_project)"
    • Changedget_clip1 field changed
      • addedInput schema / properties / id / description
        Added value: +"Clip id (from get_project or search_clips)"
    • Changedget_project1 field changed
      • addedInput schema / properties / id / description
        Added value: +"Project id (from list_projects, search_clips or create_project)"
    • Changedget_project_events2 fields changed
      • addedInput schema / properties / id / description
        Added value: +"Project id (from list_projects, search_clips or create_project)"
      • addedInput schema / properties / limit / description
        Added value: +"Most events to return (1-200, default 50)"
    • Changedget_transcript4 fields changed
      • addedInput schema / properties / fromSec / description
        Added value: +"Start of the range, seconds from the start of the video"
      • addedInput schema / properties / id / description
        Added value: +"Project id (from list_projects, search_clips or create_project)"
      • addedInput schema / properties / maxChars / description
        Added value: +"Cut the output at this many characters (1000-200000, default 60000)"
      • addedInput schema / properties / toSec / description
        Added value: +"End of the range, seconds"
    • Changedlist_projects3 fields changed
      • addedInput schema / properties / cursor / description
        Added value: +"nextCursor from the previous page; omit for the first page"
      • addedInput schema / properties / deleted
        Added value: +{
        +  "description": "true = only deleted projects that restore_project can still bring back",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / limit / description
        Added value: +"Projects per page (1-100, default 24)"
    • Changedreanalyze_project29 fields changed
      • addedInput schema / properties / id / description
        Added value: +"Project id (from list_projects, search_clips or create_project)"
      • addedInput schema / properties / settings / properties / captionPosition / description
        Added value: +"Deprecated: use captionY"
      • addedInput schema / properties / settings / properties / captionSize / description
        Added value: +"Caption size multiplier (0.5-2, default 1)"
      • addedInput schema / properties / settings / properties / captionX / description
        Added value: +"Caption centre, horizontal, as a fraction of the 9:16 frame (0-1)"
      • addedInput schema / properties / settings / properties / captionY / description
        Added value: +"Caption centre, vertical (default 0.72), as a fraction of the 9:16 frame (0-1)"
      • addedInput schema / properties / settings / properties / captions / description
        Added value: +"Caption look: karaoke (active word highlighted), box, plain, or none (no captions)"
      • addedInput schema / properties / settings / properties / emphasis / description
        Added value: +"Colour the clip's key words (default true)"
      • addedInput schema / properties / settings / properties / emphasisColor / description
        Added value: +"Colour of emphasised words, #RRGGBB (default #39FF14)"
      • addedInput schema / properties / settings / properties / focus / description
        Added value: +"Only pick clips about this topic (max 300 chars)"
      • addedInput schema / properties / settings / properties / highlightColor / description
        Added value: +"Karaoke active-word colour, #RRGGBB (default #FFD700)"
      • addedInput schema / properties / settings / properties / hookSeconds / description
        Added value: +"How long the hook shows, seconds (0-15, default 3.5)"
      • addedInput schema / properties / settings / properties / hookX / description
        Added value: +"Hook centre, horizontal, as a fraction of the 9:16 frame (0-1)"
      • addedInput schema / properties / settings / properties / hookY / description
        Added value: +"Hook centre, vertical (default 0.16), as a fraction of the 9:16 frame (0-1)"
      • addedInput schema / properties / settings / properties / instructions / description
        Added value: +"Free-text guidance for picking and titling clips (max 2000 chars)"
      • addedInput schema / properties / settings / properties / introAssetId / description
        Added value: +"Library asset id of the intro (list_bumpers); null = none"
      • addedInput schema / properties / settings / properties / introSec / description
        Added value: +"Seconds an image intro/outro shows (1-10); videos keep their own length"
      • addedInput schema / properties / settings / properties / language / description
        Added value: +"Spoken language code (e.g. en); unset = detected"
      • addedInput schema / properties / settings / properties / maxClipSeconds / description
        Added value: +"Longest clip, seconds (10-300, default 60; must be above minClipSeconds)"
      • addedInput schema / properties / settings / properties / maxClips / description
        Added value: +"Most clips to pick (1-50); unset = every moment that stands on its own"
      • addedInput schema / properties / settings / properties / minClipSeconds / description
        Added value: +"Shortest clip, seconds (5-180, default 20)"
      • addedInput schema / properties / settings / properties / outroAssetId / description
        Added value: +"Library asset id of the outro (list_bumpers); null = none"
      • addedInput schema / properties / settings / properties / outroSec / description
        Added value: +"Seconds an image intro/outro shows (1-10); videos keep their own length"
      • addedInput schema / properties / settings / properties / pauses / description
        Added value: +"Long pauses inside clips: natural (cut to 0.6 s, default), tight (0.35 s) or off"
      • addedInput schema / properties / settings / properties / reframe / description
        Added value: +"Framing: auto (detect per section), face (follow the speaker), split (two speakers stacked), stack (full frame over a face crop), fit (whole frame on blur), center (static crop), none (original aspect)"
      • addedInput schema / properties / settings / properties / showHook / description
        Added value: +"Show the hook text at the start of the clip (default true)"
      • addedInput schema / properties / settings / properties / sourceText / description
        Added value: +"Text already in the video: hide (blur captions/logos except in slide decks, default), always (blur even then), keep"
      • addedInput schema / properties / settings / properties / uppercase / description
        Added value: +"Captions in capitals"
      • addedInput schema / properties / settings / properties / vocabulary / description
        Added value: +"Names, brands and jargon to spell correctly, comma separated (max 1000 chars)"
      • addedInput schema / properties / settings / properties / wordsPerLine / description
        Added value: +"Max caption words on screen at once (1-8, default 4)"
    • Changedremove_bumper1 field changed
      • addedInput schema / properties / assetId / description
        Added value: +"Library asset id (from list_bumpers)"
    • Addedrename_bumper
    • Changedrerender_clip1 field changed
      • addedInput schema / properties / id / description
        Added value: +"Clip id, not a project id (from get_project or search_clips)"
    • Changedrerender_clips2 fields changed
      • addedInput schema / properties / clipIds / description
        Added value: +"Clip ids of that project (get_project), 1-200"
      • addedInput schema / properties / projectId / description
        Added value: +"Project id (from list_projects, search_clips or create_project)"
    • Changedrestore_bumper1 field changed
      • addedInput schema / properties / assetId / description
        Added value: +"Library asset id (from list_bumpers → removed)"
    • Addedrestore_clip
    • Addedrestore_project
    • Changedretry_project1 field changed
      • addedInput schema / properties / id / description
        Added value: +"Project id (from list_projects, search_clips or create_project)"
    • Changedsearch_clips2 fields changed
      • addedInput schema / properties / limit / description
        Added value: +"Most clips to return (1-50, default 15)"
      • addedInput schema / properties / q / description
        Added value: +"Words to find, e.g. \"budget tips\""
    • Changedset_clip_bumpers5 fields changed
      • addedInput schema / properties / clipIds / description
        Added value: +"Clip ids of that project (get_project), 1-200 per call"
      • addedInput schema / properties / introSec / description
        Added value: +"Seconds an intro image shows on these clips (1-10)"
      • addedInput schema / properties / outroSec / description
        Added value: +"Seconds an outro image shows on these clips (1-10)"
      • addedInput schema / properties / projectId / description
        Added value: +"Project the clips belong to (list_projects)"
      • changedInput schema / properties / rerender / description
        Previous value: -"true = re-render only these clips now"New value: +"true = re-render only these clips now; false = apply on their next render"
    • Changedset_default_bumper2 fields changed
      • addedInput schema / properties / assetId / description
        Added value: +"Library asset id (list_bumpers), or null for no default"
      • addedInput schema / properties / slot / description
        Added value: +"Which default to set"
    • Changedset_project_bumpers3 fields changed
      • changedInput schema / properties / introSec / description
        Previous value: -"Seconds to show an intro image (1-10); videos keep their own length"New value: +"Seconds an intro image shows in this project (1-10); videos keep their own length. Saved with the project"
      • addedInput schema / properties / outroSec / description
        Added value: +"Seconds an outro image shows in this project (1-10). Saved with the project"
      • addedInput schema / properties / projectId / description
        Added value: +"Project id (list_projects or create_project)"
    • Changedupdate_clip14 fields changed
      • addedInput schema / properties / caption / description
        Added value: +"Post caption, not burned in (max 300 chars)"
      • changedInput schema / properties / emphasis / description
        Previous value: -"Words the captions emphasise"New value: +"Words the captions colour (max 30); replaces the list"
      • addedInput schema / properties / endSec / description
        Added value: +"New end, seconds from the start of the source; clip must stay ≥3 s"
      • addedInput schema / properties / hashtags / description
        Added value: +"Hashtags without # (max 20); replaces the list"
      • addedInput schema / properties / hook / description
        Added value: +"On-screen opening line (max 80 chars)"
      • addedInput schema / properties / id / description
        Added value: +"Clip id (from get_project or search_clips)"
      • changedInput schema / properties / layoutOverride / anyOf
        Previous value: -[
        -  {
        -    "enum": [
        -      "auto",
        -      "face",
        -      "split",
        -      "stack",
        -      "fit",
        -      "center",
        -      "none"
        -    ],
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]New value: +[
        +  {
        +    "description": "Framing: auto (detect per section), face (follow the speaker), split (two speakers stacked), stack (full frame over a face crop), fit (whole frame on blur), center (static crop), none (original aspect)",
        +    "enum": [
        +      "auto",
        +      "face",
        +      "split",
        +      "stack",
        +      "fit",
        +      "center",
        +      "none"
        +    ],
        +    "type": "string"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • changedInput schema / properties / layoutSections / anyOf
        Previous value: -[
        -  {
        -    "items": {
        -      "properties": {
        -        "end": {
        -          "minimum": 0,
        -          "type": "number"
        -        },
        -        "mode": {
        -          "enum": [
        -            "auto",
        -            "face",
        -            "split",
        -            "stack",
        -            "fit",
        -            "center"
        -          ],
        -          "type": "string"
        -        },
        -        "offsetX": {
        -          "maximum": 0.5,
        -          "minimum": -0.5,
        -          "type": "number"
        -        },
        -        "start": {
        -          "minimum": 0,
        -          "type": "number"
        -        }
        -      },
        -      "required": [
        -        "start",
        -        "end",
        -        "mode"
        -      ],
        -      "type": "object"
        -    },
        -    "maxItems": 50,
        -    "type": "array"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]New value: +[
        +  {
        +    "items": {
        +      "properties": {
        +        "end": {
        +          "description": "Section end, seconds from the start of the source video",
        +          "minimum": 0,
        +          "type": "number"
        +        },
        +        "mode": {
        +          "description": "Framing for this section (same modes as the project's reframe, without none)",
        +          "enum": [
        +            "auto",
        +            "face",
        +            "split",
        +            "stack",
        +            "fit",
        +            "center"
        +          ],
        +          "type": "string"
        +        },
        +        "offsetX": {
        +          "description": "Shift the crop sideways by this fraction of the source width (-0.5 to 0.5)",
        +          "maximum": 0.5,
        +          "minimum": -0.5,
        +          "type": "number"
        +        },
        +        "start": {
        +          "description": "Section start, seconds from the start of the source video",
        +          "minimum": 0,
        +          "type": "number"
        +        }
        +      },
        +      "required": [
        +        "start",
        +        "end",
        +        "mode"
        +      ],
        +      "type": "object"
        +    },
        +    "maxItems": 50,
        +    "type": "array"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • addedInput schema / properties / layoutSections / description
        Added value: +"Framing per time range (max 50), source seconds; replaces all sections; null or [] = automatic"
      • addedInput schema / properties / rerender / description
        Added value: +"Re-render when a burned-in field changed (default true); false = keep the old video until rerender_clip"
      • addedInput schema / properties / startSec / description
        Added value: +"New start, seconds from the start of the source video"
      • changedInput schema / properties / style / anyOf
        Previous value: -[
        -  {
        -    "properties": {
        -      "captionSize": {
        -        "default": 1,
        -        "maximum": 2,
        -        "minimum": 0.5,
        -        "type": "number"
        -      },
        -      "captionX": {
        -        "default": 0.5,
        -        "maximum": 1,
        -        "minimum": 0,
        -        "type": "number"
        -      },
        -      "captionY": {
        -        "default": 0.72,
        -        "maximum": 1,
        -        "minimum": 0,
        -        "type": "number"
        -      },
        -      "captions": {
        -        "default": "karaoke",
        -        "enum": [
        -          "karaoke",
        -          "box",
        -          "plain",
        -          "none"
        -        ],
        -        "type": "string"
        -      },
        -      "emphasis": {
        -        "default": true,
        -        "type": "boolean"
        -      },
        -      "emphasisColor": {
        -        "default": "#39FF14",
        -        "pattern": "^#[0-9a-fA-F]{6}$",
        -        "type": "string"
        -      },
        -      "highlightColor": {
        -        "default": "#FFD700",
        -        "pattern": "^#[0-9a-fA-F]{6}$",
        -        "type": "string"
        -      },
        -      "hookSeconds": {
        -        "default": 3.5,
        -        "maximum": 15,
        -        "minimum": 0,
        -        "type": "number"
        -      },
        -      "hookX": {
        -        "default": 0.5,
        -        "maximum": 1,
        -        "minimum": 0,
        -        "type": "number"
        -      },
        -      "hookY": {
        -        "default": 0.16,
        -        "maximum": 1,
        -        "minimum": 0,
        -        "type": "number"
        -      },
        -      "showHook": {
        -        "default": true,
        -        "type": "boolean"
        -      },
        -      "uppercase": {
        -        "default": false,
        -        "type": "boolean"
        -      },
        -      "wordsPerLine": {
        -        "default": 4,
        -        "maximum": 8,
        -        "minimum": 1,
        -        "type": "integer"
        -      }
        -    },
        -    "type": "object"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]New value: +[
        +  {
        +    "properties": {
        +      "captionSize": {
        +        "description": "Caption size multiplier (0.5-2, default 1)",
        +        "maximum": 2,
        +        "minimum": 0.5,
        +        "type": "number"
        +      },
        +      "captionX": {
        +        "description": "Caption centre, horizontal, as a fraction of the 9:16 frame (0-1)",
        +        "maximum": 1,
        +        "minimum": 0,
        +        "type": "number"
        +      },
        +      "captionY": {
        +        "description": "Caption centre, vertical (default 0.72), as a fraction of the 9:16 frame (0-1)",
        +        "maximum": 1,
        +        "minimum": 0,
        +        "type": "number"
        +      },
        +      "captions": {
        +        "description": "Caption look: karaoke (active word highlighted), box, plain, or none (no captions)",
        +        "enum": [
        +          "karaoke",
        +          "box",
        +          "plain",
        +          "none"
        +        ],
        +        "type": "string"
        +      },
        +      "emphasis": {
        +        "description": "Colour the clip's key words (default true)",
        +        "type": "boolean"
        +      },
        +      "emphasisColor": {
        +        "description": "Colour of emphasised words, #RRGGBB (default #39FF14)",
        +        "pattern": "^#[0-9a-fA-F]{6}$",
        +        "type": "string"
        +      },
        +      "highlightColor": {
        +        "description": "Karaoke active-word colour, #RRGGBB (default #FFD700)",
        +        "pattern": "^#[0-9a-fA-F]{6}$",
        +        "type": "string"
        +      },
        +      "hookSeconds": {
        +        "description": "How long the hook shows, seconds (0-15, default 3.5)",
        +        "maximum": 15,
        +        "minimum": 0,
        +        "type": "number"
        +      },
        +      "hookX": {
        +        "description": "Hook centre, horizontal, as a fraction of the 9:16 frame (0-1)",
        +        "maximum": 1,
        +        "minimum": 0,
        +        "type": "number"
        +      },
        +      "hookY": {
        +        "description": "Hook centre, vertical (default 0.16), as a fraction of the 9:16 frame (0-1)",
        +        "maximum": 1,
        +        "minimum": 0,
        +        "type": "number"
        +      },
        +      "showHook": {
        +        "description": "Show the hook text at the start of the clip (default true)",
        +        "type": "boolean"
        +      },
        +      "uppercase": {
        +        "description": "Captions in capitals",
        +        "type": "boolean"
        +      },
        +      "wordsPerLine": {
        +        "description": "Max caption words on screen at once (1-8, default 4)",
        +        "maximum": 8,
        +        "minimum": 1,
        +        "type": "integer"
        +      }
        +    },
        +    "type": "object"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • addedInput schema / properties / style / description
        Added value: +"Caption/hook style for this clip on top of the project's; replaces the clip's override; null = follow the project"
      • addedInput schema / properties / title / description
        Added value: +"Clip title (1-120 chars)"
    • Addedupdate_project
    • Changedwait_for_project2 fields changed
      • addedInput schema / properties / id / description
        Added value: +"Project id (from create_project or list_projects)"
      • addedInput schema / properties / timeoutSeconds / description
        Added value: +"Longest to wait, seconds (1-120, default 60)"
  9. 24 tool updatesv0.1.0
    • First observedadd_bumper
    • First observedbumper_usage
    • First observedcreate_clip
    • First observedcreate_project
    • First observeddelete_project
    • First observedexport_project
    • First observedget_clip
    • First observedget_project
    • First observedget_project_events
    • First observedget_transcript
    • First observedlist_bumpers
    • First observedlist_projects
    • First observedreanalyze_project
    • First observedremove_bumper
    • First observedrerender_clip
    • First observedrerender_clips
    • First observedrestore_bumper
    • First observedretry_project
    • First observedsearch_clips
    • First observedset_clip_bumpers
    • First observedset_default_bumper
    • First observedset_project_bumpers
    • First observedupdate_clip
    • First observedwait_for_project

TDQS

A3.8/5.0

Scored across 84 tools

Disambiguation4/5

Most tools have clearly distinct resource/action targets, and descriptions explicitly cross-reference alternatives such as delete_clip vs delete_clips and rerender_clip vs rerender_clips. Still, with 84 tools there are several near-neighbor pairs (update_clip/update_project_style, set_default_bumper/set_project_bumpers/set_clip_bumpers, delete_asset/purge_asset/restore_asset) that require careful reading to avoid misselection.

Naming Consistency4/5

The surface is predominantly snake_case verb_noun (list_projects, create_clip, delete_webhook, restore_asset), which is predictable. A few names deviate into noun phrases or non-verb forms (billing_portal_url, billing_checkout_url, asset_usage), but these are minor exceptions rather than a mixed convention.

Tool Count1/5

Eighty-four tools is far beyond the 3-15 well-scoped range and exceeds even the 50+ threshold for extreme mismatch. The domain is complex, but the surface is so large that discovery and correct selection become burdensome for an agent.

Completeness4/5

Coverage is unusually comprehensive: projects, clips, transcripts, assets, templates, webhooks, billing, admin, decisions, restore/purge lifecycle, and exports are all represented. The main gap is the lack of a first-class file-upload tool, since create_project/add_asset rely on an external POST /api/upload flow or an already-created uploadId.

Maintenance

ActivityMaintained
ResponsivenessOutdated data

Related MCP Connectors

Related MCP Servers