letopis-mcp
Read-only MCP server that exposes retrieval tools over a Telegram chat archive, including archive overview, full-text search with Russian morphology, aggregates, message retrieval, and local context. It lets LLM agents answer questions from chat history without syncing Telegram, downloading files, or modifying the index.
README
<div align="center">
📜 Летопись (Letopis)
Движок архива и умного поиска по истории Telegram-чатов
Сырые сообщения в JSONL · полнотекстовый поиск с русской морфологией · качалка с менеджером
</div>
Идея
Летопись — это не бот и не сервис, а CLI-инструмент, заточенный под то, чтобы LLM-агент (в первую очередь Claude Code) мог читать историю ваших Telegram-чатов и отвечать по ней на вопросы, как по обычной базе знаний.
Архив хранится как обычные файлы — .jsonl, по одному на чат и месяц,
append-only. Поверх них строится индекс SQLite с полнотекстовым поиском (FTS5),
который понимает русские словоформы: запрос «хостинг» находит сообщения со
словом «хостингами». К поиску подключены расшифровки голосовых, имена файлов
и текст опросов.
$ ./tg search переезд хостинг --chat devops --from 2025-06
Движок и данные разделены. Этот репозиторий содержит только код — сами архивы чатов,
config.toml,.envи сессия Telegram живут в отдельном приватном репозитории, который держит вас в контроле над тем, что публично, а что нет. Подробнее — в разделе «Устройство».
✨ Возможности
| 🔎 Полнотекстовый поиск | SQLite FTS5 + pymorphy3: ищет по леммам, а не только точным словам |
| 📦 Архив как файлы | archive/<chat_id>/<YYYY-MM>.jsonl, append-only, ничего не переписывается задним числом |
| ⬇️ Качалка с менеджером | download / sync докачивают только новое; manifest.json помнит, что отслеживается |
| 🎙️ Транскрипция голосовых | локально (faster-whisper), через Telegram Premium или OpenAI Whisper API |
| 🌐 Веб-просмотрщик | чаты → топики-чипы, бесконечная прокрутка, фильтры, плеер голосовых, прыжки по реплаям |
| ⌨️ TUI-просмотрщик | тот же функционал в терминале (textual) |
| 👥 Несколько аккаунтов | разные чаты можно скачивать разными 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
Переменные окружения
| Переменная | По умолчанию | Назначение |
|---|---|---|
LETOPIS_MCP_DB |
значение [general].db из config.toml, обычно data/index.db |
Путь к SQLite-индексу; относительный путь считается от корня проекта. |
LETOPIS_MCP_CURSOR_SECRET |
нет; временный случайный секрет на процесс | HMAC-SHA256 для непрозрачных курсоров. Обязателен в production: без него курсоры не переживают рестарт процесса. |
LETOPIS_MCP_HOST |
127.0.0.1 |
Loopback-адрес bind; приложение отклоняет нелокальные адреса. |
LETOPIS_MCP_PORT |
8765 |
TCP-порт Streamable HTTP endpoint. |
LETOPIS_MCP_LOG_LEVEL |
INFO |
Уровень структурированных логов (DEBUG, INFO, WARNING, ERROR, CRITICAL). |
LETOPIS_MCP_MAX_CONCURRENCY |
8 |
Максимум конкурентных операций с read-only БД. |
LETOPIS_MCP_QUERY_TIMEOUT_SECONDS |
30.0 |
Дедлайн SQLite-запроса и ожидания слота concurrency. |
LETOPIS_MCP_ROLLING_CALLS_MAX |
60 |
Максимум завершённых вызовов в глобальном rolling-окне. |
LETOPIS_MCP_ROLLING_CHARS_MAX |
250000 |
Максимум отданных символов в том же окне. |
LETOPIS_MCP_ROLLING_WINDOW_SECONDS |
600 |
Длина 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, открыто · данные — отдельно, приватно) позволяет свободно развивать и делиться кодом, не рискуя утечкой переписки.
🧭 Команды
Поиск — основное для агента
| Команда | Что делает |
|---|---|
tg search <слова…> |
Полнотекстовый поиск. Флаги: --any (OR вместо AND), --chat, --topic, --sender, --from / --to, --media, --around N (контекст вокруг находок), --count, --by-chat / --by-topic / --by-sender (агрегаты), --rank (по релевантности), --json, --short N, --limit N|0 |
tg dump --chat X [--topic N] |
Хронологический кусок переписки целиком |
tg context --chat X --id N |
Сообщения вокруг конкретного (--before / --after / --whole-chat) |
tg chats |
Обзор чатов в архиве |
tg topics --chat X |
Топики форум-чата |
tg status |
Состояние архива и индекса |
Просмотрщики — ручной режим
| Команда | Что делает |
|---|---|
tg web |
Локальный веб-интерфейс: чаты → топики-чипы, бесконечная прокрутка, поиск с фильтрами, прыжок к дате, фильтр по автору (клик по нику), фото/видео инлайн, плеер голосовых с расшифровкой, реплаи с прыжком по треду, ссылки t.me. Порт — в config.toml [web] |
tg tui |
То же самое в терминале: / поиск · g дата · s автор · o/n старее/новее · c контекст · m открыть в Telegram · f открыть файл · Esc назад · q выход |
Качалка и менеджер
| Команда | Что делает |
|---|---|
tg dialogs |
Все чаты аккаунта (✓ — уже в архиве) |
tg download --chat <имя|id|@user> |
Скачать чат/топики и поставить на отслеживание. Флаги: --topic N, --from 2025-01, --media photo,voice|all|none |
tg sync [--chat X] |
Докачать новые сообщения всех отслеживаемых чатов |
tg media --chat X --media voice |
Докачать файлы для уже скачанных сообщений |
tg transcribe [--provider …] |
Расшифровать голосовые в текст, чтобы он попал в поиск |
tg untrack --chat X |
Снять чат с отслеживания (файлы остаются на диске) |
tg meta / tg index |
Обновить названия чатов / доиндексировать архив |
tg login [--account имя] |
Авторизовать Telegram-сессию (телефон / код / 2FA) |
Чат можно указывать как id, алиас из config.toml, часть названия, @username
или ссылку t.me/.... Новые сообщения скачиваются со всеми реакциями, опросами
и сервисными событиями.
🎙 Транскрипция голосовых
Провайдер задаётся в config.toml [transcription]:
| Провайдер | Стоимость | Требования |
|---|---|---|
whisper-local |
бесплатно, локально | faster-whisper, модель small по умолчанию |
telegram |
бесплатно | Telegram Premium на аккаунте |
openai |
платно (Whisper API) | OPENAI_API_KEY в .env |
👥 Несколько аккаунтов
[accounts]
default = "telegram.session"
backup = "sessions/backup.session"
tg login --account backup авторизует новую сессию. У download / sync /
dialogs / meta есть флаг --account. Каждый чат в манифесте закреплён за
своим аккаунтом.
🗺 Статус
| Этап | Статус | Что входит |
|---|---|---|
| A | ✅ готово | Индекс, поиск, CLI, интеграция с Claude Code |
| B | ✅ готово | Качалка (download/sync, append-only), медиа по настройкам, бэкфилл, транскрипция, менеджер (manifest.json), несколько аккаунтов, FloodWait-защита |
| C | ✅ готово | Просмотрщики: tg web (браузер, медиа) и tg tui (терминал) |
Дальше: автосинк по расписанию, OCR картинок, Azure Speech-провайдер, экспорт выборок.
🔒 Безопасность
telegram.session и .env дают полный доступ к Telegram-аккаунту.
Держите их в отдельном приватном репозитории с данными, не коммитьте в этот
и никуда не публикуйте.
<div align="center">
Сделано для того, чтобы агент помнил переписку лучше, чем вы сами.
</div>
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.