list_offersoffers:readThe classes your key may rent. Each entry states its own jurisdiction.
- required
- —
- optional
- origin
One MCP server, nine tools, and a key. Everything below has been run against the live service - no snippet here is written from memory.
In the project where the work should land. It must be a git repository with at least one commit, because the finished work comes back to it as a branch.
Download the setup into the project:
git clone https://github.com/LYT-AS/lytnodeCreate an API key in your account: /account/keys
Open the project in your agent and say:
read lytnode/docs/first-node.md and do what it saysThe agent does the rest. It asks you only for the key and, in Claude Code, one sign-in in the browser. It works the same in Claude Code, Codex, Cursor, OpenCode, Gemini and Pi.
Rather use a terminal? The same setup is one command:
cd lytnode
bash setup.sh --agent claude # or codex, cursor, opencode, gemini, piThe same whether your agent runs it or you do. Run it again later to update - there is no separate update command.
Rather do it without the setup above? Point your agent at our MCP endpoint with your key and one of the client configurations below. You then make the two keys yourself, and the skill that sends work to the node and brings it home is not installed.
# 1. Your two keys. They must be DIFFERENT.
ssh-keygen -t ed25519 -f ~/.ssh/lytnode-1 -N "" -C "lytnode first key"
ssh-keygen -t ed25519 -f ~/.ssh/lytnode-2 -N "" -C "lytnode second key"
# 2. Your API key, from the environment. Never in a config file.
export LYT_NODES_API_KEY="lyt_live_..."
# 3. Ask your agent, in its own words:
# "Estimate five Node M Dedicated in the US for two hours, then start them."A node needs two DIFFERENT SSH public keys from you. Generate both before your first call: provision_node takes them as ssh_public_key and ssh_cert_public_key, and rejects the same key twice. We never store the second one, so extend_node asks for it again.
Nine, and your key decides which of them you see. A key without nodes:write cannot spend money - that is the single most useful thing scopes do for you.
list_offersoffers:readThe classes your key may rent. Each entry states its own jurisdiction.
estimate_costoffers:readWhat a rental would cost before anything is created. Enforces nothing.
list_modelsoffers:readLocal models that fit a class, with the memory each needs and its licence.
provision_nodenodes:writeRents one node and joins it to your own tailnet.
provision_nodesnodes:writeRents several at once as a job, or adds more to a job already running.
list_nodesnodes:readYour nodes with hostname, job, expiry and state.
extend_nodenodes:writePushes the expiry out and renews your time-limited access.
release_nodenodes:writeReleases one of your nodes before its expiry.
list_toolchainsoffers:readThe developer tools a node can be prepared with, and which classes can run each of them.
Six agents, six configuration formats. Five of the blocks below were tested against a real run on 10-12 September 2026, and each note underneath is a pitfall in that client - the kind that parses cleanly and connects to nothing.
.mcp.json{
"mcpServers": {
"lytnode": {
"type": "http",
"url": "https://nodes.lyt.no/mcp",
"headers": { "Authorization": "Bearer ${LYT_NODES_API_KEY}" }
}
}
}~/.codex/config.toml[mcp_servers.lytnode]
url = "https://nodes.lyt.no/mcp"
bearer_token_env_var = "LYT_NODES_API_KEY"The key is read from the environment, never written into the file. In non-interactive runs, codex exec sets its own approval policy — pass --approve-for-me to let tool calls through (measured 2026-09-12, codex-cli 0.154.0).
~/.cursor/mcp.json{
"mcpServers": {
"lytnode": {
"url": "https://nodes.lyt.no/mcp",
"headers": { "Authorization": "Bearer ${LYT_NODES_API_KEY}" }
}
}
}In -p mode cursor-agent needs --approve-mcps before it will call a tool, and --force before it runs a shell at all. "Connection failed" from cursor-agent mcp list usually just means the key is not exported in that shell.
opencode.json{
"mcp": {
"lytnode": {
"type": "remote",
"url": "https://nodes.lyt.no/mcp",
"headers": { "Authorization": "Bearer ${LYT_NODES_API_KEY}" }
}
}
}Configure a model explicitly. Without one it picks an image model and fails with a message that says nothing about the cause.
~/.gemini/settings.json{
"mcpServers": {
"lytnode": {
"httpUrl": "https://nodes.lyt.no/mcp",
"headers": { "Authorization": "Bearer ${LYT_NODES_API_KEY}" }
}
}
}httpUrl, not url. url is the older SSE transport and connects to nothing without a useful error (measured 2026-09-10, gemini 0.59.0).
~/.pi/agent/extensions/lytnode.ts# pi has no MCP client of its own. The package installs ours:
bash install-agent.sh pi
export LYT_NODES_API_KEY="lyt_live_..."The extension reaches this one server and nothing else, and reads the key from the environment. Newer than the five above, and not yet measured against a real run the same way.
Measured against a real run, with the date. An undated claim is a guess with confidence.
A Mac node arrives prepared for Apple development. Ask for the build tools when you order, and they are in place when the machine is yours - so your agent's first command is the build, not the setup. list_toolchains shows what each class can run.
# Ask for the tools when you order. Your agent can do this in its own words:
# "Rent a Mac for 24 hours with fastlane, cocoapods and xcodes installed."
provision_node(class, hours: 24, toolchain: ["xcodes", "fastlane", "cocoapods"], ...)Available today: xcodes, fastlane, cocoapods, swiftlint, xcbeautify, eas. Xcode itself is installed with xcodes under your own Apple ID, which never reaches us. A Mac is rented for at least 24 hours - Apple's licence, not our price.
Every error carries a code, an HTTP status and enough detail to act on. Two are worth knowing before you meet them: 402 means you are over your spend cap, and waiting will not help - unlike 429, which means exactly that you should wait.
| code | HTTP | what to do |
|---|---|---|
unknown_offer_class | 400 | No such class, or not visible to your key. |
invalid_count | 400 | count must be a whole number from 1 to 10. |
invalid_agents | 400 | agents must have exactly one entry per node. |
agent_not_supported_on_class | 400 | That agent cannot be prepared on that class. |
ttl_required | 400 | Every node needs an expiry. There is no default. |
ttl_too_long | 400 | Over the maximum period. The reply names the maximum. |
spend_cap_reached | 402 | Over your spend cap. Release something, or ask us to raise it. |
unknown_job | 404 | No such job_id — including when it is not a valid uuid. |
job_not_running | 409 | That job has already settled. Start a new one. |
rate_limited | 429 | Ten calls or forty nodes an hour. Wait. |
region_not_served | 451 | We cannot serve your region. |
no_capacity | 503 | Nobody could deliver that class right now. Choose another. |
Ten calls and forty nodes an hour, at most ten nodes per call, and a spend cap that counts what you have standing rather than what you have spent. Every node must carry a time to live - there is no way to start one without an expiry.