yt-subtitles-mcp
# yt-subtitles-mcp
**Give your AI assistant the source material to summarize and discuss YouTube videos.**
A Python MCP server built with FastMCP. One call retrieves video metadata, chapters
and a timestamped transcript page. Your assistant uses that evidence to produce
summaries, key takeaways, study notes, comparisons or answers with source references.
The server does not run a separate LLM, need an AI API key, or claim to watch video
frames. It works with public YouTube videos that expose supported captions.
## Quick start
```bash
docker compose up --build -d
```
Connect a local MCP client to **http://127.0.0.1:8000/mcp** (Streamable HTTP):
```json
{
"mcpServers": {
"yt-subtitles-mcp": {
"url": "http://127.0.0.1:8000/mcp"
}
}
}
```
Configuration placement varies by client. A cloud-hosted client cannot connect to
your computer's localhost. Health endpoint: `http://127.0.0.1:8000/health`.
```bash
docker compose ps
docker compose logs -f
docker compose down
```
## What to ask your assistant
- “Summarize this video and list its main arguments with timestamps.”
- “Turn this lecture into study notes. Separate facts from the speaker's opinions.”
- “What does the speaker say about this topic? Give me the supporting passage.”
- “Compare the arguments in these two videos and link to their sources.”
For multiple videos, the assistant calls the tools for each URL. There is no search,
playlist crawling, visual analysis, speech recognition or automatic translation service.
The assistant can translate or summarize the returned text itself.
## MCP tools
| Tool | Purpose |
| --- | --- |
| `get_video_context` | Start here: metadata, chapters and transcript evidence in one request |
| `get_video_transcript` | Continue reading a long transcript or request a specific language |
| `get_video_info` | Retrieve only video metadata when the transcript is unnecessary |
| `list_transcript_languages` | List exact available language codes and caption types |
All tools accept `url`. Context/transcript tools accept an optional `language` and
`limit` (default 12,000, maximum 20,000 characters). The transcript tool also accepts
`offset` (default 0). Language selection prefers the original audio language when
available, otherwise authored captions before automatic captions. For a selected
language, authored captions take precedence. Explicit languages never silently fall
back to another language.
`get_video_context` returns `transcript_status`:
- `complete`: the response contains the full available transcript.
- `partial`: read more using `get_video_transcript` with the returned `language` and
`next_offset`; do not describe a first-page summary as covering the entire video.
- `unavailable`: metadata is still available, but `transcript_error` explains why
transcript evidence is missing. The assistant should report this limitation.
Timestamps such as `[00:01:23]` are preserved in the transcript when supplied by
YouTube. Offsets count characters in this timestamped text. Each page refetches the
source, so captions edited during pagination can change the result. Chapters are
limited to 100 entries with an explicit `chapters_truncated` flag.
## Local installation
Requires Python 3.12–3.14, [uv](https://docs.astral.sh/uv/) and
[Deno](https://docs.deno.com/runtime/getting_started/installation/) for full YouTube
support. Docker includes the JavaScript runtime and bundled yt-dlp challenge solver.
Linux and macOS are supported; Windows users can use Docker or WSL.
```bash
uv sync --frozen
uv run --frozen yt-subtitles-mcp # stdio, started by the MCP client
uv run --frozen yt-subtitles-mcp --transport http # local HTTP
uv run --frozen python -m yt_subtitles_mcp --help
```
For stdio clients, replace the path with your checkout:
```json
{
"mcpServers": {
"yt-subtitles-mcp": {
"command": "uv",
"args": ["--directory", "/absolute/path/yt-subtitles-mcp", "run", "--frozen", "yt-subtitles-mcp"]
}
}
}
```
Docker stdio after building: `docker compose run --rm -T --no-deps yt-subtitles-mcp --transport stdio`.
The old root-level scripts and old subtitle tool names have been removed; update
existing client commands and tool references to the names above.
## Project layout
```text
src/yt_subtitles_mcp/
server.py # FastMCP instance, tool contracts and safe error mapping
youtube.py # YouTube extraction, caption parsing and context assembly
models.py # Typed responses and explicit evidence/coverage boundaries
cli.py # Transport arguments and process entrypoint
__main__.py # python -m yt_subtitles_mcp
```
The distribution, CLI and GitHub project use `yt-subtitles-mcp`; the Python package
uses `yt_subtitles_mcp`. `pyproject.toml` defines dependencies and tooling, and
`uv.lock` is the single dependency lockfile. Docker installs the same package used
in development. There are no repository/service interfaces, plugin frameworks,
database, job queue or persistent cache.
## Safety and limitations
Source text is explicitly marked as untrusted. The server supplies evidence rather
than generating user-role prompts from external text. The consuming agent must not
follow instructions inside titles, chapter names or captions, or treat them as
permission to access files, secrets or other tools. This is a trust boundary, not a
promise that an LLM can never be prompt-injected.
Only canonical HTTPS YouTube video URLs are accepted. Playlist-only URLs, custom
ports, user credentials and arbitrary sites are rejected. Extraction uses no shell,
no user configuration/plugins, no browser cookies and no inherited server secrets.
Caption downloads accept only YouTube's HTTPS timedtext endpoint and no redirects.
Network operations still trust YouTube, HTTPS/DNS and the pinned extractor.
Extraction is limited to 60 seconds and 8 MiB stdout/64 KiB stderr. Caption fetching
is limited to 25 seconds and 4 MiB. Up to two source operations run concurrently;
excess work is rejected. Timeout/cancellation terminates the process group. Docker
adds a non-root user, read-only filesystem, bounded tmpfs, dropped capabilities and
CPU/memory/PID limits. Dependencies and base images are pinned; nothing self-updates.
HTTP is unauthenticated and bound to localhost through Compose. Host/Origin checks
protect the MCP endpoint against browser-origin and DNS rebinding requests; they
are not authentication for local applications. Add authentication and TLS before
any public deployment.
YouTube may rate-limit, require sign-in or block the server IP. Health checks confirm
server responsiveness, not YouTube availability. Private/restricted videos are not
bypassed. Captions can be inaccurate and omit visual information.
## Development
```bash
uv sync --frozen
uv run --frozen ruff check .
uv run --frozen ruff format --check .
uv run --frozen pytest -q
uv build
docker compose config --quiet
docker compose build
```
Tests use local fixtures and mocked YouTube responses, plus real subprocess and MCP
stdio/HTTP integration checks. GitHub Actions checks formatting, tests, packaging
and Docker build. No YouTube credentials or external video access are needed for CI.
To change dependencies, edit `pyproject.toml`, run `uv lock`, then repeat the checks.
Update image tags/digests deliberately when security releases arrive. Do not commit
cookies, credentials, transcripts or `.env` files. Report sensitive vulnerabilities
privately through GitHub's vulnerability reporting feature if enabled; use issues
for non-sensitive reproducible bugs.
## Provenance
This project started from `setkyar/youtube-subtitles-mcp` and has been reorganized
around agent-driven video understanding. No upstream license file was present in
the imported checkout. This repository is intentionally published without a project license for now.
TDQS
Scored across 4 tools
Each tool has a reasonably distinct primary role: context bootstraps, info gives lightweight metadata, list_transcript_languages enumerates codes, and get_video_transcript paginates. However, get_video_info's metadata overlaps with get_video_context's returned metadata, and context also returns transcript, creating mild ambiguity about when to use info vs. the composite tool.
All names are snake_case with a verb-first pattern (get_/list_), which is predictable. There is a minor resource-naming inconsistency: three tools scope to 'video' while list_transcript_languages scopes to 'transcript', so the resource vocabulary isn't fully uniform.
Four tools is on the thin side but appropriate for a focused transcript/metadata server where each tool has a clear jobs-to-be-done. The only questionable inclusion is get_video_info, which largely duplicates a subset of get_video_context.
The surface covers metadata, chapters, language enumeration, transcript retrieval with pagination, and explicit handling of partial/unavailable coverage, which is solid lifecycle coverage for this domain. Gaps like in-transcript search or translation are either out of scope or explicitly excluded by the descriptions.