MCP server

rent.dev is an MCP server as well as a REST API. An agent that speaks MCP gets the same account, sandboxes and payments as 32 tools, and a second endpoint with no login for prices, the provider catalog, docs and signup.

POSThttps://mcp.rent.dev/mcp

View as Markdown

Two endpoints

EndpointCredentialsTools
https://mcp.rent.dev/mcpAn API key or an OAuth access token.32: the provider catalog, funds, sandboxes, GPU pods and the account.
https://mcp.rent.dev/mcp/publicNone.rent_quote, rent_list_providers, rent_search_docs, rent_signup, rent_supply_guide

rent_list_providers answers with the catalog as compact summaries: each provider's capabilities, whether an agent can drive it, and the cheapest hourly price among its matching offerings. The whole catalog, with each row's region, source and date, is on the catalog page. A listed provider may be unpriced: its terms may permit display but not comparison, no price row may match the filters, or its rows may carry no hourly figure.

Both are stateless Streamable HTTP. Every request is a POST, there is no session to open or keep, and any other method answers 405.

Connect a client

JSON
{
  "mcpServers": {
    "rent": {
      "url": "https://mcp.rent.dev/mcp",
      "headers": {
        "Authorization": "Bearer rent_sk_…"
      }
    }
  }
}

No key yet? Call rent_signup on the public endpoint. It answers the key once, with this endpoint's URL and the header to send. Pass idempotency_key, a random value you keep, to be able to repeat the call without making a second account.

Shell
curl -s https://mcp.rent.dev/mcp/public \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "MCP-Protocol-Version: 2025-11-25" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'

The answer comes back as JSON or as an event stream with one message.

Authentication

/mcp takes the credentials the REST API takes: Authorization: Bearer rent_sk_…, X-API-Key, or an OAuth access token issued for the resource https://mcp.rent.dev/mcp. Without one it answers 401, with a WWW-Authenticate challenge that points at the protected-resource metadata (RFC 9728).

Each tool checks its scope before it runs. An API key has every scope but supply, so the partner supply tools need an OAuth token that carries it; an API key gets a forbidden result from them. An OAuth token that lacks a scope gets a 403 insufficient_scope challenge naming the scope to ask for.

A request that carries a browser Origin must come from this server's origin, from the site's, or from localhost when the endpoint itself is local. Any other origin answers 403.

Protocol versions

The server speaks MCP 2026-07-28: server/discover, and the client's version, capabilities and name in each request's _meta. A client on an earlier revision negotiates with initialize; these are accepted: 2025-11-25, 2025-06-18, 2025-03-26, 2024-11-05, 2024-10-07.

A tools/list answer may be kept for 5 minutes (ttlMs). The list does not change within a deploy.

A typical session

  1. rent_quote: what a task costs.
  2. rent_get_balance, and rent_topup when it is short.
  3. rent_create_sandbox.
  4. rent_exec, or rent_start_process for work that outlasts one call.
  5. rent_terminate_sandbox.

Tools on /mcp

ToolWhat it doesScopeHints
rent_list_providersList providersnoneread-only, idempotent
rent_quoteQuote sandbox pricesnoneread-only, idempotent
rent_get_balanceGet the balancebillingread-only, idempotent
rent_topupTop up the balancebillingopen world
rent_create_sandboxCreate a sandboxsandboxopen world
rent_get_sandboxGet a sandboxsandboxread-only, idempotent
rent_list_sandboxesList sandboxessandboxread-only, idempotent
rent_execRun a commandsandboxdestructive, open world
rent_start_processStart a background processsandboxdestructive, open world
rent_get_process_outputRead process outputsandboxread-only, idempotent, open world
rent_run_codeRun code in a kernelsandboxdestructive, open world
rent_read_fileRead a filesandboxread-only, idempotent, open world
rent_write_fileWrite a filesandboxdestructive, idempotent, open world
rent_get_port_urlGet a port URLsandboxread-only, idempotent
rent_extend_sandboxExtend a sandboxsandboxidempotent, open world
rent_pause_sandboxPause a sandboxsandboxidempotent, open world
rent_resume_sandboxResume a sandboxsandboxidempotent, open world
rent_terminate_sandboxTerminate a sandboxsandboxdestructive, idempotent, open world
rent_create_gpu_podCreate a GPU podgpuopen world
rent_list_gpu_podsList GPU podsgpuread-only, idempotent, open world
rent_terminate_gpu_podTerminate a GPU podgpudestructive, idempotent, open world
rent_set_byok_credentialStore a vendor keykeysopen world
rent_get_usageGet usagebillingread-only, idempotent, open world
rent_get_operationGet an operationnoneread-only, idempotent
rent_supply_guideRead the provider guidenoneread-only, idempotent
rent_supply_enrollEnrol nodessupplynone
rent_supply_nodesList your nodessupplyread-only, idempotent
rent_supply_nodeGet a nodesupplyread-only, idempotent
rent_supply_update_nodeChange a nodesupplyidempotent
rent_supply_probe_nodeProbe a node nowsupplyopen world
rent_supply_revoke_nodeRevoke a nodesupplydestructive, idempotent, open world
rent_supply_earningsGet your earningssupplyread-only, idempotent

