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>
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。