Skip to main content
Glama
Toligrim

letopis-mcp

by Toligrim

📜 Летопись (Letopis)

Движок архива и умного поиска по истории Telegram-чатов

Сырые сообщения в JSONL · полнотекстовый поиск с русской морфологией · качалка с менеджером

Python 3.10+ Telethon SQLite FTS5 License


Идея

Летопись — это не бот и не сервис, а 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 + 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.py MCP 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.com AND URI 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 / плагин в режиме разработчика:

  1. Откройте Developer mode → Settings → Plugins → +.

  2. Для Connection выберите «URL-адрес сервера», а не «Туннель».

  3. Укажите URL https://your-domain.example.com/mcp?k=<секрет>.

  4. Для Authentication выберите «Без авторизации». Это означает, что MCP-протокол не использует OAuth: защита уже выполнена правилом Cloudflare WAF. В форме также могут быть варианты OAuth и «Смешанные».

  5. Создайте подключение.

В этой схеме 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-аккаунту. Держите их в отдельном приватном репозитории с данными, не коммитьте в этот и никуда не публикуйте.


Сделано для того, чтобы агент помнил переписку лучше, чем вы сами.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    An 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 npm
    MIT
  • A
    license
    A
    quality
    A
    maintenance
    Local-first, read-only MCP server for searching and retrieving cited evidence from archived Telegram chats, including transcripts and media metadata.
    5
    3
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    Lets 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.
    2
    AGPL 3.0