Skip to main content
Glama
iceener

YouTube MCP Server

by iceener
README.md
# YouTube MCP Server

Fetch-native MCP server for YouTube search, channels, playlists, and video metadata. It runs on Bun and Cloudflare Workers.

Author: [overment](https://x.com/_overment)

> [!WARNING]
> Tool output comes from an external API and may be incomplete or misleading. Review results before relying on them.

> [!IMPORTANT]
> This branch targets the `2026-07-28` **release candidate** with `@modelcontextprotocol/server@2.0.0-beta.5`, `@modelcontextprotocol/client@2.0.0-beta.5`, and Zod 4. It does not claim conformance to a final dated specification or stable SDK release. Re-run the final-release gate when those artifacts are published.

## Runtime model

One fetch-native SDK handler is created per Bun process or Worker isolate. Its factory creates a fresh `McpServer` for every request. The SDK owns protocol parsing, modern negotiation, stateless legacy projection, cancellation, completion, cache metadata, and subscriptions.

`POST /mcp` accepts modern `2026-07-28` traffic and, by default, the SDK's stateless `2025-11-25` fallback. `GET /mcp` and `DELETE /mcp` return `405`; there are no endpoint sessions, replay buffers, or custom JSON-RPC dispatchers.

The HTTP shell validates Host and every present Origin, applies strict CORS, and rejects MCP bodies above `MCP_MAX_REQUEST_BYTES`.

## Install and run

Requirements: [Bun](https://bun.sh/) and a [YouTube Data API v3 key](https://console.cloud.google.com/apis/credentials).

```bash
bun install
cp .env.example .env
bun dev
```

The default MCP URL is `http://localhost:3000/mcp`.

For local workerd:

```bash
bun run dev:worker
# http://127.0.0.1:8787/mcp
```

Deploy only after replacing local URLs and allowlists in `wrangler.jsonc`:

```bash
bun run deploy
```

## YouTube credential resolution

The server resolves the provider credential in this order:

1. `X-YouTube-Api-Key` on the current MCP HTTP request;
2. a separately verified provider access token in `AuthInfo.extra.providerAccessToken`;
3. the configured `API_KEY` or static provider credential.

The credential is sent to YouTube only as the `key` query parameter. The inbound MCP bearer (`AuthInfo.token`) is never forwarded upstream.

Client-header example:

```json
{
  "mcpServers": {
    "youtube": {
      "command": "bunx",
      "args": [
        "mcp-remote",
        "http://localhost:3000/mcp",
        "--header",
        "X-YouTube-Api-Key: ${YOUTUBE_API_KEY}"
      ]
    }
  }
}
```

Server fallback:

```env
AUTH_STRATEGY=api_key
API_KEY=your-youtube-api-key
API_KEY_HEADER=x-youtube-api-key
```

`PROVIDER_API_URL` defaults to `https://www.googleapis.com/youtube/v3`. Override it only for a trusted provider-compatible endpoint or local tests.

## Exposed MCP contract

### Tools

All tools are read-only, non-destructive, idempotent, and open-world.

| Tool | Input | Structured output |
| --- | --- | --- |
| `find_channel` | `query`, optional `maxResults` (1–10, default 5) | query, count, channel summaries |
| `scan_channel` | `channelId`, optional `maxResults` (1–50, default 10) | channel ID, count, latest videos |
| `search_videos` | `query`, optional result count, order, and publication date | query, count, video summaries |
| `get_playlist` | `channelId` or `playlistId`, optional `maxResults` (default 20) | playlists or playlist videos |
| `video_details` | `videoId` | metadata, duration, statistics, tags, thumbnail |

Successful calls return both text `content` and schema-valid `structuredContent`. Expected provider/input failures return `isError: true`. Every provider fetch receives the MCP request cancellation signal.

### Prompts

- `greeting`: personalized greeting in `en`, `es`, `fr`, or `de`; language completion is supported.
- `analysis`: structured analysis at basic, intermediate, or advanced depth.
- `multimodal`: optional image, audio, and embedded-resource prompt content.

Prompt request arguments are MCP strings. Boolean-like prompt options use `"true"` and `"false"`.

### Resources

Static resources:

- `config://server` — redacted runtime configuration;
- `docs://overview` — inherited server documentation;
- `logo://server` — PNG blob;
- `logo://server/svg` — SVG text;
- `status://server` — dynamic process/isolate status.

Templated resource: `example://items/{collection}/{id}`. It supports listing, argument completion, public cache metadata, and reads. Modern clients can subscribe to list changes and `status://server` updates through `subscriptions/listen`.

## Authorization boundaries

There are two independent boundaries:

1. **MCP Resource Server authorization** controls access to `/mcp`. Set `AUTH_ENABLED=true`. `MCP_AUTH_MODE=jwt` verifies signature, issuer, audience, algorithm, expiry, client ID, and required scopes against external AS/JWKS metadata.
2. **Provider OAuth** acquires and refreshes a Google/provider credential. The inherited proxy is disabled by default (`PROVIDER_OAUTH_ENABLED=false`). Stored mode validates its opaque MCP token record and resolves the distinct provider token from protected storage.

The inherited provider OAuth code is not the default YouTube product path. It is dormant on Bun unless enabled. Workers retain the existing provider OAuth/discovery routes (`/authorize`, `/oauth/callback`, `/token`, `/revoke`, `/register`, and `/.well-known/*`) outside MCP dispatch for compatibility. These routes never parse MCP protocol messages.

Bun stores OAuth token mappings in `RS_TOKENS_FILE`; Workers preserve the `TOKENS` KV binding and key format. `RS_TOKENS_ENC_KEY` enables AES-256-GCM encryption. Provider refresh tokens stay in storage and never enter tool context.

## Worker configuration

`wrangler.jsonc` is the source of truth. It preserves the deployed Worker name and `TOKENS` namespace ID. Set secrets without placing values in config:

```bash
bunx wrangler secret put API_KEY
bunx wrangler secret put RS_TOKENS_ENC_KEY
bunx wrangler secret put PROVIDER_CLIENT_ID
bunx wrangler secret put PROVIDER_CLIENT_SECRET
```

After binding/config changes:

```bash
bun run types:worker
bun run types:worker:check
```

## Validation

```bash
bun run typecheck
bun run lint
bun run format:check
bun test
bun run build
bun run build:worker
bun run types:worker:check
bun run test:workerd
```

The protocol suite uses the official beta.5 client in modern and legacy modes. It covers exact discovery contracts, every advertised primitive, schema-valid mocked provider calls, cancellation, cache hints, completion, subscriptions, strict HTTP handling, JWT Resource Server behavior, concurrent principal isolation, preserved provider OAuth routes/storage, and MCP/provider token separation. `test:workerd` repeats modern and legacy discovery plus a mocked provider call against actual local workerd.

The pre-migration oracle and known baseline defects are recorded in [`docs/pre-migration-contract.md`](docs/pre-migration-contract.md). Candidate adaptations, OAuth reachability, storage compatibility, and removed infrastructure are recorded in [`docs/wave-5-migration.md`](docs/wave-5-migration.md).