Давайте будем честны: текущее состояние вызова функций (Function Calling) у LLM и экосистемы MCP (Model Context Protocol) вызывает изжогу в лучшем случае. Чтобы дать агенту возможность сделать curl или дернуть локальный скрипт, современному разработчику предлагают:

  1. Описать килобайты монструозных JSON-схем прямо в системном промпте (и спалить 15-30k токенов контекста еще до первого вопроса пользователя).

  2. Написать полноценный сетевой MCP-сервер со своим бойлерплейтом, SDK и зависимостями.

  3. Завернуть всё это в жирный Docker-контейнер, выделив 200 Мб ОЗУ ради выполнения элементарного скрипта на три строки.

И один bash-tool вместо десяти нормальных инструментов задачу не решает, сессии превращаются в нечто вроде (псевдокод):

bash("systemctl is-active my-app.service")
bash("journalctl -u my-app.service -n 50 --no-pager")
bash("ss -tunlp | grep 3000")
bash("kill -9 $(lsof -t -i:3000)")
bash("systemctl restart my-app.service")

Что повторяется три раза только за эту сессию, и это - совсем не DRY (Don’t Repeat Yourself). Интуитивно у инженера в этом моменте слегка подскакивает пульс и давление, и он хочет абстрагировать это в call_tool("check_journals_and_stats", {...})

А когда в проекте накапливается больше 30 таких абстракций - модель неизбежно ловит Lost in the Middle и начинает галлюцинировать схемами, а разработчик оплачивает тонны входящих токенов за документацию API, которую агент даже не собирался вызывать в текущей сессии.

Отдельное спасибо здесь стоит сказать маркетингу Anthropic - работает он отлично. Термины вроде MCP, agent, harness, tooling теперь на слуху у людей, которые до этого к тулингу LLM никакого отношения не имели, причём многие оперируют ими скорее на уровне потребления хайпа, чем реального понимания. Модели у Anthropic отличные, тут вопросов нет. Но стоит помнить, что всё вокруг их флагманского продукта (включая сам MCP как протокол) в первую очередь продвигает этот продукт, а развитие области вокруг него - уже вторичная задача.

Короче, я устал от натягивания совы на глобус именованием MCP как стандартизированного протокола для всего тулинга самой популярной области последних лет и сделал ?️ toolhub - self-hosted движок, который превращает любой консольный скрипт (Bun, Python, Bash, Go, PHP, etc) в инструмент для LLM на лету, с иерархической навигацией по дереву папок и околонулевыми требованиями к зависимостям.

Файловая система навыков вместо плоского списка

Почему контекст модели забивается? Потому что типичные MCP-прокси дают модели плоский массив из 100 тулов.

У меня подход иной - древовидная навигация:

/
├── system/
│   ├── fetch_logs
│   └── restart_service
├── database/
│   └── query_pg
└── sublime/
    └── replace-literal

Скажите мне, серьёзные разработчики, ML-щики, архитекторы, системные аналитики - не это ли было самым адекватным путём с самого начала? Хотя бы с момента, когда стало ясно, что без кеширования контекста направление продолжит быть убыточным.

Модель не знает схемы сотен инструментов заранее. На старте через hub.getSmartPrompt() в системный промпт инжектится только корневая карта дерева с краткими аннотациями назначений категорий (1–2 строки на папку). Модель стартует с базовым контрактом:

  1. listTools("/system") - посмотреть, что валяется в конкретной ветке, и подтянуть точные описания только нужных инструментов.

  2. callTool("/system/fetch_logs", { "service": "nginx" }) - вызвать конкретный инструмент с конкретными параметрами.

Результат: окно контекста чистое, модель лезет в папку только тогда, когда ей действительно нужен инструмент из этой категории. Когда она понимает, что к текущему запросу папка с ЭТИМ названием может иметь непосредственное отношение.

Важное уточнение - toolhub сознательно не работает ни с одним из стандартных интерфейсов tool calling каких-то конкретных провайдеров. Весь Re-Act loop построен на классических XML-like командах:

<hub>listTools("/")</hub>
<hub>callTool("/sublime/list-project-files", {})</hub>

