Skip to main content
Glama

gpu-broker-mcp

Stateful-сервер MCP, который обеспечивает доступ к GPU-вычислениям для AI-агентов. Агенты обнаруживают узлы, резервируют ресурсы, отправляют инференс и получают результаты через четыре MCP-инструмента — без управления SSH-ключами, IP-адресами узлов или API провайдеров напрямую.

SDK: mcp==2.0.0 (Python SDK v2, mcp.server.MCPServer) Целевая спецификация: ревизия спецификации MCP от 2026-07-28 Транспорт: Streamable HTTP, stateless-режим (stateless_http=True, json_response=True). Без сессий, без Mcp-Session-Id, без sticky-маршрутизации.

Архитектура

┌─────────────────────────────────────────────────────────────┐
│  Agent (MCP client)                                         │
│  Calls: list_nodes → reserve_node → dispatch_inference      │
│         → get_result (poll)                                 │
└────────────────────────┬────────────────────────────────────┘
                         │ JSON-RPC over Streamable HTTP
                         │ (stateless, any replica)
┌────────────────────────▼────────────────────────────────────┐
│  gpu-broker-mcp server                                      │
│                                                             │
│  ┌──────────────────┐  ┌──────────────────┐                 │
│  │ HMAC-SHA256       │  │ NodePool ABC     │                 │
│  │ Handle signing    │  │  ├ FakeNodePool   │                │
│  │ & validation      │  │  └ VastNodePool   │                │
│  └──────────────────┘  └──────────────────┘                 │
│                                                             │
│  ┌──────────────────┐  ┌──────────────────┐                 │
│  │ Error taxonomy    │  │ JobStore ABC     │                 │
│  │ (single enum,     │  │  └ InMemoryStore  │                │
│  │  structured JSON) │  │    (per-replica)  │                │
│  └──────────────────┘  └──────────────────┘                 │
└────────────────────────┬────────────────────────────────────┘
                         │ SSH (VastNodePool only)
┌────────────────────────▼────────────────────────────────────┐
│  GPU node (e.g. Vast.ai RTX 3090)                           │
│  Runs inference workload, returns stdout                    │
└─────────────────────────────────────────────────────────────┘

Брокер работает локально. Он является клиентом GPU-узлов, а не размещается на них — он выполняет HMAC-подпись и JSON-сериализацию, нагружающие CPU, и ничего, что выиграло бы от GPU.

Related MCP server: vibedonate

Зачем подписанные хэндлы вместо сессий

Состояние резервирования хранится внутри самого хэндла: base64-кодированный JSON-пейлоад (ID узла, срок действия, область применения), объединённый с его HMAC-SHA256-подписью. Секрет берётся из GPU_BROKER_SECRET, и сервер отказывается запускаться, если он не задан.

Это означает, что любая реплика, знающая секрет, может проверить хэндл, который она никогда не выдавала. Нет таблицы сессий, нет заголовка Mcp-Session-Id и нет требования sticky-маршрутизации. Балансировщик нагрузки может направлять любой запрос на любую реплику. Хэндлы имеют область применения (reserve vs task), поэтому хэндл резервирования нельзя воспроизвести как ID задачи и наоборот — неправильное использование возвращает HANDLE_SCOPE_INVALID.

Что JobStore в памяти действительно теряет между репликами — это поиск статуса задачи: реплика B не может сообщить вам статус задачи, отправленной реплике A. Это требование к общему бэкенду (Redis, Postgres), а не недостаток stateless-дизайна. Проверка подписи — критически важная для безопасности часть — полностью переносима.

Инструменты

Инструмент

Параметры

Возвращаемое значение

list_nodes

—

JSON-массив доступных узлов (id, model, vram, price, load)

reserve_node

node_id, ttl_seconds

Подписанный хэндл резервирования

dispatch_inference

handle, payload

{"task_id": "...", "status": "pending"}

get_result

task_id

{"status": "pending|completed|failed", "output": ..., "error": ...}

Сигнатуры инструментов стабильны для всех бэкендов — замена FakeNodePool на VastNodePool не меняет ни одного видимого клиенту интерфейса.

Примечание о кэшировании

list_nodes возвращает meta.ttlMs и meta.cacheScope в результате вызова инструмента. Это локальное соглашение — SEP-2549 регулирует ответы tools/list и resources/list, а не отдельные результаты tools/call. Клиенты, которые его распознают, могут кэшировать; те, кто не распознаёт, просто вызовут инструмент снова.

Заголовки маршрутизации

Сервер отправляет заголовки Mcp-Method и Mcp-Name для маршрутизации через шлюз, но не принудительно проверяет их на стороне сервера. Точка принудительной проверки — это периметр (API-шлюз, обратный прокси), а не сам брокер.

Таксономия ошибок

