Skip to main content
Glama
bartivs

yt-media-info-mcp

by bartivs
README.md
# yt-media-info MCP

[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
[![MCP](https://img.shields.io/badge/Model_Context_Protocol-Server-blueviolet)](https://modelcontextprotocol.io)
[![yt-dlp](https://img.shields.io/badge/Powered_by-yt--dlp-red)](https://github.com/yt-dlp/yt-dlp)
[![Docker](https://img.shields.io/badge/Docker-Ready-2496ed?logo=docker&logoColor=white)](#docker-compose)
[![Node.js](https://img.shields.io/badge/Node.js-18+-339933?logo=node.js&logoColor=white)](#prerequisites)

> Extract rich metadata, transcripts, and search from any yt-dlp-supported media URL — for Claude, Anthropic, and any MCP-compatible AI assistant.

**yt-media-info MCP** is a [Model Context Protocol](https://modelcontextprotocol.io) (MCP) server that lets AI assistants extract structured metadata from media URLs across **1800+ sites** using [yt-dlp](https://github.com/yt-dlp/yt-dlp) — YouTube, Vimeo, Twitch, podcasts, and more. Given a URL (video, playlist, channel, podcast), it returns title, description, duration, chapters, subtitles/captions, formats, and statistics that models can reason over.

Built to sit alongside web-search tools as a **media-enrichment step** in an information-gathering pipeline. Works with Claude Desktop, Claude Code, LiteLLM, and any MCP client over stdio or SSE.

## Works with

Compatible with any client that speaks the Model Context Protocol:

- **[Claude Desktop](https://claude.ai/download)** — via stdio transport
- **[Claude Code](https://docs.anthropic.com/en/docs/claude-code)** — via SSE transport
- **[LiteLLM](https://github.com/BerriAI/litellm)** — as an `mcp` model in the gateway config
- **Open WebUI** and any MCP-aware agent framework
- **Custom apps** — via the MCP SDK (SSE) or the plain JSON `POST /api` shortcut

## Table of Contents

- [Features](#features)
- [Use Cases](#use-cases)
- [Prerequisites](#prerequisites)
- [Installation](#installation)
- [Configuration](#configuration)
- [Usage with Claude Desktop, Claude Code, LiteLLM, and the Direct API](#usage-with-claude-desktop-claude-code-litellm-and-the-direct-api)
- [Docker Compose](#docker-compose)
- [Available MCP Tools](#available-mcp-tools)
- [Available Prompts](#available-prompts)
- [Output Conventions: snake_case fields and ISO 8601 dates](#output-conventions-snake-case-fields-and-iso-8601-dates)
- [Cookie Management and Authentication](#cookie-management-and-authentication)
- [Scope: metadata and transcripts only, no downloads](#scope-metadata-and-transcripts-only-no-downloads)
- [Development](#development)
- [License: MIT](#license-mit)

## Features

- **Extract rich metadata** from any yt-dlp-supported URL (YouTube, Vimeo, Twitch, and ~1800 more sites)
- **Fetch transcripts** with timestamps or as full text
- **Search for media** across supported platforms (supplementary discovery)
- **Curated + raw output**: focused summary at the top level, full yt-dlp info dict nested under `raw`
- **Snake_case fields, ISO 8601 dates** — matches yt-dlp's native format
- **Optional two-layer auth**: yt-dlp site credentials + bearer API key for your own endpoints
- **Multiple transport options**: stdio for Claude Desktop, SSE for web clients
- **Direct API endpoint** (`POST /api`) for quick testing without MCP protocol
- **Persistent Python backend**: no cold-start per call (imports yt-dlp once at startup)

## Use Cases

- **RAG over video** — pull a video's transcript and metadata into a retrieval pipeline so an LLM can answer questions about the content without watching it.
- **Summarize lectures, talks, and podcasts** — feed the transcript to a model for key points, notable quotes, and takeaways (see the `summarize_transcript` prompt).
- **Podcast & lecture indexing** — extract titles, descriptions, chapters, and durations to build searchable catalogs of audio/video content.
- **Accessibility via captions** — retrieve subtitles (manual or auto-generated) in any available language for transcription and translation workflows.
- **Channel & playlist research** — expand a playlist or channel into structured per-video metadata for analysis, deduplication, or ranking.
- **Media enrichment in search pipelines** — pair with a web-search tool: discover candidate URLs, then enrich each one with full metadata and transcripts before summarization.
- **Content discovery** — use `search_media` to find videos on YouTube or Google Video by query, then drill into the ones that matter.

## Prerequisites

- Node.js 18+
- Python 3.12+ (for standalone development)
- Docker + Docker Compose (for recommended deployment)

## Installation

```bash
# Clone the repository
cd yt-media-info-mcp

# Install Node dependencies
npm install

# Build the Python service Docker image
docker compose build yt-dlp-service
```

### Standalone Python service (without Docker)

If you want to run the Python service directly:

```bash
cd service
pip install -r requirements.txt
uvicorn main:app --host 0.0.0.0 --port 8000
```

Then in another terminal:
```bash
YT_MEDIA_INFO_SERVICE_URL=http://localhost:8000 npm start
```

## Configuration

### Environment Variables

| Variable | Description | Default |
|----------|-------------|---------|
| `ENABLE_SSE` | Use SSE transport (vs stdio) | `0` |
| `YT_MEDIA_INFO_PORT` | HTTP server port (SSE mode) | `9423` |
| `YT_MEDIA_INFO_HOST` | HTTP server host (SSE mode) | `0.0.0.0` |
| `YT_MEDIA_INFO_SERVICE_URL` | URL of the Python yt-dlp service | `http://yt-media-info-service:8000` |
| `YT_MEDIA_INFO_API_KEY` | Optional bearer API key for HTTP endpoints | (empty = no auth) |
| `YT_MEDIA_INFO_USERNAME` | Default username for yt-dlp site auth | (empty) |
| `YT_MEDIA_INFO_PASSWORD` | Default password for yt-dlp site auth | (empty) |
| `LOG_LEVEL` | Winston log level (error, warn, info, debug) | `info` |

Copy `.env.example` to `.env` and customize. `.env` is gitignored — use `.env.local` for per-machine secrets not tracked by git.

## Usage with Claude Desktop, Claude Code, LiteLLM, and the Direct API

### Claude Desktop (stdio)

```json
{
  "mcpServers": {
    "yt-dlp": {
      "command": "node",
      "args": ["/path/to/yt-media-info-mcp/src/index.js"],
      "env": {
        "ENABLE_SSE": "0"
      }
    }
  }
}
```

### Claude Code (SSE)

```json
{
  "mcpServers": {
    "yt-dlp": {
      "type": "sse",
      "url": "http://localhost:9423/sse"
    }
  }
}
```

### LiteLLM

```yaml
# config.yaml
model_list:
  - model_name: yt-dlp
    litellm_params:
      model: mcp
      mcp_servers:
        yt-dlp:
          transport: sse
          url: http://host.docker.internal:9423/sse
```

### Direct API

The `POST /api` endpoint bypasses the MCP protocol and returns results directly:

```bash
# Extract info
curl -X POST http://localhost:9423/api \
  -H "Content-Type: application/json" \
  -d '{
    "tool": "extract_info",
    "args": {
      "url": "https://www.youtube.com/watch?v=YE7VzlLtp-4"
    }
  }'

# Get transcript
curl -X POST http://localhost:9423/api \
  -H "Content-Type: application/json" \
  -d '{
    "tool": "get_transcript",
    "args": {
      "url": "https://www.youtube.com/watch?v=YE7VzlLtp-4",
      "language": "en"
    }
  }'

# Search media
curl -X POST http://localhost:9423/api \
  -H "Content-Type: application/json" \
  -d '{
    "tool": "search_media",
    "args": {
      "query": "python tutorial",
      "limit": 5
    }
  }'
```

### Web clients (MCP SSE)

The server exposes standard MCP SSE endpoints:

| Endpoint | Purpose |
|----------|---------|
| `GET /sse` | SSE connection stream (MCP transport) |
| `POST /messages` | Send MCP JSON-RPC messages to the server |
| `POST /api` | Direct JSON API (bypasses MCP) |
| `GET /health` | Health check |

```javascript
// Connect via MCP SDK
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';

const transport = new SSEClientTransport(new URL('http://localhost:9423/sse'));
const client = new Client({ name: 'web-app', version: '1.0' });
await client.connect(transport);

const result = await client.request(
  { method: 'tools/call', params: { name: 'extract_info', arguments: { url: 'https://www.youtube.com/watch?v=YE7VzlLtp-4' } } },
  resultSchema
);
```

## Docker Compose

```bash
docker compose up -d           # start both services
docker compose logs -f         # tail logs
docker compose down            # stop
docker compose build           # rebuild after changes
```

The `yt-dlp-service` container is persistent and stays warm. The `yt-media-info-mcp` container waits for the health check on the Python service before accepting connections.

## Available MCP Tools

### extract_info

Extracts rich metadata from a media URL.

**Parameters:**

| Parameter | Type | Description | Default |
|-----------|------|-------------|---------|
| `url` | string | Media URL to extract information from | (required) |
| `include_raw` | boolean | Include the full yt-dlp sanitized info_dict under `raw` | `true` |
| `username` | string? | Username for site authentication | `null` |
| `password` | string? | Password for site authentication | `null` |

**Output:** Curated metadata (title, description, duration, uploader, statistics, chapters, thumbnails, formats summary, subtitles available, playlist info) + optional raw info dict.

### get_transcript

Fetches subtitles or transcript text for a media URL.

**Parameters:**

| Parameter | Type | Description | Default |
|-----------|------|-------------|---------|
| `url` | string | Media URL to fetch transcript from | (required) |
| `language` | string | Preferred subtitle language code | `"en"` |
| `timestamps` | boolean | Include timestamp segments in response | `true` |
| `username` | string? | Username for site authentication | `null` |
| `password` | string? | Password for site authentication | `null` |

**Output:** Language, duration, subtitle segments (with timestamps if requested), and concatenated full_text.

### search_media

Supplementary discovery tool. Searches for media using yt-dlp's search prefixes (e.g. `ytsearch:`). This is a companion to general-purpose web search — it finds candidate URLs for further enrichment.

**Parameters:**

| Parameter | Type | Description | Default |
|-----------|------|-------------|---------|
| `query` | string | Search query | (required) |
| `limit` | integer | Maximum number of results (max 50) | `10` |
| `platform` | string | Platform to search. Supported: `youtube`, `google_videos` | `"youtube"` |

**Output:** Results array with url, title, duration_seconds, uploader, upload_date, thumbnail, view_count.

## Available Prompts

- **analyze_video**: Analyze a video/media item from its available metadata (title, description, duration, uploader, categories, optional transcript summary).
- **summarize_transcript**: Summarize a video transcript to extract key points, notable quotes, and practical takeaways.

## Output Conventions: snake_case fields and ISO 8601 dates

- **snake_case** field names (matches yt-dlp's native format)
- **ISO 8601** date strings (e.g. `"2024-01-15"` for upload_date, `"2024-01-15T14:30:00Z"` for timestamps)
- **Best-effort error handling**: complete failures return an error response; missing fields are `null`; playlist entries that fail are collected in a `failures` array

## Cookie Management and Authentication

When running in SSE mode, the server provides a web-based cookie upload form at `http://<host>:<port>/` (default `http://localhost:9423/`) for uploading Netscape-format cookie files.

### Web Upload Flow

1. **Export cookies** from your browser using yt-dlp:
   ```bash
   yt-dlp --cookies-from-browser chrome --cookies cookies.txt
   ```
   Or use a browser extension like [Get cookies.txt LOCALLY](https://chrome.google.com/webstore/detail/get-cookiestxt-locally/cclelndahbckbenkjhflpdbgdldlbecc).

2. **Open the form** at `http://localhost:9423/` in your browser.

3. **Upload** the `cookies.txt` file — the form validates the file format, writes it atomically to the shared Docker volume at `/data/cookies.txt`, and displays parsed cookie info (domains, count, earliest expiry).

4. **Delete** cookies via the form's delete button when needed.

### Endpoints

| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/` | HTML upload form |
| `POST` | `/upload-cookies` | Upload a cookies.txt file (multipart/form-data, field name `cookies`) |
| `POST` | `/delete-cookies` | Delete the cookie file |

All endpoints are protected by the same `YT_MEDIA_INFO_API_KEY` bearer auth as the other HTTP endpoints (when configured).

### Cookie File Format

The file must:
- Start with `# Netscape HTTP Cookie File`
- Be under 1 MB
- Use tab-separated Netscape cookie format

### Cookie-bot Sidecar

If you have the [cookie-bot sidecar](AGENTS.md#cookie-bot-optional-sidecar) running (opt-in via `docker compose --profile cookies up -d`), it will periodically refresh cookies from the shared volume. The web upload form is a convenient way to seed the initial cookie file — the cookie-bot then takes over automated refreshes.

> **Note:** The cookie-bot's automated refresh will overwrite a manually uploaded file. Use the web form for initial seeding, then let the bot handle refreshes.

## Scope: metadata and transcripts only, no downloads

**This server does NOT download media files.** It is a metadata enrichment and transcript extraction tool designed to work alongside other search and retrieval tools. No ffmpeg is required.

## Development

```bash
npm run dev      # nodemon auto-restart
npm run lint     # ESLint
npm run lint:fix # ESLint auto-fix
```

## License: MIT

This project is licensed under the [MIT License](LICENSE).

TDQS

A3.9/5.0

Scored across 3 tools

Disambiguation5/5

Each tool has a distinct purpose: metadata extraction, transcript retrieval, and media search. No overlap in functionality.

Naming Consistency5/5

All tool names follow a consistent verb_noun pattern (extract_info, get_transcript, search_media) using lowercase snake_case.

Tool Count5/5

Three tools is appropriate for a focused media info server, covering core operations without excess.

Completeness5/5

The tool set covers the main use cases for media information: metadata extraction, transcript fetching, and search. No obvious gaps for its stated purpose.

Maintenance

ActivityStale
ResponsivenessNo issues