Так с минимальным оверхедом на вызов мы даём возможность пользоваться тулами абсолютно любым моделям, даже если у них не встроена поддержка тулинга, или она сделана как-то странно/криво. По сути XML-обвязка (<hub>...</hub>) сопоставима или легче, чем JSON-разметка нативного tool_call у большинства провайдеров. Единственный таким образом порог входа для модели - понимание, что такое xml, json, js и древовидная навигация.

Как это работает

Поднимать Docker-контейнер на каждый чих - это дорого по ресурсам (особенно если вы крутите агентов на скромной VPS или Raspberry Pi). toolhub использует только Bun и его возможности. Глобальная зависимость всего одна, так что я даже не стал собирать для вас бойлерплейтные Dockerfile || docker-compose.yml - нужны они будут не всем, собрать самому - 3 минуты.

Механика исполнения под капотом простая как топор:

Workspace (/tmp) ➔ Инъекция (код + input.json) ➔ Установка deps ➔ runCmd с таймаутом ➔ rm -rf

  1. Создание изолята: На лету генерируется временная папка /tmp/hub_run_<timestamp>_<hash>.

  2. Инъекция кода и аргументов: В папку кладется код и input.json, а все параметры вызова автоматически пробрасываются в ENV-переменные вида $INPUT_<KEY_NAME>. Хочешь - парси JSON в коде, хочешь - пиши bash-однострочник, который сразу читает $INPUT_HOST. Никаких обвязок.

  3. Зависимости: Если прописан installCmd, он отрабатывает в контексте папки. Bun и современные пакетные менеджеры за счет глобального кэша хоста подтягивают модули за миллисекунды; для Python-скриптов аналогично используются локальные wheel-кэши либо глобальное окружение хоста.

  4. Запуск процесса: Выполняется runCmd с жестким ограничением timeoutMs. Результат считывается из output.json или stdout.

  5. Очистка: Папка воркспейса сразу же аннигилируется через rm -rf.

Если скрипт выполняется в консоли - он автоматически становится навыком ИИ. Без написания оберток на протоколе MCP.

А что с MCP?

Я не против MCP как идеи, я против его кривых реализаций. В toolhub нативная поддержка MCP реализована в двух режимах:

  1. Stateless (Stdio): Классика. Спавнится процесс -> отправляется запрос -> профит -> процесс убивается. Подходит для разовых утилит, т.е. для 90% того что есть на рынке. Так же для удалённых MCP в том числе.

  2. Stateful Pool (Persistent PID): Самое вкусное. Если ваш MCP-сервер держит тяжелый контекст (Puppeteer с авторизованной сессией браузера, SSH-соединение, пул подключений к БД) - toolhub держит процесс в памяти. Повторные вызовы отрабатывают за 10–30 мс без затрат на холодный старт. Простой регулируется через TTL (по дефолту 5 минут), после чего память освобождается. Повторное использование агентом до истечения TTL - продлевает его жизнедеятельность.

Если MCP-инструмент из внешнего сервера вам понравился, но вам хочется затюнить его описание под свою модель, навесить строгую output-schema или прописать few-shots примеры (что классический MCP напрямую не поддерживает) - кнопка MCP Promote в админке в 1 клик переносит его метаданные и схемы вызова напрямую в базу toolhub, позволяя докручивать промпт и валидацию инструмента прямо из UI.

«А если я сижу в Claude Code / Cursor и хочу дергать тулы оттуда?»

Сам я не фанат завязки на конкретные закрытые проприетарные CLI, но если вы плотно сидите на Claude Code, Cursor или Cline - концептуально вам ничего не мешает использовать toolhub как единый бэкенд навыков.

Для этого достаточно написать микроскопический Stdio-мост на 40 строк (toolhub-mcp-bridge), который будет транслировать агентские вызовы Клода в REST API вашего локального toolhub. Прописали один сервер в конфиг - и Клодоподобные на лету видят всю вашу древовидную экосистему скриптов.

Перчатка брошена: кто первый напишет и оформит красивый toolhub-mcp-bridge в виде отдельного пакета/PR - с удовольствием добавлю в официальный README и закреплю авторство!

Бесконечная Федеративность

Узлы toolhub умеют каскадироваться.

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

Для агента это выглядит как единый путь: <hub>callTool("/office/home/server/reboot", {})</hub>

