Skip to main content
Glama

gpu-broker-mcp

一个为 AI 智能体提供 GPU 算力访问经纪服务的无状态 MCP 服务器。智能体可以通过四个 MCP 工具发现节点、预留算力、派发推理任务并轮询结果——无需直接管理 SSH 密钥、节点 IP 或服务商 API。

SDK:mcp==2.0.0(Python SDK v2,mcp.server.MCPServer) 目标规范:MCP 规范修订版 2026-07-28 传输方式:Streamable HTTP,无状态模式(stateless_http=True、json_response=True)。无会话、无 Mcp-Session-Id、无粘性路由。

架构

┌─────────────────────────────────────────────────────────────┐
│  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                    │
└─────────────────────────────────────────────────────────────┘

broker 在本地运行,它是 GPU 节点的客户端,而不是驻留在节点上的组件。它执行的是受 CPU 限制的 HMAC 签名和 JSON 序列化工作,这些工作无法从 GPU 中获益。

Related MCP server: vibedonate

为什么用签名句柄而不是会话

预留状态保存在句柄本身之中:一个 base64 编码的 JSON 载荷(节点 ID、过期时间、作用域)与它的 HMAC-SHA256 签名拼接在一起。签名密钥来自 GPU_BROKER_SECRET,如果该变量未设置,服务器将拒绝启动。

这意味着任何共享密钥的副本都可以验证一个并非自己签发的句柄。没有会话表、没有 Mcp-Session-Id 头,也没有粘性路由要求。负载均衡器可以把任何请求路由到任何副本。句柄是带作用域的(reserve 与 task 区分)因此预留句柄不能当作任务 ID 重放,反之亦然——误用会返回 HANDLE_SCOPE_INVALID。

内存版 JobStore 在跨副本时唯一丢失的是作业状态查询能力:副本 B 无法告诉你派发到副本 A 上的作业的状态。这是对共享后端(Redis、Postgres)的需求,而不是无状态设计的缺陷。签名验证(安全上最关键的部分)完全可以跨副本移植。

工具

工具

参数

返回

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 网关、反向代理),而不是在 broker 内部执行。

错误分类

每个工具的错误都返回结构化 JSON,包含 code、message、retryable 和可选的 retry_after_seconds。智能体应依据 code 分发逻辑,绝不依据 message——message 是给人看的诊断信息,可能有变更。

代码

可重试

触发场景

NVML_VERSION_MISMATCH

否

GPU 主机上的 NVML 库版本与驱动版本不匹配

DRIVER_LIBRARY_MISMATCH

否

GPU 主机上 CUDA 驱动/库版本冲突

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

是

broker 与节点之间的 TLS 终止或代理层故障

JOB_NOT_FOUND

否

句柄签名有效但作业不在本副本的存储中(跨副本的内存存储可预期会这样)

主机级错误(NVML_VERSION_MISMATCH 至 DOCKER_SOCKET_PERMISSION_DENIED)是根据 vast.py:_raise_from_stderr 中 SSH stderr 内容映射的。这些模式基于 Vast.ai GPU 主机上已知的故障模式,但尚未用实际捕获的生产 stderr 做过修正验证。任务 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

测试包括:

  • 句柄往返(预留 → 派发推理 → get_result)

  • 被篡改签名的拒绝

  • 过期句柄的拒绝

  • 作用域不匹配检查

  • 跨副本查询触发 JOB_NOT_FOUND

  • 子进程无状态性测试:启动三个真实 HTTP 服务器(A 和 B 共享同一个密钥,C 使用另一个),通过 A 派一个任务,确认 A 返回 pending,B 返回 JOB_NOT_FOUND,C 拒绝签名(HANDLE_SIGNATURE_INVALID)

  • 服务器在未设置 GPU_BROKER_SECRET 时直接拒动

  • 每个错误变体的序列化往返测试

当前范围和限制

这是一个可以用到的原型,并不适用于生产。

  • FakeNodePool 返回一个静态节点列表(三个节点),不派发真正的推理任务。它适用于测试工具调用与句柄机制。

  • VastNodePool 通过访问 Vast.ai API 找到运行实例,再通过 SSH 派发推理。它能做真生产推理,但没有连接池、重试逻辑,也不负责 SSH 密钥管理等专项工作(只使用系统默认)。

  • InMemoryJobStore 重启即丢失全部状态,且不能在副本间共享作业状态。生产部署需要共享后端数据库(例如 Redis、Postgres)。

  • 对主机级错误分类的预设模式是对可能故障的猜测,尚未在真实 GPU 主机上取得生产 stderr 进行校验。

  • MCP 端点本身没有任何身份认证——任何能访问 HTTP 端口且触达相应的客户端都能调用其工具。生产环境应额外加身份验证层。

  • 没有流控、请求大小限制、操作审计日志。

Maintenance

ActivitySlowing
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers