Моя команда создает внутреннее ПО для собственных нужд. Не продукты. Ту неблагодарную работу, которая поддерживает жизнь организации по работе с разработчиками: проверка готовности к релизу, скрипт для сверки нашего календаря мероприятий, дашборд для застоявшихся пулл-реквестов. Большая часть этого написана с помощью Claude Code, и в последнее время немалая часть этого также запускается с помощью Claude Code.
В прошлом месяце я столкнулся с решением, которое откладывал. Инструменту проверки готовности к релизу нужны данные из GitHub API. Я знаю, как вызывать этот API. Чего я не знал, так это как передать эту способность агенту, и передо мной было три двери, ведущие к одним и тем же эндпоинтам.
Дверь первая: установить Octokit SDK и заставить агента писать на нем код на TypeScript. Дверь вторая: установить gh и позволить ему выполнять команды оболочки. Дверь третья: подключить GitHub MCP server и позволить ему вызывать инструменты.
Я выбрал первую дверь, затем третью, а потом снова передумал. Окончательным решением стало не сравнение производительности. Я понял, что задавал неправильный вопрос.
Что я на самом деле создаю
Каждое утро инструмент отвечает на один вопрос: безопасно ли выпускать релиз из этого репозитория?
Под капотом это четыре вызова API. Открытые пулл-реквесты, отфильтрованные до тех, которые не двигались в течение недели и не являются черновиками. Проверка результатов выполнения (check runs) последнего коммита в мейне. Любой черновик релиза, который уже существует. Затем краткая сводка, записанная в файл, который я читаю за утренним кофе.
Около 200 строк. Код не представляет интереса. Значение имеет то, кто его запускает, и это варьируется сильнее, чем я ожидал. Некоторые утра я запускаю его вручную. Большинство утр это делает задание cron. И каждые пару недель я сижу в сессии Claude Code и задаю более размытые вопросы, например, почему релиз в прошлый четверг задержался, и агенту приходится исследовать GitHub способами, которые я никак не мог предвидеть, когда писал скрипт.
Этим трем вызывающим сторонам нужны разные вещи, и это я упустил из виду.
Дверь первая: SDK
SDK — это типизированная клиентская библиотека для одного языка. Установите ее, получите автодополнение, и компилятор поймает ваши опечатки.
Именно это я написал в первую очередь, и именно на этом до сих пор работает задание cron. Типы отрабатывают свое. Прошлой весной GitHub переименовал поле в полезной нагрузке ответа, и TypeScript обнаружил это во время сборки, а не в 6 утра в логе, который никто не читал.
Где он падает, так это в импровизации. SDK — это контракт времени сборки, поэтому, чтобы агент мог его использовать, кто-то уже должен был написать вызывающий код. Находясь в сессии и задавая вопросы о релизе в четверг, агент не может обратиться к octokit.rest.checks.listForRef, если я не предоставил ему такой доступ. Он может написать новый код, и он с радостью это сделает, но тогда я компилирую одноразовый TypeScript ради ответа на вопрос, который задам только один раз. Я проделал это дважды, прежде чем признать, что это не работает.
Учетные данные — это другое дело. SDK считывает токен из окружения процесса, поэтому секрет живет там же, где и сам процесс. Это нормально для GitHub Actions со сфокусированным токеном. Гораздо хуже, когда процесс — это сессия агента на моем ноутбуке.
Дверь вторая: интерфейс командной строки
Интерфейс командной строки (CLI) оборачивает тот же API в команды, которые вы запускаете из терминала. У GitHub это gh, и он делает то, чего не может SDK: он работает до того, как кто-либо напишет код интеграции.
Claude Code уже свободно владел им, что удивило меня сильнее, чем должно было бы. Никому не пришлось учить его gh. Когда он не был уверен в каком-то флаге, он запускал gh pr list --help и читал вывод так, как это сделал бы человек. Терминал — это интерфейс, который у агентов уже есть, поэтому не нужно писать никаких адаптеров и нечего настраивать.
Это еще и практически бесплатно. CLI ничего не стоит в вашем контекстном окне до тех пор, пока агент его не запустит, и ответом будет именно то, что вы просили. Этот фильтр --jq означает, что возвращается одно целое число вместо шести объектов пулл-реквестов. Попробуйте заставить инструмент, который сам определяет форму своего ответа, сделать это.
Затраты тоже вполне реальны. Вывод представляет собой текст, поэтому что-то должно его парсить. Ошибки отображаются в виде кодов выхода и stderr вместо типизированных исключений. И gh аутентифицируется ровно как одна учетная запись, что отлично подходит для вашей собственной машины и совершенно не подходит для всего, чем пользуются двенадцать человек.
Дверь третья: сервер MCP
Model Context Protocol (MCP) — это стандарт, позволяющий модели обнаруживать и вызывать инструменты. Сервер рекламирует то, что он предлагает, а клиент загружает эти определения в контекст модели, чтобы она знала об их существовании.
Две строчки конфигурации, и агент смог читать пулл-реквесты, результаты проверок, релизы, проблемы и многое другое. Для сценария с импровизацией это был лучший опыт с большим отрывом. Никаких вызовов оболочки, никакого парсинга, структурированные аргументы на входе и структурированные результаты на выходе.
Затем я посмотрел на счет, прежде чем успел напечатать хоть слово.
Клиент загружает каждое определение инструмента заранее. Кто-то measured the GitHub MCP server at roughly 42,000 tokens схем, типов аргументов и примеров полезных нагрузок. Обзор более чем 3000 серверов показал, что медианное значение ближе к 1900, но устанавливаете вы вовсе не медианы. Серверы, к которым люди действительно тянутся, — жирные, и три из них вместе могут съесть более 10% окна в 200 000 токенов еще до начала разговора. Это контекст, который я бы предпочёл потратить на репозиторий.
Так что прекратите загружать все:
Ограничение до двух наборов инструментов и добавление флага --read-only сильно помогли сократить объем. Этот флаг пропускает все инструменты записи, что для утреннего отчета, который ничего не изменяет, обходится дешевле и безопаснее, чем любая инструкция, которую я мог бы поместить в промпт. Облегчение приходит и на уровне протокола: клиенты начали откладывать определения инструментов и позволять модели искать их по требованию, как только они превышают определенную долю окна.
Однако в MCP меня удерживала не эргономика. Это единственный из трех вариантов с моделью авторизации, которая переживает более одного пользователя. Протокол передает авторизацию для каждого конкретного пользователя, поэтому сервер действует от имени того, кто спрашивает, и может это логировать. Я не могу воссоздать это с помощью команды оболочки, и в тот день, когда наши инструменты перерастут мой ноутбук, он мне понабится.
Как я в итоге принял решение
Я перестал спрашивать, какой интерфейс лучше. Лучший вопрос — кто его вызывает, и это привело к появлению таблицы, которую я теперь храню в README репозитория:
Для средства проверки релизов выбор перестал ограничиваться одной дверью. Задание cron выполняет код SDK, потому что мне нужен компилятор и мне нужно, чтобы он громко сообщал об ошибках во время сборки. Интерактивные сеансы используют gh, потому что это ничего не стоит до тех пор, пока не будет использовано, и Claude Code не потребовалась от меня никакая помощь. Сервер MCP подключается для исследовательской работы по репозиториям, где я не могу предсказать вызовы.
Поначалу, когда я это записал, мне казалось, что это отговорка. Это не так. Три потребителя, три поверхности.
Подводные камни, с которыми я столкнулся
Посчитайте определения своих инструментов, прежде чем винить модель
Я провел полдня в уверенности, что Claude Code стал хуже справляться с задачей. Это было не так. На той неделе я добавил два MCP-сервера, и они незаметно вытеснили ту часть контекста, в которой раньше хранилось дерево файлов репозитория. Проверяйте, во сколько вам обходятся ваши серверы, прежде чем начинать переписывать промпты. Раздел инструментов в спецификации MCP стоит потраченного часа вашего времени, чтобы вы знали, что отправляется от вашего имени.
CLI, выводящий JSON, и инструмент, выводящий таблицы, — это разные вещи
gh pr list выводит таблицу для людей. gh pr list --json возвращает данные. Агенты справляются с первыми и надежно работают со вторыми. Если вы создаете CLI для управления агентами, сделайте структурированный вывод первоклассным флагом и документируйте его в --help, потому что --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’s 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 и раздел инструментов
- MCP-сервер GitHub на GitHub, включая набор инструментов и флаги только для чтения
- Руководство по GitHub CLI для флагов вывода --json и --jq
- Octokit для JavaScript
- Fern для генерации SDK и CLI из одной спецификации, а также быстрый старт с SDK
- Документация Postman MCP Generator и сам MCP Generator
- Документация Postman MCP-сервера