HubSDK берет на себя рекурсивную нормализацию путей, проксирование авторизации и прозрачное туннелирование запросов. А для защиты от дурака и циклических ссылок (когда Сервер А случайно ссылается на Сервер Б, ссылающийся на А) каждый запрос маркируется цепочкой заголовков x-hub-nodes с отслеживанием node_id каждого узла и жестким лимитом глубины (maxHops, по умолчанию задан разумный предел).

Шеринг навыков и встроенный Time-Machine

Любой инструмент или целую ветку дерева со всеми подпапками, раннерами и схемами можно экспортировать в один переносимый JSON-манифест .toolpack. Написал крутую связку для деплоя или работы с базой - выгрузил в 1 клик, закинул коллеге, он нажал «Import», и навыки сразу появились в его агенте. Так же с отдельными тулами.

А чтобы не бояться экспериментировать с промптами и кодом инструментов прямо в админке, toolhub ведет автоматический журнал ревизий (ToolVersion). Каждое сохранение создает неизменяемый снимок кода, схем и описания с возможностью отката к любой предыдущей версии в один клик.

Бенчмарки и производительность: а не медленно ли?

Главная претензия теоретиков к древовидной навигации: «Это же лишний RTT! Зачем делать listTools, если можно отправить всё сразу?»

Воспроизводимый бенчмарк-сьют - код лежит прямо в репозитории в папке basic-toolpacks/benchmark/. Замеры проводились на локальном тестовом стенде (VM в Proxmox, 12 vCPU Intel i5-14500 cpu_type: host, 32 GB RAM, Debian 12, NVMe SSD).

Методология: 100 последовательных итераций на каждый сценарий с расчетом p50, p90, p99 перцентилей + стресс-тест на параллельный спавн воркспейсов:

Сценарий / Нагрузка

Среда / Раннер

Cold Start

Avg Latency

p50

p90

p99

Throughput

listTools("/") (Корень)

Navigation Engine

5.5 ms

1.0 ms

0.8 ms

1.7 ms

2.6 ms

~990 req/s

listTools("/bench") (Ветка)

Navigation Engine

1.7 ms

1.5 ms

1.4 ms

2.1 ms

3.6 ms

~680 req/s

Bash System Telemetry

OS Spawn (/bin/sh)

33.7 ms

13.4 ms

12.7 ms

20.3 ms

27.1 ms

~75 req/s

Bun CPU Cruncher (50k iters)

Bun Runtime

30.9 ms

25.8 ms

25.2 ms

30.1 ms

38.0 ms

~39 req/s

Bun Heavy Disk I/O (25 files batch)

FS Workspace I/O

31.7 ms

34.3 ms

26.2 ms

66.7 ms

129.6 ms

~29 req/s

Python 3 SHA256 (500 hashes)

Python 3 Process

69.5 ms

64.8 ms

41.6 ms

129.1 ms

177.0 ms

~15 req/s

Важно про железо: замеры выше сделаны на мощной VM (12 vCPU/32GB) - это потолок производительности runtime’а, а не то, что вы получите на Raspberry Pi или бюджетной VPS. На слабом железе (1 vCPU/512MB) абсолютные цифры будут выше в разы, но относительная разница между навигацией по дереву (~1 мс) и полноценным спавном процесса (10-60+ мс) сохранится - узкое место всегда сам раннер, а не Navigation Engine.

Параллельный стресс-тест на изоляцию воркспейсов:

15 одновременных вызовов тулов отработали параллельно суммарно за 138 мс (Ошибок: 0, 100% изоляция ФС во временных директориях).

Что значат эти цифры?

  1. Задержка навигации по дереву каталогов составляет 1.0–1.5 мс. Замеры отражают чистый overhead самого toolhub Runtime. На фоне сетевого latency и генерации токенов LLM (500–2000 мс) оверхед toolhub - это абсолютная погрешность.

  2. Полный жизненный цикл одноразового bash-инструмента (генерация воркспейса /tmp/hub_run_*, запись input.json, спавн процесса, чтение output.json и атомарный rm -rf) занимает всего 13 миллисекунд.

