Skip to main content
Glama
BismaNwaz

youtube-mcp-server

by BismaNwaz
README.md
# youtube-mcp-server

A remote MCP server that exposes YouTube Data API v3 as tools, over Streamable HTTP,
so it can be added to claude.ai as a custom connector.

No dependencies. No build step. `node src/server.js` is the whole thing.

## Tools

| Tool | What it does | Quota cost |
|---|---|---|
| `youtube_trending` | Most popular videos for a country, optionally filtered to one category | 1 unit |
| `youtube_search` | Keyword search for videos, channels or playlists, with stats attached | 1 search call + 1 unit |
| `youtube_channel_videos` | A channel's recent uploads plus its subscriber/view totals | 3 units |
| `youtube_video_details` | Full stats for up to 50 videos in one call | 1 unit |
| `youtube_video_comments` | Top-level comments with like and reply counts | 1 unit |

Transcripts are deliberately absent. `captions.download` requires OAuth *and* edit
permission on the video, so an API key can only fetch captions for videos you own.
The unofficial scraping libraries are widely reported to be blocked from cloud IP
ranges, which is exactly where this server runs.

## Quota

A project gets 10,000 units/day in the general bucket, and `search.list` sits in a
separate bucket capped at **100 calls per day**. That shaped the tool design:
`youtube_channel_videos` goes `channels.list` → `playlistItems.list` → `videos.list`
rather than `search.list?channelId=`, so browsing a channel costs 3 general units
instead of one of only a hundred daily searches.

## Run locally

```bash
cp .env.example .env        # add your YOUTUBE_API_KEY
export $(grep -v '^#' .env | xargs)
npm start
```

```bash
curl localhost:3000/health

curl -s localhost:3000/mcp \
  -H 'content-type: application/json' \
  -H 'accept: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | head -c 400
```

## Test

```bash
npm test
```

Runs the full MCP handshake, both transport modes, all five tools and the error
paths against a stubbed YouTube API. No API key and no network required.

## Deploy to Railway

1. Push this repo to GitHub.
2. Railway → New Project → Deploy from GitHub repo → pick it.
3. Variables → add `YOUTUBE_API_KEY`.
4. Settings → Networking → Generate Domain.
5. Check `https://<your-domain>/health` returns `"apiKeyConfigured": true`.

Railway sets `PORT` itself; the server binds `0.0.0.0` and reads it.

## Add to claude.ai

Customize → Connectors → Add custom connector → `https://<your-domain>/mcp`

No OAuth fields needed — the server is authless by default. To lock it down, set
`MCP_AUTH_TOKEN` and put `Bearer <token>` in the connector's Request headers under
`authorization`.

## Environment variables

| Variable | Required | Default | Notes |
|---|---|---|---|
| `YOUTUBE_API_KEY` | yes | — | Google Cloud Console, with YouTube Data API v3 enabled |
| `PORT` | no | 3000 | Railway sets this |
| `MCP_PATH` | no | `/mcp` | Path the MCP endpoint listens on |
| `MCP_AUTH_TOKEN` | no | — | If set, every request needs `Authorization: Bearer <value>` |
| `YOUTUBE_API_BASE` | no | Google's | Only used to point the test suite at a stub |

## Design notes

Stateless. Each POST is self-contained — no `Mcp-Session-Id`, no session map — so
a restart or a second replica never produces "No valid session ID provided".

Content negotiation matches the reference SDK: an SSE frame when the client sends
`Accept: text/event-stream`, a plain JSON body otherwise.

`GET` and `DELETE` on `/mcp` return 405, which is what the Streamable HTTP spec
expects from a server with no server-initiated stream and no session to close.

No `Origin` header validation and no DNS-rebinding protection. Those defences are
meant for MCP servers bound to localhost; left on for a public deployment they
reject Anthropic's own requests, which is a common cause of `initialize` timeouts.

Tool failures come back as `isError: true` content rather than JSON-RPC errors, so
Claude reads what went wrong and can adjust instead of the call dying at the
transport layer.

Results are trimmed at 120k characters, under claude.ai's ~150k tool-result cap.