Каждая ошибка инструмента возвращает структурированный JSON с полями code, message, retryable и необязательным retry_after_seconds. Агенты должны ветвиться по code, а не по message — сообщения предназначены для чтения человеком и могут меняться.

Код

Повторяемость

Когда возникает

NVML_VERSION_MISMATCH

Нет

Версия библиотеки управления NVIDIA не соответствует драйверу на GPU-хосте

DRIVER_LIBRARY_MISMATCH

Нет

Конфликт версий драйвера/библиотеки CUDA на GPU-хосте

DPKG_LOCK_CONTENTION

Да

Блокировка менеджера пакетов удерживается другим процессом на GPU-хосте (например, unattended-upgrades)

DOCKER_SOCKET_PERMISSION_DENIED

Нет

Сокет контейнерной среды выполнения недоступен на GPU-хосте

INSUFFICIENT_VRAM

Нет

Недостаточно памяти GPU для запрошенной рабочей нагрузки

NODE_UNREACHABLE

Да

Невозможно подключиться к GPU-узлу (таймаут SSH, отказ в соединении, сбой DNS)

RESERVATION_EXPIRED

Нет

Истёк срок действия TTL подписанного хэндла

HANDLE_SIGNATURE_INVALID

Нет

HMAC-подпись не совпадает — хэндл изменён, неверный секрет или повреждённый хэндл

HANDLE_SCOPE_INVALID

Нет

Несоответствие области применения хэндла (например, передача хэндла задачи там, где ожидается хэндл резервирования)

TLS_PROXY_FAILURE

Да

Сбой TLS-терминации или прокси-слоя между брокером и узлом

JOB_NOT_FOUND

Нет

Подпись действительна, но задача отсутствует в хранилище этой реплики (ожидаемо при хранилище в памяти между репликами)

Ошибки уровня хоста (от NVML_VERSION_MISMATCH до DOCKER_SOCKET_PERMISSION_DENIED) сопоставляются из строк stderr SSH в vast.py:_raise_from_stderr. Шаблоны основаны на известных режимах сбоев GPU-хостов Vast.ai, но ещё не проверены на реально захваченных производственных строках. Задача 3 захватит дословный вывод ошибок и уточнит шаблоны сопоставления.

Быстрый старт

Режим Fake (без GPU, без API-ключа)

export GPU_BROKER_SECRET="any-secret-string"
python src/gpu_broker/server.py
# Server at http://127.0.0.1:8000/mcp

Режим Vast.ai (реальный GPU)

export GPU_BROKER_SECRET="any-secret-string"
export VASTAI_API_KEY="your-vast-api-key"

# Find and rent a node
python vast_manage.py search --gpu "RTX 3090" --max-price 0.30
python vast_manage.py rent <offer_id>
python vast_manage.py wait <instance_id>

# Start the broker (auto-detects VASTAI_API_KEY)
python src/gpu_broker/server.py

# When done
python vast_manage.py destroy <instance_id>

Запуск тестов

uv run pytest tests/ -v

Тесты включают:

  • Полный цикл хэндла (reserve → dispatch → get_result)

  • Отклонение изменённой подписи

  • Отклонение истёкшего хэндла

  • Отклонение несоответствия области применения

  • JOB_NOT_FOUND при поиске между репликами

  • Тест stateless-поведения в подпроцессе: запускает три реальных HTTP-сервера (A и B используют общий секрет, C — другой), отправляет задачу с A, подтверждает, что A возвращает pending, B возвращает JOB_NOT_FOUND, а C возвращает HANDLE_SIGNATURE_INVALID

  • Отказ при запуске без заданного секрета

  • Полный цикл сериализации для каждого варианта ошибки

Текущая область применения и ограничения

Это рабочий прототип, а не производственная система.

  • FakeNodePool возвращает статический список из трёх узлов и не выполняет реальный инференс. Полезен для тестирования взаимодействия инструментов и механики хэндлов.

  • VastNodePool запрашивает API Vast.ai для получения запущенных инстансов и выполняет инференс через SSH. Он выполняет реальную работу, но не имеет пула соединений, логики повторов или управления SSH-ключами помимо системных настроек по умолчанию.

  • InMemoryJobStore теряет всё состояние при перезапуске и не может передавать статус задач между репликами. Для производственного развёртывания нужен общий бэкенд (Redis, Postgres).

  • Шаблоны таксономии ошибок для сбоев уровня хоста — обоснованные предположения, основанные на известных режимах сбоев. Их необходимо проверить на реально захваченном stderr от GPU-хостов.

  • Нет аутентификации на самом MCP-эндпоинте — любой клиент, способный подключиться к HTTP-порту, может вызывать инструменты. Для производства нужен слой аутентификации перед сервером.

  • Нет ограничения частоты запросов, нет ограничений размера запросов, нет журналирования аудита.

Maintenance

ActivitySlowing
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers