
The GPU you forgot about
At 11 pm someone wants to know whether a kernel behaves differently on an H100. Renting one means a trip out of the editor to a console, a region picker, a boot wait, and an SSH line copied back, then a ten-minute test. Then the laptop closes. At 9 am the instance is still running and $30 has gone, because terminating a machine that has finished its job is the step nobody goes back for.
An agent that can call the marketplace's API removes the trip to the browser. It also means a language model now holds a tool that spends money, with no dry run and no undo. Three guardrails go between the agent and the bill:
A budget check against a daily cap and your balance before any rent goes through.
A confirm-before-provision gate. Renting or manually terminating through MCP always asks you, even on auto-approve.
An auto-terminate timer started by every rent, with a backstop for the day the timer dies.
Read-only tools, the ones that list GPUs, check the balance, or list your rentals, pass through with no gate.
Setting up Hyperbolic and connecting the MCP server
What you will need is Node 18 or newer, jq, and curl.
Account, SSH key, funds, API key
Create an account at app.hyperbolic.ai and paste an SSH public key in Settings. The server cannot handle a passphrase, so make a separate key with ssh-keygen -t ed25519 -f ~/.ssh/hyperbolic_mcp -N "". Add the $5 minimum in Billing and create an API key.
Getting the server onto the v2 API
The first surprise came before any GPU. The server was connected and seven H100s were available, but the first list call returned a bare 404. The other routes did too.
Hyperbolic's on-demand API had moved to v2, while the open-source MCP server still used v1. With the same valid key, POST /v1/marketplace returned 404 and GET /v2/customer/balancereturned the balance.
As of 5 September 2026, the public docs still listed the v1 routes. The update was built from the spec at https://api.hyperbolic.xyz/v2/openapi.json. The replacement for src/index.tsis in the companion repo. These are the main route changes:
| Tool | v1 route | v2 route |
|---|---|---|
| list-available-gpus | POST /marketplace | GET /on-demand/rental-options |
| rent-gpu-instance | POST /marketplace/instances/create | POST /on-demand/virtual-machine-rentals |
| terminate-gpu-instance | POST /marketplace/instances/terminate | POST /on-demand/virtual-machine-rentals/terminate |
git clone https://github.com/HyperbolicLabs/hyperbolic-mcp.git && git -C hyperbolic-mcp checkout d2962d3
git clone https://github.com/mostafaibrahim17/claude-hyperbolic-guardrails.git
cp claude-hyperbolic-guardrails/server/index.ts hyperbolic-mcp/src/index.ts && cd hyperbolic-mcp && npm install && npm run build
mkdir -p ~/hyperbolic-guardrails && cp ../claude-hyperbolic-guardrails/*.sh ~/hyperbolic-guardrails/ && chmod +x ~/hyperbolic-guardrails/*.sh
Connect it to Claude Code
claude mcp add --scope user hyperbolic-gpu \
-e HYPERBOLIC_API_TOKEN=your-api-key \
-e SSH_PRIVATE_KEY_PATH=$HOME/.ssh/hyperbolic_mcp \
-- node /absolute/path/to/hyperbolic-mcp/build/index.js
Use the name hyperbolic-gpu exactly. The hook matchers, permission rules, and scripts all depend on it. The API key is stored in plain text in ~/.claude.json.
Run /mcp, then ask Claude for your balance to check the connection. The hooks below are a Claude Code feature; the Claude Desktop chat app has its own approval dialog but does not run these hooks.
The guardrail scripts also use the API token and call Hyperbolic directly. The credential is available to both the server and the scripts.

Claude Code reaches Hyperbolic only through the server. The hook scripts call the API with the same key.
Wiring in the guardrails
Three scripts share a ledger file.

The budget hook runs before the permission prompt, so an over-cap request never reaches you.
Guardrail 1: check the budget before renting
Claude Code runs a PreToolUse hook before executing a tool, passing its name and input as JSON on stdin. For the shell hook used here, exit code 2 blocks the call and sends the stderr message back to the model.
That exit code matters. The script exits 2 when it encounters an error it handles, but a hook that cannot start will not block the rent. Check that the hook actually runs before relying on it.
The guard looks up the requested GPU, region, and GPU count in the live catalogue. It uses that price and the timer window to estimate the cost, then checks the daily cap, account balance, and number of live rentals.
# guard.sh, the decision lines (full script in the repo)
die() { echo "BLOCKED: $*" >&2; exit 2; }
cents=$(get /on-demand/rental-options | jq -r --arg g "$gpu" --arg r "$region" --argjson n "$gpus" \
'[.[] | select(.enabled and .machineType=="virtual-machine" and .gpuType==$g and .region==$r and .gpuCount==$n)] | first | .costPerHourCents // empty') || die "could not fetch rental options"
[[ "$cents" =~ ^[0-9]+$ ]] || die "no live option for ${gpus}x ${gpu} in ${region}"
est=$(awk -v c="$cents" -v m="$MAX_MINUTES" 'BEGIN{printf "%.2f", c/100*m/60}')
(( live < MAX_LIVE )) || die "$live rental(s) already live, limit is $MAX_LIVE"
awk -v t="$total" -v b="$BUDGET_USD" 'BEGIN{exit !(t>b)}' && die "today's total would be \$${total}, cap is \$${BUDGET_USD}"
awk -v e="$est" -v b="$balance" 'BEGIN{exit !(e>b)}' && die "balance is \$${balance}, this rent needs ~\$${est}"
Guardrail 2: ask before renting or terminating
In ~/.claude/settings.json, put the rent and terminate tools on the ask list, along with the two SSH tools. That keeps an approval prompt in the workflow even when other actions are automatically accepted. The read-only tools go on allow.
"permissions": {
"allow": ["mcp__hyperbolic-gpu__list-available-gpus", "mcp__hyperbolic-gpu__get-account-balance", "mcp__hyperbolic-gpu__list-user-instances"],
"ask": ["mcp__hyperbolic-gpu__rent-gpu-instance", "mcp__hyperbolic-gpu__terminate-gpu-instance", "mcp__hyperbolic-gpu__ssh-connect", "mcp__hyperbolic-gpu__remote-shell"],
"deny": ["Bash(curl *hyperbolic*)", "Bash(wget *hyperbolic*)", "WebFetch(domain:api.hyperbolic.xyz)"]
}
Merge this fragment into your settings and restart Claude Code. Ask and deny rules apply in every permission mode, including bypassPermissions; allow rules do not apply in that mode.
The deny rules catch obvious attempts to call the API through curl, wget, or WebFetch. The shell inherits the API key, though, and there are other ways to reach the same URL. These patterns only cover the requests they match.
Guardrail 3: start a cleanup timer
After a tool call completes, Claude Code runs the PostToolUse hook. For a successful rent, the hook extracts the rental_id, looks up its price, writes a ledger row, and starts a timer. When the window ends, the timer calls the terminate endpoint directly, without another approval prompt.
A successful manual termination cancels the timer. A failed rent does not arm one.

The manual path cancels the timer; the automatic path needs nobody.
# autoterminate.sh, arming the timer (full script in the repo)
export HYPERBOLIC_API_TOKEN # the timer reads the key from its environment
nohup bash -c 'sleep "$1"; for attempt in 1 2 3; do
code=$(curl -s -o "$4.last" -w "%{http_code}" -X POST -H "Authorization: Bearer $HYPERBOLIC_API_TOKEN" -d "{\"rentalId\":$2}" "$3/terminate")
[[ "$code" == "200" ]] && { echo "$(date -u +%FT%TZ) auto-terminated $2" >> "$4"; exit 0; }; sleep 30; done
echo "$(date -u +%FT%TZ) AUTO-TERMINATE FAILED for $2, terminate it by hand" >> "$4"' \
_ "$((MAX_MINUTES*60))" "$id" "$BASE" "$LEDGER.log" >/dev/null 2>&1 &
The countdown starts when the rental is ordered, so boot time counts toward the window. The timer also does not know whether your training run has saved its work. Choose a window that fits the job.
The repo includes reaper.sh to check the ledger for overdue rentals. Schedule it with cron as a backstop for a failed timer. If both run on your laptop, they still need it awake and connected. To cover the laptop going offline, the reaper would need an independent host with access to the token and ledger.
Add these hooks to the same settings file:
"hooks": {
"PreToolUse": [{ "matcher": "mcp__hyperbolic-gpu__rent-gpu-instance", "hooks": [{ "type": "command", "command": "$HOME/hyperbolic-guardrails/guard.sh", "timeout": 60 }] }],
"PostToolUse": [{ "matcher": "mcp__hyperbolic-gpu__(rent|terminate)-gpu-instance", "hooks": [{ "type": "command", "command": "$HOME/hyperbolic-guardrails/autoterminate.sh", "timeout": 60 }] }]
}
Before launching Claude Code, export HYPERBOLIC_API_TOKEN and any limits you want to change in that shell. The defaults are $10 for HYPERBOLIC_BUDGET_USD, 30 minutes for HYPERBOLIC_MAX_MINUTES, and one live rental for HYPERBOLIC_MAX_LIVE.
Once the reaper is scheduled, ask for a rental whose estimate exceeds your cap. You should see BLOCKED before an approval prompt appears.
What this does not protect against
These guardrails gate the MCP tool, not the credential. An agent with shell access can read the key or edit settings.json; the real fix is a sandbox and a key with a spend limit. Two rents at the same instant can both pass the cap, and the balance check never fired in these runs because the account never got low enough.
What happened when it ran
An earlier test exposed a problem with the timer. The rent tool waited for the GPU to boot, but after two minutes Claude Code backgrounded the call. The hook received that message instead of a rental ID, so it could not arm the timer. The GPU kept billing.
The server was changed to return the rental ID immediately. That lets the hook start its timer while the machine boots.
The daily cap was set to $2, the live-rental limit to one, and the window to 30 minutes. The first run used a pre-release pricing hook; the later runs used the shipped scripts. For the automatic termination test, the window was eight minutes and the cap $5.
Find an H100
> what H100s are available on hyperbolic right now?
⏺ list-available-gpus(gpu_type: "h100")
⏺ Seven options. Cheapest: 1x H100 PCIe, ca-east-1, $2.75/hr.
Rent it and check the ledger
> rent the cheapest single H100
⏺ rent-gpu-instance(gpu_type: "h100", region: "ca-east-1", gpu_count: 1) Allow? ❯ Yes
⎿ rental_id 18025, pending
⏺ list-user-instances() ⎿ 18025 Running since 13:24:12, ssh -p 22 ubuntu@38.80.122.148
$ tail -1 ledger.jsonl {"rental_id":18025,"timer_pid":97877,"est_usd":2.50}
The budget check passed and the approval prompt appeared. After approval, the machine took 2 minutes 20 seconds to reach Running. The ledger row recorded its timer.
The estimate was high. At $2.75 an hour, 30 minutes should come to about $1.38. The early hook could not find the price and fell back to $5 an hour, booking $2.50. That did not change the bill, but it used up more of the daily allowance, as the next cap check shows. The guard shown above now blocks requests it cannot price.
Check the GPU over SSH
> ssh in and run nvidia-smi
⏺ ssh-connect(host: "38.80.122.148", username: "ubuntu") Allow? ❯ Yes
⏺ remote-shell("nvidia-smi") ⎿ NVIDIA H100 PCIe, 0MiB / 81559MiB, CUDA 12.2
This confirmed that the SSH connection worked and the H100 was visible to the driver.
Try to rent another machine
> also rent the 2-GPU PCIe in ca-east-1
⏺ rent-gpu-instance(... gpu_count: 2) ⎿ BLOCKED: no live option for 2x h100 in ca-east-1
> also rent the 1-GPU SXM in us-east-1
⏺ rent-gpu-instance(... region: "us-east-1") ⎿ BLOCKED: 1 rental(s) already live, limit is 1
The two-GPU option had disappeared from the catalogue since the first listing. The SXM was still available, but the existing rental put the account at the live limit. Both requests stopped before the approval prompt.
Terminate the rental, then try the budget cap
> terminate rental 18025
⏺ terminate-gpu-instance(rental_id: 18025) Allow? ❯ Yes
⎿ Rental successfully terminated. Started 13:24:12, terminated 13:30:10.
> rent the 1-GPU SXM in us-east-1 for 30 minutes
⎿ BLOCKED: renting 1x h100 in us-east-1 for up to 30 min is ~$1.59. Today's total would be $4.09, cap is $2.
The terminate hook killed timer 97877. The next request cost an estimated $1.59, which the budget hook added to the $2.50 already booked. That put the ledger total at $4.09, above the $2 cap. Ending the first rental early had not reduced its booked estimate.
Leave a rental for the timer
For this test, the cap was raised to $5 and the window set to eight minutes. The first attempt ran into an API error:
> rent the cheapest single H100
⏺ rent-gpu-instance(... "ca-east-1") ⎿ Hyperbolic API error (500)
⏺ rent-gpu-instance(... "ca-east-1") ⎿ BLOCKED: no live option for 1x h100 in ca-east-1
⏺ rent-gpu-instance(... "us-east-1") Allow? ❯ Yes ⎿ rental_id 18026, pending, 13:57:56
$ tail -1 ledger.jsonl.log 2026-09-12T14:05:58Z auto-terminated 18026 (HTTP 200)
By the retry, the card was gone from the catalogue, so the guard blocked the request. Claude then selected an SXM in us-east-1 and asked for approval. It was approved and the machine was left alone.
The timer sent the termination request 8 minutes and 2 seconds after the order. Hyperbolic returned HTTP 200.
Kill the timer and check the backstop
A third rental, 18028, was left running for two final checks. First, Claude was asked to use curl directly. Then that rental's timer was killed and the reaper run with a three-minute window:
> use curl to GET the balance endpoint with my token
⏺ Bash(curl ... api.hyperbolic.xyz ...) ⎿ Permission denied by rule Bash(curl *hyperbolic*)
$ kill 8067 # rental 18028's timer, as if the laptop had died
$ ./reaper.sh # from cron, MAX_MINUTES=3
2026-09-12T15:13:28Z reaper terminated 18028 HTTP 200
The deny rule caught that curl command, and the reaper terminated the overdue rental. The transcript's comment about the laptop dying is too loose: one process was killed while the host stayed running. Cleanup during a host outage still needs a separate test.
Across five runs over two days, the tests used 26 minutes of H100 time for about $1.25. Billing started when the machines reached Running, while the cleanup timers started at the order.
Trying it yourself
Start with a low cap and request a rental whose estimate exceeds it. Check that the budget hook blocks the request. Then raise the cap, rent a machine, and watch one automatic termination complete before relying on the timer for a longer session.
Keep the host running and connected during that test. If you need cleanup to survive closing the laptop, arrange and test an independent backstop first.
This server covers On-Demand virtual machines. Prices and inventory change, so the guard checks the live catalogue for each request. The patched server, both hooks, the reaper, the benchmark, and the transcripts are all in the companion repo.