rent_list_providers

Lists rent.dev's provider catalog as compact summaries: each provider's capabilities (offerings, pricing types, interfaces, auth and product capabilities), display-only summary_verdict, agentic flag, the provenance of its cheapest matched offering and that offering's price row. Read-only and anonymous on /mcp/public. A listed provider may be unpriced: its terms may permit display but not comparison (clause names that denial and its prices are withheld), no price row may match the filters, or its rows may carry no hourly figure (a GPU row then gives unpriced_reason: monthly_rate, no_time_unit or unknown_gpu_count; a per-unit row has usd and unit but no hourly figure). include_offerings carries every matching offering, not just the cheapest. use asserts a rights use on providers and figures (default display). limit caps providers only. Every offer carries covers, gpu.count, region_id and comparison_group: the group names the purchasable unit (GPU model, count, market, pricing type, canonical region and commitment), so two offers are price-comparable exactly when their groups match. region accepts the vendor's label or the canonical id; gpu_count, covers and figure narrow the grain.

rent_quote

Dollars per completed task for rent.dev's own sandboxes beside every published-rate vendor, by workload profile, plus hourly GPU rows. Defaults: all three profiles, 2 vCPU, 4096 MiB. A quote is a price list, not a reservation; it holds nothing.

rent_get_balance

Your prepaid balance in micro-USD (1 USD = 1,000,000): total, held by running sandboxes, and available, and whether the account is frozen.

rent_topup

method "card": answers a pay link for a person (valid 24 hours; Stripe Checkout, no API key needed). On clients that take URL elicitation the link is shown to the person first, then the tool answers it. method "x402": charges USDC over MCP with x402. The first call answers isError with the PaymentRequired object (amount = credits + 5.5%; sign within its maxTimeoutSeconds, 120 s); retry the same call with the PaymentPayload in _meta["x402/payment"]. The paid call settles, credits, and answers the SettleResponse in _meta["x402/payment-response"]. The balance shows card payments once Stripe confirms them; check with rent_get_balance.

rent_create_sandbox

Starts a Linux sandbox (a Firecracker microVM) and holds its cost from your balance; it is charged by the second while it runs. Waits up to wait_s for placement and answers the sandbox; past that it answers an operation id (op_…) to poll with rent_get_operation, whose response is the sandbox. The sandbox id (lse_…) lives until rent_terminate_sandbox or the timeout (default 300 s). A short balance answers 402 insufficient_balance with a card pay link (valid 24 hours) and an x402 top-up URL. Arguments not listed here go to the REST route as they are, which refuses them by name.

rent_get_sandbox

One sandbox by id: state (requested, placing, running, paused, settling, terminated, failed), template, size, timeout_at, port-URL domain, and what it has been charged and holds.

rent_list_sandboxes

Your sandboxes, newest first. By default every state but terminated and failed; state picks one. next_cursor is opaque and only for the next call of this tool.

rent_exec

Runs a shell command (/bin/bash -l -c) in a running sandbox and answers when it exits: stdout, stderr, exit_code (null when killed by the timeout), timed_out. timeout_s defaults to 50, so the answer comes back inside common client tool timeouts. For work that runs longer (servers, builds, training), start it with rent_start_process and read its output with rent_get_process_output. Arguments not listed here go to the REST route as they are, which refuses them by name.

