Давайте будем честны: текущее состояние вызова функций (Function Calling) у LLM и экосистемы MCP (Model Context Protocol) вызывает изжогу в лучшем случае. Чтобы дать агенту возможность сделать curl или дернуть локальный скрипт, современному разработчику предлагают:
Описать килобайты монструозных JSON-схем прямо в системном промпте (и спалить 15-30k токенов контекста еще до первого вопроса пользователя).
Написать полноценный сетевой MCP-сервер со своим бойлерплейтом, SDK и зависимостями.
Завернуть всё это в жирный 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 строки на папку). Модель стартует с базовым контрактом:
listTools("/system")- посмотреть, что валяется в конкретной ветке, и подтянуть точные описания только нужных инструментов.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
Создание изолята: На лету генерируется временная папка
/tmp/hub_run_<timestamp>_<hash>.Инъекция кода и аргументов: В папку кладется код и
input.json, а все параметры вызова автоматически пробрасываются в ENV-переменные вида$INPUT_<KEY_NAME>. Хочешь - парси JSON в коде, хочешь - пиши bash-однострочник, который сразу читает$INPUT_HOST. Никаких обвязок.Зависимости: Если прописан
installCmd, он отрабатывает в контексте папки. Bun и современные пакетные менеджеры за счет глобального кэша хоста подтягивают модули за миллисекунды; для Python-скриптов аналогично используются локальные wheel-кэши либо глобальное окружение хоста.Запуск процесса: Выполняется
runCmdс жестким ограничениемtimeoutMs. Результат считывается изoutput.jsonили stdout.Очистка: Папка воркспейса сразу же аннигилируется через
rm -rf.
Если скрипт выполняется в консоли - он автоматически становится навыком ИИ. Без написания оберток на протоколе MCP.
А что с MCP?
Я не против MCP как идеи, я против его кривых реализаций. В toolhub нативная поддержка MCP реализована в двух режимах:
Stateless (Stdio): Классика. Спавнится процесс -> отправляется запрос -> профит -> процесс убивается. Подходит для разовых утилит, т.е. для 90% того что есть на рынке. Так же для удалённых MCP в том числе.
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 |
|---|---|---|---|---|---|---|---|
| Navigation Engine | 5.5 ms | 1.0 ms | 0.8 ms | 1.7 ms | 2.6 ms | ~990 req/s |
| 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 ( | 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.0–1.5 мс. Замеры отражают чистый overhead самого toolhub Runtime. На фоне сетевого latency и генерации токенов LLM (500–2000 мс) оверхед toolhub - это абсолютная погрешность.
Полный жизненный цикл одноразового bash-инструмента (генерация воркспейса
/tmp/hub_run_*, записьinput.json, спавн процесса, чтениеoutput.jsonи атомарныйrm -rf) занимает всего 13 миллисекунд.
Про базу данных: По умолчанию из коробки используется легковесная встроенная SQLite - чтобы проект поднимался без поднятия отдельных сервисов СУБД. Но благодаря тому, что весь бэкенд построен на Prisma ORM, при желании вынести ToolHub в высоконагруженный корпоративный кластер переезд на PostgreSQL, MySQL или CockroachDB сводится грубо говоря к изменению одной строчки провайдера в
schema.prisma.
Когда дерево НЕ даёт выигрыша (или даже мешает) - честный список:
Ситуация | Почему |
|---|---|
Мало тулов (до ~10) | Схема и так маленькая, а лишний |
Агент трогает почти все категории за сессию | Если он всё равно облазит 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. Первичная задача - выкинуть из стека лишний оверхед и дать нормальный инструмент для быстрой автоматизации без переплат за токены.
GitHub Repo: github.com/Talos-popcorn/toolhub
Буду рад конструктивной критике, issue и пулл-реквестам.
FADQ (Frequently Asked Dушные Questions)
Запускать такое на рабочей тачке - опасно! Да, исполнение скриптов прямо в ОС хоста без изоляции несет риски. То, что по дефолту toolhub работает без Docker - это фича для экономии ресурсов на слабых VPS и Raspberry Pi. Заворачивать сам toolhub в Docker, LXC-контейнер или отдельную VM - это задача оператора под конкретный контур безопасности. Тебе никто этого не запрещает!
Зачем мне toolhub, если есть LangChain / LlamaIndex / AutoGen? Затем, что это тяжелые фреймворки с горой абстракций, жестко завязанные на код вашего приложения. 🛠️ toolhub - это самостоятельный standalone-сервис. Вы можете менять языки программирования, переключать LLM-провайдеров или переписывать агентов с нуля - ваши инструменты остаются централизованными, версионируемыми и доступными по единому REST/SDK API.
«Древовидная навигация через listTools - это же лишние RTT (Round Trip Time)! Модель тратит лишние итерации вместо того, чтобы сразу вызвать тул!» Арифметика простая:
listTools("/")уходит в системный промпт черезgetSmartPrompt()и кэшируется точно так же, как и плоский список схем. А вот когда агент дальше идёт в конкретную папку -listTools("/database")- это уже вызов внутри диалога, и вот тут дерево начинает реально экономить, потому что в контекст попадают схемы только нужной ветки, а не всех 60 тулов разом. Проверялось на синтетических тулпаках для типичного использования toolhub - один агент, личное пользование, разработка - дерево дешевле плоского списка на 60-85% уже начиная с 30+ тулов, и разрыв растёт вместе с их числом. Подробнее - выше!«Зачем писать бэкенд на Bun/TS? Почему не на Rust/Go, если мы боремся за производительность?» Потому что Bun дает идеальный баланс: стартует за миллисекунды, мгновенно исполняет JS/TS без транспиляции и не требует компиляции под каждую платформу. Если тебе нужен Rust - пиши тул на Rust, компилируй в бинарник и вызывай его через ToolHub как
runCmd: "./my_rust_binary". ToolHub - ЯП-агностичен, а оверхед на запуск тулов благодаря Fastify минимален.«Что произойдет, если 50 агентов одновременно запустят скрипты с
pip install? Все упадет от гонки зависимостей?» Каждый вызов исполняется в абсолютном изоляте/tmp/hub_run_<timestamp>_<hash>. Папки и файлы не пересекаются в файловой системе. Если тебе нужен глобальный кэш тяжелых пакетов - используй глобальный pip-кэш на хосте или нативные бинарные раннеры. Безусловно, важно понимать, что всё зависит от раннера, тула, и прямоты твоих рук.«А если скрипт зависнет в бесконечном цикле или пожрет всю память?» Для этого у каждого тула есть жестко заданный
timeoutMs. По истечении таймаута процесс жестко гасится черезSIGKILL, воркспейс подчищается черезrm -rf, а агент получает нормальную ошибку с таймаутом, а не висит намертво.«Интеграция с Sublime Text в 2026 году?! Кто вообще пользуется Sublime, когда есть Cursor / VS Code?» Всё просто - я это сделал для себя. Мне важна скорость в 2 миллисекунды на diff, а не поедание 4 Гб ОЗУ Электроном ради подсветки пары строк. Поддержка Sublime сделана для тех, кто ценит реактивность. Хочешь под VS Code/Cursor/Windsurf/etc - SDK открыт, пиши .toolpack+bridge, PRы приветствуются. Лучшие тулпаки и тулы готов курировать, тестировать и добавлять в репо.
Почему XML-теги через обычный текст? Потому что нативный Function Calling жестко привязывает к конкретным вендорам, часто ломает стриминг и плохо работает на открытых/локальных моделях. XML-формат универсален для любых сеток. В SDK toolhub встроена базовая санитаризация (игнорирование тегов внутри markdown-блоков с кодом, исправление базовых огрехов JSON вроде одинарных кавычек), но общая культура промптинга и экранирование в UI - это, как и всегда, ответственность архитектуры вашего приложения.
Как это - нет докера? Что значит это фича? Это недоработка! toolhub рассчитан на Local-First / Trusted Node сценарии (когда агент крутится на твоем компе и выполняет ТВОИ скрипты). А если нужен Untrusted режим - докерфайл пишется за 3 секунды любой LLM. Сильно нужен готовый + композ = жду в issues и PR.
А без LLM этим вообще можно пользоваться? Да! 🛠️ toolhub - это, по сути, сверхлегкий self-hosted FaaS (Function-as-a-Service) с древовидным REST API. Если тебе нужно из Node.js бэкенда прозрачно вызывать Python-скрипты, дергать bash-автоматизацию по вебхукам или централизованно хранить утилиты с логами и версионированием - ты просто используешь REST/SDK напрямую из обычного классического кода без всяких нейросетей.
Три коммита в гитхабе? Четыре дня от роду? И ты предлагаешь мне этим пользоваться? Да так-то нет. Разработку своих проектов я веду локально в Gitea, и делюсь только тем, что действительно заслуживает шейринга. Самому проекту около 5 месяцев, а локальный репо насчитывает на данную секунду 57 коммитов. То же касается и 🧪lab и всего что я ещё захочу выложить.
