Написано 31 августа 2026 года. Это журнал решений, а не руководство. Всё здесь — про один небольшой сервер, который публикует один небольшой блог, и единственная причина, по которой это может стоить вашего времени: он настоящий, со своими шрамами, а не показательный пример, написанный, чтобы его показывать.

Часть 1 серии: зачем он существует

Часть 1 серии: зачем он существует

В конце третьей статьи цикла я написал, что большинству команд MCP-сервер сейчас не нужен и что правильный первый шаг — не написать его, а посчитать, во сколько токенов обойдутся описания инструментов в каждом запросе. Я в это верю. И при этом я его написал.

Поэтому честно начать с противоречия. По моему же совету публикующая половина этого сервера должна была остаться скриптом — и во многом ею и осталась, к этому я вернусь. Протокол оправдала другая половина, и мне понадобилось время, чтобы понять, какая половина какая.

Что это на самом деле такое

Обёртка. Собственных данных у неё нет, и если она завтра исчезнет, блог этого не заметит.

За ней стоит админский API, который и так был, — те же самые эндпоинты, которые вызывает веб-интерфейс админки. Разница ровно одна: как вызывающий доказывает, кто он. Браузер шлёт сессионную куку, сервер — API-ключ. Один API, два способа аутентификации. Ничего в календаре не переделывалось «под MCP», и не требовалось.

Проговорить это стоит прямо, потому что «написать MCP-сервер» звучит как «построить систему», а обычно это не так. Это написать описания того, что вы и так умеете, для читателя, который не видел вашей документации и не увидит.

Почему календарь, а не статьи

Единица здесь не статья. Единица — датированное намерение: этот текст, на той площадке, в тот день, от того имени, в таком-то состоянии.

Держать в голове я не мог никогда не «что написать», а что куда должно уйти, в какой день, из-под какого аккаунта и не зависит ли четверговое от того, проиндексировалось ли понедельничное. У записи есть дата, площадка, тип, публикующий профиль, тело и статус, который едет от «запланировано» через «в работе» к «сделано» или «пропущено». Типы значат больше, чем кажется: статья — это не пост, пост — это не вопрос, а вопрос — не комментарий к чужому ответу, и последним двум нужен родитель, к которому они цепляются.

Как только единицей становится датированная запись, а не текст, большинство моих реальных вопросов становятся перечислимыми. Что сегодня. Что запланировано на эту площадку в этом месяце. Что до сих пор висит в «запланировано», когда осталось три дня. Никакой интересной инженерии здесь нет — и ровно ради этого всё и существует.

Почему модель, а не скрипт

Вот место, где мой собственный совет бьёт по мне, и я лучше скажу это сам, чем дам читателю заметить.

Публикация скриптуется. Взять сохранённое тело, выбрать адаптер по площадке, отправить и записать обратно полученный URL — это функция, и в этой кодовой базе она буквально функция: диспетчер вызывается и из протокола, и прямо из командной строки, и ему всё равно. Ночному заданию, которое читает файл и отправляет его на эндпоинт, не нужны ни описания инструментов, ни модель, которая их читает, ни счёт за это.

Планирование не скриптуется, и в этом всё оправдание. Решить, что куда идёт в какой день, когда в окне уже лежит восемьдесят других записей, а площадки терпят разное, — это суждение, в котором список вызовов действительно нельзя написать заранее. Я не знаю, начиная, будет ответом три чтения и одно обновление или двадцать чтений и пересборка половины месяца. Это незнание настоящее, а не привычка, — а там, где незнание настоящее, счёт справедлив.

Итого, как можно прямее: модель планирует, код публикует. Каждый раз, когда я эту границу размывал, это чего-нибудь стоило.

Двадцать инструментов, из них пять лишних

У меня их двадцать. В третьей статье я рекомендовал пятнадцать. Оба утверждения мои, и прятать разрыв я не буду.

Они делятся на группы. Чтение и запись записей календаря — ядро. Справочники: публикующие профили, серии статей; они есть потому, что модель, которая не видит доступных личностей, одну придумает. Проверка — три инструмента, самая интересная группа, ей отдельный раздел. Публикация — два инструмента поверх диспетчера. И пятая группа, которой здесь быть вообще не должно: пять инструментов трекера вакансий, совершенно другая предметная область, заползшая в тот же сервер просто потому, что этот сервер уже был подключён.