rent_start_process

Starts a command in the background and answers at once with its process id. The process id is valid while the sandbox runs; read its output with rent_get_process_output. The process ends with the sandbox. Arguments not listed here go to the REST route as they are, which refuses them by name.

rent_get_process_output

Output of a background process after a cursor, whether it still runs, and its exit code. Omit cursor to read from the start; pass the answer's cursor back unchanged to read only newer output. Cursors and process ids are valid while the sandbox runs.

rent_run_code

Runs code on the Jupyter kernel of a code-interpreter-v1 sandbox and answers its rich results (text, images as base64, …), stdout, stderr and error. State persists between calls in one context_id; a context lives as long as the sandbox. timeout_s defaults to 50. Arguments not listed here go to the REST route as they are, which refuses them by name.

rent_read_file

Reads a file from a running sandbox. Without encoding the content comes back as UTF-8 text when it is valid UTF-8, else as base64.

rent_write_file

Writes a file in a running sandbox, creating parent directories and replacing what is there. content is UTF-8 text, or base64 with encoding base64.

rent_get_port_url

The public HTTPS URL of a port in the sandbox (https://{port}-{sandbox}.{domain}). It serves while the sandbox runs and something listens on the port.

rent_extend_sandbox

Sets a running sandbox to stop timeout_s seconds from now (at most 86400), holding the added cost first on the key that started it; a short balance answers 402 with top-up links. An API key with a spend limit cannot extend a sandbox another key started (403 forbidden).

rent_pause_sandbox

Pauses a running sandbox, keeping its memory and files; the id stays valid and rent_resume_sandbox continues it.

rent_resume_sandbox

Resumes a paused sandbox with a new timeout (default 300 s), holding its cost first on the key that started it; a short balance answers 402 with top-up links. An API key with a spend limit cannot resume a sandbox another key started (403 forbidden).

rent_terminate_sandbox

Ends a sandbox now, deleting its memory and files, and settles its charge. Terminating an ended sandbox answers it unchanged. The id keeps answering rent_get_sandbox.

rent_create_gpu_pod

Starts a GPU pod on RunPod with your own RunPod key (store it with rent_set_byok_credential, vendor runpod). RunPod bills your key by the hour until rent_terminate_gpu_pod; no rent.dev balance is used. The pod id (lse_…) lives until it is terminated. Arguments not listed are refused by name.

rent_list_gpu_pods

Your GPU pods, newest first, every state. next_cursor is opaque and only for the next call of this tool.

rent_terminate_gpu_pod

Ends a GPU pod on RunPod, deleting its container disk, and stops RunPod's billing. Terminating an ended pod answers it unchanged.

rent_set_byok_credential

Stores your own vendor key (BYOK) for e2b and vercel sandboxes or runpod GPU pods, after one read-only call to the vendor checks it. The secret is an argument of this call; it is stored sealed and never returned. fields: e2b {api_key}, vercel {token, team_id, project_id}, runpod {api_key}. The credential id (cred_…) stays valid until you delete it (DELETE /v1/byok/credentials/{id}). An API key with a spend limit cannot store one (403 forbidden): use an uncapped key or a signed-in person.

rent_get_usage

Usage over a period (default the last 30 days): what rent.dev billed for its own sandboxes, beside showback of BYOK usage your vendors bill you for.

rent_get_operation

A long-running operation by id (op_… or operations/op_…), as tools answer them for work that outlasts a call (sandbox placement, x402 settlement). Poll about every 2 s until done; then exactly one of response (the finished resource) or error (a Problem). The id stays readable after it is done.

rent_supply_guide

The guide for providers who run rent.dev nodes, as Markdown: the two ways to run a node, every step with its exact command, each node state and what to do next, every check with its fix, every problem, rates and the holdback. Read it before rent_supply_enroll. Needs no scope, and the public endpoint serves it too.

rent_supply_enroll

Makes claim tokens for new nodes, one per node, each valid for 10 minutes and shown only in this answer. lines holds the exact shell line for each token: run it on the machine and the node kit sets the machine up, claims the node, links it and reports each step. The token reaches the kit on stdin, never as a process argument. On a host the claim comes after the install, and a first install can outlast the token: when the line ends with claim_invalid, call this tool again and run the same line with the new token. That run is quick, because the host is already set up. A refusal carries retryable, owner_action_required and next; run next.command after next.after_s seconds.

rent_supply_nodes

Your nodes, newest claim first, each with its state, checks and next step. next_cursor is only for the next call of this tool.

rent_supply_node

One of your nodes: state, wire, region, ask, capacity, templates, checks with their fixes, suspension reasons, placement and setup timings. next names the one command to run and when; poll this until next.action is none.

rent_supply_update_node

Changes a node's ask, capacity, region or payout. A new ask applies to leases placed from now on; running leases keep their rates. More slots or a new region sends the node back to probing. Changing payout needs signed_nonce: a nonce from GET /v1/accounts/nonce, signed with your account's agent key over the message rent.dev payout v1\n<node_id>\n<nonce>\n<public_key> (four lines joined by newlines, no newline at the end). A refusal carries retryable, owner_action_required and next; run next.command after next.after_s seconds.

rent_supply_probe_node

Makes the node's next probe due now, and takes a suspended node back to probing. The probe starts within about a minute. Read the outcome with rent_supply_node. A probe already running is refused with probe_in_progress. A refusal carries retryable, owner_action_required and next; run next.command after next.after_s seconds.

rent_supply_revoke_node

Takes a node out for good: it gets no new leases and its link is removed. A revoked node cannot come back; enrol the machine again for a new node. Revoking a revoked node answers it unchanged. A refusal carries retryable, owner_action_required and next; run next.command after next.after_s seconds.

rent_supply_earnings

What your nodes have earned: pending, releasable, frozen and paid-out totals in micro-USD, and one page of entries, newest first. A lease shows once it settles, never while it runs. An entry becomes releasable after the holdback, which is longer when the customer paid by card. next_cursor is only for the next call of this tool.

Tools on /mcp/public

ToolWhat it doesScopeHints
rent_quoteQuote sandbox pricesnoneread-only, idempotent
rent_list_providersList providersnoneread-only, idempotent
rent_search_docsSearch the rent.dev docsnoneread-only, idempotent
rent_signupSign upnonenone
rent_supply_guideRead the provider guidenoneread-only, idempotent

rent_search_docs

Keyword search over the sections of rent.dev's full docs (llms-full.txt): each hit is one section, best first, with its heading path and Markdown. The docs are re-read every five minutes.

rent_signup

Creates a rent.dev account and answers its API key, shown this once (store it; it stays valid until revoked), with the authenticated MCP URL and the Authorization header to connect with, and a three-line REST quickstart. To be able to retry, pass idempotency_key, a random value you keep (a UUID will do): if the answer is lost, call again with the same value within 24 hours and you get the same account with a new API key that replaces the first, not a second account. That holds while the account is untouched (its key never used, nothing bound to it, no money in); after that the repeat is refused with idempotency_key_reused and answers no key. Without idempotency_key every call creates a new account. The account starts with a zero balance: a call that needs credit answers 402 with a card pay link and an x402 top-up, or use rent_topup on the authenticated server.

Results and errors

A tool answers its value as structuredContent, and as the same JSON in a text block. A refusal is an isError result whose content is the Problem the REST API would have answered, with its code. The codes are listed on the errors page.

Paying from inside a tool call

When a call needs more credit, the Problem is a 402 with topup.checkout_url and topup.x402_url. A 2026-07-28 client that takes URL elicitation is handed the pay link as an input request instead, and the call runs again once the person has answered.

rent_topup with method: "card" answers a pay link for a person, valid 24 hours. With method: "x402" it charges USDC over MCP:

  1. The first call answers isError with the x402 PaymentRequired object as its structured content.
  2. Sign it within 120 seconds and call again with the same arguments and the PaymentPayload in _meta["x402/payment"].
  3. The paid call settles, credits the balance and answers the settlement in _meta["x402/payment-response"].

A payment that is broadcast but not yet confirmed answers an operation instead. Poll it with rent_get_operation.

JSON
{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": {
    "name": "rent_topup",
    "arguments": {
      "method": "x402",
      "amount_usd": "5.00"
    },
    "_meta": {
      "x402/payment": "<PaymentPayload>"
    }
  }
}