Про базу данных: По умолчанию из коробки используется легковесная встроенная SQLite - чтобы проект поднимался без поднятия отдельных сервисов СУБД. Но благодаря тому, что весь бэкенд построен на Prisma ORM, при желании вынести ToolHub в высоконагруженный корпоративный кластер переезд на PostgreSQL, MySQL или CockroachDB сводится грубо говоря к изменению одной строчки провайдера в schema.prisma.

Когда дерево НЕ даёт выигрыша (или даже мешает) - честный список:

Ситуация

Почему

Мало тулов (до ~10)

Схема и так маленькая, а лишний listTools-запрос - это чистый оверхед без экономии

Агент трогает почти все категории за сессию

Если он всё равно облазит 50 из 60 тулов - дерево платит почти как плоский список, но ещё и с лишним RTT сверху

Задача всегда одна и та же, тул известен заранее

Навигация не нужна, если и так понятно, что вызывать

Слабая модель с плохим tool-use

Двухшаговая схема (сначала посмотреть, потом вызвать) требует дисциплины; модель послабее может путать пути или забывать зайти в папку - тут надёжность плоского списка важнее экономии токенов

Сервис с очень высокой параллельностью на одном тулсете

Нишевый кейс, не основной сценарий toolhub - подробно можно обсудить отдельно, если кому-то актуально

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

Быстрый запуск

# 1. Клонируем
git clone https://github.com/Talos-Popcorn/toolhub.git
cd toolhub

# 2. Установка зависимостей
bun install

# 3. Синхронизация схемы БД Prisma
bun run db:push

# 4. Интерактивный seed+install
# (Выбор языка системного промпта EN/RU/ZH, настройка паролей админки и агента)
bun run db:seed

# Или в тихом режиме для CI/Docker:
# bun run prisma/seed.ts --lang=ru --admin-pass=admin --agent-pass=123

# 5. Запуск в дев режиме
bun run dev

Панель управления доступна по адресу http://localhost:5173/admin/ (или порт 3000 в прод-сборке). Swagger/OpenAPI документация: http://localhost:3000/docs (в дев-режиме). Дальше - импортируем какие-нибудь тулпаки из папки basic-toolpacks и пробуем запускать их в Playground.

Интеграция в любой JS/TS код занимает пару строк:

import { HubSDK } from './SDK/JS/sdk';

const hub = new HubSDK('http://localhost:3000', '123');

async function runAgentLoop(userQuery: string) {
  const systemPrompt = await hub.getSmartPrompt();
  
  const messages = [
    { role: 'system', content: systemPrompt },
    { role: 'user', content: userQuery }
  ];

  while (true) {
    const aiResponse = await llm.generate(messages);
    const action = await hub.processAgentResponse(aiResponse);

    if (!action.called) {
      console.log('Ответ агента:', aiResponse);
      break;
    }

    messages.push({ role: 'assistant', content: aiResponse });
    messages.push({ 
      role: 'user', 
      content: `HUB_RESULT: ${JSON.stringify(action.result)}` 
    });
  }
}

Кстати, для тех, кто пробовал мой прошлый проект ?lab (статья, репо) (serverless-клиент для LLM без бэкенда) - поддержка ?️ toolhub уже встроена и прекрасно работает. Теперь можно цеплять локальные тулы прямо из браузерного интерфейса. Смотрим, как работает:

Запуск из Лабы
Запуск из Лабы
Древовидная структура
Древовидная структура
Плейграунд
Плейграунд

Лицензия и Open Source

Проект полностью открыт, распространяется под AGPL-3.0. Первичная задача - выкинуть из стека лишний оверхед и дать нормальный инструмент для быстрой автоматизации без переплат за токены.

Буду рад конструктивной критике, issue и пулл-реквестам.

