# Connect DigitalOcean to Prodpeek Profile: `digitalocean/read-only` · connection kind: `digitalocean` · tier 2 Credential: A DigitalOcean personal access token, Read scope Suggested URL: `https://api.digitalocean.com` Droplets, App Platform, managed databases, Kubernetes, networking, DNS, volumes, monitoring and the account's action log — forty tools, one token, nothing to install. ## What you must not do You cannot create this credential on the user's behalf — it needs their login and, usually, an approval step. Walk them through it and verify the result. Never ask them to paste the credential into the chat; it goes straight into Prodpeek's console, which encrypts it at rest. Do not suggest DigitalOcean's MCP endpoints (apps.mcp.digitalocean.com and its siblings) or `npx @digitalocean/mcp`. Prodpeek has a native DigitalOcean adapter that speaks API v2 directly, so one connection reads the whole account. The upstream is https://api.digitalocean.com and should not be changed. ## Grant exactly these permissions - `A personal access token with **Read** scope` — DigitalOcean tokens are Read or Read/Write for the whole account; there is no per-resource scoping. Read genuinely cannot write — that part is real and vendor-enforced — which is why every write in this profile is refused twice over. - `An expiry on the token` — DigitalOcean offers 30/60/90 days, a year, or no expiry. Take a bounded one. A token with no expiry is a credential you will forget you issued. ## Refuse these, and say why if the user asks for them - `Write (Read/Write) scope` — It would let the token create, resize, reboot and destroy every resource on the account. The gateway issues only GET and has no tool that builds a write path, but a Read/Write token means the vendor fence is gone and only the gateway is left. - `A token belonging to a human's personal account, on a team account` — It carries that person's access, disappears when they leave, and attributes every call to them rather than to the integration. ## Verify before the credential is used - The token page shows Scopes: Read (not Read/Write). - The token has an expiry date. - Test connection shows around 40 tools, not 8. - Calling `account` returns the email of the account you expected. ## Then, in Prodpeek 1. Services → Add a service → choose the profile `digitalocean/read-only`. 2. Connection kind `digitalocean`, URL `https://api.digitalocean.com`. 3. Paste the credential. It is encrypted in the store and never shown again. 4. Run **Test connection**. It lists every tool the upstream advertises and how the profile classifies each one. Anything under "not in the policy" is denied by default — report that list rather than assuming it is fine. ## Full human walkthrough ## The whole setup 1. DigitalOcean control panel → **API** → **Tokens** → **Generate New Token**. 2. Name it `prodpeek`. Set an expiry. **Scopes: Read.** Not Read/Write. 3. Copy it. 4. In Prodpeek: **Services → Add a service**, pick `digitalocean/read-only`, leave the upstream alone, paste the token, **Test connection**. That is it. Two minutes, nothing to install. ## Why you are not connecting DigitalOcean's MCP If you connected DigitalOcean through its MCP server, you got about **eight tools, all about App Platform**, and concluded — correctly — that it was not enough to be useful. That was not the policy being strict. DigitalOcean ships its MCP as **one endpoint per service**: `apps.mcp.digitalocean.com`, `droplets.mcp.digitalocean.com`, and so on. The local `npx @digitalocean/mcp` build has the same shape behind a `--services` flag that loads only the modules you name. So "connect DigitalOcean" really meant connecting it a dozen times — a dozen connections, each with its own credential and its own audit trail, each showing a twelfth of your account. DigitalOcean's API v2 is one host, one bearer token, and the whole account. So Prodpeek speaks it directly, the same way it does Grafana, SSH, Postgres, Git and Prometheus. One connection. Forty tools. ## What your agent can then do ``` do__list_droplets what exists, and whether it is up do__list_droplet_neighbors why three of them degraded at once do__list_apps App Platform, with each app's live phase do__get_deployment which step of the failed deploy failed do__list_databases engine, version, status — never the password do__list_database_firewall_rules the real reason the app cannot connect do__list_domain_records why it still resolves to the old address do__list_certificates what expires, and when do__list_alert_policies what DigitalOcean is already watching do__list_actions every resize, reboot and migration, with when ``` `list_actions` is the one people do not expect to want. It is DigitalOcean's own action log — the closest thing the platform has to an audit trail, and usually the fastest answer to *what changed just before this broke?* `list_droplet_neighbors` is the second. Shared hardware is invisible until three unrelated droplets degrade together, and then it is the only explanation that fits. ## The three endpoints that hand out credentials DigitalOcean has three reads whose response body *is* a live credential: | Endpoint | What it returns | |---|---| | `/v2/kubernetes/clusters/{id}/kubeconfig` | working cluster-admin credentials | | `/v2/registry/docker-credentials` | a docker login for your private registry | | `/v2/databases/{id}` | a `connection` object containing the database password | **The first two have no tool.** Not denied by a rule — there is no code in the adapter that builds those paths, so no argument and no bug reaches them. **The third is different**, and the difference is the interesting one. A database cluster's status, version, region and maintenance window are exactly what you want during an incident. Denying the whole endpoint to avoid one field would have cost real visibility, so `get_database` exists and the adapter removes `connection`, `private_connection` and `users` before answering. You get the diagnostics; the password is replaced with a note saying it was removed. App Platform env vars get the same treatment: the **keys** survive, the **values** do not. "Is `DATABASE_URL` set at all?" is the question worth answering. ## Why Tier 2 A DigitalOcean token is Read or Read/Write for the entire account. There is no per-resource scoping and no "may not read billing" flag. Read genuinely cannot write — real, and worth having — but it *can* read everything this profile chooses not to expose, so for several tools the gateway is the only thing saying no. Tier 2, said plainly. ## Checking the token is really Read ```bash # Should succeed curl -sS -H "Authorization: Bearer $DO_TOKEN" \ "https://api.digitalocean.com/v2/account" | head -c 200 # Should FAIL with 403: creating a tag is the cheapest harmless write curl -sS -o /dev/null -w '%{http_code}\n' -X POST \ -H "Authorization: Bearer $DO_TOKEN" -H 'Content-Type: application/json' \ -d '{"name":"prodpeek-permission-test"}' \ "https://api.digitalocean.com/v2/tags" ``` A `403` on the second is the vendor fence doing its half. A `201` means the token is Read/Write — delete it and make a Read one.