letopis-mcp
📜 Летопись (Letopis)
Движок архива и умного поиска по истории Telegram-чатов
Сырые сообщения в JSONL · полнотекстовый поиск с русской морфологией · качалка с менеджером
Идея
Летопись — это не бот и не сервис, а CLI-инструмент, заточенный под то, чтобы LLM-агент (в первую очередь Claude Code) мог читать историю ваших Telegram-чатов и отвечать по ней на вопросы, как по обычной базе знаний.
Архив хранится как обычные файлы — .jsonl, по одному на чат и месяц,
append-only. Поверх них строится индекс SQLite с полнотекстовым поиском (FTS5),
который понимает русские словоформы: запрос «хостинг» находит сообщения со
словом «хостингами». К поиску подключены расшифровки голосовых, имена файлов
и текст опросов.
$ ./tg search переезд хостинг --chat devops --from 2025-06Движок и данные разделены. Этот репозиторий содержит только код — сами архивы чатов,
config.toml,.envи сессия Telegram живут в отдельном приватном репозитории, который держит вас в контроле над тем, что публично, а что нет. Подробнее — в разделе «Устройство».
Related MCP server: telegram-user-mcp
✨ Возможности
🔎 Полнотекстовый поиск | SQLite FTS5 + |
📦 Архив как файлы |
|
⬇️ Качалка с менеджером |
|
🎙️ Транскрипция голосовых | локально (faster-whisper), через Telegram Premium или OpenAI Whisper API |
🌐 Веб-просмотрщик | чаты → топики-чипы, бесконечная прокрутка, фильтры, плеер голосовых, прыжки по реплаям |
⌨️ TUI-просмотрщик | тот же функционал в терминале ( |
👥 Несколько аккаунтов | разные чаты можно скачивать разными Telegram-аккаунтами |
🤖 Заточен под агентов | JSON-вывод, компактные короткие форматы, стабильный CLI-контракт |
🚀 Быстрый старт
git clone https://github.com/Toligrim/letopis.git
cd letopis
python3 -m venv .venv && .venv/bin/pip install -e .
.venv/bin/pip install faster-whisper # опционально: локальная транскрипция голосовыхЛетопись — это только движок. Чтобы подключить его к конкретному архиву, создайте
отдельный репозиторий с данными и положите туда обёртку ./tg:
#!/bin/sh
exec "$HOME/projects/letopis/.venv/bin/tg" "$@"Дальше всё запускается из корня репозитория с данными:
chmod +x tg
./tg login # авторизация Telegram-сессии (телефон / код / 2FA)
./tg download --chat mychat --media all
./tg index && ./tg meta
./tg search приветДвижок сам находит корень с данными: при запуске tg ищет от текущей директории
вверх папку, где рядом лежат config.toml и archive/ (либо это явно задаётся
переменной окружения TG_ROOT).
🤖 Read-only MCP для ChatGPT
Letopis может работать как read-only MCP retrieval gateway для ChatGPT:
сервер использует тот же data/index.db, что и обычный поиск, но отдаёт только
пять безопасных retrieval-инструментов — обзор архива, поиск, агрегаты, выборку
сообщений и локальный контекст. MCP-процесс не синхронизирует Telegram, не
скачивает файлы и не меняет индекс.
Установка и запуск
Установите MCP SDK и тестовые зависимости в окружение движка:
.venv/bin/pip install -e ".[mcp,test]"Запуск через entrypoint:
.venv/bin/letopis-mcpАльтернативная форма — .venv/bin/python -m tgarchive.mcp.server. По умолчанию
сервер слушает http://127.0.0.1:8765/mcp и принимает только loopback-адреса.
Для production задайте стабильный секрет курсоров и путь к индексу в окружении
процесса, например:
export LETOPIS_MCP_DB=/srv/letopis-data/data/index.db
export LETOPIS_MCP_CURSOR_SECRET='случайный-длинный-секрет'
.venv/bin/letopis-mcpПеременные окружения
Переменная | По умолчанию | Назначение |
| значение | Путь к SQLite-индексу; относительный путь считается от корня проекта. |
| нет; временный случайный секрет на процесс | HMAC-SHA256 для непрозрачных курсоров. Обязателен в production: без него курсоры не переживают рестарт процесса. |
|
| Loopback-адрес bind; приложение отклоняет нелокальные адреса. |
|
| TCP-порт Streamable HTTP endpoint. |
|
| Уровень структурированных логов ( |
|
| Максимум конкурентных операций с read-only БД. |
|
| Дедлайн SQLite-запроса и ожидания слота concurrency. |
|
| Максимум завершённых вызовов в глобальном rolling-окне. |
|
| Максимум отданных символов в том же окне. |
|
| Длина rolling-окна в секундах. |
Rate limit намеренно глобальный для одного процесса: в v1 нет OAuth и
идентифицированных principals, поэтому это не per-user ACL. MCP читает эти
переменные как конфигурацию процесса и не загружает .env автоматически.
Подключение к ChatGPT
Проверенный production-паттерн оставляет MCP-сервер на loopback и публикует наружу только HTTPS-эндпоинт через Cloudflare Tunnel:
ChatGPT → https://your-domain.example.com/mcp?k=<секрет>
→ Cloudflare WAF → Cloudflare Tunnel → 127.0.0.1:8765/mcpВ этом варианте не используется «OpenAI Secure MCP Tunnel»: публичной self-serve документации OpenAI по настройке серверной стороны этого механизма сейчас нет. Пункт «Туннель» в интерфейсе ChatGPT — отдельный нативный механизм; ниже описан рабочий вариант с обычным публичным HTTPS-туннелем.
⚙️ systemd-сервис
Запустите letopis-mcp как отдельный systemd unit под непривилегированным
пользователем. Файл unit можно разместить, например, в
/etc/systemd/system/letopis-mcp.service:
[Unit]
Description=Letopis read-only MCP server
After=network.target
[Service]
Type=simple
User=<непривилегированный-пользователь>
WorkingDirectory=<корень-репозитория-с-данными>
EnvironmentFile=/etc/default/letopis-mcp
ExecStart=<venv>/bin/letopis-mcp
Restart=always
RestartSec=5
NoNewPrivileges=true
ProtectSystem=full
[Install]
WantedBy=multi-user.targetФайл /etc/default/letopis-mcp должен находиться вне git-репозитория и
содержать как минимум конфигурацию SQLite и стабильный секрет курсоров:
LETOPIS_MCP_DB=<корень-репозитория-с-данными>/data/index.db
LETOPIS_MCP_CURSOR_SECRET=<стабильный-секрет>Замените плейсхолдеры на значения своей установки и закройте файл от лишних пользователей. После установки unit активируйте его так:
sudo systemctl daemon-reload
sudo systemctl enable --now letopis-mcp.serviceДополнительный ReadWritePaths не нужен: сервер сам открывает SQLite в режиме
mode=ro, поэтому ему нужны только права чтения на индекс и необходимые
SQLite sidecar-файлы.
🌐 Cloudflare Tunnel и HTTPS
Настройте cloudflared на проксирование публичного имени к loopback-порту
MCP-сервера. Существенная часть config.yml выглядит так:
ingress:
- hostname: your-domain.example.com
service: http://127.0.0.1:8765
originRequest:
httpHostHeader: "127.0.0.1:8765"
- service: http_status:404Важно про
Hostи DNS-rebinding-защиту. MCP SDK по умолчанию включает защиту от DNS rebinding, когда сервер привязан к loopback-адресу (127.0.0.1/localhost): заголовокHostпроверяется по списку127.0.0.1:*,localhost:*,[::1]:*. Cloudflare Tunnel по умолчанию передаёт на origin внешнийHost, напримерyour-domain.example.com, поэтому без переписывания сервер отвечает421 Invalid Host header. Эта проверка реализована вmcp/server/transport_security.pyMCP SDK.Параметр
originRequest.httpHostHeaderпереписывает заголовок только при передаче запроса к локальному origin и сохраняет защиту SDK включённой. Порт после адреса обязателен:"127.0.0.1"без:8765не пройдёт проверку, потому что шаблон127.0.0.1:*требует порт. Это правильное решение, ослаблять DNS-rebinding-защиту в приложении не нужно.
🛡️ Авторизация на границе через Cloudflare WAF
MCP v1 не реализует аутентификацию (см. раздел «Безопасность deployment»), поэтому публичный эндпоинт нужно защищать до того, как запрос попадёт в приложение. Cloudflare Access — один из вариантов, но включение Zero Trust-плана требует привязки карты даже на бесплатном тарифе. Проверенная бескарточная альтернатива — WAF Custom Rule, доступная на бесплатном плане (до пяти правил).
В Cloudflare откройте Security → Security rules → Custom rules и создайте правило:
условие:
Hostname equals your-domain.example.comANDURI Query String does not contain "k=<секрет>";действие:
Block.
В URL ChatGPT передайте тот же WAF-секрет query-параметром:
https://your-domain.example.com/mcp?k=<секрет>Секрет WAF — отдельное значение; не переиспользуйте для него
LETOPIS_MCP_CURSOR_SECRET. При такой проверке запрос без k получает 403
ещё на границе Cloudflare, и MCP-сервер его не видит. Запрос с параметром
доходит до сервера и обрабатывается нормально.
Это грубее полноценной per-request-аутентификации: статичный секрет живёт в URL и может попасть в историю или логи. Но для сценария «URL случайно узнали или endpoint просканировали» такой edge-gate закрывает доступ без изменений в сервере и без дополнительных затрат. При утечке секрет следует заменить и обновить WAF-правило и URL коннектора.
🔌 Подключение в ChatGPT
В ChatGPT создайте custom connector / плагин в режиме разработчика:
Откройте Developer mode → Settings → Plugins →
+.Для
Connectionвыберите «URL-адрес сервера», а не «Туннель».Укажите URL
https://your-domain.example.com/mcp?k=<секрет>.Для
Authenticationвыберите «Без авторизации». Это означает, что MCP-протокол не использует OAuth: защита уже выполнена правилом Cloudflare WAF. В форме также могут быть варианты OAuth и «Смешанные».Создайте подключение.
В этой схеме ChatGPT успешно обнаруживает все пять retrieval-инструментов.
Безопасность deployment
MCP-процессу нужны только data/index.db и необходимые SQLite sidecar-файлы
data/index.db-wal / data/index.db-shm. Ему не должны быть доступны
.env, telegram.session*, archive/, media или manifest. Запускайте сервер
под отдельным Unix-пользователем с минимальными правами, а синхронизацию и
индексацию выполняйте отдельным процессом с нужными правами записи.
MCP v1 не реализует аутентификацию: endpoint сам не проверяет OAuth или другой identity пользователя. Поэтому при публичной публикации нужна внешняя защита на границе, например описанная выше WAF Custom Rule.
🗂 Устройство
репозиторий с данными/
├── config.toml # настройки: аккаунты, транскрипция, веб-порт
├── .env # api_id / api_hash Telegram
├── telegram.session # сессия аккаунта (и доп. сессии из [accounts])
├── tg -> letopis/.venv/bin/tg # обёртка-энтрипоинт
├── data/
│ └── index.db # SQLite + FTS5 — производный, пересобирается
└── archive/
├── manifest.json # какие чаты отслеживаем, каким аккаунтом, какие медиа качаем
└── <chat_id>/
├── 2025-06.jsonl # сырые сообщения этого месяца — источник истины
├── 2025-07.jsonl
├── transcripts.jsonl # расшифровки голосовых/кружков
├── media_index.jsonl # реестр скачанных файлов
└── media/ # сами файлыJSONL — источник истины. Файлы по месяцам,
syncтолько дописывает новые сообщения, старые никогда не трогает.index.db— производный слой. Можно удалить и пересобрать (./tg index --rebuild) в любой момент без потери данных.manifest.json— менеджер. Переживает пересборку индекса; хранит, какие чаты/топики отслеживаются и какие типы медиа для них качать.
Такое разделение (движок в git, открыто · данные — отдельно, приватно) позволяет свободно развивать и делиться кодом, не рискуя утечкой переписки.
🧭 Команды
Поиск — основное для агента
Команда | Что делает |
| Полнотекстовый поиск. Флаги: |
| Хронологический кусок переписки целиком |
| Сообщения вокруг конкретного ( |
| Обзор чатов в архиве |
| Топики форум-чата |
| Состояние архива и индекса |
Просмотрщики — ручной режим
Команда | Что делает |
| Локальный веб-интерфейс: чаты → топики-чипы, бесконечная прокрутка, поиск с фильтрами, прыжок к дате, фильтр по автору (клик по нику), фото/видео инлайн, плеер голосовых с расшифровкой, реплаи с прыжком по треду, ссылки |
| То же самое в терминале: |
Качалка и менеджер
Команда | Что делает |
| Все чаты аккаунта (✓ — уже в архиве) |
| Скачать чат/топики и поставить на отслеживание. Флаги: |
| Докачать новые сообщения всех отслеживаемых чатов |
| Докачать файлы для уже скачанных сообщений |
| Расшифровать голосовые в текст, чтобы он попал в поиск |
| Снять чат с отслеживания (файлы остаются на диске) |
| Обновить названия чатов / доиндексировать архив |
| Авторизовать Telegram-сессию (телефон / код / 2FA) |
Чат можно указывать как id, алиас из config.toml, часть названия, @username
или ссылку t.me/.... Новые сообщения скачиваются со всеми реакциями, опросами
и сервисными событиями.
🎙 Транскрипция голосовых
Провайдер задаётся в config.toml [transcription]:
Провайдер | Стоимость | Требования |
| бесплатно, локально |
|
| бесплатно | Telegram Premium на аккаунте |
| платно (Whisper API) |
|
👥 Несколько аккаунтов
[accounts]
default = "telegram.session"
backup = "sessions/backup.session"tg login --account backup авторизует новую сессию. У download / sync /
dialogs / meta есть флаг --account. Каждый чат в манифесте закреплён за
своим аккаунтом.
🗺 Статус
Этап | Статус | Что входит |
A | ✅ готово | Индекс, поиск, CLI, интеграция с Claude Code |
B | ✅ готово | Качалка ( |
C | ✅ готово | Просмотрщики: |
Дальше: автосинк по расписанию, OCR картинок, Azure Speech-провайдер, экспорт выборок.
🔒 Безопасность
telegram.session и .env дают полный доступ к Telegram-аккаунту.
Держите их в отдельном приватном репозитории с данными, не коммитьте в этот
и никуда не публикуйте.
Сделано для того, чтобы агент помнил переписку лучше, чем вы сами.
This server cannot be deployed
Maintenance
Related MCP Connectors
Agent-native MCP server over the public saagarpatel.dev corpus. Read-only, stateless.
Search your AI chat history (ChatGPT, Claude, Codex) from any MCP client. Remote, private, read-only
Telegram bridge for your MCP-compatible agent. Bidirectional, no LLM in our stack.
Related MCP Servers
- AlicenseAqualityAmaintenanceMCP server that exposes any Telegram-Archive instance to LLMs, enabling message search, chat browsing, and access to archived Telegram history.10132 npm7GPL 3.0
- AlicenseNot gradedqualityDmaintenanceAn MCP server that connects to Telegram as your real user account and exposes read-only tools to read and search messages, list chats and folders, inspect group info, and download media.16 npmMIT
- AlicenseAqualityAmaintenanceLocal-first, read-only MCP server for searching and retrieving cited evidence from archived Telegram chats, including transcripts and media metadata.53MIT
- AlicenseNot gradedqualityAmaintenanceLets AI agents search and read your personal Telegram and WhatsApp conversations through a self-hosted, read-only MCP server backed by a single SQLite file.2AGPL 3.0