FADQ (Frequently Asked Dушные Questions)
  1. Запускать такое на рабочей тачке - опасно! Да, исполнение скриптов прямо в ОС хоста без изоляции несет риски. То, что по дефолту toolhub работает без Docker - это фича для экономии ресурсов на слабых VPS и Raspberry Pi. Заворачивать сам toolhub в Docker, LXC-контейнер или отдельную VM - это задача оператора под конкретный контур безопасности. Тебе никто этого не запрещает!

  2. Зачем мне toolhub, если есть LangChain / LlamaIndex / AutoGen? Затем, что это тяжелые фреймворки с горой абстракций, жестко завязанные на код вашего приложения. ?️ toolhub - это самостоятельный standalone-сервис. Вы можете менять языки программирования, переключать LLM-провайдеров или переписывать агентов с нуля - ваши инструменты остаются централизованными, версионируемыми и доступными по единому REST/SDK API.

  3. «Древовидная навигация через listTools - это же лишние RTT (Round Trip Time)! Модель тратит лишние итерации вместо того, чтобы сразу вызвать тул!» Арифметика простая: listTools("/") уходит в системный промпт через getSmartPrompt() и кэшируется точно так же, как и плоский список схем. А вот когда агент дальше идёт в конкретную папку - listTools("/database") - это уже вызов внутри диалога, и вот тут дерево начинает реально экономить, потому что в контекст попадают схемы только нужной ветки, а не всех 60 тулов разом. Проверялось на синтетических тулпаках для типичного использования toolhub - один агент, личное пользование, разработка - дерево дешевле плоского списка на 60-85% уже начиная с 30+ тулов, и разрыв растёт вместе с их числом. Подробнее - выше!

  4. «Зачем писать бэкенд на Bun/TS? Почему не на Rust/Go, если мы боремся за производительность?» Потому что Bun дает идеальный баланс: стартует за миллисекунды, мгновенно исполняет JS/TS без транспиляции и не требует компиляции под каждую платформу. Если тебе нужен Rust - пиши тул на Rust, компилируй в бинарник и вызывай его через ToolHub как runCmd: "./my_rust_binary". ToolHub - ЯП-агностичен, а оверхед на запуск тулов благодаря Fastify минимален.

  5. «Что произойдет, если 50 агентов одновременно запустят скрипты с pip install? Все упадет от гонки зависимостей?» Каждый вызов исполняется в абсолютном изоляте /tmp/hub_run_<timestamp>_<hash>. Папки и файлы не пересекаются в файловой системе. Если тебе нужен глобальный кэш тяжелых пакетов - используй глобальный pip-кэш на хосте или нативные бинарные раннеры. Безусловно, важно понимать, что всё зависит от раннера, тула, и прямоты твоих рук.

  6. «А если скрипт зависнет в бесконечном цикле или пожрет всю память?» Для этого у каждого тула есть жестко заданный timeoutMs. По истечении таймаута процесс жестко гасится через SIGKILL, воркспейс подчищается через rm -rf, а агент получает нормальную ошибку с таймаутом, а не висит намертво.

  7. «Интеграция с Sublime Text в 2026 году?! Кто вообще пользуется Sublime, когда есть Cursor / VS Code?» Всё просто - я это сделал для себя. Мне важна скорость в 2 миллисекунды на diff, а не поедание 4 Гб ОЗУ Электроном ради подсветки пары строк. Поддержка Sublime сделана для тех, кто ценит реактивность. Хочешь под VS Code/Cursor/Windsurf/etc - SDK открыт, пиши .toolpack+bridge, PRы приветствуются. Лучшие тулпаки и тулы готов курировать, тестировать и добавлять в репо.

  8. Почему XML-теги через обычный текст? Потому что нативный Function Calling жестко привязывает к конкретным вендорам, часто ломает стриминг и плохо работает на открытых/локальных моделях. XML-формат универсален для любых сеток. В SDK toolhub встроена базовая санитаризация (игнорирование тегов внутри markdown-блоков с кодом, исправление базовых огрехов JSON вроде одинарных кавычек), но общая культура промптинга и экранирование в UI - это, как и всегда, ответственность архитектуры вашего приложения.

  9. Как это - нет докера? Что значит это фича? Это недоработка! toolhub рассчитан на Local-First / Trusted Node сценарии (когда агент крутится на твоем компе и выполняет ТВОИ скрипты). А если нужен Untrusted режим - докерфайл пишется за 3 секунды любой LLM. Сильно нужен готовый + композ = жду в issues и PR.

  10. А без LLM этим вообще можно пользоваться? Да! ?️ toolhub - это, по сути, сверхлегкий self-hosted FaaS (Function-as-a-Service) с древовидным REST API. Если тебе нужно из Node.js бэкенда прозрачно вызывать Python-скрипты, дергать bash-автоматизацию по вебхукам или централизованно хранить утилиты с логами и версионированием - ты просто используешь REST/SDK напрямую из обычного классического кода без всяких нейросетей.

  11. Три коммита в гитхабе? Четыре дня от роду? И ты предлагаешь мне этим пользоваться? Да так-то нет. Разработку своих проектов я веду локально в Gitea, и делюсь только тем, что действительно заслуживает шейринга. Самому проекту около 5 месяцев, а локальный репо насчитывает на данную секунду 57 коммитов. То же касается и ?lab и всего что я ещё захочу выложить.

