В октябре 2025-го один-единственный коммит на GitHub добавил 47 LLM-сгенерированных «скиллов» в популярный репозиторий агентов. Один из них учил Claude запускать npx react-codeshift. Пакета не существовало. Никогда не существовало. Claude галлюцинировал его во время обучения, автор не проверил, и за несколько недель файл разошёлся по 237 форкам. Реальные разработчики запускали команду в реальных терминалах. npm радостно пытался разрезолвить фантомный пакет тысячи раз.
Это та часть истории про ИИ-агентов, которая не попадает на keynote. Агенты дрейфуют. Дай им shell и размытую задачу — потянутся к самой правдоподобно звучащей команде, даже если её не существует. И чем более общий инструмент даёшь, тем сильнее дрейф.
Стандартный ответ в 2026-м: напиши кастомный MCP-сервер. Определи действия, которые хочешь дать агенту, дай им строгие схемы, выпусти отдельным бинарником, зарегистрируй в конфиге агента. Ограничь модель, дав ей конкретные инструменты вместо bash.
Стандартный ответ верен в принципе и неверен на практике. Писать настоящий MCP-сервер — JSON-RPC framing, stdio handshake, схемы, отдельное дерево зависимостей — ради одной shell-команды это инженерный театр. Большинство команд сдаются и живут с дрейфом.
Octomind 0.29.0 закрывает этот разрыв. Кастомный MCP не должен быть проектом. Он должен быть файлом в вашем репо.
Проблема дрейфа в цифрах
Если наблюдали за работой агента какое-то время — видели дрейф:
- Запускает
npm testвpnpmмонорепо. - Curl'ит
https://staging.example.com/apiвместо вашего реального, под auth-gatehttps://api-staging.acme.internal/v3. - Пытается установить пакет с почти, но не совсем правильным именем.
- Использует голый
git tagвместо релизного скрипта команды.
Майская 2025 статья RAG-MCP: Mitigating Prompt Bloat in LLM Tool Selection (Gan & Sun) прогнала MCP стресс-тест и обнаружила: baseline-точность выбора инструмента схлопывается до 13.62% на масштабе — модель уже не может надёжно выбрать правильный инструмент, потому что каждое описание инструмента, которое ей приходится рассматривать, забивает промпт и размывает внимание. Их фикс на основе retrieval повышает это более чем в три раза до 43.13%, попутно срезая токены промпта более чем на 50%. Всё ещё ужасная точность, но направление кривой — вот что важно: чем больше инструментов запихиваете, тем хуже модель их выбирает.
Anthropic, авторы MCP-протокола, признали то же самое 4 ноября 2025-го:
"In cases where agents are connected to thousands of tools, they'll need to process hundreds of thousands of tokens before reading a request." (В случаях, когда агенты подключены к тысячам инструментов, им придётся обработать сотни тысяч токенов до того, как они прочтут запрос.)
Их собственные бенчмарки показали: Drive → Salesforce flow стоил 150 000 токенов при наивной загрузке MCP. С code-execution и on-demand discovery инструментов тот же flow прошёл в 2 000 токенов — сокращение на 98.7%. Их follow-up Tool Search Tool (API version 20251119) откладывает определения инструментов до момента, когда они нужны; документация утверждает, что это обычно режет раздувание контекста более чем на 85%, и отмечает, что «Claude's ability to correctly pick the right tool degrades significantly once you exceed 30–50 available tools» (способность Claude правильно выбирать инструмент существенно деградирует, как только превышаете 30–50 доступных инструментов).
Урок, к которому все сходятся: меньше, уже, проектно-релевантнее — каждый раз бьёт гигантский каталог. Модель работает лучше. Счёт за токены падает. Дрейф падает вместе с ним.
Так что правильный вопрос — не «как мне написать больше MCP?». Он звучит: «как сделать так, чтобы те, что мне реально нужны, ничего не стоили в написании и доставке?»
Почему «просто напиши MCP-сервер» не масштабируется
Февральский 2026 анализ Bloomberry 1 412 MCP-серверов обнаружил, что 38.7% из них поставляются вообще без аутентификации, медианный сервер выставляет всего пять инструментов, и примерно половина публикующих компаний не имеет публичного API в принципе. Формулировка Bloomberry: «MCP might not be a wrapper on an existing API. It might be the first machine-readable interface they've ever shipped» (MCP может быть не обёрткой над существующим API. Может быть первым machine-readable интерфейсом, который они когда-либо выпускали). Большая часть взрыва — это люди, разбирающиеся в продакшене.
Проблема не в том, что протокол сломан. Проблема в том, что единица работы неправильная для проектно-специфичных действий. Писать Python или Node MCP-сервер со stdio framing, регистрацией схем и отдельным деплой-пайплайном — правильная форма для «GitHub API» или «Postgres database». Неправильная форма для «запусти наш staging deploy script» — куска кода в сорок строк bash, уже лежащего в bin/deploy, который вы хотите, чтобы агент звал по имени вместо того, чтобы угадывать.
Практики приходят к тому же выводу. Из треда на Hacker News «MCP explained without hype or fluff» (item 44063141), коммент от 0x457:
"Many of MCPs shouldn't have existed, IMO. […] I wanted amazon q to interact with github, so I added to its context that it can use
gh-cliand it it worked pretty well. […] To make its and mine life easier, common things were saved as scripts inbin/and Justfile (also generated by it). […] Using github's official MCP bricks chat session every time for me." (Многим MCP не следовало существовать. Я хотел, чтобы amazon q взаимодействовал с github — добавил в контекст, что он может использоватьgh-cli, и работало вполне нормально. Чтобы упростить жизнь и себе, и ему, частые штуки сохранялись скриптами вbin/и Justfile (тоже сгенерированный им). Официальный MCP от github у меня каждый раз кладёт чат-сессию.)
И dvt:
"MCP is bloated AI hype that basically solves nothing […]. It's APIs talking to APIs that talk to other APIs." (MCP — раздутый ИИ-хайп, который по сути ничего не решает. Это API, говорящие с API, которые говорят с другими API.)
Оба коммента правы для случаев, которые описывают. CLI работают. Локальные скрипты в bin/ работают. Заворачивать однострочную shell-команду в 200-строчный MCP-сервер, потому что это единственный одобренный паттерн, — вот что сломано.
Что такое MCP-инструмент на самом деле
Сними протокол — и MCP-инструмент это три вещи:
- Имя и описание — чтобы модель знала, когда за ним тянуться.
- Схема параметров — чтобы модель знала, что передавать.
- Исполняемый файл — который делает работу.
Всё остальное — JSON-RPC framing, stdio handshake, capability negotiation — забота рантайма, не автора. Статья 2023 года Tool Documentation Enables Zero-Shot Tool-Usage with Large Language Models (Hsieh et al.) эмпирически показала: для незнакомых инструментов ясная документация бьёт few-shot демонстрации — и что «zero-shot prompts with only tool documentation» могут сравняться с few-shot промптами на out-of-distribution задачах. Импликация: короткое, точное описание инструмента ценнее сложной схемы или стопки примеров.
Если инструмент короткий и доки короткие, весь интерфейс должен выражаться несколькими строками комментариев в шапке скрипта, который делает работу.
Это и есть то, что делает Octomind 0.29.0.
Паттерн: .agents/tools/<name>
Вот весь контракт:
mkdir -p .agents/tools
cat > .agents/tools/deploy <<'EOF'
#!/usr/bin/env bash
# @description Deploy the API service to a target environment using our team's pipeline.
# @param *env string Target environment (staging|production)
# @param dry_run boolean If true, validate without applying
set -euo pipefail
cd "$OCTOMIND_WORKDIR"
env="$OCTOMIND_PARAM_ENV"
dry="${OCTOMIND_PARAM_DRY_RUN:-false}"
if [[ "$dry" == "true" ]]; then
./bin/deploy --env "$env" --dry-run
else
./bin/deploy --env "$env"
fi
EOF
chmod +x .agents/tools/deploy
Три вещи только что произошли:
- Octomind обнаружил скрипт при следующем запуске сессии.
- Распарсил строки
@descriptionи@paramв JSON-Schema определение инструмента. - Модель теперь видит инструмент
deploy— заскоупленный на этот репо — сenv(обязательным) иdry_run(опциональным). Она выберетdeploy(env="staging")вместоbash("./bin/deploy --env staging"), потому что у именованного инструмента яснее соотношение сигнал/шум.
Эта предпочтительность — именно тот guardrail, который вам нужен. Модель больше не свободно ассоциирует от «deploy» к «kubectl apply» к «helm install». У неё одна точка входа, с типизированным параметром, делающая реальный деплой команды.
Чтобы проверить связку, запустите однострочник валидации:
echo "/mcp" | octomind run
Увидите local MCP-сервер в выводе с deploy под ним. Если скрипт не появляется, рантайм скажет ровно почему — неверные права, отсутствует @description, точка в имени файла.
Анатомия шапки
Ведущий блок комментариев — это весь интерфейс. Octomind перечитывает его на каждом ходу (дёшево — один read_dir) и пересобирает схему. Отредактировал и сохранил — следующий вызов инструмента использует новую версию. Никакого демона перезапускать не надо.
#!/usr/bin/env bash
# @description Short summary the model sees. Continuation lines without an
# @-tag append to the previous one, so multi-line descriptions Just Work.
# @param *target string Path to operate on (* prefix = required)
# @param force boolean Overwrite if the destination exists
# @param count integer Number of iterations
# @param tags array JSON list, e.g. ["foo","bar"]
Префикс комментария может быть # (bash, python, ruby, lua, perl, awk), // (node, deno) или -- (lua, SQL-обёртки). Рантайму всё равно, какой язык делает работу.
Когда модель вызывает инструмент, octomind отдаёт параметры двумя способами:
| Канал | Форма |
|---|---|
| stdin | Один JSON-объект, затем EOF: {"target":"src/x","force":true} |
| env | OCTOMIND_PARAM_TARGET=src/x, OCTOMIND_PARAM_FORCE=true, плюс OCTOMIND_WORKDIR и OCTOMIND_TOOL_NAME |
Bash-скрипты обычно читают env-переменные. Python-скрипты обычно парсят stdin JSON. Оба приходят на каждом вызове. Stdout становится результатом, который видит модель. Stderr добавляется с маркером [stderr]. Ненулевой exit — ошибка инструмента.
Это вся поверхность.
Четыре реальных дрейфа, четыре реальных инструмента
Мы видели, как агент падал на каждом из этих в наших собственных репо, пока не написали обёртку. Обёртка остановила падение.
1. Дрейф: «Запускает npm test в pnpm монорепо»
Агент видит package.json в корне, набирает npm test, получает стену ошибок «no script found» и докладывает, что проект сломан.
Обёртка — .agents/tools/test:
#!/usr/bin/env bash
# @description Run the project test suite. Defaults to fast unit tests.
# @param scope string One of: unit, integration, all (default: unit)
# @param package string Optional package filter, e.g. @acme/api
set -euo pipefail
cd "$OCTOMIND_WORKDIR"
scope="${OCTOMIND_PARAM_SCOPE:-unit}"
pkg="${OCTOMIND_PARAM_PACKAGE:-}"
case "$scope" in
unit) cmd=("pnpm" "-r" "test:unit") ;;
integration) cmd=("pnpm" "-r" "test:integration" "--" "--runInBand") ;;
all) cmd=("pnpm" "-r" "test") ;;
*) echo "Unknown scope: $scope" >&2; exit 2 ;;
esac
[[ -n "$pkg" ]] && cmd+=("--filter" "$pkg")
exec "${cmd[@]}"
Теперь агент видит test(scope, package) в своём списке инструментов. Описание говорит ему про дефолты. Он зовёт test() и получает реальный результат с первой попытки. Никто в команде больше не печатает «используй pnpm -r test:unit» в чат.
2. Дрейф: «Галлюцинировал staging-URL»
Агенту нужно прочитать запись из реального staging API по https://api-staging.acme.internal/v3/users/42, который требует заголовок X-Acme-Token. Вместо этого он curl'ит https://staging.example.com/api/users/42 — URL, который он выдумал из обучающих данных, — ничего не получает и докладывает, что сервис лежит.
Обёртка — .agents/tools/api:
#!/usr/bin/env bash
# @description Call our internal staging API. Returns raw JSON.
# @param *path string Path under /v3, e.g. /users/42
# @param method string HTTP method (default: GET)
# @param body string Optional JSON body for POST/PUT/PATCH
set -euo pipefail
: "${ACME_STAGING_TOKEN:?ACME_STAGING_TOKEN not set in environment}"
path="$OCTOMIND_PARAM_PATH"
method="${OCTOMIND_PARAM_METHOD:-GET}"
body="${OCTOMIND_PARAM_BODY:-}"
url="https://api-staging.acme.internal/v3${path}"
args=(-sS -X "$method" -H "X-Acme-Token: $ACME_STAGING_TOKEN")
[[ -n "$body" ]] && args+=(-H "Content-Type: application/json" --data "$body")
curl "${args[@]}" "$url"
URL больше не дрейфует, потому что URL больше не работа модели. Агент зовёт api(path="/users/42"), а реальный URL живёт в скрипте. Токен приходит из окружения разработчика (.envrc, direnv, ваш shell rc) — инструмент закоммичен, секрет — нет.
3. Дрейф: «Четвёртый раз переобъясняет, как устроен feature-flag сервис»
Агент дебажит UI-проблему. Вы напоминаете ему (опять), что feature-flag сервис существует, что флаги отличаются по окружениям и что staging — релевантное. Он кивает, забывает, спрашивает снова в следующей сессии.
Обёртка — .agents/tools/flags:
#!/usr/bin/env python3
# @description Return the currently enabled feature flags for an environment.
# @param env string Environment to inspect (default: staging)
import json, os, sys, urllib.request
params = json.load(sys.stdin) if not sys.stdin.isatty() else {}
env = params.get("env") or os.environ.get("OCTOMIND_PARAM_ENV") or "staging"
url = f"https://flags.acme.internal/api/snapshot?env={env}"
req = urllib.request.Request(url, headers={"X-Acme-Token": os.environ["ACME_TOKEN"]})
with urllib.request.urlopen(req, timeout=10) as r:
flags = json.load(r)
enabled = sorted(k for k, v in flags.items() if v.get("enabled"))
print("\n".join(enabled) if enabled else "(no flags enabled)")
Модель теперь открывает сессию, видит flags в списке инструментов и вызывает сама, когда надо. Вы перестаёте быть человеческим кэшем для «что включено на staging».
4. Дрейф: «Использовал git tag вместо нашего релизного скрипта»
Ваша команда сделала acme-cli, чтобы бамп версии, ченджлоги и tag-then-push случались в правильном порядке. Агент его пропускает, запускает голый git tag v1.2.3, ломает конвенцию, и теперь следующий CI-ран не может найти ченджлог.
Обёртка — .agents/tools/release:
#!/usr/bin/env bash
# @description Cut a release using the team's acme-cli. Bumps version, updates
# CHANGELOG, tags the commit, and pushes. Use bump=patch unless minor or major
# is explicitly requested.
# @param *bump string One of: patch, minor, major
# @param dry_run boolean If true, show what would happen without pushing
set -euo pipefail
cd "$OCTOMIND_WORKDIR"
bump="$OCTOMIND_PARAM_BUMP"
dry="${OCTOMIND_PARAM_DRY_RUN:-false}"
args=(release --bump "$bump")
[[ "$dry" == "true" ]] && args+=(--dry-run)
acme-cli "${args[@]}"
Вот этот переубеждает соседей по команде. Агент теперь сам следует релизной конвенции — версия, ченджлог, тег, push — потому что инструмент существует в репо, и модель выбирает его вместо голого git.
Кумулятивный эффект: оно живёт в репо
Пост Anthropic про code-execution в MCP утверждает: будущее — за динамическим обнаружением: грузить инструменты, когда нужны, выбрасывать иначе. Shebang-паттерн — local-first версия той же идеи. Три свойства складываются:
1. Узкая поверхность, низкий дрейф. Модель видит именованное, параметризованное действие с однострочным описанием вместо общего shell. Hsieh et al. обнаружили, что само описание делает основную работу для выбора инструмента — примеры nice-to-have. Shebang-шапка — ровно правильная форма для этого: имя, описание, типизированные параметры, всё в блоке комментариев, который автор инструмента уже поддерживает рядом с кодом.
2. Вся команда получает это бесплатно. Традиционный MCP-сервер живёт на ноутбуке одного разработчика. Когда сокомандник клонирует репо, сервера там нет, агент инструмента не видит, и дрейф возвращается. Скрипт .agents/tools/ закоммичен в git. Клон, octomind run — те же инструменты, что у всех.
3. Инструменты эволюционируют вместе с кодом. Переименовали сервис? Обновите обёртку в том же PR. Добавили feature-флаг? Обновите обёртку в том же PR. Ваш ИИ-тулинг подлежит код-ревью, как и всё остальное. Дрейф между тем, как агент думает, репо выглядит, и как репо выглядит на самом деле — медленногорящий убийца агентской надёжности — закрывается автоматически.
После недели такой работы агент в терминале каждого разработчика сходится к одному поведению, потому что все запускают одни и те же инструменты из одного и того же репо. Институциональное знание перестаёт быть «что я сказал агенту в чате» и становится версионируемым, проверяемым артефактом.
Когда всё-таки тянуться за MCP-сервером
Локальные инструменты покрывают проектно-специфичные 90%. Не покрывают всё. Используйте матрицу:
| Хотите… | Используйте |
|---|---|
| Проектно-специфичное действие (deploy, test, query флагов, hit внутреннего эндпойнта) | .agents/tools/ shebang |
| Инжектить доменные инструкции в контекст | Skills |
| Переиспользуемый кросс-проектный инструмент с богатой схемой и многошаговой логикой | Авторить tap с MCP-сервером |
| Долгоживущий процесс (connection pool, browser session, websocket) | Внешний stdio/http MCP-сервер |
Ментальная модель: если тело вашего MCP-сервера это «распарси параметры, спавни этот бинарник, верни stdout», вам не нужен сервер — вам нужен 15-строчный shell-скрипт.
Цикл валидации
Запускайте это после написания или изменения любого инструмента:
echo "/mcp" | octomind run
Поднимает сессию в текущей директории, перечисляет каждый MCP-сервер в скоупе и выходит. Увидите:
local: ✅ Running
Type: LocalTools
Configured tools: deploy, test, api, flags, release
Если инструмента нет:
| Симптом | Причина | Фикс |
|---|---|---|
Нет в выводе /mcp |
Не исполняемый | chmod +x .agents/tools/<name> |
Нет в выводе /mcp |
Отсутствует @description |
Добавьте строку |
Нет в выводе /mcp |
В имени файла есть . |
Уберите расширение (deploy, не deploy.sh) |
| Инструмент падает при вызове | Несовпадение чтения параметров | Убедитесь, что читаете OCTOMIND_PARAM_* или stdin, не $1 |
Для более глубокого дебага: OCTOMIND_LOG=debug echo "/mcp" | octomind run покажет, почему каждый кандидат-файл был принят или пропущен.
Рантайм относится к .agents/tools/ как hot-reloaded — отредактировал, сохранил, следующий вызов инструмента использует новую версию. Никакого octomind restart, никакого демона. Пиши, сохраняй, вызывай.
С чего начать
Если octomind у вас нет:
# macOS
brew install muvon/tap/octomind
# Any platform
cargo install octomind
Потом в любом репо:
mkdir -p .agents/tools
cat > .agents/tools/branch <<'EOF'
#!/usr/bin/env bash
# @description Print the current branch name.
git -C "$OCTOMIND_WORKDIR" rev-parse --abbrev-ref HEAD
EOF
chmod +x .agents/tools/branch
echo "/mcp" | octomind run
Должны увидеть local со списком branch. Закоммитьте:
git add .agents/tools/branch
git commit -m "agent: add branch-name tool for AI sessions"
Вот и весь паттерн. Каждый разработчик, кто пуллит репо, получает инструмент. Каждый запуск агента в директории его видит. Никакой дополнительной настройки нигде ни на чьей машине.
FAQ
Почему обёрнутый инструмент снижает дрейф, если базовая команда та же?
Потому что модель больше не выбирает между тысячами одинаково правдоподобных shell-вызовов. Она выбирает между горсткой именованных, типизированных инструментов с описаниями, написанными для неё. RAG-MCP замерил эффект: точность выбора инструмента схлопывается по мере роста каталога. Малое именованное множество с ясными описаниями — эмпирически правильная форма для tool-using LLM.
Это специфично для Octomind?
Обнаружение .agents/tools/ — фича рантайма Octomind. Внутри Octomind каждая модель — Claude, Kimi, GLM, GPT, локальные модели — видит их как стандартные MCP-инструменты. Если ещё используете Claude Desktop или Cursor, можно обернуть .agents/tools/ тонким MCP-шимом, но самый большой leverage — внутри Octomind, где обнаружение автоматическое и project-scoped.
Что с секретами?
Инструмент в репо. Секрет — нет. Скрипты читают ACME_TOKEN (или похожее) из окружения разработчика — direnv, .envrc, shell rc. Коммитьте инструмент, не credential. Если env-переменной нет, выходите с ненулевым кодом и понятным сообщением, чтобы модель показала реальную ошибку, а не выдумала её.
Может агент запускать любой из этих инструментов без моего одобрения каждого вызова?
Да — тот же permission flow, что у любого MCP-инструмента. Auto-approve для read-only (branch, flags). Always-ask для разрушающих (deploy, release). Octomind относится к локальным инструментам как к first-class в permission-системе.
Что если хочу один и тот же инструмент в нескольких репо?
Два варианта. Лёгкий: симлинк .agents/tools/foo на общую локацию. Правильный: запилить tap — реестр в стиле Homebrew. Локальные инструменты — для этого репо специфично. Tap'ы — для кросс-проектной переиспользуемости.
Работает ли в CI / неинтерактивных запусках?
Да. octomind run --format=plain и --format=jsonl оба уважают .agents/tools/. Мы гоняем agent-driven код-ревью в собственном CI таким образом — инструменты lint, test и coverage ревью-агента живут в .agents/tools/ и оборачивают точные команды команды.
Имя инструмента конфликтует со встроенным. Что происходит?
Встроенный побеждает. Не получится затенить shell или view, назвав скрипт shell — octomind логирует коллизию и оставляет оригинал. Намеренный guardrail.
Сдвиг
Сейчас рассказывают историю, что будущее агентов — это больше MCP-серверов: больше каталоги, богаче экосистемы, больше интеграций. Данные из RAG-MCP, аудит Bloomberry, пост Anthropic про code-execution и разработчики, тихо возвращающиеся к CLI, — всё указывает в другую сторону: агенты, лучше всех работающие в реальных репо, видят меньше инструментов, более узкие инструменты и инструменты, написанные под проект, в котором они находятся.
Скрипт .agents/tools/ — наименьшая возможная единица этого паттерна. Шестьдесят секунд от «я опять смотрю, как агент дрейфует на этом» до «у агента есть инструмент, у команды он есть, он в git, поехали дальше».
Попробуйте на следующей штуке, которую ловите себя на том, что объясняете дважды.
— Don
Octomind — open source под Apache-2.0. Фича локальных инструментов появилась в 0.29.0. Если что-то здесь разблокирует ваш воркфлоу, откройте issue — фичи, запрошенные в мае, обычно выезжают в июне.


