Skip to content

cloud.redu/mcp

by redu.cloud · remote · no sign-in to list tools · v0.1.1 · published by cloud.redu

Agent-native cloud, EU-hosted. Provision VMs, networks & databases on redu.cloud via MCP.

Deletes · 16Changes · 419 high findings

88 tools: 16 can delete or cancel things, 41 change things and 31 only read.

Scanned Oct 21, 2026

23 checks matched something in the tool definitions. No finding is not a safety guarantee. How the checks work.

claude mcp add --transport http redu https://mcp.redu.cloud/mcp

Findings

Matches of fixed checks, with the text they matched.

9 high7 warn7 info
  • The description refers to ".env".

    Matched text

    description: "...ode_modules/.git/.venv/build output and .env by default; honors .gitignore when is_g..."

    What this check looks for
  • The description refers to ".env".

    Matched text

    description: "...eflight: returns a podman-compose.yml + .env so the user can run the app (and a thro..."

    What this check looks for
  • The input schema at properties.public_key.description refers to "id_rsa.pub" and "id_ed25519.pub".

    Matched text

    inputSchema.properties.public_key.description: "...lic key to import (the contents of your id_rsa.pub or id_ed25519.pub)."

    What this check looks for
  • The description refers to "~/.ssh/" and "~/.ssh/redu-deploy".

    Matched text

    description: "...et_deployment - so the command uses `-i ~/.ssh/<keypair_name>` and authenticates with t..."

    What this check looks for
  • The input schema at properties.keypair_name.description refers to "~/.ssh/".

    Matched text

    inputSchema.properties.keypair_name.description: "...y VM). When given, the command uses `-i ~/.ssh/<keypair_name>` so it authenticates with..."

    What this check looks for
  • The input schema at properties.env.description refers to ".env".

    Matched text

    inputSchema.properties.env.description: "...vars APPENDED to the compose project's .env (for ${VAR} interpolation). We never re..."

    What this check looks for
Show 17 more findings
  • The input schema at properties.database.description refers to ".env".

    Matched text

    inputSchema.properties.database.description: "...URL/PG*/MYSQL_* appended to the project .env. 'compose' = the stack's own db service..."

    What this check looks for
  • The description refers to ".env".

    Matched text

    description: "...s, the host's memory/disk/load, and the .env KEY NAMES. Do NOT loop on get_deploymen..."

    What this check looks for
  • The description refers to "/opt/app-src/.env".

    Matched text

    description: "...- the keys in the env files on the VM (/opt/app-src/.env and friends), never the values. Most of..."

    What this check looks for
  • The server marks "check_deploy_prerequisites" read-only (readOnlyHint: true), and its own name, description or input give the class "write".

    Matched text

    annotations: {"readOnlyHint":true,"openWorldHint":true}; The name "check_deploy_prerequisites" says "deploy": a word for a change.

    What this check looks for
  • The server marks "deploy_overview" read-only (readOnlyHint: true), and its own name, description or input give the class "write".

    Matched text

    annotations: {"readOnlyHint":true,"openWorldHint":true}; The name "deploy_overview" says "deploy": a word for a change.

    What this check looks for
  • The server marks "prepare_upload" read-only (readOnlyHint: true), and its own name, description or input give the class "write".

    Matched text

    annotations: {"readOnlyHint":true,"openWorldHint":true}; The name "prepare_upload" says "upload": a word for a change.

    What this check looks for
  • The server marks "select_surface" read-only (readOnlyHint: true), and its own name, description or input give the class "write".

    Matched text

    annotations: {"readOnlyHint":true,"openWorldHint":true}; The name "select_surface" says "select": a word for a change.

    What this check looks for
  • The server marks "plan_deploy" read-only (readOnlyHint: true), and its own name, description or input give the class "write".

    Matched text

    annotations: {"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":true}; The name "plan_deploy" says "deploy": a word for a change.

    What this check looks for
  • The server marks "wait_for_deployment" read-only (readOnlyHint: true), and its own name, description or input give the class "write".

    Matched text

    annotations: {"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":true}; The name "wait_for_deployment" says "wait": a word for reading.

    What this check looks for
  • The server marks "get_domain_verification" read-only (readOnlyHint: true), and its own name, description or input give the class "write".

    Matched text

    annotations: {"readOnlyHint":true,"openWorldHint":true}; The name "get_domain_verification" says "get": a word for reading.

    What this check looks for
  • Infowhoami

    The description mentions "credential".

    Matched text

    description: "Verifies your redu.cloud credential with a REAL server round-trip and retur..."

    What this check looks for
  • The description mentions "credential".

    Matched text

    description: "...approve, because a key is a long-lived credential that acts on the account and ANY usage..."

    What this check looks for
  • The description mentions "credential".

    Matched text

    description: "...short-lived ticket minted from your MCP credential - NO API key needed in the command and..."

    What this check looks for
  • The description is 5,004 characters long, over the 2,000 this check looks for.

    Matched text

    description ends: "...unless route_next's conditions for building the Dockerfile with deploy_app hold."

    What this check looks for
  • The description is 5,022 characters long, over the 2,000 this check looks for.

    Matched text

    description ends: "...apshot upload can take several minutes; poll list_clusters until CREATE_COMPLETE."

    What this check looks for
  • The description is 2,271 characters long, over the 2,000 this check looks for.

    Matched text

    description ends: "...chine. For the app tier rather than the database, use upgrade_to_cluster instead."

    What this check looks for
  • The description mentions "credentials".

    Matched text

    description: "...decisions, or anything needing external credentials. TASK FORMAT (strict, one line each): `..."

    What this check looks for

Tools

Open a tool for its reasons and full description. Descriptions are the publisher's own words, shown as they are.

88 tools