Комментарии (16)


  1. r_o_m_k_o_l_a
    10.09.2026 13:44

    К разделу про зависшие скрипты: если внутри Python-инструмента тоже запускаются процессы, у Popen.communicate(timeout=...) есть неочевидная особенность. По таймауту он выбрасывает TimeoutExpired, но оставляет процесс работать. В документации для этого случая показаны proc.kill() и повторный proc.communicate(), чтобы дочитать вывод и дождаться завершения. У subprocess.run(timeout=...) завершение дочернего процесса уже встроено. Это полезно учитывать при переносе консольной утилиты в инструмент: иначе агент уже получил ошибку, а запущенная утилита ещё выполняет свою работу. Рецепт: https://docs.python.org/3/library/subprocess.html#subprocess.Popen.communicate


    1. Talos13 Автор
      10.09.2026 13:44

      Ценное предостережение, спасибо!

      Если внутри самого Python-скрипта запускаются вложенные сабпроцессы через голый Popen без subprocess.run или обвязки try/finally с proc.kill(), они могут остаться зомби в ОС даже после падения основного скрипта.

      Со стороны рантайма ToolHub внешний вызов глушится по жесткому таймауту на уровне ОС (через PID родительского процесса и последующую зачистку воркспейса), но чистоплотность внутренних форков внутри самого скрипта - это зона ответственности автора конкретного тула, и об этом важно помнить.

      Подождём ещё подобных edge case’ов, и в ридми их в дальнейшем добавлю обязательно.


      1. r_o_m_k_o_l_a
        10.09.2026 13:44

        Раз собираете edge cases — два наших, оба про то же место.


  1. gybson_63
    10.09.2026 13:44

    И чем это сильно отличается от skill?


    1. Talos13 Автор
      10.09.2026 13:44

      Примерно тем же, чем идея пожарить картошку отличается от плиты и сковородки.

      Покажите ваш конкретный "skill" - подробно на примере и рассмотрим чем отличается.


      1. gybson_63
        10.09.2026 13:44

        Да хоть такой

        brave-search-skills/skills/web-search/SKILL.md at main · brave/brave-search-skills

        Вообще на curl можно много скилов сделать. Т.е. такие же скилы есть для запуска из командной строки приложений служебных.

        Или такое
        1c-oData-skill/skills/odata/SKILL.md at main · gybson63/1c-oData-skill
        Здесь обертка mcp нужна только потому, что в винде проблема с русским языком бывает в скриптах


        1. Talos13 Автор
          10.09.2026 13:44

          Супер примеры! Это как раз идеальная демонстрация разницы между Prompt-driven подходом и Runtime-driven. Смотрите, оба ваших скилла - это гигантские инструкции на несколько тыщ токенов каждая.

          В вашем подходе:

          1. Вы заставляете LLM держать в контексте огромную простыню документации (разницу между OData v3/v4, правила substringof, необходимость собирать Base64 для Basic Auth, URL-encoding кириллицы %D0%...) и тд.

          2. Модель на каждом шаге сама пытается собрать длинную команду для универсального fetch/curl. Она может ошибиться в кавычках, забыть закодировать строку или сгенерировать невалидный заголовок.

          3. Если подключить 5–10 таких интеграций (1С, Brave, GitLab, Jira, Postgres) через скиллы - агент выжрет 20-30k токенов контекста еще до начала диалога.

          А сейчас мы возьмём и сделаем, к примеру, второй (потому что он посложнее и поинтереснее как по мне) ваш скилл в виде тула для тулхаба:

          Код
          import { readFileSync, writeFileSync } from 'fs';
          
          interface Input {
            entityType: 'Catalog' | 'Document' | 'InformationRegister' | 'AccumulationRegister';
            name: string;
            filter?: string;
            select?: string;
            top?: number;
          }
          
          async function run() {
            try {
              const input: Input = JSON.parse(readFileSync('input.json', 'utf-8'));
              const { entityType = 'Catalog', name, filter, select, top = 10 } = input;
          
              const baseUrl = process.env.ODATA_URL || 'http://localhost/base/odata/standard.odata';
              const user = process.env.ODATA_USER || 'Администратор';
              const pass = process.env.ODATA_PASS || 'пароль';
          
              // 1. Авто-кодирование кириллических сущностей 1С (Catalog_Сотрудники -> Catalog_%D0%A1...)
              const resource = `${entityType}_${encodeURIComponent(name)}`;
              
              // 2. Сборка OData v3 Query-параметров
              const params = new URLSearchParams();
              params.append('$format', 'json');
              if (top) params.append('$top', top.toString());
              if (select) params.append('$select', select);
              if (filter) params.append('$filter', filter);
          
              const auth = 'Basic ' + Buffer.from(`${user}:${pass}`, 'utf-8').toString('base64');
          
              const res = await fetch(`${baseUrl}/${resource}?${params.toString()}`, {
                headers: {
                  'Authorization': auth,
                  'Accept': 'application/json'
                }
              });
          
              if (!res.ok) {
                throw new Error(`1C OData Error ${res.status}: ${await res.text()}`);
              }
          
              const data = await res.json();
              
              // 1C отдает массив записей в поле .value
              writeFileSync('output.json', JSON.stringify(data.value || data));
            } catch (err: any) {
              writeFileSync('output.json', JSON.stringify({ error: err.message }));
            }
          }
          
          run();
          

          Это псевдокод, с 1C конкретно дел не имею, может потребовать адаптации под реалии, но суть - сколько тысяч токенов и минут сэкономили?)

          Итог простой: В случае со SKILL.md мы перекладываем на дорогую и недетерминированную LLM работу парсера, кодировщика строк и генератора curl-команд, попутно сжигая контекст на чтение инструкций. В случае с toolhub - мы отдаём эту рутину обычному коду за 5 миллисек, а модели оставляем только высокоуровневое намерение (“дай сотрудников с фамилией Иванов”), экономя неимоверное количество токенов и своего времени в процессе самой работы, тратя лишь часть его на разработку тула/тулпака.

          Вот в этом и есть фундаментальная разница: скилл учит модель, как костылить запрос в терминале, а toolhub даёт готовый атомарный микросервис, знать модели детали реализации которого для эффективной работы досконально не обязательно.

          Бонусом: все ваши скиллы model-specific, а тулхабом одинаково не смогут пользоваться разве что уж совсем маленькие и глупые модельки.

          Прям рекомендую попробовать в отдельном проекте, если понравится - переносить постепенно все .md на него. Разница в удобстве колоссальная.


          1. gybson_63
            10.09.2026 13:44

            Так в этом и смысл скила и отличие от MCP. Позволяем LLM самому написать, даже, например, запрос T-SQL адекватный именно текущему запросу. Ваш инструмент как обертка над скриптами может быть хорошей идеей. Но это надо доказать. И надо найти границу за которой ваш инструмент не станет тем же Курсором или Кодекс. Вы думаете они там ничего такого не придумали?

            Я больше скажу, mcp без соответствующих ему скилов может вообще быть бесполезен полностью, его не смогут позвать.

            Т.е. если человек не умеет свистеть, то ему можно дать свисток. Но ему все-равно надо будет объяснить когда, зачем и как свистеть, этого не избежать.


            1. Talos13 Автор
              10.09.2026 13:44

              Позволяем LLM самому написать, даже, например, запрос T-SQL адекватный именно текущему запросу.

              Так toolhub ровно это и позволяет! Никто не заставляет хардкодить логику. Вы можете сделать тул callTool("/db/execute_sql", { "query": "SELECT ... FROM ..." }), где модель сама на лету генерирует любой T-SQL под текущий контекст. Но сам коннект к СУБД, пул соединений, таймауты и отлов ошибок берет на себя рантайм, а не LLM через консольные костыли.

              И надо найти границу за которой ваш инструмент не станет тем же Курсором или Кодекс. Вы думаете они там ничего такого не придумали?

              Именно что придумали! Но они зашили это внутрь своих закрытых, платных и проприетарных экосистем. Та же фича у Claude Code, когда модель сама пишет скрипт и крутит его во временном окружении - это ровно тот же паттерн. Смысл toolhub - дать этот же уровень удобства в виде открытого, self-hosted и ЯП-агностичного Function-as-a-service движка для любой модели (включая локальные через Ollama или в связке со своим UI).

              mcp без соответствующих ему скилов может вообще быть бесполезен полностью, его не смогут позвать. Т.е. если человек не умеет свистеть, то ему можно дать свисток. Но ему все-равно надо будет объяснить когда, зачем и как свистеть...

              Тут вы на 100% правы, инструмент без контекста применения бесполезен. Возможно, я недостаточно явно подсветил это в статье: в toolhub именно что уже заложен этот механизм, причем иерархически:

              • На уровне папки (у каждой папки есть appendPrompt - добавочный промпт, его можно как подмешивать в системный так и просто кормить им модель): Когда агент делает listTools("/1c"), он вместе со списком тулов получает мета-инструкцию: правила предметной области, когда какой тул звать и в каком порядке.

              • На уровне тула (описание конкретного тула + примеры few shots): У каждого инструмента есть не просто типы параметров, а текстовое описание поведения и few-shot примеры входных/выходных данных.

              Разница лишь в доставке: мы не вываливаем на модель инструкции ко всем 50 "свисткам" на старте диалога (сжигая все бабки и лимиты), а отдаем объяснение к конкретному "свистку" ровно в тот момент, когда агент зашел в соответствующую категорию.


              1. Zerrdex
                10.09.2026 13:44

                Отличная идея - продолжайте развивать! Хотя инструменты мсп и можно (и нужно) отправлять в кеш, экономия токена, а так же ограничивать аллоу списками отсекая лишние и ненужные, итогом будет понимание, что гораздо дешевле, быстрее и качественнее вызвать короткий точечный скрипт в нужное и заранее известное место.


                1. Talos13 Автор
                  10.09.2026 13:44

                  Спасибо за поддержку!

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

                  Будем развивать дальше, планов по тулхабу ещё вагон!


              1. gybson_63
                10.09.2026 13:44

                Сейчас все новое и все имеет шанс. Вот например https://github.com/refly-ai/refly


                1. Talos13 Автор
                  10.09.2026 13:44

                  Согласен! да, на рефли тоже натыкался какое-то время назад. Кстати, они тоже пришли к той же мысли что и мы с вами выше - Skills are not prompts. They are durable infrastructure. Выходит, тоже что-то знают)) правда, рефли это больше про тяжелый энтерпрайз, n8n like графы, очень много интеграций под капотом. Тулхаб таки что-то вроде минималистичный unix-way рантайм, но общая суть безусловно схожа - отделить слой тулинга от слоя мышления.


                  1. gybson_63
                    10.09.2026 13:44

                    Мне, например, в первую очередь было бы важно как можно более точное определение нужного скила. И чтобы в одном месте лежали. Т.е. опять куда-то в сторону MCP это все смещается. Т.е. поверх этой файловой системы скилов еще семантический поиск и тонкая CLI обертка. Примерно как код индексируют.


                    1. Talos13 Автор
                      10.09.2026 13:44

                      Так они сейчас и так лежат в одном месте - единая база, рантайм и админка, а не размазаны по файлам проекта) в том и удобство

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

                      Про тонкую CLI/MCP-обёртку - ровно об этом в статье и написал) Stdio-мост на 50 строк, чтобы тот же Claude Code или Cursor видели весь тулхаб как один MCP-сервер - это идеальный юзкейс. Пока в статье возложил это на сообщество, подожду немного - если никто не накидает PR, сам за вечер оформлю. Просто не хотелось с первого же дня превращать ToolHub в очередной обслуживающий адаптер под Claude Code - важнее было показать и обкатать саму концепцию дерева и рантайма в чистом виде.


                      1. gybson_63
                        10.09.2026 13:44

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