Это продолжение заметки «Подключил свой Telegram к ChatGPT. Потом туда же подключился Claude». Там я писал, как вообще появился telegram-business-mcp: сначала как личный мост между Telegram Business, ChatGPT и Claude.
Тогда задача была довольно простой.
Я хотел написать в ChatGPT:
Что мне сегодня писал конкретный человек в Telegram?
И чтобы модель действительно сходила в переписку, нашла нужный чат, прочитала сообщения и ответила.
Потом появилась отправка сообщений.
Потом фотографии, голосовые, файлы.
Потом тот же MCP подключился к Claude.
В какой-то момент стало понятно, что получился уже не маленький скрипт для себя.
И вот здесь началась менее романтичная часть истории.
Чтобы дать доступ к своему Telegram самому себе, достаточно убедиться, что всё работает.
Чтобы предложить тот же инструмент другим людям, этого внезапно недостаточно.
Между «работает» и «это продукт» оказалась ещё одна система
Первая версия жила очень просто:
Telegram → collector → SQLite → MCP → ChatGPT / Claude
На своём сервере я контролировал всё.
Один пользователь.
Один Telegram.
Один архив.
Один человек, который примерно помнит, почему всё устроено именно так.
Для hosted-сервиса конструкция уже другая.
Появились отдельные пользователи, авторизация, OAuth, scopes, tenant isolation, публичный домен, политика приватности, terms, demo-режим и нормальный процесс подключения.
В старой статье я даже отдельно написал, что SQLite прекрасен для персонального архива, но такую архитектуру я бы не стал автоматически превращать в корпоративный multi-tenant продукт.
Через несколько недель я, разумеется, начал превращать её в multi-tenant продукт.
Не автоматически.
Это важное уточнение.
Проект получил имя Lemma Telepath.
Теперь это hosted MCP-сервис, который подключается к Telegram Business через официальный API и даёт MCP-клиенту инструменты для работы с перепиской.
То есть пользователь подключает свой Telegram один раз, а дальше может открыть ChatGPT или Claude и написать примерно то же самое, с чего всё началось:
Посмотри, что там мне написали.
Только теперь между этой фразой и Telegram уже находится сервис, который должен быть понятен не только мне.
Потом я нажал Submit
Следующим логичным шагом была публикация.
С MCP сейчас происходит интересная вещь.
Протокол довольно быстро превращается из технической детали в отдельный слой экосистемы.
Есть сервер.
Есть клиенты.
Есть permissions.
Есть каталог инструментов.
Есть модели, которые должны понимать, какой инструмент можно вызвать, что он изменит и чего от него вообще ожидать.
Поэтому я отправил Telepath на ревью для публикации в OpenAI.
И почему-то ожидал, что самая нервная часть будет связана с инфраструктурой.
OAuth.
Редиректы.
Домен.
Авторизация.
Какой-нибудь неожиданный HTTP status.
Но первые конкретные замечания оказались интереснее.
Проверять начали описания инструментов.
Tool description внезапно перестал быть документацией
У Telepath сейчас несколько десятков MCP tools.
Например:
telegram_get_messagestelegram_search_messagestelegram_get_filetelegram_send_messagetelegram_set_reaction
Для человека это выглядит почти как обычная документация API.
Название.
Описание.
JSON schema.
Несколько annotations.
Но для модели это не совсем документация.
Это контракт.
И здесь разница становится важной.
Если написать человеку:
Инструмент ставит reaction на сообщение.
Человек, скорее всего, спросит, на какое именно сообщение и от чьего имени.
Модель вполне может решить, что инструмент умеет ставить reaction везде, где ей дали chat_id и message_id.
А Telegram Business API так не работает.
Например, бот не может просто поставить реакцию от моего имени в произвольном личном Business-диалоге. У разных типов чатов и разных способов подключения есть свои границы.
Сам код это уже знал.
Описание инструмента оказалось менее точным.
Scanner сформулировал проблему примерно так:
This tool’s description appears to claim capabilities that do not match its behavior.
И мне эта формулировка неожиданно понравилась.
Потому что это действительно баг.
Не в коде.
В обещании.
Работало правильно. Было описано неправильно
Два reaction-инструмента пришлось переписать так, чтобы из самого description было однозначно понятно, где именно они работают.
Никакой новой функции я не добавил.
Никакую старую не починил.
Telegram API не изменился.
Изменилось несколько предложений текста.
Но для агента это вполне реальное изменение интерфейса.
До этого инструмент говорил:
я умею X
После:
я умею X только здесь, вот в этом контексте и вот от чьего имени
С обычным API такую разницу иногда можно спрятать в документации где-нибудь на третьей странице.
С agent tools документация фактически участвует в runtime.
Модель читает её перед тем, как принять решение.
И это, кажется, одна из тех вещей, которые я раньше понимал скорее теоретически. После ревью стало совсем наглядно.
Потом всплыл openWorldHint
Следующее замечание выглядело ещё скучнее.
У telegram_get_file стояла annotation:
openWorldHint: false
При этом инструмент идёт в Telegram и получает оттуда реальный файл.
Проверка совершенно справедливо ответила:
This tool is marked openWorldHint: false, but its behavior appears to interact with an independently controlled external system.
То есть инструмент read-only.
Он ничего не удаляет.
Ничего не отправляет.
Не меняет состояние Telegram.
Но это ещё не означает, что он работает в закрытом мире.
Telegram — внешняя система.
Файл существует там.
Его доступность зависит не от моего MCP-сервера.
И вот здесь я впервые очень чётко увидел разницу между двумя понятиями:
readOnly ≠ closed world
До этого annotation воспринимался почти как мелкая метаинформация вокруг настоящего кода.
На практике эта метаинформация сообщает модели, с каким типом реальности она сейчас взаимодействует.
Мы поправили openWorldHint у нескольких Telegram-инструментов.
Снова без изменения поведения.
Сервер делал ровно то же самое до и после коммита.
Но описание сервера стало точнее соответствовать серверу.
В итоге правка выглядела почти смешно
После всей разработки изменение можно было описать примерно так:
- уточнили descriptions двух reaction tools;
- исправили annotations у инструментов, работающих с Telegram API;
- поведение инструментов не меняли.
В кодовой базе это далеко не самый впечатляющий commit.
Но, кажется, именно такие изменения лучше всего показывают переход от проекта «для себя» к публичному инструменту.
Когда делаешь вещь только для себя, у системы есть огромный скрытый dependency:
автор всё понимает
Ты знаешь, что конкретно означает название функции.
Ты помнишь ограничения Telegram.
Ты знаешь, почему здесь нельзя сделать reaction.
Ты понимаешь, что этот tool ходит наружу.
Ты автоматически достраиваешь недостающий контекст в голове.
У публичного MCP такого dependency быть не должно.
У модели моей головы нет.
У пользователя тем более.
Всё, что важно для безопасного использования инструмента, должно находиться в самом контракте.
Самое смешное — код оказался простой частью
Я довольно долго воспринимал productization как техническую работу вокруг основного проекта.
Вот есть сам Telepath.
А вокруг него нужно сделать OAuth, сайт, документацию, permissions, deployment и прочую обязательную обвязку.
Теперь мне кажется, граница проходит немного иначе.
Сам tool состоит не только из функции, которая выполняется на сервере.
Он выглядит скорее так:
behavior + schema + description + permissions + annotations
Если одна из этих частей врёт, весь инструмент описан неправильно.
Даже если функция внутри работает идеально.
Для обычного backend это можно назвать проблемой документации.
Для агента это уже часть поведения системы.
Потому что решение о вызове принимает не человек, который месяцами читал документацию.
Его принимает модель прямо сейчас.
Где сейчас Telepath
На момент этой записи проект уже довольно далеко ушёл от первого персонального прототипа.
Он работает как hosted MCP.
Есть отдельная авторизация пользователей.
Telegram подключается через официальный Business API.
Один backend можно использовать из разных MCP-клиентов.
Сервер опубликован в MCP Registry.
И сейчас идут процессы публикации в экосистемах OpenAI и Anthropic.
Результат ревью я пока не знаю.
Поэтому здесь нет красивого финала вида:
Нас приняли.
Может быть, впереди ещё десять замечаний.
Может быть, какое-нибудь из них заставит переделать уже не description, а половину архитектуры.
Но мне как раз нравится зафиксировать момент до результата.
Потому что обычно после успешного запуска вся дорога схлопывается в одну строчку:
Published
А внутри этой строчки находится довольно много странной инженерной работы.
Я завёл для этого отдельный Telegram-канал
У проекта стало достаточно мелких событий, которые уже бессмысленно превращать в отдельные статьи.
Прошёл очередной этап review.
Появился новый tool.
Что-то опубликовали в registry.
Изменился onboarding.
Вышел релиз.
Для таких вещей я сделал отдельный канал Lemma Telepath — @lemma_telepath.
Там будут короткие апдейты по ревью, листингам, релизам и самому MCP.
А сюда мне всё-таки хочется приносить вещи, после которых меняется понимание того, что я вообще строю.
Сегодня изменение примерно такое.
Три недели назад я думал о MCP tool как о функции, которую модель может вызвать.
Теперь думаю о нём скорее как об обещании, которое сервер даёт модели.
Функция должна работать.
Но сначала инструмент должен честно объяснить, что именно он умеет. И где заканчиваются его возможности.
Три недели назад я учил модель читать мой Telegram. Теперь учу коннектор точно рассказывать модели, что он способен сделать.