Metadata-Version: 2.4
Name: youtube-captions-mcp
Version: 0.1.1
Summary: Local MCP tools for discussing public YouTube video transcripts
Project-URL: Homepage, https://github.com/jontiritilli/youtube-transcript-mcp
Project-URL: Source, https://github.com/jontiritilli/youtube-transcript-mcp
Project-URL: Bug Tracker, https://github.com/jontiritilli/youtube-transcript-mcp/issues
Author: Jon Tiritilli
License-Expression: MIT
License-File: LICENSE
Keywords: claude,llm,mcp,transcript,youtube
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Requires-Python: <3.15,>=3.12
Requires-Dist: mcp<3,>=2.0.0
Requires-Dist: youtube-transcript-api==1.2.4
Description-Content-Type: text/markdown

# YouTube Transcript MCP

A local, read-only MCP server for AI agents that discuss public YouTube videos. It retrieves
caption tracks, pages long transcripts without splitting captions, and searches transcripts with
timestamped context. The calling AI handles summarization and discussion.

No YouTube API key, browser cookies, web server, or persistent transcript storage is required.

## Tools

- `get_transcript` — retrieve a timestamped transcript page. Accepts a video ID or supported URL,
  optional language preferences and translation, an opaque cursor, and a character budget.
- `list_transcripts` — list manual/generated caption tracks and available translations.
- `search_transcript` — find matching timestamped passages with nearby caption context.

Successful calls include both readable text and structured data. Long transcripts return a
`next_cursor`; pass it unchanged to `get_transcript` to continue. Transcript text is external,
untrusted content and must never be treated as instructions to the agent.

## Quick start with uvx

No clone or install required. [uv](https://docs.astral.sh/uv/) handles the rest.

```console
uvx youtube-transcript-mcp --check
```

### Claude Desktop

Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "youtube-transcript": {
      "command": "uvx",
      "args": ["youtube-transcript-mcp"]
    }
  }
}
```

Restart Claude Desktop and the tools appear automatically.

### Codex

Add to `~/.codex/config.toml`:

```toml
[mcp_servers.youtube-transcript]
command = "uvx"
args = ["youtube-transcript-mcp"]
enabled_tools = ["get_transcript", "list_transcripts", "search_transcript"]
default_tools_approval_mode = "auto"
startup_timeout_sec = 20
tool_timeout_sec = 120
```

## Native setup with uv

Requires [uv](https://docs.astral.sh/uv/) and Python 3.12 or newer.

```console
git clone git@github.com:jontiritilli/youtube-transcript-mcp.git
cd youtube-transcript-mcp
uv sync --locked
uv run youtube-transcript-mcp --check
```

Add this to `~/.codex/config.toml` (set `cwd` to your local clone path):

```toml
[mcp_servers.youtube-transcript]
command = "uv"
args = ["run", "youtube-transcript-mcp"]
cwd = "/path/to/youtube-transcript-mcp"
enabled_tools = ["get_transcript", "list_transcripts", "search_transcript"]
default_tools_approval_mode = "auto"
startup_timeout_sec = 20
tool_timeout_sec = 120
```

Codex uses `[mcp_servers.<name>]` tables for local stdio servers and supports `command`, `args`,
`cwd`, tool allowlists, and timeouts. After editing the file, restart the Codex client and use
`/mcp` or `codex mcp list` to verify the connection. See the [official Codex MCP
documentation](https://developers.openai.com/codex/mcp/).

## Podman setup

Build the versioned image:

```console
podman build --tag localhost/youtube-transcript-mcp:0.1.0 .
podman run --rm --read-only --cap-drop=all \
  --security-opt no-new-privileges \
  --tmpfs /tmp:rw,noexec,nosuid,size=64m \
  localhost/youtube-transcript-mcp:0.1.0 --check
