Let an agent test in its own cluster
In this step a coding agent runs the loop of the Quick start for you: it creates a throwaway cluster, deploys upcheck there from your upcheck.yaml, runs your tests against it, reports, and deletes the cluster. It works through the portal’s MCP tools with an API key that carries only the scopes it needs, and every call it makes is recorded.
What you will have
Section titled “What you will have”A report from your agent on one version of upcheck, tested in a cluster that existed only for the test, and a log of every call the agent made, from creating that cluster to deleting it. upcheck-prod is not touched.
What you need
Section titled “What you need”- Steps 1 and 2 done. Your team’s first cluster started its subscription; the API cannot start one (it answers
payment_method_required), so a team’s first cluster is always created in the portal. Keep yourupcheck.yaml. - A release with the PaaSbox Platform,
2026.10.2or newer, on the stable channel. A cluster the agent creates takes the release of its channel, stable unless the call names another. - A team admin’s account. Only team admins create keys, and a key acts with the rights of the admin who made it: creating and deleting a cluster need a team admin.
- A Hetzner project for test clusters, connected like the one in step 1. A throwaway cluster gets a copy of its project’s token, so the agent never handles a Hetzner token; in a project that holds only test clusters, that copy reaches nothing else.
- A coding agent that speaks MCP over HTTP, such as Claude Code, on a machine with
kubectland Python 3.10 or later. - The image to test in a registry the cluster can pull from, and your tests: a command that checks the app over HTTP, given its address. What the tests check is yours to decide.
- Room for one more cluster in your team’s limit of 10.
Create the key
Section titled “Create the key”-
Open API keys & agents. Under Create a key, enter the Name
claude-code, tick the Scopesk3s:read,k3s:write,k3s:accessandk3s:destructive, and set Expires to in 30 days. Choose Create key. -
Copy the key. The page shows it once and keeps only a hash of it.
Each scope opens a set of tools, and the agent gets exactly those. k3s:read lists projects, server types, clusters, add-ons and operations. k3s:write switches add-ons, here the PaaSbox Platform on the test cluster; it would also take snapshots and schedule upgrades. k3s:access gets kubeconfigs. k3s:destructive creates and deletes clusters.
A scope is not limited to one cluster: this key could also switch add-ons on upcheck-prod, upgrade it or delete it. Give k3s:destructive only to an agent you watch, as here. A key for an agent that works on your stages alone should not carry it. To keep production out of a key’s reach entirely, keep it in a team the key does not belong to (Guardrails for agents).
Connect the agent
Section titled “Connect the agent”-
Connect your agent on the same page shows the MCP endpoint, the portal’s address followed by
/mcp/. Add it to Claude Code:Terminal window export PAASBOX_API_KEY=<the key>claude mcp add --transport http paasbox https://<the portal>/mcp/ \--header "Authorization: Bearer $PAASBOX_API_KEY" -
Download the kubeconfig helper the page links,
paasbox_kubeconfig.py, into the agent’s working directory, and runpip install cryptography. -
Let the agent ask you before it calls
create_cluster,delete_cluster,restore_snapshotordetach_cluster: in Claude Code, do not add them to the allowed tools.
When the agent connects, the server tells it which team it acts in, as whom, and with which scopes. With all four scopes it sees all 18 tools.
Give it the task
Section titled “Give it the task”Start the agent in the directory with upcheck.yaml and your tests, and give it the task:
Test the upcheck image registry.example.com/upcheck:<tag> in a throwaway PaaSbox cluster.1. Create the cluster upcheck-test-1 in the Hetzner project for tests, location fsn1, server type cpx22, with a copy of the project's token. Open its API only to this machine's address.2. Wait until it is ready. Switch on the paasbox-platform add-on, profile saas-http01, ACME e-mail you@example.com, with Flux, and wait until it reports applied.3. Get an admin kubeconfig for one hour with paasbox_kubeconfig.py. Never show it or the private key.4. Apply upcheck.yaml in namespace upcheck with spec.image set to the image above and without spec.exposure. Wait until the SaaSApplication is Ready.5. Port-forward svc/upcheck-web to localhost:8000 and run ./smoke-test.sh http://localhost:8000.6. Report what passed and what failed.7. Delete the cluster without a final snapshot.What the agent does with it:
-
It calls
list_projectsandlist_server_typesfor the project’s ID and the server types with their memory and Hetzner’s monthly net price. -
It calls
create_clusterwithnameandconfirmbothupcheck-test-1,hcloud_token{"mode": "copy"}and your address inallowed_api_ranges. Your agent asks you first; say yes. The portal refuses the call unlessconfirmis the new cluster’s name, and runs the create page’s checks: your team’s limit, the accepted terms, billing. -
It polls
get_clusteruntilstateisready. In the lab a cluster was ready 2 minutes 46 seconds after the request; 69 of them copied the node image into the project, which only the project’s first cluster waits for. -
It calls
set_addonwithaddonpaasbox-platform,enabledtrue,options{"profile": "saas-http01", "acmeEmail": "you@example.com"}andalso["flux"], which switches Flux on in the same save, and pollslist_addonsuntil the platform reportsapplied. The create call has aplatformfield too, but the API has no field for the ACME e-mail that every profile needs, so a create withplatformis refused; the portal’s create page asks for the e-mail, the API does not. -
It makes a key pair with
python3 paasbox_kubeconfig.py keygen --out key.pem, passes the public half torequest_kubeconfigwithroleadmin, and pollsget_kubeconfig_result. The result is encrypted to its key and handed out once;paasbox_kubeconfig.py decryptopens it on the agent’s machine. The portal cannot read it. -
It applies the edited copy of
upcheck.yaml, which creates the namespaceupchecktoo. Withoutspec.exposurethe platform publishes nothing: no Ingress, no certificate, no DNS name to wait for, and the Django profile accepts any host name over plain HTTP.kubectl -n upcheck get saasappshows the phaseReadyonce the migration has run and the components are up. -
It runs your tests through
kubectl -n upcheck port-forward svc/upcheck-web 8000:80and reports. -
It calls
delete_clusterwithconfirmupcheck-test-1andfinal_snapshotfalse: the cluster has no bucket, and a final snapshot would stay on the disk that is deleted. Your agent asks you again. Billing stops at once; in the lab a delete took 14 seconds, and no resource with the cluster’s label was left.
Read what it did
Section titled “Read what it did”On API keys & agents, Recent activity lists the last calls; All activity has all of them, filtered by Key. Each MCP call is one row, reads and refusals included: when it ran, the key, the call, the cluster and the outcome, with the code of a refusal such as confirmation_required. The arguments are kept with every secret replaced by [redacted]. What the agent ran with kubectl is not in this log: those calls went to the cluster’s own API.
In progress Paddle charges a further cluster when it is first ready, prorated to the end of the billing period, and credits the rest when it is deleted; upcheck-prod keeps the team’s subscription running meanwhile. Hetzner billed the test cluster’s server by the hour, at its own price.
The MCP tools reference lists every tool with its scope and arguments; Guardrails for agents says what each limit stops and what it does not.
What you have now
Section titled “What you have now”An agent that tests a change in a cluster built for the test and removes the cluster afterwards, with a key that carries the scopes you chose, and a record of each call. The next step gives upcheck a second stage.