OAuth для собственных MCP-клиентов
Если ты пишешь свой MCP-клиент (CLI, SDK, плагин для IDE), эта страница описывает нашу реализацию OAuth: какие стандарты мы поддерживаем, endpoint'ы, правила matching'а redirect_uri и как чинить типичные ошибки. Пользователям Claude.ai и ChatGPT эта страница не нужна — смотри Quickstart.
Какие стандарты мы реализуем
mcp-telegram.com полностью соответствует OAuth-спекам, на которые опирается экосистема MCP — без проприетарных расширений и без захардкоженных в клиентах секретов.
- RFC 6749 — OAuth 2.0 Authorization Framework — базовый authorization code grant с refresh-токенами
- RFC 7591 — Dynamic Client Registration — любой клиент регистрируется сам через /oauth/register, без предварительной координации
- RFC 7636 — PKCE (Proof Key for Code Exchange) — для публичных клиентов обязателен S256 challenge
- RFC 8252 — OAuth 2.0 for Native Apps — loopback IP redirect URI с гибкостью эфемерного порта
- RFC 8414 — OAuth 2.0 Authorization Server Metadata — discovery-документ на /.well-known/oauth-authorization-server
- RFC 9728 — OAuth 2.0 Protected Resource Metadata — discovery границы MCP-ресурса
Endpoint'ы
Authorization code flow с PKCE
Стандартный поток RFC 6749 §4.1 + RFC 7636. Никаких сюрпризов — если твоя OAuth-библиотека умеет «authorization code + PKCE», всё заработает.
Зарегистрируй клиент
POST /oauth/register с { redirect_uris: ["http://127.0.0.1:<порт>/callback"], client_name: "имя-клиента" }. Сервер вернёт client_id и client_secret (секрет необязателен для публичных PKCE-клиентов, но получить его не помешает).
Сгенерируй PKCE-пару
Сделай случайный code_verifier (43–128 символов), затем посчитай code_challenge = BASE64URL(SHA256(code_verifier)). Сохрани verifier локально для шага 4.
Открой /oauth/authorize в браузере
Перенаправь юзера на /oauth/authorize?response_type=code&client_id=...&redirect_uri=...&state=...&code_challenge=...&code_challenge_method=S256. Сервер покажет QR-код; юзер сканит его в Telegram-приложении и аутентифицируется.
Получи authorization code
После сканирования QR сервер редиректит на твой redirect_uri с ?code=...&state=.... Проверь, что state совпадает с тем, что ты отправлял.
Обменяй code на токен
POST /oauth/token с grant_type=authorization_code, code, redirect_uri, code_verifier, client_id. Получишь access_token (TTL 10 лет — см. ниже Token lifetime) и refresh_token. Используй access_token как Authorization: Bearer ... в запросах к /mcp.
Правила matching'а redirect_uri
Мы строго следуем RFC 8252 §7.3 и §8.4. Алгоритм matching'а зависит от того, является ли зарегистрированный URI loopback IP-литералом.
Loopback (http://127.0.0.1 или http://[::1])
Если ты зарегистрировал loopback URI, то на /authorize redirect_uri должен точно совпадать по scheme, host, path и query — но порт может отличаться. Этого требует RFC 8252 §7.3, потому что нативные клиенты биндят OS-assigned эфемерный порт, который может меняться между запусками.
- Зарегистрировано http://127.0.0.1:36201/callback — совпадает с http://127.0.0.1:50000/callback ✅
- Зарегистрировано http://127.0.0.1:36201/callback — НЕ совпадает с http://127.0.0.1:50000/different-path: path должен быть точный
- Зарегистрировано http://[::1]:36201/cb — НЕ совпадает с http://127.0.0.1:36201/cb: IPv4 и IPv6 loopback — это разные идентификаторы
HTTPS (https://your-domain.example/...)
Точное побайтовое совпадение, включая path, query и порт (RFC 6749 §3.1.2). Без гибкости.
localhost (НЕ рекомендуется)
http://localhost мы рассматриваем как обычное hostname — нужен точный match, гибкости по порту нет. RFC 8252 §8.3 рекомендует использовать IP-литералы (127.0.0.1 / [::1]) вместо localhost, потому что разрешение DNS зависит от реализации.
PKCE обязателен
code_challenge_method=S256 нужен в каждом authorize-запросе. Plain method отклоняется. Если совсем пропустить code_challenge, обмен на /token упадёт на валидации code_verifier.
Время жизни токена
TTL access_token намеренно большой (10 лет), потому что не все MCP-клиенты надёжно сохраняют refresh_token. refresh_token всё равно выдаётся и ротируется на каждом refresh (RFC 6819 §5.2.2.3). Юзер может отозвать все токены в любой момент на /my/sessions или через logout в Telegram. Также можно явно сделать POST /oauth/revoke (RFC 7009), чтобы завершить сессию.
Типичные ошибки
HTTP 400 «Invalid redirect_uri» на /oauth/authorize
Параметр redirect_uri не совпадает ни с одним URI, зарегистрированным для этого client_id. Для loopback-клиентов: проверь, что scheme + host + path + query совпадают точно (гибкий только порт). Для HTTPS-клиентов: нужно полное побайтовое равенство — следи за trailing-слешами и URL-кодированием.
HTTP 400 «Unknown client»
client_id не существует в нашей БД. Либо пропущен шаг /oauth/register, либо ответ с регистрацией потерялся до сохранения client_id, либо клиент был отозван юзером через /my/clients.
Клиент постоянно показывает «Needs Auth»
Если твой клиент сохраняет access_token, но не refresh_token, ты увидишь это, когда access_token истечёт. Наши access-токены имеют TTL 10 лет специально, чтобы это смягчить, но если твой клиент сам пер-expir'ит токены раньше, нужно либо сохранять refresh_token, либо вызывать /oauth/revoke + повторный auth по требованию.
Проверенные клиенты
Подтверждённая совместимость. Если ты собрал что-то новое и оно работает — пришли PR, добавим в этот список.
- Claude.ai web — через встроенный MCP connector
- ChatGPT Apps — через встроенный MCP connector
- Hermes Agent — open-source CLI MCP-клиент (RFC 8252 loopback flow)
- Cursor MCP — IDE-плагин (RFC 8252 loopback flow)
- Любой клиент, который соответствует RFC 6749 + RFC 7591 + RFC 7636 + RFC 8252, должен работать без правок с нашей стороны.
Несколько Telegram-аккаунтов на одном OAuth-подключении
С версии v2.32.0 одно OAuth-подключение может держать несколько Telegram-идентичностей и переключаться между ними одним tool-вызовом — без цикла Disconnect/Connect, без регистрации нового клиента.
Посмотреть что привязано
Вызовите telegram-accounts-list. Вы увидите основной (OAuth-bound) аккаунт со значком ⭐ если он активный, плюс любые вторичные, которые вы добавили.
Привязать ещё один аккаунт
Вызовите telegram-accounts-add с опциональной меткой (например, "testing"). Tool вернёт одноразовую ссылку (TTL 10 минут) — откройте её на любом устройстве и сосканируйте QR Telegram-аккаунтом, который хотите добавить.
Переключить активный аккаунт
Вызовите telegram-accounts-switch с identifier=label, @username, account_id или 'primary'. Следующий telegram-* вызов сразу пойдёт от выбранного аккаунта. Без переподключения.
Отвязать вторичный аккаунт
Вызовите telegram-accounts-remove identifier=… для вторичного. Сам Telegram-аккаунт НЕ выходит из системы — отвязывается только привязка здесь. Основной аккаунт так удалить нельзя; используйте Disconnect в клиенте для полного выхода.
Каждое OAuth-подключение изолировано: аккаунты, которые вы добавили через Hermes, не видны Claude.ai или ChatGPT, даже если это та же Telegram-идентичность. Чтобы подключить разные аккаунты к разным MCP-клиентам, зарегистрируйте каждый клиент отдельно и используйте telegram-accounts-add внутри его сессии.