Робимо MCP-сервер для субтитрів з YouTube

Офіційного способу завантажити субтитри з чужого відео на YouTube не існує. У Data API є ендпоінт captions.download, але він вимагає OAuth від власника каналу — марно, якщо ви просто хочете прочитати чиюсь лекцію.

Тому всі користуються тим самим приватним ендпоінтом, у який ходять застосунки YouTube: youtubei/v1/player. POST з ID відео та контекстом клієнта — у відповідь список доріжок субтитрів, кожна з підписаним URL. Ключ API не потрібен.

Дві речі з'їдять вам півдня.

Питайте як телефон, а не як браузер. Надішліть контекст клієнта WEB — і отримаєте цілком пристойний список доріжок, URL яких повертає HTTP 200 з порожнім тілом. YouTube тепер хоче там proof-of-origin токен. Контексти ANDROID та IOS досі віддають справжній текст. Той самий запит, одне поле різниці, і помилка виглядає як успіх.

Автоматичні субтитри парсяться інакше. Доріжки приходять як srv3 XML. У написаних людиною текст лежить прямо в <p>. В автоматичних кожне слово розбите на сегменти <s>, які треба зшивати назад, плюс порожні <p> суто для таймінгу. Не врахуєте — і транскрипт лекції буде переважно з порожніх рядків.

Сама MCP-частина — найлегша. Офіційний Go SDK бере на себе протокол; ви пишете один інструмент, що приймає URL і повертає текст. Одне попередження: не вмикайте stateless-режим SDK. Він відповідає на GET /mcp кодом 405, а конектор Claude відкриває саме цей стрім під час налаштування, вирішує, що у вашого сервера немає інструментів, і показує порожній список. Мої тести на curl робили тільки POST, тому я довго дивився на «this connector has no tools available».

А далі справжня стіна: YouTube блокує IP дата-центрів. Не все підряд — конкретні відео, з вашого конкретного сервера, днями. youtube-transcript-api описує ту саму поведінку і радить купити резидентні проксі.

Спершу я спробував бути розумним. Cookie jar, прогрітий на головній сторінці, visitor ID, справжня залогінена кука, навіть заголовок SAPISIDHASH, який надсилає власний веб-клієнт YouTube. Усе правильно. Усе марно. Той самий бінарник з іншого IP пройшов без питань.

Рішення виявилось нудним. ssh -N -D 1080 на іншу машину, яка в мене вже була, і YT_PROXY_URL=socks5://tunnel:1080. Go'шний net/http вміє SOCKS5 нативно, тож це коштувало нуль рядків коду. Сім відео, які цілий день відмовляли, запрацювали одразу.

День археології відбитків, переможений SSH-тунелем.