В рабочих чатах Telegram рядом живут сообщения о релизах, вопросы подрядчиков, согласования и короткие «посмотри, пожалуйста». Чтобы понять, что требует внимания, приходится открывать несколько чатов и восстанавливать контекст из отдельных реплик.
Я сделал TelegramMCP — открытый сервер, который подключает личный аккаунт Telegram к ИИ-приложению с поддержкой MCP. Ассистент получает инструменты для чтения папок и чатов, поиска бесед и отправки текстовых сообщений. Уже поверх этих инструментов его можно попросить собрать главное из выбранной переписки или подготовить ответ.
В статье расскажу, как устроено подключение через TDLib, зачем понадобилась поддержка папок, как запустить проект и какие проблемы ещё остаются.
Репозиторий: pborisovb-commits/TelegramMCP
Лицензия: MIT.
Публичный выпуск: v0.2.0, preview.
Какая задача за этим стоит
Мне хотелось обращаться к чатам телеги из локальной нейронки. Уже готовых решений или mcp серверов для телеги я не нашел, максимум - боты, которых нужно добавлять в чаты, но, личных чатов они не увидят и проблему не закроют. Поэтому я решил попробовать написать такой инструмент сам, как бы забавно не звучало, через ту же ИИшку. Пример запроса, который помогает решать этот проект:
Покажи чаты из папки «Работа». Прочитай последние сообщения в выбранных чатах и выдели вопросы, где требуется моё решение. Отдельно перечисли то, что уже закрыли.
Для этого ассистенту нужен доступ к данным: список чатов, их принадлежность к папкам и сами сообщения. Интерпретация — что считать проблемой, как сформулировать итог и какой ответ предложить — остаётся на стороне модели.
Что уже работает
Сервер подключается к пользовательскому аккаунту. Для авторизации нужны api_id и api_hash приложения Telegram, а затем вход по QR-коду. Сессия сохраняется локально.
Сейчас доступны семь MCP-инструментов:
|
Инструмент |
Назначение |
|---|---|
|
|
Проверить подключённый аккаунт |
|
|
Получить папки Telegram и их идентификаторы |
|
|
Получить чаты основного списка, архива или выбранной папки |
|
|
Найти беседы по названию |
|
|
Найти публичный чат по |
|
|
Прочитать последние сообщения выбранного чата |
|
|
Отправить текст от имени аккаунта |
Поиск чатов здесь не означает полнотекстовый поиск по всей переписке. А чтение последних сообщений не равно полной выгрузке истории — это важно для запросов вроде «разбери всё за неделю».
Как устроено подключение
Путь запроса выглядит так:
ИИ-приложение с поддержкой MCP
→ локальный stdio
→ TelegramMCP на Python
→ TDLib через JSON-интерфейс и ctypes
→ Telegram
TDLib — официальная библиотека Telegram для создания клиентов. Она берёт на себя сетевую часть и работу с локальными данными. TelegramMCP при этом остаётся независимым проектом: официальная здесь библиотека, а не мой сервер.
Код разделён на несколько небольших частей:
-
tdjson.pyзагружает библиотеку и предоставляет доступ к её JSON-интерфейсу; -
client.pyобрабатывает авторизацию, запросы, ответы и обновления; -
api.pyпревращает ответы TDLib в компактные Python-структуры; -
server.pyобъявляет инструменты MCP.
TDLib работает асинхронно: отправка запроса и получение ответа — отдельные операции. В клиенте есть фоновый поток чтения событий. Запрос получает уникальный @extra, а вызывающая сторона ждёт ответ с таким же идентификатором. Обновления, которые приходят независимо от запросов, обрабатываются отдельно.
На стороне модели нет необходимости видеть полный объект TDLib. Для сообщения сервер возвращает идентификаторы сообщения, чата и отправителя, дату, признак исходящего сообщения и текст. Для медиа пока используются короткие обозначения и подпись, если она есть. Голосовые сообщения не расшифровываются.
Для запуска я использую Docker. ИИ-приложение вызывает скрипт, тот запускает контейнер, а запросы и ответы идут через стандартные потоки ввода-вывода. Отдельного веб-интерфейса или HTTP-порта у сервера пока нет.
Почему понадобились именно папки Telegram
Один из первых практических запросов был простым: показать рабочие чаты так, как они уже разложены в Telegram.
Папка «Работа» — это не отдельная групповая беседа. В ней могут быть личные диалоги, группы, каналы и боты. Один чат может одновременно входить в несколько папок. Поэтому фильтровать беседы по словам в названии было бы недостаточно.
В реализации список папок приходит через обновление updateChatFolders. Клиент сохраняет его состояние, а list_chat_folders отдаёт названия, идентификаторы и дополнительные поля.
Когда ассистент выбирает папку, сервер использует список TDLib типа chatListFolder. Правила состава папки остаются на стороне Telegram.
Условная последовательность вызовов MCP выглядит так:
list_chat_folders()
list_chats(folder_id=7, limit=100, offset=0)
list_chats(folder_id=7, limit=100, offset=100)
Здесь 7 — пример. Реальный ID нужно брать из результата первого вызова.
Есть и менее заметный нюанс: TDLib может ещё не загрузить нужную часть списка. Сервер проверяет результат getChats и при необходимости вызывает loadChats. Для list_chats неполная загрузка не должна незаметно превращаться в ответ «в папке только два чата».
При этом пагинация не является снимком на фиксированный момент: новые сообщения меняют порядок бесед. Для задач, где важен точный состав выборки, это ограничение нужно учитывать.
Пример: собрать рабочую сводку
Я начинал с ограниченной выборки. Сначала попросил показать папку, затем выбрал конкретные чаты и только после этого дал запрос на анализ:
Прочитай последние 100 сообщений в каждом из этих трёх чатов. Составь сводку: что требует решения, что ждёт ответа и что закрыли. Для каждого пункта укажи чат и ID сообщения-основания. Не считай задачу выполненной, если в переписке есть только обещание её выполнить. Укажи фактический диапазон дат прочитанных сообщений. Ничего не отправляй.
Число сообщений — граница запроса, а не гарантия покрытия рабочего дня. В активном чате сто сообщений могут описывать последний час; в другом — несколько дней. Если модель не получила весь нужный период, корректный результат должен это отражать.
Ниже вымышленный IT-пример, иллюстрирующий формат сводки, а не результаты замера или реальные события компании:
Требует внимания
• Релиз ждёт доступов к серверу клиента.
• Часть заявок не передаётся в CRM; подтверждения восстановления нет.
Нужно решение
• Команда уточняет, что брать первым: исправление интеграции или новые функции.
Закрыто
• Тестовый стенд обновлён. Мониторинг настроен.
Следующим запросом можно подготовить черновик: «Предложи ответ команде с приоритетом восстановления CRM. Покажи текст, но не отправляй». Также настроил интеграцию с рабочим битриксом, и теперь после моего согласования отправляется не только ответ в чат телеги, но и задача в битру
Как запустить
Основной проверенный вариант — Windows x86-64 и Docker Desktop с Linux-контейнерами. Также потребуется ИИ-приложение, которое умеет запускать локальные MCP-серверы через stdio. Обычная страница веб-чата сама локальную программу не запустит.
Скачиваем проект:
git clone https://github.com/pborisovb-commits/TelegramMCP.git
cd TelegramMCP
На my.telegram.org в разделе API development tools получаем api_id и api_hash. Это реквизиты приложения Telegram, не токен BotFather.
Запускаем подготовку:
powershell -NoProfile -ExecutionPolicy Bypass -File .setup.ps1
Скрипт создаёт .env, каталог td_data и собирает Docker-образ. Существующие настройки и сессию он не перезаписывает. ExecutionPolicy Bypass относится только к этому запуску PowerShell.
В .env заполняем свои значения:
TG_API_ID=YOUR_API_ID
TG_API_HASH=YOUR_API_HASH
TG_DATABASE_DIR=/data
TG_API_ID должен быть числом. /data — путь внутри контейнера, связанный с локальным каталогом td_data.
Далее выполняем вход:
powershell -NoProfile -ExecutionPolicy Bypass -File .login-local.ps1
На телефоне открываем Telegram → Настройки → Устройства → Подключить устройство и сканируем QR-код из терминала. Если включена двухэтапная защита, потребуется пароль Telegram. Дожидаемся Logged in as … и нажимаем Enter. При следующих запусках используется сохранённая сессия.
В настройках MCP-клиента указываем локальную команду powershell.exe с аргументами:
-NoProfile -ExecutionPolicy Bypass -File C:ProjectsTelegramMCPstart-mcp.ps1
Путь нужно заменить на свой. Для клиентов с JSON-конфигурацией mcpServers:
{
"mcpServers": {
"telegram": {
"command": "powershell.exe",
"args": [
"-NoProfile",
"-ExecutionPolicy", "Bypass",
"-File", "C:\Projects\TelegramMCP\start-mcp.ps1"
]
}
}
}
Сам start-mcp.ps1 отдельно запускать не нужно: им управляет MCP-клиент. После подключения первый запрос — «Покажи мои папки Telegram». Подробности и диагностика есть в русском README.
Что означает «локальный» в этом проекте
На компьютере находятся сервер, ключи приложения и сохранённая Telegram-сессия. Но если подключить облачную модель, ИИ-клиент может передавать ей результаты чтения переписки. Локальный контейнер сам по себе не делает обработку полностью офлайн.
Есть ещё два важных момента.
Первый: инструмент send_message действительно отправляет сообщения от имени аккаунта. В сервере сейчас нет отдельного окна подтверждения и обязательного параметра согласования. Запрос «ничего не отправляй» полезен как инструкция модели, но технический контроль разрешений нужно настраивать в MCP-клиенте. Отдельный режим только для чтения есть в планах, в этой версии его ещё нет. Поэтому пока я явно указал в системном промте, чтобы модель не юзала эту фичу, без моего согласования))
Второй: папка Telegram удобна для выбора контекста, но не является ограничением прав. Сервер не запрещает обращение к другим доступным аккаунту чатам. Папка «Работа» не превращает подключение в доступ исключительно к рабочей переписке.
Содержимое сообщений тоже следует считать данными, а не командами для ассистента: текст из чата не должен автоматически разрешать отправку сообщений или другие действия.
Файлы .env и td_data нельзя прикладывать к Issues или публиковать вместе с логами. Для отзыва доступа нужно завершить сессию tg-mcp в настройках устройств Telegram. Политика безопасности есть в репозитории.
Что удалось проверить и что сломалось в реальном использовании
Для публичной версии проверены Docker-сборка, пять модульных тестов, импорт MCP-сервера и загрузка TDLib. GitHub Actions запускает тесты и проверку библиотеки без личной Telegram-сессии.
В повседневном использовании обнаружилась проблема с завершением процессов. Скрипт запуска каждый раз создаёт новый контейнер. В одном из случаев одновременно осталось пять экземпляров сервера, использующих одну папку сессии. При проверке они были запущены клиентом моей модели; автоматический рестарт Docker был отключён.
Наличие --rm этого не предотвращает: контейнер удаляется после завершения, но сначала процесс должен действительно закончить работу. Отключение интеграции в приложении тоже не гарантировало завершение уже работавших экземпляров в наблюдавшемся случае.
Исправление жизненного цикла — ближайший приоритет; в опубликованный preview оно не включено. Сейчас нужно использовать одно активное подключение на сессию и при необходимости останавливать относящиеся к нему контейнеры. Удалять td_data для устранения блокировки базы нельзя.
Ограничения и следующие шаги
В текущем виде проект подходит для экспериментов с рабочей перепиской в MCP-клиенте. До универсального Telegram-клиента ему далеко:
-
нет загрузки вложений и расшифровки голосовых;
-
нет полноценного поиска по тексту всей истории и архивного экспорта;
-
не поддерживаются редактирование папок и секретные чаты;
-
отправка сообщений ограничена текстом;
-
нет встроенного расписания, CRM-интеграции и автоматического назначения задач.
Готовая TDLib в репозитории — версия 1.8.68 для Linux x86-64, используемая внутри Docker. Воспроизводимая сборка с закреплённой ревизией — ещё одна задача в roadmap.
Ближайшие приоритеты — корректное завершение сервера, диагностика запуска и проверка переподключений. Затем — режим только для чтения и расширение проверенной совместимости. Автоматические сводки и CRM стоит добавлять отдельными сценариями с явно заданными правами и правилами запуска.
Код открыт под MIT. Если попробуете проект, интереснее всего узнать, удалось ли запустить его по инструкции, с какой моделькой он работает у вас и какой операции не хватает для повседневной работы. Для этого в репозитории есть формы Issues или можно писать комменты сюда.
Автор: pborisovb
