Skip to main content
Glama
README.md
# ytt-mcp

A deliberately tiny, single-user MCP server with one tool: `get_transcript`. It runs on a Cloudflare Worker and protects the MCP endpoint with a standards-compliant OAuth flow backed by one password.

It follows the transcript retrieval flow used by [jdepoix/youtube-transcript-api](https://github.com/jdepoix/youtube-transcript-api): load the watch page, call YouTube's InnerTube player API, prefer a manual caption track over an autogenerated one, then fetch and parse the timed-text XML. That project is the reference and is MIT licensed; this Worker is a small TypeScript implementation using only `fetch`.

## Run locally

Install dependencies and create your local secret file:

```sh
pn install
cp .dev.vars.example .dev.vars
```

Set a long password in `.dev.vars`, then start the Worker:

```sh
pn dev
```

The MCP endpoint is:

```text
http://localhost:8787/mcp
```

An unauthenticated request returns `401` and OAuth discovery information. The easiest way to test the complete browser login is with the MCP Inspector:

```sh
pn dlx @modelcontextprotocol/inspector
```

In the Inspector, select **Streamable HTTP**, enter `http://localhost:8787/mcp`, and connect. Its OAuth flow opens the ytt-mcp authorization page. Enter the password from `.dev.vars`.

## Deploy

Create the production KV namespace:

```sh
pn exec wrangler kv namespace create OAUTH_KV
```

Replace the placeholder `id` under `kv_namespaces` in `wrangler.jsonc` with the returned ID (this repo is already configured with its production namespace). Set the production password as an encrypted Worker secret:

```sh
pn exec wrangler secret put MCP_PASSWORD
```

Deploy manually:

```sh
pn deploy
```

Pushes to `master` also deploy through `.github/workflows/deploy.yml`. Add these secrets under **GitHub repository → Settings → Secrets and variables → Actions**:

- `CLOUDFLARE_API_TOKEN` — a Cloudflare API token with **Workers Scripts: Edit** and **Workers KV Storage: Edit** permissions.
- `CLOUDFLARE_ACCOUNT_ID` — your Cloudflare account ID.

The `MCP_PASSWORD` remains a Cloudflare Worker secret configured with Wrangler; do not add it to the GitHub workflow.

Configure your MCP client with:

```text
https://ytt-mcp.<your-subdomain>.workers.dev/mcp
```

Do not put the production password in `wrangler.jsonc`, `.dev.vars.example`, or Git. Consider adding a Cloudflare rate-limiting rule for `/authorize` to reduce password-guessing attempts.

## Tool

### `get_transcript`

```json
{
  "video_id": "dQw4w9WgXcQ",
  "languages": ["en"]
}
```

`languages` is optional and defaults to `["en"]`. Codes are checked in priority order. For each code, a manually created transcript is preferred over an autogenerated transcript, matching the reference project.

The result is JSON text containing video/language metadata and `snippets` with `text`, `start`, and `duration`.

## Authentication

[`@cloudflare/workers-oauth-provider`](https://github.com/cloudflare/workers-oauth-provider) handles OAuth discovery, dynamic client registration, PKCE, authorization codes, access tokens, refresh tokens, and bearer-token validation. OAuth data is stored in the `OAUTH_KV` namespace.

The custom `/authorize` page asks for one shared password and maps every successful login to the fixed user `owner`. There are no accounts or tenants. Existing access tokens remain valid after changing the password until they expire or are removed from KV.

YouTube can rate-limit or block cloud data-center IPs, and some videos require a PO token; those cases are returned as MCP tool errors.

## ChatGPT compatibility patch

A two-line `pnpm patch` (in `patches/`) widens the OAuth library's CIMD handling so connectors that declare `token_endpoint_auth_method: "private_key_jwt"` (e.g. ChatGPT) are accepted and treated as public clients:

- `CIMD_ALLOWED_AUTH_METHODS` gains `"private_key_jwt"` so the metadata document passes validation.
- The negotiated auth method maps `private_key_jwt` → `"none"`, so the token exchange works.

Security implications: the client's JWT assertion is not verified — the flow is protected instead by PKCE (S256, enforced for public clients), single-use authorization codes stored in KV, and `redirect_uri` validation against the client's metadata document. This is a deliberate trade-off for a single-password personal server. Re-apply with `pnpm install` after any upgrade of `@cloudflare/workers-oauth-provider`.