Deletes or cancels 16Claude Code: deny
  • delete_api_keyDeletes one of the account's API keys, by id or by name.No findings

    Why it is in this group

    • The name "delete_api_key" says "delete": a word for removing or ending something.
    • The server marks it destructive (destructiveHint: true).
    Description, as the server wrote it
    Deletes one of the account's API keys, by id or by name. Anything that uses the key stops working at once, so the first call deletes nothing: it shows the key (name, scopes, last used) for the user to approve; call again with confirm:true only after their yes. Use it to clean up a key you created for a task that is finished.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: true
    • idempotentHint: true
    • openWorldHint: true
  • delete_backupDeletes a backup by ID.No findings

    Why it is in this group

    • The name "delete_backup" says "delete": a word for removing or ending something.
    • The server marks it destructive (destructiveHint: true).
    Description, as the server wrote it
    Deletes a backup by ID.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: true
    • idempotentHint: true
    • openWorldHint: true
  • delete_clickhouseDeletes a managed ClickHouse database and its underlying VM.No findings

    Why it is in this group

    • The name "delete_clickhouse" says "delete": a word for removing or ending something.
    • The description says "cannot be undone".
    • The server marks it destructive (destructiveHint: true).
    Description, as the server wrote it
    Deletes a managed ClickHouse database and its underlying VM. Pass the numeric id from list_clickhouse_databases. This cannot be undone.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: true
    • idempotentHint: true
    • openWorldHint: true
  • delete_clusterDeletes an autoscaling cluster - tears down the WHOLE cluster: every autoscaled EXTRA member VM, the load balancer, the pool, the cluster security group, and the cluster's *.redu.c…No findings

    Why it is in this group

    • The name "delete_cluster" says "delete": a word for removing or ending something.
    • The description says "Cannot be undone".
    • The server marks it destructive (destructiveHint: true).
    Description, as the server wrote it
    Deletes an autoscaling cluster - tears down the WHOLE cluster: every autoscaled EXTRA member VM, the load balancer, the pool, the cluster security group, and the cluster's *.redu.cloud proxy host. Your SOURCE (hero) VM is NOT part of the stack and is NOT deleted - it was the always-on baseline member but it is your own VM; delete_instance it separately if you no longer need it. Pass stack_name + stack_id from list_clusters. Deleting extra members individually does NOT work (the autoscaling group re-creates them under load) - this deletes the group itself. The managed database / external store the cluster used is NOT touched (data safety); delete_database it separately when you are done with the data. Cannot be undone.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: true
    • idempotentHint: true
    • openWorldHint: true
  • delete_databaseDeletes a managed Postgres database and its underlying VM.No findings

    Why it is in this group

    • The name "delete_database" says "delete": a word for removing or ending something.
    • The description says "cannot be undone".
    • The server marks it destructive (destructiveHint: true).
    Description, as the server wrote it
    Deletes a managed Postgres database and its underlying VM. Pass the numeric database id from list_databases. This cannot be undone.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: true
    • idempotentHint: true
    • openWorldHint: true
  • delete_deploymentDeletes a deployment and its underlying app VM, with the data kept on that machine (its named volumes): the only way kept data is deleted, so ask the user first.No findings

    Why it is in this group

    • The name "delete_deployment" says "delete": a word for removing or ending something.
    • The description says "Cannot be undone".
    • The server marks it destructive (destructiveHint: true).
    Description, as the server wrote it
    Deletes a deployment and its underlying app VM, with the data kept on that machine (its named volumes): the only way kept data is deleted, so ask the user first. Pass the numeric id from list_deployments. IMPORTANT: its managed datastores are NOT deleted (data safety): its database, Redis and Qdrant keep running and billing, and this tool returns each id so you can delete_database / delete_relational_database / delete_redis / delete_qdrant them when you're done with the data. Cannot be undone.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: true
    • idempotentHint: true
    • openWorldHint: true
  • delete_dns_entryRemoves one public name (a DNS entry), by its id from list_dns_entries or by the name itself.No findings

    Why it is in this group

    • The name "delete_dns_entry" says "delete": a word for removing or ending something.
    • The server marks it destructive (destructiveHint: true).
    Description, as the server wrote it
    Removes one public name (a DNS entry), by its id from list_dns_entries or by the name itself. The name stops serving at once, and anything reached through it (a site, an API, an OAuth callback) stops working with it. When the name belongs to one of your instances or deployments, that instance's own list of its names is updated too, the way the console's DNS view does it; the instance, its app and its data are untouched. The first call removes nothing: it shows the entry for the user to approve; call again with confirm:true only after their yes. Use it to clear the names a release_domain refusal (DOMAIN_IN_USE) lists. redu's own platform names cannot be removed.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: true
    • idempotentHint: true
    • openWorldHint: true
  • delete_instancePermanently deletes an instance.No findings

    Why it is in this group

    • The name "delete_instance" says "delete": a word for removing or ending something.
    • The description says "Permanently deletes".
    • The server marks it destructive (destructiveHint: true).
    Description, as the server wrote it
    Permanently deletes an instance. This cannot be undone.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: true
    • idempotentHint: true
    • openWorldHint: true
  • delete_keypairRemoves an SSH keypair from your account by name.No findings

    Why it is in this group

    • The name "delete_keypair" says "delete": a word for removing or ending something.
    • The server marks it destructive (destructiveHint: true).
    Description, as the server wrote it
    Removes an SSH keypair from your account by name.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: true
    • idempotentHint: true
    • openWorldHint: true
  • delete_qdrantDeletes a managed Qdrant and its underlying VM, with every collection in it.No findings

    Why it is in this group

    • The name "delete_qdrant" says "delete": a word for removing or ending something.
    • The description says "cannot be undone".
    • The server marks it destructive (destructiveHint: true).
    Description, as the server wrote it
    Deletes a managed Qdrant and its underlying VM, with every collection in it. Pass the numeric id from list_qdrant. This cannot be undone.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: true
    • idempotentHint: true
    • openWorldHint: true
  • delete_redisDeletes a managed Redis instance and its underlying VM.No findings

    Why it is in this group

    • The name "delete_redis" says "delete": a word for removing or ending something.
    • The description says "cannot be undone".
    • The server marks it destructive (destructiveHint: true).
    Description, as the server wrote it
    Deletes a managed Redis instance and its underlying VM. Pass the numeric id from list_redis. This cannot be undone.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: true
    • idempotentHint: true
    • openWorldHint: true
  • delete_relational_databaseDeletes a managed MySQL/MariaDB database and its underlying VM.No findings

    Why it is in this group

    • The name "delete_relational_database" says "delete": a word for removing or ending something.
    • The description says "cannot be undone".
    • The server marks it destructive (destructiveHint: true).
    Description, as the server wrote it
    Deletes a managed MySQL/MariaDB database and its underlying VM. Pass the numeric id from list_relational_databases. This cannot be undone.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: true
    • idempotentHint: true
    • openWorldHint: true
  • delete_snapshotDeletes a snapshot by ID.No findings

    Why it is in this group

    • The name "delete_snapshot" says "delete": a word for removing or ending something.
    • The server marks it destructive (destructiveHint: true).
    Description, as the server wrote it
    Deletes a snapshot by ID.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: true
    • idempotentHint: true
    • openWorldHint: true
  • delete_volumePermanently deletes a block storage volume and the data on it.No findings

    Why it is in this group

    • The name "delete_volume" says "delete": a word for removing or ending something.
    • The description says "Permanently deletes".
    • The server marks it destructive (destructiveHint: true).
    Description, as the server wrote it
    Permanently deletes a block storage volume and the data on it. This cannot be undone. The volume must be 'available' (detached) - detach_volume it first, and note you cannot detach a running instance's boot disk. A volume left behind after a restore keeps billing, so delete the ones you no longer need. Any BACKUP the volume was restored from is independent and survives this.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: true
    • idempotentHint: true
    • openWorldHint: true
  • release_domainRemoves a custom domain from this account: a verified domain is no longer verified for it (using it again needs a new TXT check), and a pending claim is cancelled.No findings

    Why it is in this group

    • The description's first verb is "remove": a word for removing or ending something.
    • The server marks it destructive (destructiveHint: true).
    Description, as the server wrote it
    Removes a custom domain from this account: a verified domain is no longer verified for it (using it again needs a new TXT check), and a pending claim is cancelled. The first call releases nothing: it shows the domain for the user to approve; call again with confirm:true only after their yes. The second call reports the platform's answer: released, or refused. A refusal because public names still use the domain (DOMAIN_IN_USE) lists those names, each with its id: remove each with delete_dns_entry (it asks for the user's yes first), then call this again. Releasing a domain deletes no deployment, instance or cluster.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: true
    • idempotentHint: true
    • openWorldHint: true
  • upgrade_to_haTurns an EXISTING, running single-machine managed Postgres or ClickHouse into a THREE-machine highly available cluster, in place, keeping the data and the same username, password a…1 finding

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    • The server marks it destructive (destructiveHint: true).

    Findings

    • Info

      The description is 2,271 characters long, over the 2,000 this check looks for.

      Matched text

      description ends: "...chine. For the app tier rather than the database, use upgrade_to_cluster instead."

      What this check looks for
    Description, as the server wrote it
    Turns an EXISTING, running single-machine managed Postgres or ClickHouse into a THREE-machine highly available cluster, in place, keeping the data and the same username, password and database name. Use this when the user has a live database and wants it to survive a machine or host failure: they do NOT have to create a new one and migrate by hand. How it works: a full backup is taken first (a safety copy while the upgrade runs, deleted when it ends: the old machine is never touched and stays the restore point), the cluster is built alongside, the data is copied in and verified by counting both sides, and only then does the database's address move to the load balancer. The original machine is left running and untouched throughout, so a failure changes nothing and the user is never without a database. SAY THREE THINGS TO THE USER AND GET A YES BEFORE CALLING: (1) COST, it becomes three machines behind a load balancer instead of one, so roughly 3x the hourly rate, and the old single machine keeps billing for 3 days, until it is removed automatically; (2) WRITE LOSS, anything written to the old machine during the copy is not carried over (see acknowledge_write_loss); (3) THEY MUST REPOINT THE APP afterwards, the connection address changes and nothing redeploys their app for them. Takes about 20 minutes and returns immediately; poll list_databases / list_clickhouse_databases until the address changes. Requires a payment method: high availability is not available on the no-card trial. ⛔ CLICKHOUSE ONLY, a FOURTH thing to say: after the upgrade every table has to use a REPLICATED engine. The existing tables are converted for them as part of the copy, but any NEW `CREATE TABLE ... ENGINE = MergeTree` is refused from then on (ClickHouse error 56) and has to be written `ENGINE = ReplicatedMergeTree`. It is refused rather than accepted because a plain MergeTree on three machines replicates its schema and not its rows, which would leave two of the three answering with an empty table and no error. If their app ships plain-MergeTree migrations (Plausible, PostHog and Langfuse all do), those migrations will fail after the upgrade and they should stay on the single machine. For the app tier rather than the database, use upgrade_to_cluster instead.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: true
    • idempotentHint: false
    • openWorldHint: true