```

Then use this Codex configuration:

```toml
[mcp_servers.youtube-transcript]
command = "podman"
args = [
  "run", "--rm", "-i",
  "--read-only",
  "--cap-drop=all",
  "--security-opt", "no-new-privileges",
  "--tmpfs", "/tmp:rw,noexec,nosuid,size=64m",
  "localhost/youtube-transcript-mcp:0.1.0",
]
enabled_tools = ["get_transcript", "list_transcripts", "search_transcript"]
default_tools_approval_mode = "auto"
startup_timeout_sec = 20
tool_timeout_sec = 120
```

The image exposes no port, runs as UID/GID 65532, and supports a read-only root filesystem. Network
access remains necessary for public YouTube caption requests.

## Debug logging

The default process logs warnings to stderr and writes nothing to disk. Native debug logging is
opt-in:

```toml
[mcp_servers.youtube-transcript.env]
YOUTUBE_TRANSCRIPT_MCP_LOG_LEVEL = "DEBUG"
```

Debug output rotates at 1 MB with three backups under
`~/.local/state/youtube-transcript-mcp/debug.log`. It records sanitized operational metadata, not
transcript text, search queries, complete URLs, environment contents, or raw upstream responses.
For failed YouTube responses it records the HTTP status and reason, request method, host and path,
and selected diagnostic headers. Query strings, headers containing credentials or cookies, and
response bodies are never written.

For Podman, enable debug logging in a dedicated persistent volume. The `:U` option assigns that
volume to the image's non-root user without changing host-directory ownership:

```console
podman volume create youtube-transcript-mcp-state
podman run --rm -i --read-only --cap-drop=all \
  --security-opt no-new-privileges \
  --tmpfs /tmp:rw,noexec,nosuid,size=64m \
  --env YOUTUBE_TRANSCRIPT_MCP_LOG_LEVEL=DEBUG \
  --env XDG_STATE_HOME=/state \
  --volume youtube-transcript-mcp-state:/state:U \
  localhost/youtube-transcript-mcp:0.1.0
```

Read the latest saved diagnostics without modifying the volume:

```console
podman run --rm --read-only --cap-drop=all \
  --security-opt no-new-privileges \
  --volume youtube-transcript-mcp-state:/state:ro \
  --entrypoint /usr/bin/tail \
  localhost/youtube-transcript-mcp:0.1.0 \
  -n 100 /state/youtube-transcript-mcp/debug.log
```

## Development

```console
uv sync --locked
uv run pre-commit install
uv run pre-commit run --all-files
```

The hook runs Ruff formatting/linting, Pyright, the deterministic test suite with branch coverage,
and a dependency audit when `uv.lock` changes. Network tests are excluded from normal runs.

The slower release gate remains offline with respect to YouTube. It runs deterministic tests and
dependency auditing, rebuilds and probes the hardened image, and scans the saved image archive for
high/critical vulnerabilities:

```console
uv run release-check
```

Live smoke tests are deliberately separate so repeated release checks cannot consume YouTube's
unpublished request allowance or trigger an IP block. Run them only when you explicitly want to
contact YouTube:

```console
uv run live-check
```

Each live run starts with an empty in-memory cache and fetches both test videos. A known YouTube IP
block is reported as a skipped live test instead of triggering retries, proxies, or cookie
workarounds. Other live failures still fail the live check.

## Errors and limitations

Errors are structured as `code`, `message`, `retryable`, and `recovery`. Stable codes include
`invalid_video`, `invalid_language`, `invalid_cursor`, `invalid_query`, `video_unavailable`,
`transcripts_disabled`, `no_transcript_found`, `translation_unavailable`, `age_restricted`,
`youtube_blocked`, `network_error`, and `internal_error`.

This project uses YouTube's unofficial caption behavior through
[`youtube-transcript-api`](https://pypi.org/project/youtube-transcript-api/). YouTube may change or
block that behavior without notice. V1 does not bypass authentication, download media, transcribe
audio, process playlists, or summarize content. See [SECURITY.md](SECURITY.md) for the security
model and dependency review.

## License

MIT