Никто не решает завести двадцать инструментов. У вас одиннадцать, потом сокращение, экономящее шаг, потом парный листинг к уже существующему, потом вторая предметная область, потому что это проще, чем поднимать второй сервер, — и однажды утром список перестаёт помещаться на экран. Каждый из них в день добавления был очевидно оправдан. Это и есть режим отказа: прополка — не решение, принимаемое один раз, а то, что делают постоянно и против себя. Я не делал.

Инструменты, которым нет соответствия в API

Самое интересное в этом сервере — набор инструментов, которых в нижележащем API нет и не нужно: три проверяющих.

Двое из них работают до того, как что-либо куда-либо уйдёт. Они знают по площадке и по типу записи, что публикующий путь способен отрисовать на самом деле, и отказывают записи, которая выйдет покорёженной. Третий вычитывает опубликованную страницу обратно и жалуется.

Ни один REST-эндпоинт таких не завёл бы. Человеку они тоже не нужны: тот, кто сам вставляет статью в редактор, видит, что таблица выглядит не так. Они существуют потому, что вызывающий — модель, и потому, что мне нужно было ограждение, которое нельзя обговорить. Публикующие инструменты сначала гоняют проверку и отказываются публиковать запись с ошибками; чтобы пройти мимо, нужен явный флаг, а это решение человека, а не шаг, который агент проскакивает.

Если отойти на шаг, это и есть общий ответ на вопрос «что обёртка добавляет к API». Описания, написанные для чтения. Намеренно более узкий набор действий, чем есть в API. И точка, где кто-то может посмотреть, что сейчас уйдёт, и сказать «нет».

Почему кнопку всё-таки жмёт человек

Medium в этой системе никогда не публикует сам. Адаптер создаёт черновик и останавливается. Человек открывает его и жмёт «опубликовать».

Это не осторожность ради осторожности, это асимметрия. Статья на dev.to, сообщение в Telegram и вопрос на Stack Exchange уходят живьём, потому что там плохой пост можно поправить или снести ценой, которую я готов заплатить. У обрезанной истории на Medium есть адрес, который уже кто-то увидел, а перезаливка заново играет в ту же гонку, которая её и сломала. Правило, к которому я пришёл, звучит не «всегда подтверждать» и не «никогда». Оно звучит так: автоматизировать до последнего обратимого шага, а необратимый оставить решением.

Поиск, который я намеренно не сделал

Самое полезное решение в этом проекте — инструмент, которого нет.

Рядом с основным сервером живёт второй, крошечный: он читает архив сообщений. У него ровно один инструмент — достать сообщения по ключам, с необязательным числом соседних для контекста. Искать он не умеет. Это сделано нарочно. Поиск живёт в конструкторе запросов в админке, где пишешь SQL, видишь SQL и правишь SQL, когда он вернул не то. Второй непрозрачный поиск — когда спрашиваешь, получаешь результат и не можешь увидеть, что совпало, а что пропущено, — ровно то, из-за чего прежний чат оказался бесполезен настолько, что его убрали.

Обобщённая форма этого решения — единственное, что я бы сохранил, выбросив всё остальное. Не ставьте инструмент перед моделью там, где ценность нижележащей вещи в том, что человек видит, как она работает.

Что во второй части

Что сломалось. Три вещи, и третья сломана до сих пор, пока я это пишу: проверка в моём собственном ограждении, которая отчитывается об успехе по площадкам, о которых никогда не слышала. Ровно тот отказ, о котором весь этот цикл, — в коде, который должен был его предотвращать.

Вторая часть — что сломалось и чему научило. Полный разбор инцидента с обрезанными публикациями — здесь. Три статьи про протокол: откуда взялись MCP и REST, сколько MCP стоит в токенах и из чего сделан MCP-сервер.

Источники

Мой собственный MCP-сервер календаря продвижения, его README и рунбук docs/publishing.md. Спецификация MCP публикуется на modelcontextprotocol.io; использованы редакции 2025-06-18 и 2026-07-28.