Changes things 41Claude Code: ask
  • attach_volumeAttaches an EXISTING block volume to a running instance as an extra disk.No findings

    Why it is in this group

    • The name "attach_volume" says "attach": a word for a change.
    Description, as the server wrote it
    Attaches an EXISTING block volume to a running instance as an extra disk. This is how you finish a RESTORE after a VM is gone: restore_backup (volumeName) rebuilds the data into a new volume, then attach_volume puts that volume on a replacement instance - then SSH in (get_ssh_command) and mount it (`lsblk` to find it, usually /dev/vdb, then `mount /dev/vdb /mnt/...`). The volume must be 'available' (not already in-use - detach_volume it first) and in the same project. Attaching does NOT mount or boot from it: the instance keeps booting from its own root disk, the volume shows up as an extra block device. Poll list_volumes until status is 'in-use'.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: true
  • check_agent_prerequisitesChecks if your account has everything needed to run autonomous coding agents.No findings

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    • The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.
    Description, as the server wrote it
    Checks if your account has everything needed to run autonomous coding agents. Returns a checklist: API key validity, private network, SSH keypair, and billing. Safe to call anytime - read-only. Call this first before create_controller.

    Annotations the server declares

    • readOnlyHint: true
    • openWorldHint: true
  • check_deploy_prerequisitesThe deploy ENTRY GATE - run this first when a user wants to deploy.1 finding

    Why it is in this group

    • The name "check_deploy_prerequisites" says "deploy": a word for a change.
    • The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.

    Findings

    • Warn

      The server marks "check_deploy_prerequisites" read-only (readOnlyHint: true), and its own name, description or input give the class "write".

      Matched text

      annotations: {"readOnlyHint":true,"openWorldHint":true}; The name "check_deploy_prerequisites" says "deploy": a word for a change.

      What this check looks for
    Description, as the server wrote it
    The deploy ENTRY GATE - run this first when a user wants to deploy. Verifies the account is ready and AUTO-SELECTS the network_id + keypair_name to pass to deploy_app (so you never hunt for them): (1) account reachable + quota, (2) a private network (picks your default - every account has one), (3) an SSH keypair (if you have none, returns ssh_key_recipe: the exact local ssh-keygen → import_keypair → write SSH_KEY_LOCATION-to-.env steps - run them, in yolo mode without asking). Returns { ready, network_id, keypair_name, missing, ssh_key_recipe }.

    Annotations the server declares

    • readOnlyHint: true
    • openWorldHint: true
  • create_api_keyMints a NEW least-privilege redu API key for a deployed app or automation to use (e.g.1 finding

    Why it is in this group

    • The name "create_api_key" says "create": a word for a change.

    Findings

    • Info

      The description mentions "credential".

      Matched text

      description: "...approve, because a key is a long-lived credential that acts on the account and ANY usage..."

      What this check looks for
    Description, as the server wrote it
    Mints a NEW least-privilege redu API key for a deployed app or automation to use (e.g. to add a backup feature). GUARDRAIL: the first call does NOT create anything. It returns a confirmation the user must approve, because a key is a long-lived credential that acts on the account and ANY usage of it is BILLED to the account owner. Show the user the name, the exact scopes, and the billing notice, get their explicit approval, then call again with confirm:true. This is required even in yolo/auto mode. The secret is returned ONCE.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: true
  • create_backupCreates a backup, by volumeId or by instanceId (the instance's volume is resolved for you).No findings

    Why it is in this group

    • The name "create_backup" says "create": a word for a change.
    Description, as the server wrote it
    Creates a backup, by volumeId or by instanceId (the instance's volume is resolved for you). The backup is held independently of the instance, so it survives the VM. Poll list_backups until the new backup is available.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: true
  • create_clickhouseProvisions a managed ClickHouse database (OLAP / columnar analytics engine, Apache-2.0) on a dedicated VM on your private network - its OWN resource, NOT a relational database.No findings

    Why it is in this group

    • The name "create_clickhouse" says "create": a word for a change.
    Description, as the server wrote it
    Provisions a managed ClickHouse database (OLAP / columnar analytics engine, Apache-2.0) on a dedicated VM on your private network - its OWN resource, NOT a relational database. Requires a recent plan_managed_datastore. Use it for analytics / observability workloads that need a column store (PostHog, Langfuse, event analytics, time-series). It is PRIVATE - reachable only from another instance on the same private network, via the DB's internal/private IP on the ClickHouse HTTP port 8123 (CLICKHOUSE_HOST/PORT/USER/PASSWORD/DB env, http://host:8123). Get the ids from plan_managed_datastore/list_flavors (use m1.small+ - ClickHouse needs >=2GB RAM), list_private_networks, list_keypairs. Provisioning takes ~5 min; poll list_clickhouse_databases until status='ready'. HIGH AVAILABILITY: pass ha:true to get THREE machines on three different physical hosts behind a load balancer instead of one: all three take reads and writes, so losing a machine costs no failover and no write pause, and the replacement refills itself from the survivors before it serves again. It costs about 3x the hourly rate (three machines instead of one) and provisions more slowly. Default is a single machine; show the user the price difference and get an explicit yes before turning HA on.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: true
  • create_controllerCreates a self-configuring controller VM on redu.cloud.No findings

    Why it is in this group

    • The name "create_controller" says "create": a word for a change.
    Description, as the server wrote it
    Creates a self-configuring controller VM on redu.cloud. Auto-discovers your network, Ubuntu image, and flavor. Generates and uploads an SSH keypair if you don't have one. The VM bootstraps itself - no SSH or VPN needed. After ~7 minutes, authorize the fleet (run the authorize.sh one-liner it returns) then call trigger_agent_batch.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: true
  • create_databaseProvisions a managed PostgreSQL database on a dedicated VM on your private network.No findings

    Why it is in this group

    • The name "create_database" says "create": a word for a change.
    Description, as the server wrote it
    Provisions a managed PostgreSQL database on a dedicated VM on your private network. Requires a recent plan_managed_datastore. For app deployments, prefer deploy_app database:'managed' so plan_deploy includes and wires the DB automatically. It is PRIVATE - reachable only from another instance on the same private network, via the DB's internal/private IP (not a public address). Get the ids from plan_managed_datastore/list_flavors/list_private_networks/list_keypairs. Provisioning takes ~5 min; poll list_databases until status='ready', then the connection details (private_ip, port 5432, db_name, db_user) are populated. HIGH AVAILABILITY: pass ha:true to get THREE machines on three different physical hosts behind a load balancer with automatic failover instead of one machine: the database survives losing a machine, and nobody has to fail it over by hand. It costs about 3x the hourly rate (three machines instead of one), it provisions more slowly, and one connection string still serves the whole thing. Default is a single machine; show the user the price difference and get an explicit yes before turning HA on.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: true
  • create_instanceCreates a raw compute instance for custom OS/cloud-init workflows.No findings

    Why it is in this group

    • The name "create_instance" says "create": a word for a change.
    Description, as the server wrote it
    Creates a raw compute instance for custom OS/cloud-init workflows. For app/source deployments, prefer check_deploy_prerequisites -> plan_deploy -> deploy_app/deploy_compose; do not use create_instance as a shortcut around the deployment plan. Requires a recent plan_instance. For reduOS, set cloud_init_template='reduos' and flavor m1.xlarge (16GB). SSH keypair must exist. For a public web app on a raw VM, pass dns_entries (gives a .redu.cloud URL through redu's automatic public proxy). The origin app should listen on dns_entries[].port; do not install nginx on the VM unless the app itself needs it, because redu.cloud already creates the public Nginx proxy host.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: true
  • create_media_spaceCreates a private Redu media space: a small NFS VM backed by a persistent volume, intended for WordPress/WooCommerce wp-content/uploads in autoscaling clusters.No findings

    Why it is in this group

    • The name "create_media_space" says "create": a word for a change.
    Description, as the server wrote it
    Creates a private Redu media space: a small NFS VM backed by a persistent volume, intended for WordPress/WooCommerce wp-content/uploads in autoscaling clusters. Prefer letting deploy_app/deploy_compose create this by passing create_media_space:true after plan_deploy approval; use this tool when manually preparing or reusing shared uploads storage.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: true
  • create_qdrantProvisions a managed Qdrant vector database on a dedicated VM.No findings

    Why it is in this group

    • The name "create_qdrant" says "create": a word for a change.
    Description, as the server wrote it
    Provisions a managed Qdrant vector database on a dedicated VM. Requires a recent plan_managed_datastore (engine 'qdrant'). Reachable from your private network at <private_ip>:6333 (REST) and :6334 (gRPC), and at a public https URL; every request must carry the api-key header (always required). Backups: set_managed_backups with service 'qdrant'. FOR AN APP YOU DEPLOY YOU DO NOT NEED THIS: report vector_engines:['qdrant'] in send_signals and the deploy starts and wires a managed Qdrant itself (a compose stack's own qdrant service is replaced by it). Provisioning takes a few minutes; poll list_qdrant until status='ready'.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: true
  • create_redisProvisions a managed Redis instance on a dedicated VM on your private network.No findings

    Why it is in this group

    • The name "create_redis" says "create": a word for a change.
    Description, as the server wrote it
    Provisions a managed Redis instance on a dedicated VM on your private network. Requires a recent plan_managed_datastore. It is PRIVATE - reachable only from another instance on the same private network, via its internal/private IP on port 6379 (not a public address). AUTH (requirepass) is always enabled. Get the ids from plan_managed_datastore/list_flavors, list_private_networks (or check_deploy_prerequisites), list_keypairs - use the SAME network_id as the app that will connect. Provisioning takes ~5 min; poll list_redis until status='ready', then the connection details (private_ip, port 6379) are populated. Wire an app with REDIS_URL=redis://:<password>@<private_ip>:6379 (pass it via deploy_app env).

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: true
  • create_relational_databaseProvisions a managed MySQL (or MariaDB) database on a dedicated VM on your private network - the relational-database resource (use this instead of create_database when the app need…No findings

    Why it is in this group

    • The name "create_relational_database" says "create": a word for a change.
    Description, as the server wrote it
    Provisions a managed MySQL (or MariaDB) database on a dedicated VM on your private network - the relational-database resource (use this instead of create_database when the app needs MySQL/MariaDB, e.g. WordPress, NextCloud, Matomo, many PHP/LAMP apps). Requires a recent plan_managed_datastore. For app deployments, prefer deploy_app database:'managed' with db_engine mysql/mariadb so plan_deploy includes and wires the DB automatically. It is PRIVATE - reachable only from another instance on the same private network, via the DB's internal/private IP (port 3306), not a public address. Get the ids from plan_managed_datastore/list_flavors/list_private_networks/list_keypairs. Provisioning takes ~5 min; poll list_relational_databases until status='ready', then the connection details (private_ip, port 3306, db_name, db_user) are populated. MySQL is created with mysql_native_password auth so older clients/apps connect cleanly. (ClickHouse is a separate resource - use create_clickhouse / list_clickhouse_databases.)

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: true
  • create_snapshotCreates a snapshot of a running instance.No findings

    Why it is in this group

    • The name "create_snapshot" says "create": a word for a change.
    Description, as the server wrote it
    Creates a snapshot of a running instance.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: true
  • create_volumeCreates a block storage volume.No findings

    Why it is in this group

    • The name "create_volume" says "create": a word for a change.
    Description, as the server wrote it
    Creates a block storage volume.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: true
  • deploy_appDeploys an app to a VM and exposes it at a public https://<name>-<id>.redu.cloud URL.No findings

    Why it is in this group

    • The name "deploy_app" says "deploy": a word for a change.
    Description, as the server wrote it
    Deploys an app to a VM and exposes it at a public https://<name>-<id>.redu.cloud URL. The container is built ON the VM. PREREQS - run check_deploy_prerequisites first for network_id + keypair_name, then plan_deploy for cost approval. Source can be git repo or prepare_upload source_token. PORT must be the real app listen port. DATASTORES ARE MANAGED BY DEFAULT: each one you reported in send_signals runs as a managed store started at your signal and is wired with no settings (DATABASE_URL and PG*/MYSQL_*, REDIS_URL, QDRANT_URL with QDRANT_API_KEY); without a signal, pass database:'managed' (db_engine 'postgres', or 'mysql'/'mariadb' for WordPress/Matomo/LAMP), redis:'managed' or qdrant:'managed'. For WordPress/WooCommerce cluster intent, do not use generic stateless deploy: pass app_profile, cluster_target:true, database:'managed', db_engine:'mariadb' or 'mysql', cluster_media_mode:'media_space', and either media_space_id or create_media_space:true. Redu mounts the media space into wp-content/uploads and refuses unsafe local uploads. Build+provision takes minutes; poll list_deployments/get_deployment. ROUTE: when plan_deploy returned recommended_route:'compose', do NOT use this tool for that folder: deploy its recommended_compose_file with deploy_compose (compose_file, recommended_service as service, recommended_port as port), for the reason in route_reason. The exceptions are route_next's: this tool builds the folder's Dockerfile instead only if the user asked for a build from source, the user changed the code the Dockerfile builds (check git status and unpushed commits in the folder), or the image cannot be pulled.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: true
  • deploy_composeDeploys a MULTI-CONTAINER app - a repo that ships docker-compose.yml / compose.yaml - onto ONE VM via podman-compose, and exposes one or more services at redu.cloud URLs.2 findings

    Why it is in this group

    • The name "deploy_compose" says "deploy": a word for a change.

    Findings

    • High

      The input schema at properties.env.description refers to ".env".

      Matched text

      inputSchema.properties.env.description: "...vars APPENDED to the compose project's .env (for ${VAR} interpolation). We never re..."

      What this check looks for
    • High

      The input schema at properties.database.description refers to ".env".

      Matched text

      inputSchema.properties.database.description: "...URL/PG*/MYSQL_* appended to the project .env. 'compose' = the stack's own db service..."

      What this check looks for
    Description, as the server wrote it
    Deploys a MULTI-CONTAINER app - a repo that ships docker-compose.yml / compose.yaml - onto ONE VM via podman-compose, and exposes one or more services at redu.cloud URLs. Use this instead of deploy_app when the repo is a compose stack. Same prereqs + source modes as deploy_app; always run plan_deploy first. PORT is the HOST port for the exposed service. DATASTORES ARE MANAGED BY DEFAULT: each one you reported in send_signals runs as a managed store; the stack keeps its own settings, and its own copy of each store is replaced by a forwarder of the same name to the managed one (the stack's own user, password and database created there, its init scripts run once). Pass no database/redis/qdrant settings; 'compose' (the stack's own) only when the user asked for one VM. For WordPress/WooCommerce cluster intent, do not leave the compose db service/local uploads as state: pass app_profile, cluster_target:true, database:'managed', db_engine:'mariadb' or 'mysql', cluster_media_mode:'media_space', and either media_space_id or create_media_space:true. Redu writes an override file that points the WordPress service at managed DB env and mounts the media space into /var/www/html/wp-content/uploads. Poll get_deployment until ready. ROUTE: when plan_deploy returned recommended_route:'compose', this is the tool for that folder, even when it also has a Dockerfile: pass its recommended_compose_file as compose_file, recommended_service as service and recommended_port as port (the HOST port the app service publishes, not the port the app listens on inside its container). route_reason says why the platform deploys it that way, and route_next when to build the Dockerfile with deploy_app instead.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: true
  • deploy_overviewOrientation for deploying an app on redu.cloud: the end-to-end flow, the two source modes (git vs upload), how to handle a missing Dockerfile or a database, when (and when NOT) to …1 finding

    Why it is in this group

    • The name "deploy_overview" says "deploy": a word for a change.
    • The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.

    Findings

    • Warn

      The server marks "deploy_overview" read-only (readOnlyHint: true), and its own name, description or input give the class "write".

      Matched text

      annotations: {"readOnlyHint":true,"openWorldHint":true}; The name "deploy_overview" says "deploy": a word for a change.

      What this check looks for
    Description, as the server wrote it
    Orientation for deploying an app on redu.cloud: the end-to-end flow, the two source modes (git vs upload), how to handle a missing Dockerfile or a database, when (and when NOT) to split a deploy across multiple VMs, verifying a deploy functionally, and how to debug a failed build. Call this first when a user asks to deploy/ship/host an app and you're unsure where to start.

    Annotations the server declares

    • readOnlyHint: true
    • openWorldHint: true
  • deploy_vpnStands up a redu VPN gateway (WireGuard tunnelled over wstunnel on WSS/443) on your PRIVATE network, so you (or your team) can reach the private IPs of the VMs you deployed on redu…No findings

    Why it is in this group

    • The name "deploy_vpn" says "deploy": a word for a change.
    Description, as the server wrote it
    Stands up a redu VPN gateway (WireGuard tunnelled over wstunnel on WSS/443) on your PRIVATE network, so you (or your team) can reach the private IPs of the VMs you deployed on redu, over a single https endpoint - no UDP, no extra ports. This is the TENANT VPN for reaching your OWN deployed resources; it is NOT a general internet VPN. GUARDRAIL: the first call does NOT deploy - it returns the plan + hourly cost for approval, because it creates a BILLED VM. Show the user, get their explicit 'go', then call again with confirm:true (required even in yolo mode). The gateway asset is redu's, deployed onto YOUR VM (you can SSH in to read it). Fresh WireGuard keys are generated per deploy; the client PRIVATE key is returned ONCE and is never stored server-side.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: true
  • detach_volumeDetaches a block volume from an instance, leaving the volume (and its data) intact and 'available' to attach elsewhere.No findings

    Why it is in this group

    • The name "detach_volume" says "detach": a word for a change.
    Description, as the server wrote it
    Detaches a block volume from an instance, leaving the volume (and its data) intact and 'available' to attach elsewhere. Unmount it INSIDE the guest first (`umount /mnt/...`) or you risk a dirty filesystem. You cannot detach an instance's ROOT/boot disk while it runs. Use this to move a data volume to another VM, or to free a restore target before restore_backup writes into it. Poll list_volumes until status is 'available'.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: true
  • get_domain_verificationStarts (or resumes) a claim on a custom domain and returns the DNS TXT record that proves it is the user's: they add it at the domain's DNS provider, then you call verify_domain.1 finding

    Why it is in this group

    • The name "get_domain_verification" says "get": a word for reading.
    • The description's first verb is "start": a word for a change.
    • The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.

    Findings

    • Warn

      The server marks "get_domain_verification" read-only (readOnlyHint: true), and its own name, description or input give the class "write".

      Matched text

      annotations: {"readOnlyHint":true,"openWorldHint":true}; The name "get_domain_verification" says "get": a word for reading.

      What this check looks for
    Description, as the server wrote it
    Starts (or resumes) a claim on a custom domain and returns the DNS TXT record that proves it is the user's: they add it at the domain's DNS provider, then you call verify_domain. Until it verifies, list_domains shows the domain as pending. A pending claim's TXT value can change (values are specific to each account), so add exactly the value get_domain_verification returns now, never one saved earlier or seen on another account; if verify_domain keeps failing, call get_domain_verification again and update the record when the value differs. A name under redu.cloud needs no verification.

    Annotations the server declares

    • readOnlyHint: true
    • openWorldHint: true
  • import_keypairRegisters an existing SSH public key on your account.1 finding

    Why it is in this group

    • The name "import_keypair" says "import": a word for a change.

    Findings

    • High

      The input schema at properties.public_key.description refers to "id_rsa.pub" and "id_ed25519.pub".

      Matched text

      inputSchema.properties.public_key.description: "...lic key to import (the contents of your id_rsa.pub or id_ed25519.pub)."

      What this check looks for
    Description, as the server wrote it
    Registers an existing SSH public key on your account. Use this to import your own public key so you can SSH into instances. The private key never leaves your machine.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: true
  • instance_actionStart, stop, or reboot an instance.No findings

    Why it is in this group

    • The description's first verb is "start": a word for a change.
    Description, as the server wrote it
    Start, stop, or reboot an instance. action must be START, STOP, REBOOT_SOFT, or REBOOT_HARD.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: true
  • integrate_overviewOrientation for wiring a redu.cloud capability (backups, DNS, extra storage, a managed DB, ...) INTO an app already deployed on redu, e.g.No findings

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    • The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.
    Description, as the server wrote it
    Orientation for wiring a redu.cloud capability (backups, DNS, extra storage, a managed DB, ...) INTO an app already deployed on redu, e.g. 'add a backup feature to the Supabase I deployed on redu'. Explains the pattern: mint a LEAST-PRIVILEGE scoped API key (with the user's approval via create_api_key), inject it into the app, and call the redu API from the app. Call this when a user asks to add/integrate a redu feature into a running deployment and you are unsure how.

    Annotations the server declares

    • readOnlyHint: true
    • openWorldHint: true
  • plan_deployTurns YOUR repo classification (you scan the repo and pass what you found) into a complete, approvable deploy plan WITHOUT creating anything.2 findings

    Why it is in this group

    • The name "plan_deploy" says "deploy": a word for a change.
    • The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.

    Findings

    • Info

      The description is 5,004 characters long, over the 2,000 this check looks for.

      Matched text

      description ends: "...unless route_next's conditions for building the Dockerfile with deploy_app hold."

      What this check looks for
    • Warn

      The server marks "plan_deploy" read-only (readOnlyHint: true), and its own name, description or input give the class "write".

      Matched text

      annotations: {"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":true}; The name "plan_deploy" says "deploy": a word for a change.

      What this check looks for
    Description, as the server wrote it
    Turns YOUR repo classification (you scan the repo and pass what you found) into a complete, approvable deploy plan WITHOUT creating anything. ⚡ REDU NEEDS THREE FILES IF THEY EXIST - redu.md, the compose file, the Dockerfile - and there are two ways to give them. ⭐ BEST, for an upload-mode deploy: run prepare_upload FIRST and pass its `source_token`; redu reads all three straight out of the upload you already made, the upload stays deployable, and you emit nothing. Pasting the same files costs you 20-29 KB of output for bytes the server already has. Otherwise (git mode) paste `redu_md` (cat redu.md), `compose_yaml`, `dockerfile`. Either way you do NOT read or interpret them; redu parses them SERVER-SIDE and returns (a) a short digest, (b) `pin_dname` so a redeploy keeps the SAME public URL, and (c) `preflight` - preemptive fixes for known failure patterns found in YOUR repo, each learned from a real failed build. Giving redu these files is the single highest-value thing you can do for a first deploy. picks the VM + managed-Postgres sizes, prices them at the real pricing_rules rates, and checks they FIT your quota - so a plan that can't provision is caught HERE, before any spend. You pass what you detected in the repo (runtime, port, needs_postgres/redis/clickhouse/vector_db); it returns resources + £/hr + £/mo + a feasibility verdict + a checkpoint summary to confirm with the user. Defaults: app VM m1.medium, managed Postgres m1.small, managed ClickHouse m1.medium; pass single_vm to collapse the app + Postgres onto one VM. SET needs_clickhouse:true FOR ANY ANALYTICS-SHAPED APP (Plausible, PostHog, Langfuse, Matomo, SigNoz, or anything with a clickhouse image / CLICKHOUSE_* env / a ClickHouse client dep): those products keep config in Postgres and EVERY EVENT in ClickHouse, so the events tier is a second VM with a second line on the bill: measured 2026-08-07, omitting it quoted GBP 53.29/mo for a GBP 65.99/mo deployment. It is sized, quota-checked and priced here; unlike Postgres and Redis it is not auto-wired by deploy_app, so the plan tells you to run plan_managed_datastore engine:'clickhouse' -> create_clickhouse and pass CLICKHOUSE_* env yourself. A Qdrant vector DB is managed like the other datastores (vector_engines in send_signals; qdrant:'managed' without a signal); pgvector lives in the managed Postgres. In a session with send_signals the platform sizes every machine by measurement, so heavy_build, memory_heavy and needs_compose change no size there. Any containerizable app works (node, python, go, ...) - it deploys as a container, so the language doesn't gate it. Set serves_http:false for a non-web repo (a library, CLI, or language runtime with no HTTP server) and it returns a clean not-a-web-service verdict instead of a costed VM plan. Set heavy_build:true for resource-heavy builds (compiled-from-source native code, a monorepo/turborepo build, a large Node heap) and it raises the app VM to a build-capable floor so the on-VM build doesn't get OOM-killed. Set memory_heavy:true for a RAM-forward app whose persistent state lives in a MANAGED DB / external store (Next.js like cal.com/cal.diy, Rails, Django, JVM/Java apps) - it sizes onto a memory-optimized SMALL-DISK flavor (m1.mem16/m1.mem32: full RAM, a lean 40 GB disk instead of 160 GB) that costs less and snapshots/clusters far faster; do NOT set it if the app keeps lots of data on local disk. Also returns a brand-named markdown report (Mermaid diagram + cost) to SHOW the user only in guided mode; write no file from it. Every deploy leaves TWO MANDATORY files at the repo root with DIFFERENT purposes, both written ONCE at the end from the deployment that actually ran (get_deployment), after you verified it works: redu-deploy-plan.md = what this run deployed, and redu.md = the DURABLE deploy memory the NEXT deploy reads. If a redu.md exists, READ it FIRST and reuse its known-good plan + recorded fixes; if NONE exists, one MUST be created at the end of the deploy (get_deployment returns redu_md_bootstrap_markdown for exactly that case; when a redu.md DOES exist, pass it as redu_md and write the merged redu_md_markdown). They are SEPARATE files - even if your own memory/notes from a prior deploy call redu-deploy-plan.md 'the record', the durable record is redu.md, so do not skip creating it. ROUTE: from a source_token (pass port too: it tells the platform which compose service is the app) the plan can also carry recommended_route:'compose', the platform's route for that folder, with every recommended_* and route_* field it sent (recommended_compose_file, recommended_service, recommended_port, recommended_image, route_reason, route_next): then deploy that compose file with deploy_compose (compose_file), with recommended_service as service and recommended_port as port (the HOST port the app service publishes, not the port the app listens on inside its container), not deploy_app, unless route_next's conditions for building the Dockerfile with deploy_app hold.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: true
  • plan_instanceAggregates images/flavors/keypairs/networks/security groups into human-friendly choices.No findings

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    • The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.
    Description, as the server wrote it
    Aggregates images/flavors/keypairs/networks/security groups into human-friendly choices. Does not create anything. Happy path: import_keypair → plan_instance → create_instance → get_ssh_command. Call this second (after import_keypair, if you have no keypairs).

    Annotations the server declares

    • readOnlyHint: true
    • openWorldHint: true
  • plan_managed_datastorePlans a direct managed datastore create without provisioning anything.No findings

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    • The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.
    Description, as the server wrote it
    Plans a direct managed datastore create without provisioning anything. Use this before create_database, create_relational_database, create_redis, create_clickhouse or create_qdrant (engine 'qdrant', the vector database). For app deployments you need none of these: datastores are managed by default, so the ones you report in send_signals are started and wired for you. Pass ha:true to plan a highly available datastore (postgres or clickhouse): three machines instead of one, surviving the loss of a machine without manual failover, at roughly 3x the hourly rate. The returned plan carries the machine count so you can show the user the cost before anything is created.

    Annotations the server declares

    • readOnlyHint: true
    • openWorldHint: true
  • prepare_uploadReturns the LOCAL shell commands to package your working directory and upload it for an upload-mode deploy (no git, no PAT).3 findings

    Why it is in this group

    • The name "prepare_upload" says "upload": a word for a change.
    • The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.

    Findings

    • High

      The description refers to ".env".

      Matched text

      description: "...ode_modules/.git/.venv/build output and .env by default; honors .gitignore when is_g..."

      What this check looks for
    • Info

      The description mentions "credential".

      Matched text

      description: "...short-lived ticket minted from your MCP credential - NO API key needed in the command and..."

      What this check looks for
    • Warn

      The server marks "prepare_upload" read-only (readOnlyHint: true), and its own name, description or input give the class "write".

      Matched text

      annotations: {"readOnlyHint":true,"openWorldHint":true}; The name "prepare_upload" says "upload": a word for a change.

      What this check looks for
    Description, as the server wrote it
    Returns the LOCAL shell commands to package your working directory and upload it for an upload-mode deploy (no git, no PAT). Run them in the user's terminal, capture `source_token` from the upload's JSON response, then call deploy_app with that source_token (omit repo). The upload authenticates AUTOMATICALLY with a short-lived ticket minted from your MCP credential - NO API key needed in the command and nothing secret is printed (it falls back to needing $REDU_API_KEY only if minting is unavailable). Excludes node_modules/.git/.venv/build output and .env by default; honors .gitignore when is_git_repo=true.

    Annotations the server declares

    • readOnlyHint: true
    • openWorldHint: true
  • restore_backupRestores a backup into an existing volume (volumeId), an instance's volume (instanceId), or a new one (volumeName).No findings

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    Description, as the server wrote it
    Restores a backup into an existing volume (volumeId), an instance's volume (instanceId), or a new one (volumeName). Poll list_volumes for the restored volume. DISASTER RECOVERY (the original VM is gone, so its volume died with it): restore with volumeName to rebuild the data as a NEW volume. To bring the MACHINE back, pass that volume as create_instance's boot_volume_id - it boots from the restored disk, so the VM returns with its filesystem intact and nothing to mount. Use attach_volume instead only when you want the data as an EXTRA disk on an existing machine (then get_ssh_command -> lsblk -> mount). A restored volume is inert until it is booted from or attached.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: true
  • scaffold_localOPTIONAL preflight: returns a podman-compose.yml + .env so the user can run the app (and a throwaway local Postgres) on THEIR machine before deploying to redu - to see it run / san…1 finding

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    • The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.

    Findings

    • High

      The description refers to ".env".

      Matched text

      description: "...eflight: returns a podman-compose.yml + .env so the user can run the app (and a thro..."

      What this check looks for
    Description, as the server wrote it
    OPTIONAL preflight: returns a podman-compose.yml + .env so the user can run the app (and a throwaway local Postgres) on THEIR machine before deploying to redu - to see it run / sanity-check the container. Requires local podman/podman-compose. It's a suggestion, not a gate - skip it and go straight to deploy_app any time. Honest caveat: the local Postgres is NOT the managed Postgres, so a green local run does not prove the prod DB wiring.

    Annotations the server declares

    • readOnlyHint: true
    • openWorldHint: true
  • select_surfaceFor a repo with SEVERAL runnable parts, call this BEFORE plan_deploy.1 finding

    Why it is in this group

    • The name "select_surface" says "select": a word for a change.
    • The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.

    Findings

    • Warn

      The server marks "select_surface" read-only (readOnlyHint: true), and its own name, description or input give the class "write".

      Matched text

      annotations: {"readOnlyHint":true,"openWorldHint":true}; The name "select_surface" says "select": a word for a change.

      What this check looks for
    Description, as the server wrote it
    For a repo with SEVERAL runnable parts, call this BEFORE plan_deploy. You enumerate the candidate surfaces (find every Dockerfile/Containerfile, OPEN each, classify by its EXPOSE + CMD - never by directory name) and pass them in; redu RANKS them with fixed rules (a standalone browser desktop/noVNC > a self-contained web app > a keys-required playground > an API > a headless worker > docs/examples). GUIDED: returns the ranked list to present to the user, who picks. YOLO: auto-selects the TOP-ranked surface. Then run plan_deploy on the chosen surface's path + http_port. (A single-surface repo doesn't need this.)

    Annotations the server declares

    • readOnlyHint: true
    • openWorldHint: true
  • set_cluster_domainChanges the PUBLIC HOSTNAME a cluster serves on, so the user can put THEIR OWN production domain (shop.acme.com, app.theircompany.com) in front of it instead of the auto-generated …No findings

    Why it is in this group

    • The name "set_cluster_domain" says "set": a word for a change.
    Description, as the server wrote it
    Changes the PUBLIC HOSTNAME a cluster serves on, so the user can put THEIR OWN production domain (shop.acme.com, app.theircompany.com) in front of it instead of the auto-generated redu.cloud URL. That is what this tool is for: a real product on a real brand. The cluster itself is untouched - same members, same load balancer, same data - only the name in front of it changes. Pass stack_name + stack_id from list_clusters. TWO RULES, and getting either wrong wastes a call: (1) A CUSTOM DOMAIN MUST BE VERIFIED FIRST. Call get_domain_verification for the TXT record, have the user add it at their DNS provider, then call verify_domain until it reports verified (list_domains shows what is already verified). Binding an unverified domain is refused, on purpose: without proof of ownership anyone could bind, and obtain a certificate for, a hostname that is not theirs. Also make sure the domain's own DNS actually points at this cluster, or it will verify and still not serve. (2) A redu.cloud NAME CANNOT BE FREELY CHOSEN. The redu.cloud base domain is shared by every customer, so names under it always keep a short-id suffix - the real form is `<label>-<8chars>.redu.cloud`. Ask for a bare `myapp.redu.cloud` and you will NOT get it: the server normalises it and binds something else. If the first caller could pick any name under the shared domain, the good ones would all be gone. SO: the response carries `access_point`, the hostname the server ACTUALLY bound, which may differ from what you asked for. Report that value to the user and use it everywhere afterwards - never repeat your requested label back as if it were the URL. Finally, changing the hostname changes the URL: anything wired to the old one (OAuth callbacks, cookie domains, webhooks, an app told its own address) breaks until it is updated, so say so before you call this.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: true
  • set_managed_backupsTurns nightly AUTOMATED (scheduled) backups ON or OFF for a MANAGED data service - the toggle that create_backup (a one-off volume snapshot) is NOT.No findings

    Why it is in this group

    • The name "set_managed_backups" says "set": a word for a change.
    Description, as the server wrote it
    Turns nightly AUTOMATED (scheduled) backups ON or OFF for a MANAGED data service - the toggle that create_backup (a one-off volume snapshot) is NOT. Once enabled, redu's nightly job backs the service up on its own and prunes to the retention window; see them with list_backups and recover with the service's restore. Works for managed Postgres/MySQL/MariaDB/Redis/Qdrant/ClickHouse and can be flipped ANY time after provisioning, not only at create. Requires a card (automated backups are a paid feature; no-card trials cannot enable them). Pass the service type + its numeric id, enabled, and optional retention (days).

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: true
  • setup_agent_fleetComplete one-shot setup: validates prerequisites, creates a controller VM + worker VMs, auto-creates a public HTTPS URL on port 7070, seeds a starter ROADMAP.md into the repo if ab…1 finding

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.

    Findings

    • Info

      The description mentions "credentials".

      Matched text

      description: "...decisions, or anything needing external credentials. TASK FORMAT (strict, one line each): `..."

      What this check looks for
    Description, as the server wrote it
    Complete one-shot setup: validates prerequisites, creates a controller VM + worker VMs, auto-creates a public HTTPS URL on port 7070, seeds a starter ROADMAP.md into the repo if absent, and returns the trigger token. Call this when a user says 'set up autocoding agents for my repo' or 'I want agents to work on my codebase'. HOW THE AGENT WORKS: each worker runs Claude Code inside the repo, implements one task, runs the test suite, and opens a pull request. It excels at focused, single-PR, testable units of work - add an endpoint, write tests for a module, fix a specific bug, add a UI page - and is poor at vague/large tasks, design decisions, or anything needing external credentials. TASK FORMAT (strict, one line each): `- [ ] **Title** - short description *(agent-ready)*` - the `- [ ]` checkbox, `**bold title**`, ` - ` separator, and `*(agent-ready)*` are ALL required; `##` headings and plain bullets are ignored. After this returns, the user needs to: (1) authorize the fleet by running the authorize.sh one-liner it returns (it runs `claude setup-token` for a long-lived token installed on the controller) - agents use the user's existing Claude Max/Pro subscription, NOT an API key. This is a shell command the USER runs in their own terminal; do NOT try to read or push the user's credentials yourself. The controller takes ~7 min to boot, so PREFER to poll get_agent_status until it reports the controller is reachable and present the authorize command only once it's ready - that way the user doesn't run it into a long wait. (The command also waits on its own, showing a live progress counter, so a user who runs it early is fine too.) (2) add well-scoped tasks in the format above to ROADMAP.md; (3) call trigger_agent_batch.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: true
  • setup_agent_sessionOne-shot tool that guides you through the complete agent setup: checks prerequisites, creates the controller VM, and returns next steps.No findings

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    Description, as the server wrote it
    One-shot tool that guides you through the complete agent setup: checks prerequisites, creates the controller VM, and returns next steps. Ideal for first-time setup. Call this when a user says 'set up autocoding agents for my repo'.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: true
  • trigger_agent_batchStarts the autonomous agent batch on your controller VM.No findings

    Why it is in this group

    • The name "trigger_agent_batch" says "trigger": a word for a change.
    Description, as the server wrote it
    Starts the autonomous agent batch on your controller VM. Agents read agent-ready tasks from your ROADMAP.md, implement them in parallel, and open PRs. No VPN needed - runs over HTTPS; the controller stays running after you disconnect. Each ROADMAP task MUST be a single checkbox line `- [ ] **Title** - short description *(agent-ready)*` (the `- [ ]`, `**bold title**`, and `*(agent-ready)*` are all required; `##` headings and plain bullets are ignored). If this returns 'No agent-ready tasks', the tasks are mis-formatted - fix them to that exact format and retry.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: false
    • openWorldHint: true
  • update_clusterUpdates a running autoscaling cluster to a new app version IN PLACE, with NO URL change: snapshots the 'hero' VM (the source/master VM you SSH into and change - it is also the alwa…No findings

    Why it is in this group

    • The name "update_cluster" says "update": a word for a change.
    Description, as the server wrote it
    Updates a running autoscaling cluster to a new app version IN PLACE, with NO URL change: snapshots the 'hero' VM (the source/master VM you SSH into and change - it is also the always-on baseline member that serves traffic), then redu rolling-updates the autoscaled extra members to that snapshot ONE AT A TIME and refreshes the image the group boots future members from - the SAME load balancer and SAME *.redu.cloud URL are kept, and the service stays up during the roll. This is the ONLY way to update a cluster: you do NOT edit live members (they are disposable clones); you change the hero VM (SSH into it, deploy the new version), then update_cluster snapshots it and spreads it to the fleet (the hero already runs your change; this propagates it to the autoscaled members). It is low-disruption, not zero-downtime: measured, a rolling update failed about 2% of requests, in one short burst at each member swap, and never dropped below a majority of members. Never promise the user zero downtime. That holds ONLY if the app starts on every boot - a member whose app is launched by a first-boot-only script returns empty after replacement, never rejoins the pool, and the roll silently drains the cluster. The snapshot upload can take several minutes on a large disk; poll list_clusters until the stack status is UPDATE_COMPLETE.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: true
  • upgrade_to_clusterTHE WAY TO MAKE AN EXISTING APP HIGHLY AVAILABLE.1 finding

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.

    Findings

    • Info

      The description is 5,022 characters long, over the 2,000 this check looks for.

      Matched text

      description ends: "...apshot upload can take several minutes; poll list_clusters until CREATE_COMPLETE."

      What this check looks for
    Description, as the server wrote it
    THE WAY TO MAKE AN EXISTING APP HIGHLY AVAILABLE. If the user asks for high availability, redundancy, no single point of failure, surviving a host or VM failure, or 'stay up if a machine dies', this is the tool: point it at their running VM and pass high_availability:true. It needs no rebuild and no redeploy, and the URL does not change. Turns a single running instance into a horizontally-autoscaling cluster: snapshots the instance and puts your SOURCE VM behind a load balancer as its ALWAYS-ON BASELINE MEMBER, then an autoscaling group adds EXTRA members (booted from that snapshot) on CPU load and scales them back down to zero. FLAVOR - LEAD WITH A MEMORY-OPTIMIZED SMALL-DISK FLAVOR (m1.mem16/m1.mem32) for the cluster, and ideally have the SOURCE VM on one too: clustering snapshots the source VM's FULL disk, so DISK size (not RAM) drives how long the cluster takes to build and to add each member under load - a 40 GB mem flavor is several times faster to snapshot and scale than a 160 GB standard flavor (m1.xlarge) and costs less for the same RAM. If the source VM is on a big-disk flavor, RECOMMEND migrating the app onto a mem-flavor VM first (a root disk cannot be shrunk in place), even though it is extra work - it pays off on every scale-out, and a clustered app is stateless anyway (state in the managed DB) so the lean disk fits. Expect a DRASTICALLY better clustering experience on a mem flavor: far faster cluster-create, snapshots, and every scale-out. The user can override with any flavor. At rest ONLY your source VM serves - there is NO idle extra VM to pay for (the source VM is the cluster's minimum, so the floor is 0 extra members). Use it to LOAD-SCALE a stateless app tier while managed services hold state: it becomes highly-available UNDER LOAD (multiple members behind the LB), but at rest a SINGLE source VM serves - and that source VM is a plain VM, not an autoscaled member, so it is NOT auto-replaced if it fails while idle (only the autoscaled extra members are ASG-managed and self-healed). If you need always-on redundancy, keep the app under enough load to hold >=1 extra member, or use a separate always-on setup. BILLED - at rest it costs just your source VM (which you already run) plus the load balancer; under load it adds up to max_size EXTRA members at the member flavor (flavor_id), billed only while they run. In guided mode show the cost that way (now: source VM already running + the LB; under load: up to max_size x the member flavor) and get the user's explicit go first. redu automatically repoints the extra members from the old single-VM URL to the load-balancer URL across app config. It REFUSES a STATEFUL VM with 409 cluster_needs_stateless unless confirm_stateless:true. To have redu help with a stateful VM instead of refusing, pass auto_restructure:true: for a single_vm Postgres the fully automatic move is switched off for now (409 auto_restructure_unavailable: move the database yourself first, then confirm_stateless:true); for a compose-stack DB it provisions the matching managed DB (set restructure_engine, e.g. 'mysql'/'mariadb' for WordPress) and returns migration commands to run from the app VM. WordPress/WooCommerce is not generic autoscaling: managed DB alone is not enough because wp-content/uploads is file state. Use app_profile:'wordpress'/'woocommerce', cluster_media_mode:'media_space', and either media_space_id or create_media_space:true so all members mount the same uploads filesystem; otherwise the backend refuses with 409 cluster_needs_media_space. PUT THE CLUSTER ON THE SAME private network as the managed DB and media space. HA: cluster members are spread across DIFFERENT physical hosts automatically, and an autoscaled member that is destroyed is REBUILT AUTOMATICALLY in 1.5 to 5 minutes depending on how it failed with no action from you (the always-on source/hero VM is a plain VM and is NOT covered by that). CRITICAL for members: the app must start on EVERY boot (systemd unit or container restart policy) - if it only starts from a first-boot cloud-init script, a rebooted or resized member comes back with no app, silently never rejoins the load balancer, and the cluster quietly loses capacity with nothing reporting an error. Pass startup_command if the app does not already auto-start on boot, and have it bind its port only once it is genuinely ready to serve (the health check can only see whether the port is open). SEQUENCING - this catches people: the snapshot is taken IMMEDIATELY, and every member boots from it, so the source VM's app must already be RUNNING before you call this. Clustering a freshly-created VM whose cloud-init has not finished captures an image with no enabled service, and all members then come up ACTIVE while failing the load-balancer health check forever - a cluster that looks built and serves nothing. Verify the app answers on its port first (get_ssh_command, or just fetch the VM's URL). The snapshot upload can take several minutes; poll list_clusters until CREATE_COMPLETE.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: true
  • verify_deploymentAsks redu to re-probe a deployment's public URL and update its status from what it actually observes.No findings

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    Description, as the server wrote it
    Asks redu to re-probe a deployment's public URL and update its status from what it actually observes. USE THIS AFTER YOU HAVE FIXED A FAILED DEPLOY - a deployment that ended 'error' or 'build_failed' but is now serving becomes 'ready' again, and you do NOT need to redeploy it to get a green status. Redeploying working infrastructure to clear a red flag wastes the user's money and throws away the fix. You cannot set the status yourself: this tool triggers a check, and the probe decides. Readiness means the URL answers with a non-5xx (401/403/404 count - they prove the app is answering), the same rule the deploy's own health gate uses. A deployment with no public URL (a headless worker) cannot be verified this way; use get_containers for its container state instead.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: true
  • verify_domainChecks the DNS TXT ownership record of a custom domain and, when it matches, marks the domain verified for this account.No findings

    Why it is in this group

    • Nothing in its name or description says what it does, so it is counted as a write.
    Description, as the server wrote it
    Checks the DNS TXT ownership record of a custom domain and, when it matches, marks the domain verified for this account. Call get_domain_verification first for the record, have the user add it at their DNS provider, then call this until it reports verified (a new record can take minutes, sometimes hours, to show). A verified domain can be bound: dname on deploy_app/deploy_compose, dns_entries on create_instance, or set_cluster_domain. A pending claim's TXT value can change (values are specific to each account), so add exactly the value get_domain_verification returns now, never one saved earlier or seen on another account; if verify_domain keeps failing, call get_domain_verification again and update the record when the value differs.

    Annotations the server declares

    • readOnlyHint: false
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: true
  • wait_for_deployment⭐ CALL THIS INSTEAD OF POLLING.2 findings

    Why it is in this group

    • The name "wait_for_deployment" says "wait": a word for reading.
    • The description's first verb is "call": a word for a change.
    • The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.

    Findings

    • High

      The description refers to ".env".

      Matched text

      description: "...s, the host's memory/disk/load, and the .env KEY NAMES. Do NOT loop on get_deploymen..."

      What this check looks for
    • Warn

      The server marks "wait_for_deployment" read-only (readOnlyHint: true), and its own name, description or input give the class "write".

      Matched text

      annotations: {"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":true}; The name "wait_for_deployment" says "wait": a word for reading.

      What this check looks for
    Description, as the server wrote itContains invisible characters, shown as [U+…]
    ⭐ CALL THIS INSTEAD OF POLLING. Waits up to 50 seconds per call (an MCP client cuts a call at about 60 s) for the deployment to reach a TERMINAL state (ready, build_failed, error or vm_missing) and returns, in ONE response, everything you would otherwise fetch afterwards: the HTTP status of the PUBLIC url (not the VM's private interface, which can answer 401 while the public path still 502s), the container table with health and restarts, the host's memory/disk/load, and the .env KEY NAMES. Do NOT loop on get_deployment: that is 8-9 round trips per deploy that learn nothing, and then three more asking for what this already returned. ⚠[U+FE0F] A TIMEOUT IS NOT A FAILURE: if it returns timed_out:true the build is simply still running, so call it again (a typical deploy takes 2 to 5 calls). Only status decides success. On build_failed or error, call get_deployment(id) for the full build_log, which is the diagnosis.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: true
Only reads 31Claude Code: allow
  • architecture_diagramRead-only.No findings

    Why it is in this group

    • The description's first verb is "read": a word for reading.
    Description, as the server wrote it
    Read-only. Fetches EVERY resource on your redu.cloud account (VMs, volumes, private networks, managed databases: Postgres/MySQL/MariaDB/ClickHouse/Redis/Qdrant, load balancers, and DNS access points) and renders ONE Mermaid diagram of the whole account, grouped into per-private-network subgraphs with repetition collapsed into count nodes (e.g. "App VM x12"). Returns report_markdown to save as redu_architecture_diagram.md at the repo root, PLUS a redu_md_section that enriches the repo's redu.md with the current account topology, so the NEXT deploy is account-aware: it integrates with services that already exist and picks models/versions/sizes that fit the stack (better decisions, not just avoiding duplicate networks/databases). Nothing is created or changed.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: true
  • get_agent_statusReturns the current status of your controller VM.No findings

    Why it is in this group

    • The name "get_agent_status" says "get": a word for reading.
    Description, as the server wrote it
    Returns the current status of your controller VM. On a freshly set-up VM also returns the trigger token needed to authorize the fleet and to call trigger_agent_batch. Call this after setup_agent_fleet to confirm the VM is ready.

    Annotations the server declares

    • readOnlyHint: true
    • openWorldHint: true
  • get_container_logsTHE APPLICATION'S OWN LOGS - what `docker logs`/`podman logs` would show for each container in a deployment.No findings

    Why it is in this group

    • The name "get_container_logs" says "get": a word for reading.
    Description, as the server wrote it
    THE APPLICATION'S OWN LOGS - what `docker logs`/`podman logs` would show for each container in a deployment. This is the tool for 'it deployed fine but it does not work': a 500, a crash loop, a failed DB connection, a missing env var all announce themselves here and NOWHERE else. ⛔ DO NOT use get_instance_logs for this. That returns the VM's SERIAL CONSOLE (kernel messages and cloud-init), which answers a question nobody debugging an app has - and on this platform it goes permanently silent once the machine finishes booting. build_log does not contain runtime output either; it stops when the build does. Default depth answers instantly from the VM's last report; a bigger `tail` or any `since` asks the VM for a fresh pull and takes up to ~15s. Secret-shaped values (PASSWORD=, TOKEN=, API_KEY=) are redacted in transit.

    Annotations the server declares

    • readOnlyHint: true
    • openWorldHint: true
  • get_containersWHAT IS ACTUALLY RUNNING on a deployment's VM: every container's name, image, state, health, restart count, exit code and published ports - including the ones that have CRASHED, wh…No findings

    Why it is in this group

    • The name "get_containers" says "get": a word for reading.
    Description, as the server wrote itContains invisible characters, shown as [U+…]
    WHAT IS ACTUALLY RUNNING on a deployment's VM: every container's name, image, state, health, restart count, exit code and published ports - including the ones that have CRASHED, which are the ones you need. Use this the moment a deployment says 'ready' but the URL misbehaves, and before you reach for SSH. get_deployment returns the deployment ROW (status, URL, build log); it cannot see inside the VM, so a stack where one of five services is restarting looks identical to a healthy one there. This is that missing view. It answers from the VM's own report (every 15s), so it costs no SSH round trip. If the VM has never reported, it SAYS SO rather than returning an empty list - 'no containers' and 'I cannot see' are different answers and only one of them means your app is broken. ⚠[U+FE0F] For a deployment that has been turned into a CLUSTER, this describes the original source VM only; autoscaled members are deliberately silent, so use list_clusters for member-level state.

    Annotations the server declares

    • readOnlyHint: true
    • openWorldHint: true
  • get_deploymentFetches ONE deployment by its numeric id (from list_deployments).No findings

    Why it is in this group

    • The name "get_deployment" says "get": a word for reading.
    Description, as the server wrote it
    Fetches ONE deployment by its numeric id (from list_deployments). Returns its current status, the public access_point URL, the underlying VM id, AND the build_log - IN FULL when status is 'build_failed' or 'error', which is when it is the diagnosis and you should read it; otherwise the LAST 40 LINES only, with build_log_truncated:true and build_log_total_lines so you can tell a short build from a shortened log. A ready deployment's log is not worth the tokens and used to make this response exceed the caller's output limit. Also returns a reality report_markdown showing the REAL provisioned size + cost (the plan was only an estimate; the user may have up-sized).

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: true
  • get_env_keysWHICH ENVIRONMENT VARIABLES the deployment was actually given, BY NAME - the keys in the env files on the VM (/opt/app-src/.env and friends), never the values.1 finding

    Why it is in this group

    • The name "get_env_keys" says "get": a word for reading.

    Findings

    • High

      The description refers to "/opt/app-src/.env".

      Matched text

      description: "...- the keys in the env files on the VM (/opt/app-src/.env and friends), never the values. Most of..."

      What this check looks for
    Description, as the server wrote it
    WHICH ENVIRONMENT VARIABLES the deployment was actually given, BY NAME - the keys in the env files on the VM (/opt/app-src/.env and friends), never the values. Most of the time the real question is 'did DATABASE_URL get wired, or is the app reading a name I did not set', and the name answers it: you can confirm the DB was connected, spot a typo'd key, and see which of the app's documented variables are missing, without touching a secret. ⛔ VALUES ARE NOT AVAILABLE HERE AND WILL NOT BE. They never leave the VM. If you genuinely need one (a generated admin password, say), SSH in with get_ssh_command and read that single key: `sudo grep '^KEY=' /opt/app-src/.env`. The file is chmod 600 root:root, so it needs sudo. Also reports each file's permission mode, which is how you catch a world-readable env file.

    Annotations the server declares

    • readOnlyHint: true
    • openWorldHint: true
  • get_instance_logsThe VM's SERIAL CONSOLE: kernel messages and early-boot/cloud-init output.No findings

    Why it is in this group

    • The name "get_instance_logs" says "get": a word for reading.
    Description, as the server wrote it
    The VM's SERIAL CONSOLE: kernel messages and early-boot/cloud-init output. ⛔ THIS IS NOT YOUR APPLICATION'S LOGS. For a deployed app - a crash loop, a 500, a failed DB connection - use get_container_logs, which returns what the containers actually printed. Reach for this one only for boot-level questions: the machine never came up, the disk did not mount, cloud-init failed before the app existed. Note that on this platform the console goes silent once a machine is past early boot, so an empty result here says nothing about a running VM.

    Annotations the server declares

    • readOnlyHint: true
    • openWorldHint: true
  • get_resource_usageHOW MUCH ROOM IS LEFT on a deployment's VM: total/used/available memory, swap, disk free, load average, CPU count, and OOM kills in the last 24h - plus per-container memory and CPU…No findings

    Why it is in this group

    • The name "get_resource_usage" says "get": a word for reading.
    Description, as the server wrote it
    HOW MUCH ROOM IS LEFT on a deployment's VM: total/used/available memory, swap, disk free, load average, CPU count, and OOM kills in the last 24h - plus per-container memory and CPU. Use it to answer 'is this flavor big enough', to size the NEXT deploy of the same app honestly instead of guessing, and to explain a container that keeps dying (an OOM kill leaves no application log at all - the process is shot, so the only trace is the kernel's, and that is why oom_kills_24h is here). A full disk presents as a dozen unrelated failures and is the first thing to rule out. Every one of 34 measured deploy runs got these numbers by SSHing in and running free/df/docker stats; this is the same data with no round trip.

    Annotations the server declares

    • readOnlyHint: true
    • openWorldHint: true
  • get_ssh_commandReturns the SSH command to connect to an instance via the redu.cloud TCP proxy.2 findings

    Why it is in this group

    • The name "get_ssh_command" says "get": a word for reading.

    Findings

    • High

      The description refers to "~/.ssh/" and "~/.ssh/redu-deploy".

      Matched text

      description: "...et_deployment - so the command uses `-i ~/.ssh/<keypair_name>` and authenticates with t..."

      What this check looks for
    • High

      The input schema at properties.keypair_name.description refers to "~/.ssh/".

      Matched text

      inputSchema.properties.keypair_name.description: "...y VM). When given, the command uses `-i ~/.ssh/<keypair_name>` so it authenticates with..."

      What this check looks for
    Description, as the server wrote it
    Returns the SSH command to connect to an instance via the redu.cloud TCP proxy. ACCEPTS an instance id, a deployment's id / name / instance_id, OR a MANAGED DATASTORE's id / name / instance_id (Postgres, MySQL/MariaDB, ClickHouse, Redis) - it resolves all of them, so you do not have to work out which one you are holding (a deployment's instance_id is a placeholder until provisioning finishes, and passing it used to fail). ⭐ SSH TO THE DATASTORE VM IS HOW YOU REACH A PRIVATE-NETWORK-ONLY DATABASE BEFORE ANY APP VM EXISTS: the datastore's own VM is SSH-reachable and ships the client on PATH (psql at /usr/bin/psql), so you can apply a schema or rotate a seeded admin account without an app VM to tunnel through. For a DEPLOYMENT VM (created by deploy_app/deploy_compose) pass keypair_name - read it from get_deployment - so the command uses `-i ~/.ssh/<keypair_name>` and authenticates with the RIGHT key instead of your default identity (without it, SSH to a deploy VM usually fails). The tool also best-effort looks up the keypair from the deployment if you omit it. Example: ssh -i ~/.ssh/redu-deploy -o IdentitiesOnly=yes -p 22011 ubuntu@myinstance-abc12345.redu.cloud

    Annotations the server declares

    • readOnlyHint: true
    • openWorldHint: true
  • list_api_keysLists the account's API keys: id, name, scopes, when each was created and last used, and when it expires.No findings

    Why it is in this group

    • The name "list_api_keys" says "list": a word for reading.
    Description, as the server wrote it
    Lists the account's API keys: id, name, scopes, when each was created and last used, and when it expires. Never the secret (redu keeps only a hash). Use it to find a key to delete with delete_api_key, or to check what keys exist before minting another.

    Annotations the server declares

    • readOnlyHint: true
    • openWorldHint: true
  • list_backupsLists your volume backups.No findings

    Why it is in this group

    • The name "list_backups" says "list": a word for reading.
    Description, as the server wrote it
    Lists your volume backups.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: true
  • list_clickhouse_databasesLists your managed ClickHouse databases (OLAP / analytics - its own resource, not a relational DB).No findings

    Why it is in this group

    • The name "list_clickhouse_databases" says "list": a word for reading.
    Description, as the server wrote it
    Lists your managed ClickHouse databases (OLAP / analytics - its own resource, not a relational DB). Once a row's status is 'ready' it carries the private-network connection details (private_ip, ClickHouse HTTP port 8123, db_name, db_user).

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: true
  • list_clustersLists your autoscaling clusters.No findings

    Why it is in this group

    • The name "list_clusters" says "list": a word for reading.
    Description, as the server wrote it
    Lists your autoscaling clusters.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: true
  • list_databasesLists your managed PostgreSQL databases.No findings

    Why it is in this group

    • The name "list_databases" says "list": a word for reading.
    Description, as the server wrote it
    Lists your managed PostgreSQL databases. Once a row's status is 'ready', it carries the private-network connection details (private_ip, port 5432, db_name, db_user).

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: true
  • list_deploymentsLists your app deployments.No findings

    Why it is in this group

    • The name "list_deployments" says "list": a word for reading.
    Description, as the server wrote it
    Lists your app deployments. Each row carries status (provisioning/ready/build_failed/error), the current build phase while provisioning (installing/source/building/built/starting), the public access_point URL, port, repo, and build_log on failure.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: true
  • list_dns_entriesLists the public names (DNS entries) on this account: each entry's id, the names it serves (domain_names) and the address it forwards to.No findings

    Why it is in this group

    • The name "list_dns_entries" says "list": a word for reading.
    Description, as the server wrote it
    Lists the public names (DNS entries) on this account: each entry's id, the names it serves (domain_names) and the address it forwards to. delete_dns_entry removes one by its id.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: true
  • list_domainsLists the custom domains on this account with their status: verified (it can be bound with dname on deploy_app/deploy_compose, dns_entries on create_instance, or set_cluster_domain…No findings

    Why it is in this group

    • The name "list_domains" says "list": a word for reading.
    Description, as the server wrote it
    Lists the custom domains on this account with their status: verified (it can be bound with dname on deploy_app/deploy_compose, dns_entries on create_instance, or set_cluster_domain) or pending (its TXT check has not passed: get_domain_verification gives the record, verify_domain checks it). The public names that use a domain are in list_dns_entries. release_domain removes a domain from the account.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: true
  • list_flavorsLists available instance sizes.No findings

    Why it is in this group

    • The name "list_flavors" says "list": a word for reading.
    Description, as the server wrote it
    Lists available instance sizes.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: true
  • list_imagesLists available OS images.No findings

    Why it is in this group

    • The name "list_images" says "list": a word for reading.
    Description, as the server wrote it
    Lists available OS images.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: true
  • list_instancesLists your compute instances.No findings

    Why it is in this group

    • The name "list_instances" says "list": a word for reading.
    Description, as the server wrote it
    Lists your compute instances.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: true
  • list_keypairsLists your SSH keypairs.No findings

    Why it is in this group

    • The name "list_keypairs" says "list": a word for reading.
    Description, as the server wrote it
    Lists your SSH keypairs. If empty, call import_keypair first before creating instances.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: true
  • list_media_spacesLists Redu media spaces: private NFS media VMs backed by persistent volumes.No findings

    Why it is in this group

    • The name "list_media_spaces" says "list": a word for reading.
    Description, as the server wrote it
    Lists Redu media spaces: private NFS media VMs backed by persistent volumes. For WordPress/WooCommerce clusters, reuse one on the app's private network for wp-content/uploads, or pass create_media_space:true to deploy_app/deploy_compose/upgrade_to_cluster so Redu creates one.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: true
  • list_private_networksLists your private networks.No findings

    Why it is in this group

    • The name "list_private_networks" says "list": a word for reading.
    Description, as the server wrote it
    Lists your private networks.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: true
  • list_qdrantLists your managed Qdrant vector databases (create_qdrant, or started for a deploy).No findings

    Why it is in this group

    • The name "list_qdrant" says "list": a word for reading.
    Description, as the server wrote it
    Lists your managed Qdrant vector databases (create_qdrant, or started for a deploy). Once a row's status is 'ready' it carries private_ip (REST port 6333, gRPC 6334); the API key is never listed.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: true
  • list_redisLists your managed Redis instances.No findings

    Why it is in this group

    • The name "list_redis" says "list": a word for reading.
    Description, as the server wrote it
    Lists your managed Redis instances. Once a row's status is 'ready' it carries the private-network connection details (private_ip, port 6379) - connect from another instance on the same private network with redis-cli -h <private_ip> -p 6379 -a <password>.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: true
  • list_regionsLists available regions.No findings

    Why it is in this group

    • The name "list_regions" says "list": a word for reading.
    Description, as the server wrote it
    Lists available regions.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: true
  • list_relational_databasesLists your managed MySQL/MariaDB databases (the relational-database resource).No findings

    Why it is in this group

    • The name "list_relational_databases" says "list": a word for reading.
    Description, as the server wrote it
    Lists your managed MySQL/MariaDB databases (the relational-database resource). Each row carries its engine ('mysql'|'mariadb'); once status is 'ready' it has the private-network connection details (private_ip, port 3306, db_name, db_user).

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: true
  • list_security_groupsLists your security groups.No findings

    Why it is in this group

    • The name "list_security_groups" says "list": a word for reading.
    Description, as the server wrote it
    Lists your security groups.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: true
  • list_snapshotsLists your instance snapshots.No findings

    Why it is in this group

    • The name "list_snapshots" says "list": a word for reading.
    Description, as the server wrote it
    Lists your instance snapshots.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: true
  • list_volumesLists your block storage volumes.No findings

    Why it is in this group

    • The name "list_volumes" says "list": a word for reading.
    Description, as the server wrote it
    Lists your block storage volumes.

    Annotations the server declares

    • readOnlyHint: true
    • destructiveHint: false
    • idempotentHint: true
    • openWorldHint: true
  • whoamiVerifies your redu.cloud credential with a REAL server round-trip and returns your identity + live quota.1 finding

    Why it is in this group

    • The name "whoami" says "whoami": a word for reading.

    Findings

    • Info

      The description mentions "credential".

      Matched text

      description: "Verifies your redu.cloud credential with a REAL server round-trip and retur..."

      What this check looks for
    Description, as the server wrote it
    Verifies your redu.cloud credential with a REAL server round-trip and returns your identity + live quota. A green result means the key actually works - not merely that one is configured. Use this first if anything is 401ing.

    Annotations the server declares

    • readOnlyHint: true
    • openWorldHint: true

This is not a safety rating, and no finding is not a safety guarantee. The groups and findings come from fixed checks of the tool definitions the server returned on the date shown, not from its code or what it does when called. How the checks work.

Install

Pinned to version 0.1.1. Values in angle brackets are yours to fill in.

claude mcp add --transport http redu https://mcp.redu.cloud/mcp

Run in a terminal.

Claude Code permissions

Allow the reads, ask before a change, deny the deletes. Paste into .claude/settings.json.

.claude/settings.json
{
  "permissions": {
    "allow": [
      "mcp__redu__whoami",
      "mcp__redu__list_api_keys",
      "mcp__redu__list_instances",
      "mcp__redu__list_flavors",
      "mcp__redu__list_images",
      "mcp__redu__list_keypairs",
      "mcp__redu__list_security_groups",
      "mcp__redu__list_private_networks",
      "mcp__redu__list_volumes",
      "mcp__redu__list_snapshots",
      "mcp__redu__list_backups",
      "mcp__redu__list_clusters",
      "mcp__redu__list_dns_entries",
      "mcp__redu__list_domains",
      "mcp__redu__list_regions",
      "mcp__redu__list_databases",
      "mcp__redu__list_relational_databases",
      "mcp__redu__list_media_spaces",
      "mcp__redu__list_clickhouse_databases",
      "mcp__redu__list_redis",
      "mcp__redu__list_qdrant",
      "mcp__redu__get_ssh_command",
      "mcp__redu__get_instance_logs",
      "mcp__redu__list_deployments",
      "mcp__redu__get_deployment",
      "mcp__redu__get_containers",
      "mcp__redu__get_container_logs",
      "mcp__redu__get_resource_usage",
      "mcp__redu__get_env_keys",
      "mcp__redu__architecture_diagram",
      "mcp__redu__get_agent_status"
    ],
    "ask": [
      "mcp__redu__check_deploy_prerequisites",
      "mcp__redu__deploy_overview",
      "mcp__redu__create_api_key",
      "mcp__redu__integrate_overview",
      "mcp__redu__prepare_upload",
      "mcp__redu__scaffold_local",
      "mcp__redu__plan_instance",
      "mcp__redu__plan_managed_datastore",
      "mcp__redu__select_surface",
      "mcp__redu__plan_deploy",
      "mcp__redu__create_media_space",
      "mcp__redu__upgrade_to_cluster",
      "mcp__redu__update_cluster",
      "mcp__redu__set_cluster_domain",
      "mcp__redu__import_keypair",
      "mcp__redu__instance_action",
      "mcp__redu__create_instance",
      "mcp__redu__create_database",
      "mcp__redu__create_relational_database",
      "mcp__redu__create_clickhouse",
      "mcp__redu__create_redis",
      "mcp__redu__create_qdrant",
      "mcp__redu__deploy_app",
      "mcp__redu__deploy_compose",
      "mcp__redu__deploy_vpn",
      "mcp__redu__verify_deployment",
      "mcp__redu__wait_for_deployment",
      "mcp__redu__create_snapshot",
      "mcp__redu__create_backup",
      "mcp__redu__restore_backup",
      "mcp__redu__set_managed_backups",
      "mcp__redu__create_volume",
      "mcp__redu__attach_volume",
      "mcp__redu__detach_volume",
      "mcp__redu__get_domain_verification",
      "mcp__redu__verify_domain",
      "mcp__redu__check_agent_prerequisites",
      "mcp__redu__create_controller",
      "mcp__redu__trigger_agent_batch",
      "mcp__redu__setup_agent_session",
      "mcp__redu__setup_agent_fleet"
    ],
    "deny": [
      "mcp__redu__delete_api_key",
      "mcp__redu__delete_cluster",
      "mcp__redu__delete_keypair",
      "mcp__redu__delete_instance",
      "mcp__redu__delete_database",
      "mcp__redu__delete_relational_database",
      "mcp__redu__delete_clickhouse",
      "mcp__redu__upgrade_to_ha",
      "mcp__redu__delete_redis",
      "mcp__redu__delete_qdrant",
      "mcp__redu__delete_deployment",
      "mcp__redu__delete_snapshot",
      "mcp__redu__delete_backup",
      "mcp__redu__delete_volume",
      "mcp__redu__release_domain",
      "mcp__redu__delete_dns_entry"
    ]
  }
}

Tools that only read are allowed, tools that change things ask first, tools that delete or cancel are denied. It assumes the server is installed as redu. Review it before you use it.