17 сентября 2026 г.
Моя команда создает внутреннее программное обеспечение для собственных нужд. Не продукты. Это не самая гламурная работа, которая поддерживает функционирование отдела по связям с разработчиками: проверка готовности к релизу, скрипт для сверки нашего календаря событий, панель мониторинга для «зависших» pull-реквестов. Большая часть этого кода пишется с помощью Claude Code, и в последнее время значительная его часть также выполняется с помощью Claude Code.
В прошлом месяце я столкнулся с решением, которое долго откладывал. Инструменту проверки готовности к релизу нужны данные из GitHub API. Я знаю, как вызывать этот API. Но я не знал, как передать эту возможность агенту, и передо мной было три двери, ведущие к одним и тем же эндпоинтам.
Дверь первая: установить SDK Octokit и заставить агента писать на нем код на TypeScript. Дверь вторая: установить gh и позволить ему выполнять команды оболочки. Дверь третья: подключить GitHub MCP-сервер и позволить ему вызывать инструменты.
Я выбрал первую дверь, затем третью, а потом снова передумал. То, что окончательно решило вопрос, было не бенчмарком. Я просто понял, что задавал неправильный вопрос.
Что я на самом деле создаю
Каждое утро инструмент отвечает на один вопрос: безопасно ли делать релиз из этого репозитория?
В основе этого лежат четыре API-вызова. Открытые pull-реквесты, отфильтрованные по тем, которые не обновлялись неделю и не являются черновиками. Проверка запусков (check runs) последнего коммита в ветке main. Любой черновик релиза, который уже существует. Затем краткое резюме, записанное в файл, который я читаю за утренним кофе.
Около 200 строк кода. Сам код не представляет интереса. Важным оказалось то, кто его запускает, и это происходит чаще, чем я ожидал. По утрам я иногда запускаю его вручную. Чаще всего это делает cron-задача. И каждые пару недель я сижу в сессии Claude Code, задавая более расплывчатые вопросы, например, почему релиз в прошлый четверг был задержан, где агенту нужно «покопаться» в GitHub способами, которые я не предвидел, когда писал скрипт.
Этим трем вызывающим сторонам нужны разные вещи, и это та часть, которую я упустил.
Дверь первая: SDK
SDK — это типизированная клиентская библиотека для одного языка. Установите ее, получите автодополнение, и компилятор поймает ваши опечатки.
Это то, что я написал первым, и именно это до сих пор запускает cron-задача. Типы отрабатывают свое. Прошлой весной GitHub переименовал поле в полезной нагрузке ответа, и TypeScript поймал это на этапе сборки, а не в 6 утра в логах, которые никто не читал.
Где он проигрывает, так это в импровизации. SDK — это контракт на этапе сборки, поэтому, чтобы агент мог его использовать, кто-то должен был заранее написать вызывающий код. Сидя в сессии и спрашивая о релизе в четверг, агент не может обратиться к octokit.rest.checks.listForRef, если я его не предоставил. Он может написать новый код, и он с радостью это сделает, но тогда я компилирую одноразовый TypeScript, чтобы ответить на вопрос, который я задам только один раз. Я сделал это дважды, прежде чем признал, что это не работает.
Учетные данные — это другое дело. SDK считывает токен из переменных окружения процесса, поэтому секрет живет там же, где и процесс. Это нормально для GitHub Actions с ограниченным токеном. Менее нормально, когда процесс — это сессия агента на моем ноутбуке.
Дверь вторая: интерфейс командной строки (CLI)
Интерфейс командной строки (CLI) оборачивает тот же API в команды, которые вы запускаете из терминала. У GitHub это gh, и он делает одну вещь, которую не может SDK: он работает до того, как кто-либо напишет код интеграции.
Claude Code уже владел им, что удивило меня больше, чем следовало бы. Никому не пришлось учить его gh. Когда он не был уверен в флаге, он запускал gh pr list --help и читал вывод так, как это сделал бы человек. Терминал — это интерфейс, который у агентов уже есть, поэтому не нужно писать адаптер и нечего настраивать.
Это также почти бесплатно. CLI ничего не стоит в вашем контекстном окне до того момента, пока агент его не запустит, а ответ — это именно то, что вы просили. Этот фильтр --jq означает, что возвращается одно целое число вместо шести объектов pull-реквестов. Попробуйте заставить инструмент, который владеет собственной структурой ответа, сделать это.
Затраты тоже реальны. Вывод — это текст, поэтому что-то должно его парсить. Ошибки отображаются как коды выхода и stderr вместо типизированных исключений. И gh аутентифицируется как одна конкретная личность, что хорошо для вашего собственного компьютера, но является неправильным ответом для всего, чем пользуются двенадцать человек.
Дверь третья: MCP-сервер
Model Context Protocol (MCP) — это стандарт, позволяющий модели обнаруживать и вызывать инструменты. Сервер рекламирует то, что он предлагает, а клиент загружает эти определения в контекст модели, чтобы она знала об их существовании.
Две строки конфигурации, и агент мог читать pull-реквесты, проверки, релизы, задачи и многое другое. Для импровизационных задач это был лучший опыт с большим отрывом. Ничего не нужно вызывать через оболочку, ничего не нужно парсить, структурированные аргументы на входе и структурированные результаты на выходе.
Затем я посмотрел на счет, прежде чем успел напечатать хоть слово.
Клиент загружает каждое определение инструмента заранее. Кто-то измерил, что GitHub MCP-сервер содержит примерно 42 000 токенов схем, типов аргументов и примеров полезной нагрузки. Обзор более 3000 серверов показал, что медиана ближе к 1900, но медианы — это не то, что вы устанавливаете. Серверы, к которым люди действительно обращаются, — это «толстые» серверы, и три из них вместе могут съесть более 10% окна в 200 000 токенов еще до начала разговора. Это контекст, который я предпочел бы потратить на репозиторий.
Так что перестаньте загружать все подряд:
Ограничение до двух наборов инструментов и добавление --read-only сильно сократило объем. Этот флаг пропускает все инструменты записи, что для утреннего отчета, который ничего не меняет, дешевле и безопаснее, чем любая инструкция, которую я мог бы поместить в промпт. Облегчение приходит и на уровне протокола, поскольку клиенты начали откладывать определения инструментов и позволять модели искать их по запросу, как только они пересекают определенную долю окна.
Однако в игре MCP удержала не эргономика. Это единственный из трех вариантов с моделью авторизации, которая выживает при работе с более чем одним пользователем. Протокол поддерживает аутентификацию для каждого пользователя, поэтому сервер действует от имени того, кто запрашивает, и может это логировать. Я не могу воссоздать это с помощью команды оболочки, и в тот день, когда наши инструменты перерастут мой ноутбук, мне это понадобится.
Как я на самом деле принял решение
Я перестал спрашивать, какой интерфейс лучше. Лучший вопрос — кто его вызывает, и это привело к таблице, которую я теперь храню в README репозитория:
Для проверки релизов ответ перестал быть одной дверью. Cron-задача запускает код SDK, потому что мне нужен компилятор, и я хочу, чтобы он громко сообщал об ошибках на этапе сборки. Интерактивные сессии используют gh, потому что это ничего не стоит, пока не используется, и Claude Code не потребовалась моя помощь. MCP-сервер используется для исследовательской работы по репозиториям, где я не могу предсказать вызовы.
Когда я впервые записал это, мне показалось, что это уход от ответа. Это не так. Три потребителя, три поверхности.
Подводные камни, с которыми я столкнулся
Считайте свои определения инструментов, прежде чем винить модель.
Я провел вторую половину дня, будучи уверенным, что Claude Code стал хуже справляться с задачей. Это было не так. На той неделе я добавил два MCP-сервера, и они незаметно вытеснили из контекста ту часть, где раньше хранилось дерево файлов репозитория. Проверяйте, сколько ресурсов потребляют ваши серверы, прежде чем переписывать промпты. Раздел инструментов в спецификации MCP стоит того, чтобы потратить на него час вашего времени — так вы будете знать, что именно отправляется от вашего имени.
CLI, выводящий JSON, и CLI, выводящий таблицы — это разные инструменты.
gh pr list отображает таблицу для людей. gh pr list --json возвращает данные. Агенты справляются с первым и надежно работают со вторым. Если вы создаете CLI для управления агентами, сделайте структурированный вывод обязательным флагом и задокументируйте его в --help, потому что именно туда заглянет агент.
Режим «только чтение» — самый простой способ обеспечить безопасность.
Почти все, что делают мои инструменты, — это чтение. Отключение записи устранило целый класс ошибок, которые мне приходилось обходить с помощью промптов, и одновременно сократило определения инструментов.
Не поддерживайте три поверхности вручную.
Именно это заставило меня изменить свое мнение о проблеме. Я почти написал небольшую обертку CLI вокруг нашего внутреннего API, чтобы агенты могли с ним работать. Затем я подумал о том, чтобы поддерживать эту обертку в актуальном состоянии вместе с SDK, а обе их — в синхронизации с API, вручную и бесконечно, и просто закрыл редактор.
Одна спецификация, три поверхности.
Так что эти три варианта — не соперники. Это проекции одного и того же API, предназначенные для разных потребителей, и единственный разумный способ получить их все — генерировать их из одной спецификации, а не писать и поддерживать каждую вручную.
Это та часть, которой я опасался, и оказалось, что она в основном решена. Правда, не одним инструментом, что мне потребовалось время принять.
Fern для SDK и CLI.
Fern считывает файл OpenAPI и создает обе поверхности, с которыми взаимодействует человек. Типобезопасные SDK на девяти языках и CLI, публикуемый в npm с прогрессивным раскрытием и автоматическим версионированием.
Обе цели находятся в одной группе generators.yml, поэтому одна команда fern generate пересобирает их вместе, и они не могут разойтись. Это было именно то возражение, из-за которого я чуть не начал писать обертку вручную, и оно исчезло.
Чего Fern не делает, так это не генерирует MCP-сервер для вашего API. Он размещает MCP-сервер документации, что звучит похоже, но таковым не является. Он отвечает на вопросы о вашем API. Он не вызывает его. Это действительно полезно, когда агент пытается понять, как ваши эндпоинты сочетаются друг с другом, но это не третья поверхность.
Postman MCP Generator для MCP-сервера.
Для этого работает Postman MCP Generator. Вы выбираете запросы, которые хотите сделать доступными, Postman превращает каждый из них в инструмент, и вы скачиваете сервер, который работает с агентами, совместимыми с MCP, включая Claude Code, Cursor и VS Code Copilot.
Одно ограничение, которое стоит знать перед планированием: генератор задокументирован для запросов из Postman API Network, поэтому самый простой путь сегодня — это API, уже опубликованные там. Для чисто внутренних нужд вы сначала импортируете спецификацию в рабочую область, и я бы проверил, подходит ли вам этот путь, прежде чем строить на нем конвейер.
Стоит знать, что Postman также запускает MCP-сервер для своей собственной платформы и поставляет плагин для Claude Code, так что сторона агентов здесь хорошо изучена.
Связывание их вместе.
Связь между ними — это не экспорт из одного инструмента в другой. Оба читают один и тот же файл OpenAPI: Fern генерирует из него, Postman импортирует его. Это оставляет ровно один артефакт, который нужно поддерживать в актуальном состоянии, в чем и заключается весь смысл:
Поместите спецификацию под контроль версий, запускайте fern generate в CI при каждом изменении и делайте повторный импорт в Postman по тому же триггеру. Один коммит в openapi.yml, три пересобранные поверхности, ничего не нужно синхронизировать вручную.
Это то, что решило проблему выбора, над которой я бился месяц. Вы перестаете выбирать один вариант и начинаете поставлять все, и каждый потребитель берет то, что ему подходит.
Попробуйте это на своем собственном API.
Возьмите тот API, который ваша команда вызывает чаще всего, и проведите сравнение самостоятельно. Достаточно одного дня:
- Напишите самый маленький полезный скрипт с использованием официального SDK. Отметьте, помогли ли типы что-то обнаружить.
- Проделайте ту же работу через CLI поставщика с включенными флагами структурированного вывода и сравните размеры ответов.
- Подключите MCP-сервер поставщика. Прежде чем отправить сообщение, проверьте, какую долю вашего контекстного окна заняли определения инструментов. Ограничьте наборы инструментов и проверьте снова.
Затем спросите себя, кто будет вызывать это через полгода. Если ваш собственный код приложения — генерируйте SDK. Если агент работает без присмотра — генерируйте CLI. Если команда стоит за одной общей интеграцией — разворачивайте MCP-сервер. Для большинства API, стоящих усилий, честный ответ — все три, и тогда вопрос не в том, что вы выберете, а в том, генерируете ли вы их из спецификации или поддерживаете вручную.
Я хотел бы знать, где этот подход не срабатывает у других людей. Арифметика токенов, в частности, меняется каждые несколько месяцев, и цифры, которые я измерил в июле, могут не продержаться до конца года.
Ресурсы.
- Спецификация Model Context Protocol и раздел инструментов.
- GitHub MCP server на GitHub, включая набор инструментов и флаги «только чтение».
- Руководство по GitHub CLI для флагов вывода --json и --jq.
- Octokit для JavaScript.
- Fern для генерации SDK и CLI из одной спецификации, плюс краткое руководство по SDK.
- Документация Postman MCP Generator и сам MCP Generator.
- Документация Postman MCP server.
