{"id":"8877d505-78f6-4401-9909-0cf371b1f398","entityType":"agent","slug":"clawhub-benkalsky-cloudways-mcp","name":"Cloudways MCP","canonicalUrl":"https://www.xpersona.co/agent/clawhub-benkalsky-cloudways-mcp","canonicalPath":"/agent/clawhub-benkalsky-cloudways-mcp","generatedAt":"2026-10-10T10:43:16.292Z","source":"CLAWHUB","claimStatus":"UNCLAIMED","verificationTier":"NONE","summary":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-10T05:20:18.733Z","emptyReason":null},"description":"Operational guide for managing Cloudways servers and applications, across one or several Cloudways accounts, via the official Cloudways MCP server (Cloudways' hosted MCP / Remote MCP, per their support docs). Use whenever the user mentions Cloudways, a Cloudways server or app, server monitoring, app monitoring, bandwidth, disk usage, PHP/MySQL/traffic analytics, Varnish cache, app cloning, backups/restore on Cloudways, Git deployments on Cloudways, SSL/Let's Encrypt on Cloudways, malware scans / Security Suite, staging sync, team members, AgencyOS client billing, or running an audit/onboarding on a Cloudways-hosted client site. Any write operation (start/stop/restart server, backup, restore, update CNAME, purge cache, change service state, git pull, SSL install/revoke, IP whitelist update, staging sync, team/billing changes, delete server/app) requires explicit confirmation of target server/app and intended action before execution.","descriptionLabel":"Source description","evidenceSummary":"Capability contract not published. No trust telemetry is available yet. 1.7K downloads reported by the source. Last updated 10/10/2026.","installCommand":"clawhub skill install s17b8qfjfq0g8kveh2g8v1179h83ejwb:cloudways-mcp","sourceUrl":"https://clawhub.ai/benkalsky/cloudways-mcp","homepage":"https://clawhub.ai/benkalsky/skills/cloudways-mcp","primaryLinks":[{"label":"View on ClawHub","url":"https://clawhub.ai/benkalsky/cloudways-mcp","kind":"source"},{"label":"Homepage","url":"https://clawhub.ai/benkalsky/skills/cloudways-mcp","kind":"homepage"}],"safetyScore":84,"overallRank":62,"popularityScore":64,"trustScore":null,"claimedByName":null,"isOwner":false,"seoDescription":"Cloudways MCP technical dossier on Xpersona with agent coverage, OPENCLEW support, and live trust metadata."},"coverage":{"evidence":{"source":"public-profile","verified":false,"confidence":"medium","updatedAt":"2026-10-10T05:20:18.733Z","emptyReason":null},"protocols":[{"protocol":"OPENCLEW","label":"OpenClaw","status":"self-declared","notes":"Declared in the public agent profile."}],"capabilities":[],"verifiedCount":0,"selfDeclaredCount":1,"capabilityMatrix":{"rows":[{"key":"OPENCLEW","type":"protocol","support":"unknown","confidenceSource":"profile","notes":"Listed on profile"}],"flattenedTokens":"protocol:OPENCLEW|unknown|profile"}},"adoption":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-10T05:20:18.733Z","emptyReason":null},"stars":null,"forks":null,"downloads":1660,"packageName":null,"latestVersion":"1.5.3","tractionLabel":"1.7K downloads"},"release":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-10T05:20:18.722Z","emptyReason":null},"lastUpdatedAt":"2026-10-10T05:20:18.733Z","lastCrawledAt":"2026-10-10T05:20:18.722Z","lastIndexedAt":null,"nextCrawlAt":"2026-10-11T05:20:18.722Z","lastVerifiedAt":null,"highlights":[{"version":"1.5.3","createdAt":"2026-09-13T22:58:22.484Z","changelog":"Cloudways MCP Skill v1.5.3 - Updated tool catalog, monitoring, and maintenance workflow references for accuracy and alignment with current Cloudways MCP documentation. - Improved organization by removing the redundant skill-card.md file. - No changes to core safety policies or operational logic; content and workflows remain authoritative per the updated references.","fileCount":11,"zipByteSize":80192},{"version":"1.5.2","createdAt":"2026-09-13T19:31:22.467Z","changelog":"Cloudways MCP v1.5.2 - Updated documentation in SKILL.md and all workflow/reference files for clarity and accuracy. - Expanded and clarified installation, monitoring, automation, and onboarding references. - Removed outdated skill-card.md. - Improved safety and confirmation instructions around write operations and multi-account management. - No changes to core logic or tooling; all updates are documentation and workflow clarifications.","fileCount":11,"zipByteSize":66831},{"version":"1.5.1","createdAt":"2026-09-13T17:45:09.074Z","changelog":"cloudways-mcp v1.5.1 - Added a new safety rule: avoid sweeping credential-returning tools (`server_get`, `app_get`, `app_credentials`) and explained the risks of inventory tools exposing sensitive data. - Clarified that inventory tools (`server_list`, `app_list`) can still return credentials, and provided guidance to avoid leaking them in transcripts or automations. - Updated documentation across references for safety and operational best practices. - Added bridge/package.json and bridge/package-lock.json; removed deprecated skill-card.md. - Minor editorial corrections for clarity and workflow organization.","fileCount":11,"zipByteSize":62122},{"version":"1.5.0","createdAt":"2026-09-13T14:47:08.929Z","changelog":"cloudways-mcp v1.5.0 - Updated documentation in SKILL.md with revised safety guidance and workflow references. - Improved clarity around multi-account handling and explicit confirmation requirements for write operations. - references/installation.md updated; skill-card.md removed for streamlined documentation. - No functional changes to tool usage or behavior; updates focus on guidance and usability.","fileCount":9,"zipByteSize":45210},{"version":"1.4.1","createdAt":"2026-08-25T00:38:40.235Z","changelog":"cloudways-mcp v1.4.1 - Updated documentation in SKILL.md with improved instructions and clarified operational/safety rules. - references/installation.md updated with latest installation guidance. - Removed obsolete skill-card.md file.","fileCount":9,"zipByteSize":43251},{"version":"1.4.0","createdAt":"2026-07-22T09:42:35.507Z","changelog":"cloudways-mcp 1.4.0 - Reference and documentation structure updated: `skill-card.md` removed, `SKILL.md` and `references/installation.md` changed. - No functional changes to core workflows. - Documentation clarifications and improvements; see updated SKILL.md for details.","fileCount":9,"zipByteSize":42638},{"version":"1.3.1","createdAt":"2026-07-20T17:15:42.819Z","changelog":"Cloudways MCP 1.3.1 — Expanded feature & reference coverage - Added support and documentation for new Cloudways MCP tools: SSL (Let's Encrypt), malware/security suite, staging sync, team, and AgencyOS billing features. - Updated references and tool catalog links to match latest official Cloudways documentation, with more granular tool descriptions and roles. - Improved safety and confirmation instructions for all write operations, including new credential handling (Access Token via X-Access-Token header). - Expanded quick routes and workflows to cover onboarding, automations, enhanced maintenance (including security), and multi-account environments. - Cleaned up documentation and removed deprecated files (including old skill-card.md).","fileCount":9,"zipByteSize":41981},{"version":"1.2.2","createdAt":"2026-07-03T14:49:36.655Z","changelog":"cloudways-mcp 1.2.2 - Expanded and clarified safety rules, especially regarding `execute_tool`/toolset-proxy calls and their risk inheritance. - Updated guidance and table for write operations: reference to `references/tools-catalog.md` is now authoritative for required confirmations and destructive actions. - Improved clarity around multi-account risk and reinforced confirmation patterns for destructive (W!) operations. - Updated, reorganized, and clarified several workflows and reference files. - Removed deprecated skill-card.md file.","fileCount":9,"zipByteSize":32449}]},"execution":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No published capability contract is available yet."},"installCommand":"clawhub skill install s17b8qfjfq0g8kveh2g8v1179h83ejwb:cloudways-mcp","setupComplexity":"low","setupSteps":["Install using `clawhub skill install s17b8qfjfq0g8kveh2g8v1179h83ejwb:cloudways-mcp` in an isolated environment before connecting it to live workloads.","No published capability contract is available yet, so validate auth and request/response behavior manually.","Review the upstream CLAWHUB listing at https://clawhub.ai/benkalsky/cloudways-mcp before using production credentials."],"contract":{"contractStatus":"missing","authModes":[],"requires":[],"forbidden":[],"supportsMcp":false,"supportsA2a":false,"supportsStreaming":false,"inputSchemaRef":null,"outputSchemaRef":null,"dataRegion":null,"contractUpdatedAt":null,"sourceUpdatedAt":null,"freshnessSeconds":null},"invocationGuide":{"preferredApi":{"snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-benkalsky-cloudways-mcp/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-benkalsky-cloudways-mcp/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-benkalsky-cloudways-mcp/trust"},"curlExamples":["curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-benkalsky-cloudways-mcp/snapshot\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-benkalsky-cloudways-mcp/contract\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-benkalsky-cloudways-mcp/trust\""],"jsonRequestTemplate":{"query":"summarize this repo","constraints":{"maxLatencyMs":2000,"protocolPreference":["OPENCLEW"]}},"jsonResponseTemplate":{"ok":true,"result":{"summary":"...","confidence":0.9},"meta":{"source":"CLAWHUB","generatedAt":"2026-10-10T10:43:16.288Z"}},"retryPolicy":{"maxAttempts":3,"backoffMs":[500,1500,3500],"retryableConditions":["HTTP_429","HTTP_503","NETWORK_TIMEOUT"]}},"endpoints":{"dossierUrl":"https://www.xpersona.co/api/v1/agents/clawhub-benkalsky-cloudways-mcp/dossier","snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-benkalsky-cloudways-mcp/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-benkalsky-cloudways-mcp/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-benkalsky-cloudways-mcp/trust"}},"reliability":{"evidence":{"source":"runtime-metrics","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No trust, reliability, or runtime telemetry is available."},"trust":{"status":"unavailable","handshakeStatus":"UNKNOWN","verificationFreshnessHours":null,"reputationScore":null,"p95LatencyMs":null,"successRate30d":null,"fallbackRate":null,"attempts30d":null,"trustUpdatedAt":null,"trustConfidence":"unknown","sourceUpdatedAt":null,"freshnessSeconds":null},"decisionGuardrails":{"doNotUseIf":["Contract metadata is missing or unavailable for deterministic execution."],"safeUseWhen":[],"riskFlags":["missing_or_unavailable_contract","trust_data_unavailable","schema_references_missing"],"operationalConfidence":"low"},"executionMetrics":{"observedLatencyMsP50":null,"observedLatencyMsP95":null,"estimatedCostUsd":null,"uptime30d":null,"rateLimitRpm":null,"rateLimitBurst":null,"lastVerifiedAt":null,"verificationSource":null},"runtimeMetrics":{"successRate":null,"avgLatencyMs":null,"avgCostUsd":null,"hallucinationRate":null,"retryRate":null,"disputeRate":null,"p50Latency":null,"p95Latency":null,"lastUpdated":null}},"benchmarks":{"evidence":{"source":"no-benchmark-data","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No benchmark suites or observed failure patterns are available."},"suites":[],"failurePatterns":[]},"artifacts":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-10T05:20:18.733Z","emptyReason":null},"readme":"Skill: Cloudways MCP\n\nOwner: benkalsky\n\nSummary: Operational guide for managing Cloudways servers and applications, across one or several Cloudways accounts, via the official Cloudways MCP server (Cloudways' hosted MCP / Remote MCP, per their support docs). Use whenever the user mentions Cloudways, a Cloudways server or app, server monitoring, app monitoring, bandwidth, disk usage, PHP/MySQL/traffic analytics, Varnish cache, app cloning, backups/restore on Cloudways, Git deployments on Cloudways, SSL/Let's Encrypt on Cloudways, malware scans / Security Suite, staging sync, team members, AgencyOS client billing, or running an audit/onboarding on a Cloudways-hosted client site. Any write operation (start/stop/restart server, backup, restore, update CNAME, purge cache, change service state, git pull, SSL install/revoke, IP whitelist update, staging sync, team/billing changes, delete server/app) requires explicit confirmation of target server/app and intended action before execution.\n\nTags: latest:1.5.3\n\nVersion history:\n\nv1.5.3 | 2026-09-13T22:58:22.484Z | auto\n\nCloudways MCP Skill v1.5.3\n\n- Updated tool catalog, monitoring, and maintenance workflow references for accuracy and alignment with current Cloudways MCP documentation.\n- Improved organization by removing the redundant skill-card.md file.\n- No changes to core safety policies or operational logic; content and workflows remain authoritative per the updated references.\n\nv1.5.2 | 2026-09-13T19:31:22.467Z | auto\n\nCloudways MCP v1.5.2\n\n- Updated documentation in SKILL.md and all workflow/reference files for clarity and accuracy.\n- Expanded and clarified installation, monitoring, automation, and onboarding references.\n- Removed outdated skill-card.md.\n- Improved safety and confirmation instructions around write operations and multi-account management.\n- No changes to core logic or tooling; all updates are documentation and workflow clarifications.\n\nv1.5.1 | 2026-09-13T17:45:09.074Z | auto\n\ncloudways-mcp v1.5.1\n\n- Added a new safety rule: avoid sweeping credential-returning tools (`server_get`, `app_get`, `app_credentials`) and explained the risks of inventory tools exposing sensitive data.\n- Clarified that inventory tools (`server_list`, `app_list`) can still return credentials, and provided guidance to avoid leaking them in transcripts or automations.\n- Updated documentation across references for safety and operational best practices.\n- Added bridge/package.json and bridge/package-lock.json; removed deprecated skill-card.md.\n- Minor editorial corrections for clarity and workflow organization.\n\nv1.5.0 | 2026-09-13T14:47:08.929Z | auto\n\ncloudways-mcp v1.5.0\n\n- Updated documentation in SKILL.md with revised safety guidance and workflow references.\n- Improved clarity around multi-account handling and explicit confirmation requirements for write operations.\n- references/installation.md updated; skill-card.md removed for streamlined documentation.\n- No functional changes to tool usage or behavior; updates focus on guidance and usability.\n\nv1.4.1 | 2026-08-25T00:38:40.235Z | auto\n\ncloudways-mcp v1.4.1\n\n- Updated documentation in SKILL.md with improved instructions and clarified operational/safety rules.\n- references/installation.md updated with latest installation guidance.\n- Removed obsolete skill-card.md file.\n\nv1.4.0 | 2026-07-22T09:42:35.507Z | auto\n\ncloudways-mcp 1.4.0\n\n- Reference and documentation structure updated: `skill-card.md` removed, `SKILL.md` and `references/installation.md` changed.\n- No functional changes to core workflows.\n- Documentation clarifications and improvements; see updated SKILL.md for details.\n\nv1.3.1 | 2026-07-20T17:15:42.819Z | auto\n\nCloudways MCP 1.3.1 — Expanded feature & reference coverage\n\n- Added support and documentation for new Cloudways MCP tools: SSL (Let's Encrypt), malware/security suite, staging sync, team, and AgencyOS billing features.\n- Updated references and tool catalog links to match latest official Cloudways documentation, with more granular tool descriptions and roles.\n- Improved safety and confirmation instructions for all write operations, including new credential handling (Access Token via X-Access-Token header).\n- Expanded quick routes and workflows to cover onboarding, automations, enhanced maintenance (including security), and multi-account environments.\n- Cleaned up documentation and removed deprecated files (including old skill-card.md).\n\nv1.2.2 | 2026-07-03T14:49:36.655Z | auto\n\ncloudways-mcp 1.2.2\n\n- Expanded and clarified safety rules, especially regarding `execute_tool`/toolset-proxy calls and their risk inheritance.\n- Updated guidance and table for write operations: reference to `references/tools-catalog.md` is now authoritative for required confirmations and destructive actions.\n- Improved clarity around multi-account risk and reinforced confirmation patterns for destructive (W!) operations.\n- Updated, reorganized, and clarified several workflows and reference files.\n- Removed deprecated skill-card.md file.\n\nv1.2.1 | 2026-06-03T22:26:25.117Z | auto\n\ncloudways-mcp v1.2.1\n\n- Added explicit MIT license declaration in SKILL.md.\n- Updated version number and metadata in SKILL.md for consistency.\n- No functional or workflow changes; documentation only.\n\nv1.2.0 | 2026-06-03T20:08:16.441Z | auto\n\n**Cloudways MCP 1.2.0 — Alignment with Official Tool Naming and Scope**\n\n- Updated tool references and documentation to match the official Cloudways MCP tool names from the support article.\n- Clarified that SSL/Let's Encrypt and SSH/MySQL IP whitelisting are not MCP tools; these actions are to be performed in the Cloudways UI or direct API.\n- Expanded and revised the safety and confirmation guidelines for destructive operations, aligning with the official MCP capabilities and terminology.\n- Updated server/app operation lists, replacing legacy tool names (e.g., `list_servers` → `server_list`) and reflecting actual destructive actions available on the official MCP (e.g., `server_delete`, `app_delete`).\n- Improved authentication instructions to specify the required HTTP headers and connection endpoint for the official MCP.\n- Revised documentation in installation, tool catalog, workflows, and onboarding guides to ensure accuracy with the official Cloudways MCP server.\n\nv1.1.0 | 2026-06-03T19:25:37.564Z | auto\n\n**Cloudways MCP 1.1.0 — Now targets official Cloudways Remote MCP only**\n\n- Updated scope to support only Cloudways' official hosted MCP (community/self-hosted path removed).\n- Clarified that available tool names and workflows are illustrative, not guaranteed; users should verify against the live tool catalog.\n- Revised authentication details to match the official connection method.\n- Cleaned up references to deprecated community implementations.\n- Removed obsolete/irrelevant documentation files and examples.\n\nv1.0.0 | 2026-06-02T23:27:48.510Z | auto\n\nCloudways MCP skill v1.0.0 — initial release\n\n- Provides operational guidance for managing Cloudways servers and applications via Cloudways MCP (official or self-hosted).\n- Supports monitoring, maintenance, onboarding, and automation tasks across one or more Cloudways accounts.\n- Includes strict safety rules: explicit confirmation required before any write/destructive operation, with clear account and action details.\n- Covers both official Cloudways MCP and the self-hosted `cw-mcp` implementation, with selection guidance.\n- Organizes workflows and quick access routes for installation, monitoring, maintenance, onboarding, and multi-account management.\n- Emphasizes read-only actions by default, and details confirmation patterns for sensitive operations.\n\nArchive index:\n\nArchive v1.5.3: 11 files, 80192 bytes\n\nFiles: bridge/package-lock.json (35638b), bridge/package.json (365b), references/installation.md (30469b), references/tools-catalog.md (32146b), references/workflows-automation.md (18248b), references/workflows-maintenance.md (43429b), references/workflows-monitoring.md (14250b), references/workflows-onboarding.md (15537b), skill-card.md (3331b), SKILL.md (20353b), _meta.json (132b)\n\nFile v1.5.3:SKILL.md\n\n---\nname: cloudways-mcp\nversion: 1.5.3\nlicense: MIT\ndescription: |\n  Operational guide for managing Cloudways servers and applications, across one or several Cloudways accounts, via the official Cloudways MCP server (Cloudways' hosted MCP / Remote MCP, per their support docs).\n  Use whenever the user mentions Cloudways, a Cloudways server or app, server monitoring, app monitoring, bandwidth, disk usage, PHP/MySQL/traffic analytics, Varnish cache, app cloning, backups/restore on Cloudways, Git deployments on Cloudways, SSL/Let's Encrypt on Cloudways, malware scans / Security Suite, staging sync, team members, AgencyOS client billing, or running an audit/onboarding on a Cloudways-hosted client site.\n  Any write operation (start/stop/restart server, backup, restore, update CNAME, purge cache, change service state, git pull, SSL install/revoke, IP whitelist update, staging sync, team/billing changes, delete server/app) requires explicit confirmation of target server/app and intended action before execution.\n---\n\n# Cloudways MCP — Operational Skill\n\nManaging Cloudways infrastructure through the Cloudways MCP server.\n\n> **Connection:** This skill targets the **official Cloudways (Remote) MCP** — an MCP hosted by Cloudways at `https://mcp.cloudways.com/mcp/` that you connect to directly. The source of truth for connecting is the **official article**: `support.cloudways.com/en/articles/14654372`. See `references/installation.md`.\n>\n> **Tool names match the official articles.** The tool catalog and workflows in this skill use the official Cloudways MCP tool names (verified against the setup article and the dedicated [tools article](https://support.cloudways.com/en/articles/15798823-cloudways-mcp-server-tools)). Always treat the live `mcp__cloudways*__*` tools as the source of truth if Cloudways changes them (see \"Versioning and source of truth\" below).\n\n> **Context:** The skill is built for day-to-day work managing clients/environments on Cloudways — monitoring, routine maintenance, onboarding/audit for new clients, and automations. All monetary values reported by the API are in $ (USD), not ₪.\n\n---\n\n## Quick Route\n\n| Intent | Load |\n|--------|------|\n| Initial installation/configuration of the MCP server | `references/installation.md` |\n| Don't know which tool exists / searching for a tool by name | `references/tools-catalog.md` |\n| Monitoring, status check, bandwidth, analytics | `references/workflows-monitoring.md` |\n| Cache clear, SSL, backup, restart, IP whitelist | `references/workflows-maintenance.md` |\n| New audit / onboarding a new client | `references/workflows-onboarding.md` |\n| Building an automated workflow for n8n/Make/Claude Code | `references/workflows-automation.md` |\n| Multiple Cloudways accounts / multi-account configuration | `references/installation.md` (Multi-account section) |\n\n**Load only what's needed.** Maximum 2-3 references per task. If the user just asks \"show me my servers\", don't load the entire catalog — call `server_list` directly.\n\n---\n\n## Safety rules (read before every operation)\n\n1. **Selecting the correct account — before anything else (multi-account).** There are **multiple Cloudways accounts**, each as a separate MCP connection with its own prefix (e.g. `mcp__cloudways-clientA__*`). Before every call — verify which account it belongs to. If more than one account is connected and it's not clear from context which one is meant — **stop and ask**, don't guess. server/app IDs are **not interchangeable between accounts** — ID 1234567 in account A is an entirely different resource (or nonexistent) in account B. Don't take an ID from one account's response and run it against another account. See the \"Multi-account\" section below.\n\n2. **Write operations require explicit confirmation.** Before every call to a tool that belongs to the Write category (see list below), present to the user: **the account**, the tool name, the target server/application (ID + name), the parameters, the expected impact. Wait for a confirmation response before executing. Don't assume that confirming one operation grants confirmation for further operations — nor that confirmation on one account applies to another.\n\n3. **Backup before a significant change.** Before `app_restore`, `app_delete`, `varnish_manage`, `varnish_app_manage`, or any configuration change — check with the user whether a recent backup exists. If not, offer to run `app_backup` / `server_backup` first.\n\n4. **Multiple services = multiplied risk.** Cloudways usually hosts **several applications on the same server**. `server_stop`, `server_restart`, or `server_delete` affects **all** the applications. Always make sure the user is aware of the list of applications on the server before a server-level operation.\n\n5. **`server_delete`, `app_delete`, and `app_cname_delete` = immediate destruction in production.** Requires double confirmation (W!): of both the operation and the specific domain/application/server.\n\n6. **Credentials.** Each account authenticates with its own **Access Token** (case-sensitive `X-Access-Token` header; roles + legacy-key migration in the Authentication section below). Don't print tokens in responses. Don't mix credentials between accounts. If the user asks to see them, refer them to platform.cloudways.com → API section.\n\n7. **Don't sweep the credential-returning tools.** `server_get`, `app_get` and `app_credentials` return master, database and SSH credentials inside their ordinary payloads. Running one of them over **every** server or **every** app pulls the account's secrets into the conversation, where they stay for the rest of it — and telling yourself to keep them out of the *report* comes too late to help. Use `server_list` / `app_list` for inventory; call the other three for a specific field nothing else returns, or for a task the user actually asked for.\n   **But do not read \"inventory\" as \"credential-free.\"** The live server describes `app_list` as returning “ID, label, application type, version, domain, **and credentials**”, and both list tools are built from the same `GET /server` payload that makes `server_get` a credential tool. What makes them the right choice is that they answer the inventory question in **one call per account or per server** instead of one per app — not that their responses are known to be clean. Take the IDs and the fields you came for; never paste a raw list response into a report, a ticket or an automation. And when a job requires that no credential enter the transcript **at all**, build the roster outside the conversation — the Cloudways Platform UI, or a direct `GET /server` piped through a field filter on your side — and bring back only ids and labels.\n\n8. **Read-only by default.** If the user just asks \"show me / check / monitor\" — always choose the appropriate read-only tool. Don't suggest a destructive operation unless the user explicitly asked for it.\n\n9. **`execute_tool` / toolset-proxy calls inherit their target tool's R/W/W! risk.** Most tools live in on-demand toolsets and are invoked through the `execute_tool` proxy (or surfaced via `get_toolset_tools`). Calling a write/destructive tool through the proxy is exactly as consequential as calling it directly — apply the **same** confirmation (and double-confirmation for W!) as you would for the named tool.\n\n---\n\n## Write operations require confirmation — the catalog is authoritative\n\n**The authoritative list is `references/tools-catalog.md`: every tool flagged `W` or `W!` there requires explicit confirmation before execution, and every `W!` requires the double-confirmation pattern.** The grouping below is **illustrative, not exhaustive** — the live MCP (v1.2) exposes 244 tools — 241 across 22 toolsets plus 3 meta-tools — and 179 of them are **not** in your default tool list. If a tool is not named here but is flagged `W`/`W!` in the catalog (or its live schema describes a destructive/irreversible action), it needs the **same** confirmation. Never treat \"it's not in this list\" as \"it's safe to run without confirmation.\"\n\n**Server level (affects all applications on the server):**\n- `server_start`, `server_stop`, `server_restart`\n- `server_delete` ⚠️⚠️ (W! — immediate destruction)\n- `server_scale` ⚠️ (W! — resize CPU/RAM with brief downtime for every app on the server)\n- `server_backup`, `server_backup_settings_update`, `server_snapshot_frequency_update`\n- `server_local_backup_delete` ⚠️ (W! — permanently deletes local backup snapshots)\n- `server_package_update` (W! for the uninstall variant — removes a package + its data)\n- `server_master_username_update`, `server_master_password_update` ⚠️ (W! — old SSH/SFTP creds stop working immediately)\n- `service_start`, `service_stop`, `service_restart` (Apache/Nginx/Memcached/MySQL/Varnish)\n- `varnish_manage`\n\n**App level:**\n- `app_create`, `app_clone`, `app_clone_to_server`, `staging_app_clone`, `staging_app_clone_to_server` (create new copies — consume resources)\n- `app_backup`, `app_restore` ⚠️⚠️ (W! — full overwrite of current state)\n- `app_restore_rollback` ⚠️ (W! — overwrites current state with the pre-restore copy; only within the rollback window)\n- `app_local_backup_delete` ⚠️ (W! — permanently deletes the local pre-restore snapshot)\n- `app_delete` ⚠️⚠️ (W! — immediate destruction)\n- `app_db_password_update`, `app_admin_password_update`, `app_credentials_update`, `app_credentials_delete` ⚠️ (W! — old credentials stop working immediately)\n- `app_enforce_https_update`, `app_stack_update`, `app_reset_permissions`, `app_wp_multisite_update`\n- `app_cname_update`, `app_cname_delete` ⚠️ (W! — can break production)\n- `app_purge_cache`, `varnish_app_manage`\n\n**DNS / Projects / SSH keys (live via their toolsets):**\n- `dns_made_easy_delete_domains`, `dns_made_easy_delete_records` ⚠️ (W! — DNS records/domains gone; can break mail + site resolution)\n- `project_delete` ⚠️ (W! — ungroups the project)\n- `ssh_key_delete` ⚠️ (W! — revokes SSH/SFTP access immediately)\n\n**Add-ons:**\n- `addon_activate`, `addon_activate_on_server`, `addon_deactivate`, `addon_deactivate_on_server` (W — can change the account subscription / incur cost)\n\n**Security — SSL & IP access (new in MCP v1.2):**\n- `security_lets_encrypt_install`, `security_lets_encrypt_renew`, `security_lets_encrypt_auto_renewal`\n- `security_lets_encrypt_revoke`, `security_remove_own_ssl` ⚠️ (W! — HTTPS breaks until a new cert is installed)\n- `security_update_whitelisted_ips` ⚠️ (W! — **replaces** the SSH/SFTP or MySQL whitelist; a wrong list locks people out — always read the current list first)\n- `security_whitelist_ip_siab`, `security_whitelist_ip_adminer`\n\n**Security Suite / staging / team / transfer (new in MCP v1.2):**\n- `security_suite_app_files_restore` ⚠️ (W! — restoring quarantined files can put malware back on the live site)\n- `security_suite_server_countries_blacklist_update` ⚠️ (W! — blocks all traffic from entire countries)\n- `staging_sync_tables`, `staging_sync_code` ⚠️ (W! — overwrite data/code on the target; confirm **direction** — a push to live overwrites production)\n- `team_member_update`, `team_member_delete` ⚠️ (W! — changes/revokes a person's access)\n- `server_transfer_request` ⚠️ (W! — hands server **ownership** to another Cloudways account)\n- `agency_os_*` create/update (W — client-facing financial records: clients, services, plan prices, tax rates, invoices)\n- `agency_os_client_delete` ⚠️ (W! — deletes a client-facing financial record, archiving their services + invoice history)\n- `copilot_subscribe`, `copilot_plan_change`, `addon_upgrade` (W — change the account's subscription cost)\n\n**Git deployment:**\n\n- `git_clone`, `git_pull` (can break production if there's a conflict)\n- `git_generate_key` ⚠️ (W! — overwrites the app's existing deploy key; the old key stops working)\n\n---\n\n## Confirmation pattern for a destructive operation\n\nBefore execution, present a block like this:\n\n```\n🔒 Confirm operation execution?\n   Account: clientA (mcp__cloudways-clientA)\n   Tool: server_stop\n   Server: production-shop-il (ID: 1234567)\n   Applications affected: woocommerce-prod, staging-clone, admin-tools\n   Impact: all 3 applications will be offline until a manual restart\n   Proceed? (yes / no / pause and check backup first)\n```\n\nWait for an explicit response. A literal \"yes\" only = confirmation. Implied consent is not enough. The **account line is mandatory** when more than one account is connected — it prevents executing an operation on the wrong account.\n\n---\n\n## Authentication — quick overview\n\nThe official MCP is hosted at `https://mcp.cloudways.com/mcp/` and authenticates via two **case-sensitive** HTTP headers:\n\n- `X-Access-Token` — a Cloudways **Access Token** generated in the platform\n- `X-Mcp-Host` — the client identifier (`claude-code` / `claude-desktop`; full list of official values in `references/installation.md`)\n\nTokens are **role-based (RBAC)**: **READ** (look-ups only), **LIMITED** (selected endpoint groups), **FULL ACCESS** (everything, including destructive actions). Start new integrations with READ and escalate only when a connection must write. This platform-level control **complements** (does not replace) this skill's write-confirmation discipline — even FULL ACCESS has no per-tool gating at the MCP layer.\n\n> **Legacy:** the old `X-CW-Email` + `X-CW-Api-Key` API-key headers are deprecated — the API key stops working on **October 15, 2026** — and until migrated such connections retain unrestricted full-account access. Migration: `references/installation.md`.\n\nTreat tokens like passwords and **never print them in responses**. If the user asks to see one, refer them to platform.cloudways.com.\n\nFor the full connection, role guidance, and multi-account setup, see `references/installation.md`.\n\n---\n\n## Multi-account — working with multiple Cloudways accounts\n\nThere are usually **multiple Cloudways accounts** (different clients / different environments). Each account is connected as a **separate** MCP connection with its own credentials, and therefore appears in Claude with **its own prefix**:\n\n```\nmcp__cloudways-clientA__server_list\nmcp__cloudways-clientB__server_list\nmcp__cloudways-internal__server_list\n```\n\n> The configuration (how multiple accounts are connected — one connection per account, each with its own credentials) is documented in `references/installation.md` section **Multi-account configuration**. The runtime rules are here.\n\n### The golden rule: identify the account before every operation\n\n1. **A single account connected** → use it, no need to ask.\n2. **Multiple accounts connected** → determine which account the request belongs to **before** you call a tool:\n   - If the user explicitly specified a client/account (\"check clientB's prod\") → use the matching connection.\n   - If the server/domain name unambiguously identifies a single account → you may infer, but explicitly state which account you're operating on.\n   - If **it's unclear** → stop and ask: \"Which account? (clientA / clientB / internal)\". Don't guess, and don't run on all of them \"just to be safe\".\n\n### Complete isolation between accounts\n\n- **IDs don't cross accounts.** A server_id / app_id you received from `mcp__cloudways-clientA` is valid **only** against clientA. Never take an ID from one account's response and pass it to a tool of another connection.\n- **Per-account confirmation.** A write confirmation on one account does not apply to another. Every write operation on a new account = a new confirmation block (including the account line).\n- **Credentials don't mix.** Each connection has its own Access Token. Don't assume the same credentials work on another account.\n\n### Cross-account search (read only)\n\nWhen the user asks for something broad — \"which account does the domain shop.example.co.il live on?\", \"give me a disk overview for all accounts\" — it's permitted and legitimate to **read (read-only)** from all the connections, but:\n- Run the same sequence of reads on each connection **separately**, and tag each result with the account name.\n- Summarize in a table with a clear \"Account\" column.\n- **Never** perform a broad write operation across multiple accounts without individual confirmation for each one.\n\n```\nExample tagging in the response:\n| Account  | Server           | disk |\n|----------|------------------|------|\n| clientA  | prod-shop-il     | 87%  |\n| clientB  | prod-blog        | 41%  |\n| internal | ops-tools        | 63%  |\n```\n\n---\n\n## Common usage patterns (examples)\n\n### Quick snapshot of an account\n```\n1. server_list               → list of all servers\n2. copilot_insights_list     → active insights/alerts\n```\n\n(No account/whoami tool exists — infer the account from the connection prefix + `server_list`.)\n\n### Health check before a weekend (production client)\n```\n1. server_list                  → the fleet (server_get would add master credentials)\n2. monitoring_server_graph      → metrics (CPU/mem/etc.)\n3. app_list                     → per server, the application roster. server_list returns an\n                                  app COUNT, not the IDs step 4 needs. One call per server,\n                                  and rule 7 on what that one payload may carry\n4. monitoring_app_summary       → for each application from step 3\n5. copilot_insights_list        → open insights/alerts\n6. monitoring_server_summary    → disk/bandwidth; if disk > 80% — red flag\n```\n\n### Checking an app's details\n```\n1. app_list                  → find the app: id, label, type, version, domain\n2. app_settings_get          → its setting flags (XML-RPC, GEO-IP, password protection, …)\n3. monitoring_app_summary    → what it is doing right now\n```\n\n(`app_get` is not in this list on purpose: it returns the application's **database\ncredentials** beside fields the three calls above already give you. Step 1 is the roster you\nusually already hold from this conversation. Every app-scoped call takes a `server_id` beside\nthe app id — `app_settings_get` and `monitoring_app_summary` included — so an app id on its own\nruns **nothing**, read or write; if you do not know the server, ask for it or for the app's\nname/URL. What differs between a read and a write is confirmation: a read on a known\nserver/app pair can simply run, while a **write** needs name + URL from the roster first — a\nmistyped id that belongs to another app is still a valid id. When you hold no roster: for a\n**name** you are looking up, `app_list` on the server is the one API route (one call; rule 7\nsays what its payload may carry — take the one row, paste none of it); for an **id** you already\nknow, `app_list` is the wrong tool, because it covers every app on the server, and one\n`app_get` for that app — or the app's page in the UI — exposes strictly less. Reach for\n`app_get` otherwise only for a field none of these return, and accept what comes with\nit.)\n\n(SSL / Let's Encrypt **is** an MCP tool as of v1.2 — `security_lets_encrypt_install` / `_renew` / `_auto_renewal` / `_revoke`, via the security toolset. Install/renew are W; revoke is W!.)\n\nFor more detailed patterns, load the relevant workflows.\n\n---\n\n## Versioning and source of truth\n\n- **Tool names match the official articles.** The tool names and categories in this catalog are **verified** against the official Cloudways support articles (setup + tools reference, MCP v1.2). They are the official MCP tool names. The v1.2 additions have not yet been re-enumerated against the live server — see the note in `references/tools-catalog.md`.\n- **The live MCP remains the source of truth if Cloudways changes them.** If a tool name or capability differs from what's documented here, the live list of tools connected in Claude (`mcp__cloudways*__*`) wins — check it and update the catalog accordingly.\n- **Every write tool still goes through the confirmation pattern.** W = single confirmation, W! = double-confirmation (destructive — e.g. `server_delete`, `app_delete`, `app_restore`, `app_cname_delete`), per `references/tools-catalog.md`. This discipline is **more** important now that the official destructive tools are confirmed to exist.\n\nFile v1.5.3:_meta.json\n\n{\n  \"ownerId\": \"kn7afv05r120atbc75whrv1zkx825tt5\",\n  \"slug\": \"cloudways-mcp\",\n  \"version\": \"1.5.3\",\n  \"publishedAt\": 1789340302484\n}\n\nFile v1.5.3:references/installation.md\n\n# Installation — Cloudways MCP Server\n\nThis skill targets the **official Cloudways (Remote) MCP** — a Cloudways-hosted MCP at `https://mcp.cloudways.com/mcp/` that you connect to directly, without self-hosting.\n\n> **Source of truth:** [How to Use Cloudways MCP Server for AI-Based Server Management](https://support.cloudways.com/en/articles/14654372-how-to-use-cloudways-mcp-server-for-ai-based-server-management). The endpoint, headers, and steps below are from that article; if Cloudways changes them, the article wins.\n\n**Prerequisites**\n\n- A valid Cloudways account with API access.\n- A Cloudways **Access Token** (see Step 1).\n- **Node.js v24.14.1+** (only for the Claude Desktop path, which uses the `mcp-remote` bridge). Claude Code connects over native HTTP and does not need it.\n\n---\n\n## Step 1 — Generate an Access Token\n\n1. Log in to [platform.cloudways.com](https://platform.cloudways.com).\n2. Open the **API** section (bottom-left of the platform — the same place the legacy API key lived).\n3. Generate an **Access Token** (Access Token Details → Create Access Token). Note: only the **primary account owner** can create/manage API credentials — team-member accounts have no API Integration section. Give the token a name, an expiration period (1 day → never; shortest that works), and a **role**:\n   - **READ** — look-ups only (status, config, monitoring). Recommended starting point for every new integration, and for monitoring-only connections.\n   - **LIMITED** — only the endpoint groups you select.\n   - **FULL ACCESS** — everything the account can do, including destructive actions. Only for connections that genuinely need to make changes.\n4. Copy the token.\n\n> Treat the token like a password — never commit it or print it in responses. Prefer one token **per integration** (per MCP connection), each with the minimum role it needs, so tokens can be revoked individually.\n\n> **Legacy API key — deprecated.** The old flow (API key + `X-CW-Email`/`X-CW-Api-Key` headers) still works but the API key **stops working on October 15, 2026**. If you have an existing connection using the old headers, regenerate as an Access Token and update the connection before then. Until migrated, a legacy connection retains **unrestricted full-account access** — the RBAC roles apply only to Access Tokens. And note there is still **no per-tool permission control at the MCP layer** beyond the token's role: a FULL ACCESS token can call every tool.\n\n**Required headers** (every request; header names are case-sensitive):\n\n| Header | Value |\n|--------|-------|\n| `X-Access-Token` | your Cloudways Access Token |\n| `X-Mcp-Host` | the client you connect from — official values: `claude-code`, `claude-desktop`, `cursor`, `Devin` (the client formerly called Windsurf — the article's own snippet sends the capitalised `Devin`, and header values are case-sensitive), `vs-code`, `gemini-cli`, `codex`, `codex-cli` |\n\n---\n\n## Step 2 — Connect Claude to the MCP\n\n### Env var (zero-config — devices and cloud sessions)\n\nThe repo commits a `.mcp.json` whose `X-Access-Token` header reads\n`${CLOUDWAYS_ACCESS_TOKEN:-}` — a placeholder, never a real token. Set that variable — in\nyour shell profile on a device, or in the claude.ai cloud environment's environment\nvariables for web/phone sessions — and the connection (named `cloudways-env`) authenticates\nautomatically. While the variable is unset the config still parses (the `:-` default), but\nthe connection can't authenticate and shows as unavailable in `/mcp` — expected until you\nprovide the token. The `cloudways-env` name is deliberate: project scope beats user scope on\nname collisions, so it never shadows a `cloudways` or `cloudways-<client>` connection you\nadd with `claude mcp add -s user`. Never put a real token in `.mcp.json` itself; it is\ntracked in git. The committed `.claude/settings.json` sets `enableAllProjectMcpServers`,\nwhich is why the project-scope server activates without a prompt once the folder is trusted —\nto opt out locally, set `\"enableAllProjectMcpServers\": false` in `.claude/settings.local.json`\n(not committed). Cloud environments with a restricted network policy must allow\n`mcp.cloudways.com`.\n\n### Claude Code (native HTTP — recommended)\n\n```bash\nclaude mcp add --transport http \\\n  --header 'X-Access-Token: ${CLOUDWAYS_ACCESS_TOKEN:-}' \\\n  --header \"X-Mcp-Host: claude-code\" \\\n  -s user \\\n  cloudways https://mcp.cloudways.com/mcp/\n```\n\n**Keep the single quotes, and do not put the token's value here.** See \"Handling the\ntoken safely\" below: quoted this way, the shell never expands it, so neither this\ncommand's arguments nor the stored config holds the secret — Claude Code expands it from\nits own environment when it opens the connection.\n\n`-s user` stores it at the user level so it persists across projects. Verify with `claude mcp list`; remove later with `claude mcp remove cloudways`.\n\n### Claude Desktop (via `mcp-remote` bridge — needs Node v24+)\n\nClaude Desktop does not natively support remote HTTP MCP servers, so it uses the `mcp-remote` Node bridge. Config file:\n\n- **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`\n- **Windows:** `%APPDATA%\\Claude\\claude_desktop_config.json`\n\n**First, install the bridge from the lockfile shipped with this skill.** `bridge/` (beside\nthis `references/` directory) carries a `package.json` and a `package-lock.json` covering **82\nentries — 81 packages, every one with an integrity hash**, plus the root. `npm ci` installs\nexactly what that lockfile names and resolves nothing of its own:\n\n```bash\n# && throughout: a failed delete or copy must not reach npm ci, which would\n# then install from whatever lockfile is still there.\nrm -rf ~/.cloudways-mcp-bridge &&                    # see the note below\n  cp -R <skill-dir>/bridge ~/.cloudways-mcp-bridge &&\n  cd ~/.cloudways-mcp-bridge && npm ci\n```\n\n```powershell\n# Windows. -ErrorAction Stop on every step: PowerShell's default is Continue,\n# which REPORTS a failed delete or copy and then carries on - so a locked file\n# or a permissions error would leave the old tree in place and run npm ci\n# against the stale lockfile, or run it in the caller's own directory. Only a\n# missing directory is expected here, and Test-Path handles that case without\n# silencing the others.\n$bridge = \"$HOME\\.cloudways-mcp-bridge\"\nif (Test-Path -LiteralPath $bridge) {\n  Remove-Item -LiteralPath $bridge -Recurse -Force -ErrorAction Stop\n}\nCopy-Item -LiteralPath <skill-dir>\\bridge -Destination $bridge -Recurse -ErrorAction Stop\nSet-Location -LiteralPath $bridge -ErrorAction Stop\nnpm ci\nif ($LASTEXITCODE -ne 0) { throw \"npm ci failed - the bridge is not installed\" }\n```\n\n> **The delete is load-bearing when you re-run this after a lockfile update.** `cp -R src dst`\n> copies *into* `dst` when `dst` already exists, giving you\n> `~/.cloudways-mcp-bridge/bridge/package-lock.json` while the old lockfile stays where it was\n> — so `npm ci` reinstalls the **stale** graph and reports success. That is the failure this\n> whole section exists to prevent, arriving through the update path. The directory is ours and\n> holds nothing but the copied files and `node_modules`, so removing it costs nothing.\n\n**Then put the token in a header file, not in the config.** Claude Desktop performs no\n`${VAR}` expansion, so a token written into `claude_desktop_config.json` is a literal secret in\na file people screenshot and paste; and a token passed as `--header` is in the process's\nargument list, which every other user on the machine can read from `ps`. `mcp-remote`'s\n`--header-file` avoids both — one `Name: value` per line, `#` starts a comment, whitespace\nafter the colon is trimmed:\n\n**The file goes OUTSIDE the bridge directory** — `~/.cloudways-mcp-bridge` is deleted and\nrecreated by every re-install above, which would take the token with it and leave Claude Desktop\nfailing at startup until you typed it again:\n\n```bash\numask 077                                   # applies to files this shell CREATES\nmkdir -p ~/.config/cloudways-mcp\nTMP=$(mktemp ~/.config/cloudways-mcp/headers.XXXXXX)   # X's at the END; BSD mktemp ignores them elsewhere\nprintf 'X-Mcp-Host: claude-desktop\\n' > \"$TMP\"\nprintf 'X-Access-Token: '               >> \"$TMP\"\nread -rs TOKEN && printf '%s\\n' \"$TOKEN\" >> \"$TMP\" && unset TOKEN\nchmod 600 \"$TMP\" && mv -f \"$TMP\" ~/.config/cloudways-mcp/headers.txt || rm -f \"$TMP\"\n```\n\n**Why a new file and a `mv` rather than `> headers.txt`:** `umask` applies only when a file is\n**created**. Rotating a token by redirecting over an existing `headers.txt` writes the new secret\ninto whatever mode that file already had — group- or world-readable if it was ever created by\nhand, restored from a backup, or copied from another machine. Writing a fresh 600 file and moving\nit into place also means there is no moment when a half-written header file is the live one.\n\n`read -rs` keeps the value off the terminal and out of shell history — paste at the silent\nprompt and press Return. Check it afterwards with `ls -l ~/.config/cloudways-mcp/headers.txt`\n(expect `-rw-------`) and `cut -d: -f1 ~/.config/cloudways-mcp/headers.txt` (prints the header\n**names** only).\n\n```powershell\n# Windows equivalent, in the same order the POSIX recipe achieves with umask: a NEW file,\n# restricted BEFORE the secret goes into it, then moved into place.\n# Read-Host -AsSecureString keeps the value off the console; it still lands on disk in\n# cleartext, which is what the ACL is for.\n$dir  = \"$HOME\\.config\\cloudways-mcp\"\nNew-Item -ItemType Directory -Force -Path $dir -ErrorAction Stop | Out-Null\n$file = Join-Path $dir 'headers.txt'\n$tmp  = Join-Path $dir ('headers.' + [IO.Path]::GetRandomFileName())\n$me   = [Security.Principal.WindowsIdentity]::GetCurrent().Name   # DOMAIN\\user, always resolvable\n$bstr = [IntPtr]::Zero\n\ntry {\n  New-Item -ItemType File -Path $tmp -ErrorAction Stop | Out-Null\n\n  # Break inheritance and grant only the current user - the NTFS equivalent of chmod 600 -\n  # while the file is still EMPTY. icacls is a NATIVE command: PowerShell does not raise on\n  # its exit status, so check it, and fail before any token exists on disk.\n  icacls $tmp /inheritance:r /grant:r \"${me}:(R,W)\" > $null\n  if ($LASTEXITCODE -ne 0) { throw \"icacls failed (exit $LASTEXITCODE); no token was written\" }\n\n  $secure = Read-Host -AsSecureString 'Cloudways Access Token'\n  # SecureStringToBSTR allocates an UNMANAGED cleartext copy. Keep the pointer so the finally\n  # block can zero and free it - dropping it inline would leave the token in this process's\n  # memory for as long as the session lives.\n  $bstr  = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($secure)\n  $plain = [Runtime.InteropServices.Marshal]::PtrToStringBSTR($bstr)\n  Set-Content -LiteralPath $tmp -Encoding ascii -ErrorAction Stop -Value @(\n    'X-Mcp-Host: claude-desktop'\n    \"X-Access-Token: $plain\"\n  )\n  $plain = $null\n\n  # The move carries this file's ACL over the old one, whatever the old one was.\n  Move-Item -LiteralPath $tmp -Destination $file -Force -ErrorAction Stop\n}\nfinally {\n  # Zero and free the unmanaged copy on every path, including a throw between the two.\n  if ($bstr -ne [IntPtr]::Zero) { [Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr) }\n  # Anything that threw above leaves no half-written token behind.\n  if (Test-Path -LiteralPath $tmp) { Remove-Item -LiteralPath $tmp -Force -ErrorAction SilentlyContinue }\n}\n```\n\n(The managed `$plain` string cannot be zeroed — .NET strings are immutable, so setting the\nvariable to `$null` only drops the reference and leaves the value for the garbage collector.\nRun this in a shell you then close, not in a long-lived session you keep around.)\n\n(Written from Microsoft's documented behaviour for `icacls` and `Read-Host`; like the rest of\nthe Windows path here, it has not been exercised on a Windows machine.)\n\nThen point Claude Desktop at the installed executable and that file:\n\n```json\n{\n  \"mcpServers\": {\n    \"cloudways\": {\n      \"command\": \"/Users/<you>/.cloudways-mcp-bridge/node_modules/.bin/mcp-remote\",\n      \"args\": [\n        \"https://mcp.cloudways.com/mcp/\",\n        \"--header-file\", \"/Users/<you>/.config/cloudways-mcp/headers.txt\"\n      ]\n    }\n  }\n}\n```\n\n`/Users/<you>` above is a placeholder — the JSON needs **absolute** paths, and the commands\nabove wrote the files under `$HOME`. Print the two real values rather than typing them:\n\n```bash\nprintf '%s\\n' \"$HOME/.cloudways-mcp-bridge/node_modules/.bin/mcp-remote\" \\\n               \"$HOME/.config/cloudways-mcp/headers.txt\"\n```\n\nOn Windows both paths change — the launcher is the `.cmd` shim, and the header file is under\nthe profile directory — and `$HOME` is **not** reliably `C:\\Users\\<you>`: a relocated, network\nor non-C-drive profile puts both files somewhere else, and a config with a hard-coded C-drive\npath then fails to find either. Let PowerShell build the block from the same `$HOME` the setup\nused, which also escapes the backslashes for you:\n\n```powershell\n@{\n  command = \"$HOME\\.cloudways-mcp-bridge\\node_modules\\.bin\\mcp-remote.cmd\"\n  args    = @(\n    'https://mcp.cloudways.com/mcp/',\n    '--header-file',\n    \"$HOME\\.config\\cloudways-mcp\\headers.txt\"\n  )\n} | ConvertTo-Json\n```\n\nPaste the result as the `\"cloudways\"` entry under `\"mcpServers\"`. No hard-coded\n`C:\\Users\\<you>` appears here on purpose: on a relocated, network or non-C-drive profile that\npath is simply wrong, and an example carrying it is the thing people copy.\n\n\nA header file it cannot read is a **fatal** error, not a warning, so a wrong path fails at\nstartup instead of connecting unauthenticated. Verified against `mcp-remote@0.14.0` installed\nfrom the shipped lockfile: it logs `Loaded 2 header(s)` and `Using custom headers:\nX-Mcp-Host, X-Access-Token` — the names, never the value.\n\n(The `.cmd` shim is what Windows needs — the extensionless file beside it is POSIX-only. That\npath is npm's documented layout and has not been exercised on a Windows machine.)\n\n> **There is deliberately no `npx` alternative here any more.** Earlier versions offered one as\n> a collapsed fallback. `npx mcp-remote@0.14.0` pins the named package and **nothing underneath\n> it**: the ~80 transitive dependencies are resolved fresh whenever the npx cache is empty, so a\n> version published inside one of their ranges between now and your next launch executes in the\n> process holding your Access Token. Anyone who can run `npx` can run the `npm ci` above — it is\n> one command more against a lockfile that ships with this skill — so the fallback bought\n> convenience that was never worth its exposure. If `npm ci` fails, fix that (registry, proxy,\n> Node version) rather than reaching for a path that resolves at launch.\n\n> **Pin `mcp-remote`.** A launcher that resolves at run time executes whatever the registry\n> serves at that moment, into a process that reads your Access Token from `headers.txt` — so a\n> compromised release, maintainer account or transitive dependency would receive it. The\n> version is pinned deliberately, in `bridge/package.json` and the lockfile beside it; bump it\n> after reading the upstream release notes, and record the new digest here in the same commit.\n>\n> ```\n> mcp-remote@0.14.0\n> sha512-QBYGz02kc2AhhM6RNDzNyoA/FlzwJCNYPFF+o3opSvwfo5lnp8mcVIj/Zqw92dPuoafyLBKQZkpaEJdzlB/png==\n> ```\n>\n> To check what the registry served you, compute the digest from the bytes — **do not compare\n> against the line `npm pack` prints**, which elides the middle\n> (`sha512-QBYGz02kc2Ahh[...]kpaEJdzlB/png==`), so a comparison against it only ever checks a\n> prefix and a suffix:\n>\n> ```bash\n> npm pack mcp-remote@0.14.0\n> printf 'sha512-%s\\n' \"$(openssl dgst -sha512 -binary mcp-remote-0.14.0.tgz | openssl base64 -A)\"\n> ```\n>\n> (`npm pack --json` also emits the full value, if you would rather read npm's own figure than\n> compute one.) A digest **recorded here, out of band** is what makes this worth running: npm\n> forbids republishing a version with different content, so it catches a registry that later\n> serves different bytes for 0.14.0.\n>\n> **What the pin does NOT cover: everything underneath it.** `mcp-remote@0.14.0` fixes one\n> package, and the digest above covers one tarball; its ~80 dependencies would be resolved\n> fresh by any launcher that resolves at run time, so a newly published version inside one of\n> their ranges would run with your Access Token even though the digest still matches. That is\n> what the shipped lockfile and `npm ci` exist to prevent, and why no `npx` recipe remains.\n>\n> **One dependency is pinned past its parent's range.** `express@4.22.2` — the newest 4.x, and\n> what `mcp-remote` asks for — requires `qs@~6.15.1`, and every `qs` below 6.16.0 carries two\n> advisories (an array-limit bypass and a DoS through an attacker-controlled `isBuffer`). There\n> is no express release that widens the range, so `bridge/package.json` carries\n> `\"overrides\": { \"qs\": \"6.16.0\" }`. This is not a guess about compatibility: `body-parser`, in\n> this same tree and from the same maintainers, already requires `~6.16.0`, so the override\n> collapses two copies of `qs` into the patched one. Regenerating the lockfile moved that single\n> version and nothing else (82 entries → 81, `npm audit`: **0 vulnerabilities**), and `npm ci`\n> from it still produces a working `node_modules/.bin/mcp-remote`.\n>\n> **`npm ci` against the shipped lockfile has to be the first command that touches the\n> registry.** Generating your own lockfile with `npm install` resolves the graph at that moment\n> and then freezes whatever it found — so a first install during a compromise locks the bad\n> version in, and the `npm ci` after it faithfully reproduces it. The point of shipping\n> `bridge/package-lock.json` is that the resolution happened once, here, at a known date.\n>\n> To be exact about what that buys, because \"vetted\" does a lot of work: the graph is **pinned\n> and reproducible**, with integrity hashes for every package. It is not a claim that 81\n> packages' source has been read. Bumping `mcp-remote` means regenerating the lockfile in the\n> same commit.\n\n> **The secret is now in `~/.config/cloudways-mcp/headers.txt`, and it is still a secret at\n> rest.** Keep it at mode 600, back it up nowhere, and give the token the **smallest role that\n> works** (READ for monitoring-only). It lives outside `~/.cloudways-mcp-bridge` on purpose:\n> that directory is deleted and recreated on every re-install, so a token kept inside it would\n> vanish on the next lockfile bump and take the connection down with it. `claude_desktop_config.json` no longer contains it — that file can be\n> pasted into an issue or a screen share without leaking anything — but the header file can't.\n> The `${VAR}` expansion used in the Claude Code section above is Claude Code's own; Claude\n> Desktop performs none, which is why the file exists. (`mcp-remote` does expand `${VAR}` inside\n> a header **value** from its own environment, if you have somewhere better than a file to keep\n> it — a launcher that exports it from a keychain, say.)\n\n> **Header-file format:** `Name: value`, one per line; `#` starts a comment; whitespace after\n> the colon is trimmed, and CRLF line endings are handled. Header names are case-sensitive.\n> After saving either file, **fully quit** Claude Desktop (Cmd-Q / tray → Quit — closing the\n> window is not enough) and reopen.\n\n> Keep real credentials out of version control. For Claude Code, put the token in the `CLOUDWAYS_ACCESS_TOKEN` env var (the committed `.mcp.json` reads it, and so does the user-scope form above) — never edit a real token into `.mcp.json`, which is a **tracked** file. See `.mcp.json.example` in the repo root for the per-account shape. Header names are case-sensitive.\n\n### Other clients (Cursor, Devin, VS Code Copilot, Gemini CLI, Codex)\n\nSame endpoint and headers everywhere; only the config-file shape and the `X-Mcp-Host` value differ per client. The [official article](https://support.cloudways.com/en/articles/14654372-how-to-use-cloudways-mcp-server-for-ai-based-server-management) has the full snippet for each — but three of them use a **non-obvious key**, and the article warns that a wrong variant **fails silently**:\n\n| Client | Config file | URL key | Headers key | `X-Mcp-Host` |\n|--------|-------------|---------|-------------|--------------|\n| Cursor | `~/.cursor/mcp.json` | `url` | `headers` | `cursor` |\n| Devin (ex-Windsurf) | `~/.codeium/devin/mcp_config.json` | **`serverUrl`** | `headers` | `Devin` |\n| VS Code (Copilot) | `~/Library/Application Support/Code/User/mcp.json` (macOS) / `%APPDATA%\\Code\\User\\mcp.json` | `url`, and the file uses **`\"servers\"` (not `\"mcpServers\"`)** plus a required **`\"type\": \"http\"`** | `headers` | `vs-code` |\n| Gemini CLI | `~/.gemini/settings.json` | **`httpUrl`** — `url` there attempts an SSE connection instead and fails | `headers` | `gemini-cli` |\n| Codex / Codex CLI | `~/.codex/config.toml` | `url` | **`[mcp_servers.cloudways.http_headers]`** (a separate TOML table) | `codex` / `codex-cli` |\n\n**Cursor connects over native HTTP** — no Node and no `mcp-remote`; the bridge is only a fallback for proxy/connection problems (and then it needs Node v24+, as Claude Desktop does). The article also offers a one-click \"Install in Cursor\" button that prompts for the credentials.\n\n---\n\n## Handling the token safely\n\nAn Access Token carries whatever role it was issued with — up to FULL ACCESS, which is\neverything the Cloudways account can do, including destructive server and app actions and\nbilling. Keep the value in **one** place, the environment Claude Code starts with, and put a\n*placeholder* everywhere else.\n\n**Never pass the value to `claude mcp add`.** Written as `\"X-Access-Token: $TOKEN\"`, the\nshell expands it before launching anything, so the live token is in that process's argument\nlist — readable through `ps` or `/proc/<pid>/cmdline` by any other user on the machine while\nthe command runs — and `claude mcp add` then stores the **resolved value** in\n`~/.claude.json` in plaintext, where it stays until you remove the connection. Single-quoted\nas `'X-Access-Token: ${CLOUDWAYS_ACCESS_TOKEN:-}'`, neither the argument list nor the stored\nconfig carries the secret. Claude Code expands `${VAR}` in headers for local- and\nuser-scoped entries in `~/.claude.json`, not only in a project `.mcp.json`; an unset\nreference produces a `Missing environment variables` warning in `claude mcp list`.\n\n**Where the value lives.** It must be in the environment Claude Code itself starts with:\n\n```bash\nprintf 'Cloudways Access Token: '\nread -rs CLOUDWAYS_ACCESS_TOKEN; echo\nexport CLOUDWAYS_ACCESS_TOKEN\n```\n\n`read -rs` does not echo the token and never writes it to `~/.zsh_history` /\n`~/.bash_history`, but it lasts only for that shell — start Claude Code **from it**. For a\npersistent setup, put the export in your shell profile at mode 600, or better, read it from\na keychain rather than storing it inline:\n\n```bash\nexport CLOUDWAYS_ACCESS_TOKEN=\"$(security find-generic-password -s cloudways-api -w)\"   # macOS\n```\n\nIn claude.ai cloud sessions the equivalent is the environment's own environment variables.\n\n**If a token may have been exposed** — pasted into a chat, committed, left in a history file\nor an old `~/.claude.json` entry — **revoke it at platform.cloudways.com → API** and issue a\nnew one with the smallest role that works. To check whether an old connection left a literal\nbehind, **parse** the config rather than grepping it (JSON may put a value on the line after\nits key), and report names rather than values:\n\n```bash\nnode -e '\nconst fs = require(\"fs\"), p = require(\"os\").homedir() + \"/.claude.json\";\nlet c; try { c = JSON.parse(fs.readFileSync(p, \"utf8\")); }\ncatch (e) { console.error(\"could not read \" + p); process.exit(1); }\nconst all = [...Object.entries(c.mcpServers || {}),\n             ...Object.values(c.projects || {}).flatMap(x => Object.entries(x.mcpServers || {}))];\nconst literal = v => { const re = /\\$\\{[A-Za-z_][A-Za-z0-9_]*(?::-([^}]*))?\\}/g;\n                       let n = v.replace(re, \"\").length;\n                       for (const m of v.matchAll(re)) n += (m[1] || \"\").length;\n                       return n; };\nconst hits = all.filter(([, s]) => { const v = (s.headers || {})[\"X-Access-Token\"];\n                                     return typeof v === \"string\" && literal(v) > 0; })\n                .map(([n]) => n);\nconsole.log(hits.length\n  ? \"Literal token stored in: \" + hits.join(\", \") + \" — revoke it and re-add with the placeholder form.\"\n  : \"No literal token in ~/.claude.json.\");\n'\n```\n\nIt counts literal material inside `:-` defaults as well as outside the placeholders — a\ntoken hides just as well in `${CLOUDWAYS_ACCESS_TOKEN:-cw_live}`, which is still plaintext\nin the file and is still what gets sent whenever the variable is unset.\n\n---\n\n## Multi-account configuration — multiple Cloudways accounts\n\nEach Cloudways account is a **separate** MCP connection with its own Access Token, so it appears under its own prefix (`mcp__cloudways-clientA__*`). Give each a descriptive, client-based name — that name becomes the tool prefix. Same endpoint for all; only the `X-Access-Token` differs.\n\nGive each account its **own variable name** and reference it as a placeholder, so no\ntoken reaches a command line or a config file:\n\n```bash\n# one `claude mcp add` per account, each reading its own variable:\nclaude mcp add --transport http \\\n  --header 'X-Access-Token: ${CLOUDWAYS_TOKEN_CLIENTA:-}' \\\n  --header \"X-Mcp-Host: claude-code\" \\\n  -s user cloudways-clientA https://mcp.cloudways.com/mcp/\n\nclaude mcp add --transport http \\\n  --header 'X-Access-Token: ${CLOUDWAYS_TOKEN_CLIENTB:-}' \\\n  --header \"X-Mcp-Host: claude-code\" \\\n  -s user cloudways-clientB https://mcp.cloudways.com/mcp/\n```\n\nExport `CLOUDWAYS_TOKEN_CLIENTA` / `CLOUDWAYS_TOKEN_CLIENTB` in the environment Claude\nCode starts with. The header sent is always `X-Access-Token`; only the source differs per\nconnection, which is what keeps the accounts separated.\n\n(See `.mcp.json.example` for the JSON form across multiple accounts.)\n\n### Safety rules for multi-account (mandatory)\n\n- **Consistent names:** uniform `cloudways-<client>` prefix so Claude (and you) immediately recognize which account each tool belongs to.\n- **Separate secrets:** don't keep all the tokens in one place. Prefer a secrets manager (a vault project per client) over plain config files.\n- **Don't mix:** never reuse one token across accounts, and never take a server/app ID from one account against another's connection.\n- **Scope by role:** generate each connection's token with the **minimum role** it needs — READ for monitoring/audit connections, LIMITED for specific workflows, FULL ACCESS only where changes are genuinely required. Set an expiration and rotate; revoke a client's token the moment the engagement ends.\n- **Runtime:** account identification, cross-account search, and per-account write-confirmations are documented in `SKILL.md` → **Multi-account**.\n\n---\n\n## Step 3 — Verify the connection\n\nIn Claude, ask: **\"Show me all my Cloudways servers\"** → calls `server_list` and returns your servers (name, status, provider, region, IP). That round-trip confirms the endpoint + credentials.\n\n(There is no `ping` / `customer_info` tool on the official MCP — `server_list` is the liveness + auth check. Identify which account you're on by the connection prefix.)\n\n| Symptom | Meaning | Fix |\n|---------|---------|-----|\n| Connection failed / red indicator | wrong URL | endpoint must be exactly `https://mcp.cloudways.com/mcp/` (trailing slash) |\n| `401 Unauthorized` | bad credentials | re-check the Access Token (case-sensitive header); it may be expired or revoked — regenerate in the platform |\n| Write tool fails but reads work | token role too narrow | the connection uses a READ (or too-narrow LIMITED) token; use a token whose role covers the operation |\n| No `mcp__cloudways*__*` tools | not connected / stale cache | restart the client (see \"Tools not appearing\" below) |\n| Timeout | transient network | retry after a moment |\n| `mcp-remote not found` (Desktop) | bridge not installed, or Node missing | install Node.js v24+, then re-run the `npm ci` install above; the config points at `~/.cloudways-mcp-bridge/node_modules/.bin/mcp-remote`, not at a PATH lookup |\n\nTo test credentials directly against the public Cloudways API, independent of the MCP layer (useful to isolate \"bad credentials\" from \"MCP connection problem\"):\n\n```bash\n# Access Token (current):\ncurl -H \"Authorization: Bearer YOUR_ACCESS_TOKEN\" \"https://api.cloudways.com/api/v2/server\"\n\n# Legacy API key (works until the EOL, 2026-10-15):\ncurl -X POST \"https://api.cloudways.com/api/v1/oauth/access_token\" \\\n  -H \"Content-Type: application/x-www-form-urlencoded\" \\\n  -d \"email=YOUR_EMAIL&api_key=YOUR_API_KEY\"\n```\n\nAPI reference: <https://developers.cloudways.com/> — note the tools article's `oauth_access_token_generate` tool does **not** exist on the live MCP (verified 2026-07-20); mint direct-API tokens through the Cloudways platform instead.\n\n---\n\n## Tools not appearing / after an update\n\nMCP clients **cache the tool list** on first connect. If new tools don't show up, or the agent says a tool doesn't exist:\n\n- **Quickest:** in the client's MCP server settings, toggle `cloudways` off then on.\n- **If no toggle:** **fully quit** the client (Cmd-Q on macOS / File → Exit / tray → Quit — closing the window is not enough) and reopen.\n\nThen re-test with \"Show me all my Cloudways projects\" (`project_list`).\n\n> Note the intentional design: even when correctly connected, the client sees only **65 tools** (62 direct + 3 meta-tools). The 62 direct tools are **members of the toolsets**, not a separate tier — so of the 244 total (241 toolset members + 3 meta-tools, live-verified 2026-07-20) the hidden remainder is **179**, discovered on demand via `list_available_toolsets` / `get_toolset_tools` / `execute_tool`. Their absence from the visible list is **not** a caching problem.\n\n---\n\n## Notes\n\n- This skill does **not** cover self-hosting an MCP server — the official hosted MCP is the supported path.\n- Tool names throughout this skill match the official articles' catalog (see `tools-catalog.md`). The **live** `mcp__cloudways*__*` tools remain the source of truth if Cloudways adds or renames any.\n\nFile v1.5.3:references/tools-catalog.md\n\n# Tools Catalog — Cloudways MCP\n\nThe official tool catalog for the **Cloudways (Remote) MCP** (`https://mcp.cloudways.com/mcp/`), taken from the official support articles: [Cloudways MCP Server Tools](https://support.cloudways.com/en/articles/15798823-cloudways-mcp-server-tools) (the dedicated tool reference) and [How to Use Cloudways MCP Server](https://support.cloudways.com/en/articles/14654372-how-to-use-cloudways-mcp-server-for-ai-based-server-management) (setup). Tools appear in Claude as `mcp__cloudways__<tool>` (or `mcp__cloudways-<client>__<tool>` per account).\n\n**MCP v1.2 scale:** **244 tools** — 241 spread across 22 toolsets plus the 3 meta-tools, live-verified 2026-07-20 (see the toolset table below). This matches the [v1.2 announcement](https://www.cloudways.com/blog/cloudways-mcp-v1-2-112-new-tools-role-based-access-tokens-and-full-cloudways-api-coverage/) exactly (112 added in v1.2, covering essentially the full Cloudways API). Your client initially sees only **65 tools** (62 high-frequency direct tools + the 3 meta-tools below). Those 62 are **members of the toolsets**, not a separate tier, so the hidden remainder is **179** (241 − 62) — discovered and invoked on demand through the meta-tools.\n\n> **The official tools article over-lists.** It documents endpoint-style aliases for the Security and Service categories (`security_dns_create`, `service_state_update`, …) plus whole categories — Bot Protection, Client Billing, CloudwaysCDN legacy, a \"Lists API\", `oauth_access_token_generate` — that **have no live counterpart**. Live re-enumeration on 2026-07-20 found 64 such phantom identifiers and **zero** real alias mismatches: every name in the tables below matches the live `tool_name` exactly. They have been removed from this catalog; if you see them in Cloudways' docs, don't build automation on them.\n\nFlags: **R** = read-only · **W** = write (requires confirmation) · **W!** = destructive / irreversible (requires double confirmation).\n\n> The **live server is the source of truth.** Every section of this file — including the \"New in MCP v1.2\" half — was reconciled against the live MCP on 2026-07-20 via `list_available_toolsets` + `get_toolset_tools` on a connected account: all 22 toolsets enumerated, every tool name verified byte-for-byte, phantom entries removed. You do **not** need to memorize tool names to use the MCP (it resolves natural language), but knowing them sharpens prompts and lets you confirm an action maps to the tool you expect.\n>\n> **Most tools are grouped into on-demand toolsets.** Only a subset appears in your default tool list; the rest live inside toolsets (`apps`, `servers`, `git`, `ssh_keys`, `projects`, `staging_management`, `dns_made_easy`, `cloudflare`, and the v1.2 additions) and are invoked through the `execute_tool` proxy. A tool being absent from the default list does **not** mean it is absent from the MCP — `git_*`, `project_*`, `ssh_key_*`, `app_restore_rollback`, `app_db_password_update`, `server_local_backup_delete`, and `staging_app_clone*` are all live via their toolsets (live-verified). Note that `staging_app_clone*` lives in the `apps` toolset, not `staging_management`.\n>\n> **Toolset descriptions overstate their own contents.** `list_available_toolsets` is reliable for *routing* but not as a capability contract — several descriptions advertise operations that have no tool: `staging_management` (delete latest staging backup — actually `app_local_backup_delete` in `apps`), `security` (install custom SSL — only the *remove* counterpart exists), `cloudways_bot` (list/paginate alerts — only the mark-read tools exist), `copilot` (severity summary, alert-by-id — only `copilot_insights_list`), `agency_os` (disconnect agency, subscribe client to plan, attach/cancel client service, client+service summary, mark invoice paid, reminders — none of the 7 exist). Confirm with `get_toolset_tools` before promising a capability.\n>\n> There is no `ping` / `customer_info` / `rate_limit_status` tool — verify connectivity with `server_list` and identify the account by the connection prefix.\n>\n> **Last verified:** 2026-07-20 against the **live MCP** (all 22 toolsets enumerated on a connected account); content sourced from the official v1.2 support articles 2026-07-19. **Note:** three categories are split across the two halves of this file — Cloudflare CDN, Add-on Management, and Copilot each have a pre-v1.2 table above and an \"expanded in v1.2\" table below; check both.\n\n## Live toolset counts (2026-07-20)\n\n| Toolset | Tools | Toolset | Tools |\n|---------|-------|---------|-------|\n| `apps` | 52 | `addons` | 10 |\n| `servers` | 28 | `dns_made_easy` | 9 |\n| `security_suite` | 26 | `monitoring` | 9 |\n| `agency_os` | 23 | `copilot` | 9 |\n| `cloudflare` | 15 | `cloudways_bot` | 7 |\n| `security` | 13 | `services` | 7 |\n| `git` | 6 | `projects` | 4 |\n| `staging_management` | 5 | `team_member` | 4 |\n| `supervisord` | 5 | `ssh_keys` | 3 |\n| `app_vulnerability` | 2 | `transfer_server` | 3 |\n| `operation` | 1 | `safe_update` | **0** |\n\n**241 toolset tools + 3 meta-tools = 244**, of which 62 toolset members are surfaced directly and the other 179 are on-demand only. `safe_update` is declared by the server but **empty in the current build** — its description states it is \"declared but not yet populated\", and `execute_tool` returns *\"tool not found in toolset\"* for anything in it. SafeUpdate (managed WordPress core/plugin/theme updates with visual-regression checks and auto-rollback) is therefore **UI-only** for now; re-check this toolset after future MCP releases.\n\n---\n\n## Server Management\n\n| Tool | Flag | What it does |\n|------|------|--------------|\n| `server_list` | R | List all servers (status, provider, region, IP, app count). |\n| `server_get` | R | Detailed info on one server (hosted apps + configuration). |\n| `server_create` | W | Create a server on DigitalOcean/AWS/GCE/Vultr/Linode with an initial app. |\n| `server_start` | W | Start a stopped server. |\n| `server_stop` | W | Stop a running server. |\n| `server_restart` | W | Restart a server to apply configuration changes. |\n| `server_delete` | W! | Permanently delete a server and all its data. |\n| `server_backup` | W | Create a full backup of a server. |\n| `server_scale` | W! | Up/downgrade server size (CPU/RAM) — brief downtime. |\n| `server_scale_volume` | W | Change data-volume size (Amazon & GCE only). |\n| `server_clone` | W | Clone a server (apps, optionally settings/domains/cron/SSL). |\n| `server_update_label` | W | Rename a server. |\n| `server_disk_usage_fetch` | W | Initiate a disk-usage fetch operation. |\n| `server_snapshot_frequency_update` | W | Configure snapshot frequency (AWS/GCE); empty disables. |\n| `server_backup_settings_update` | W | Update backup settings (frequency, retention, off-server/local). |\n| `server_local_backup_delete` | W! | Delete local backups stored on the server. |\n| `server_package_update` | W! | Install/uninstall/upgrade packages (PHP, MySQL/MariaDB…). Tagged W! because the **uninstall** action removes package data (destructive — double-confirm); install/upgrade are ordinary writes. Discover installable versions via Cloudways' raw `/packages` API endpoint — there is **no** `packages_list` MCP tool (the article's \"Lists API\" does not exist on the live server). |\n| `server_maintenance_window_get` | R | Retrieve maintenance-window settings. |\n| `server_maintenance_window_update` | W | Set maintenance window (days + time slot). |\n| `server_master_username_update` | W! | Update master username (SSH/SFTP) — the old username stops working immediately. |\n| `server_master_password_update` | W! | Update master password (SSH/SFTP). |\n| `server_storage_attach` | W | Attach Block Storage volume (DigitalOcean only). |\n| `server_storage_scale` | W | Resize Block Storage volume (DigitalOcean only). |\n| `operation_status` | R | Check the status of an async operation. |\n\n## Application Management\n\n| Tool | Flag | What it does |\n|------|------|--------------|\n| `app_list` | R | List all applications on a server. |\n| `app_get` | R | Detailed info on one application. |\n| `app_create` | W | Create an app (WordPress, Laravel, Magento…). |\n| `app_delete` | W! | Permanently delete an app and its data. |\n| `app_clone` | W | Clone an app on the same server. |\n| `app_clone_to_server` | W | Clone an app to a different server. |\n| `staging_app_clone` | W | Clone a staging app on the same server. |\n| `staging_app_clone_to_server` | W | Clone a staging app to a different server. |\n| `app_backup` | W | Create an application backup. |\n| `app_backup_status_get` | R | Status of an in-progress app backup. |\n| `app_restore` | W! | Restore an app to a previous backup (local/remote). |\n| `app_restore_rollback` | W! | Roll back the last restore (return to pre-restore state). |\n| `app_local_backup_delete` | W! | Delete the local backup made during a restore. |\n| `app_credentials` | R | Get SSH/SFTP credentials for an app. |\n| `app_credentials_create` | W | Create an additional SSH/SFTP credential. |\n| `app_credentials_update` | W! | Rename / change password of a credential — the old username/password stop working immediately. |\n| `app_credentials_delete` | W! | Delete an access credential. |\n| `app_purge_cache` | W | Clear all cache layers (app, Varnish, object). |\n| `app_update_label` | W | Rename an application. |\n| `app_vulnerabilities_list` | R | List WordPress vulnerabilities with severity scores. |\n| `app_vulnerabilities_refresh` | W | Trigger a new vulnerability scan. |\n| `app_cname_update` | W | Update the app's primary domain (CNAME). |\n| `app_cname_delete` | W! | Delete the CNAME (revert to default Cloudways URL) — removes a customer-facing domain; can break production. |\n| `app_aliases_update` | W | Update secondary domains (aliases). |\n| `app_cron_list_get` | R | List scheduled cron jobs for an app. |\n| `app_cron_list_update` | W | Update the app's cron jobs. **Deprecated on the live MCP** — the underlying `/app/manageCronList` endpoint returns HTTP 500; use `app_cron_optimizer_update` for WP-Cron, or edit the crontab over SSH. |\n| `app_cron_optimizer_update` | W | Toggle Cron Optimizer (system cron vs WP-Cron). |\n| `app_db_password_update` | W! | Update the app's MySQL/MariaDB password. |\n| `app_symlink_update` | W | Change where `public_html` points. |\n| `app_webroot_update` | W | Change the webroot (e.g. Laravel `/public_html/public`). |\n| `app_cors_headers_update` | W | Update CORS headers. |\n| `app_webp_redirection_update` | W | Toggle WebP redirection. |\n| `app_enforce_https_update` | W | Toggle HTTPS redirection (force HTTP→HTTPS). |\n| `app_reset_permissions` | W | Reset file/folder ownership and modes. |\n| `app_fpm_settings_get` | R | Retrieve PHP-FPM configuration. |\n| `app_fpm_settings_update` | W | Configure PHP-FPM (workers, children, memory…). |\n| `app_varnish_settings_get` | R | Retrieve Varnish config (TTL, paths, exclusions). |\n| `app_varnish_settings_update` | W | Update Varnish config. |\n| `app_ssh_access_get` | R | Current SSH access status for an app. |\n| `app_ssh_access_update` | W | Enable/disable SSH access. |\n| `app_access_state_get` | R | Access state (public vs maintenance mode). |\n| `app_access_state_update` | W | Set public / maintenance mode. |\n| `app_settings_get` | R | Retrieve app setting flags (XML-RPC, GEO-IP, device…). |\n| `app_geo_ip_header_update` | W | Toggle the GEO-IP header. |\n| `app_xmlrpc_update` | W | Toggle WordPress XML-RPC (off recommended). |\n| `app_device_detection_update` | W | Toggle Device Detection (desktop/mobile cache split). |\n| `app_ignore_query_string_update` | W | Toggle the Ignore-Query-String cache rule. |\n| `app_php_direct_execution_update` | W | Toggle direct PHP execution from uploads (WP security). |\n| `app_admin_password_update` | W! | Update the installed app's admin password (e.g. WP admin). |\n| `app_password_protection_get` | R | Current HTTP Basic Auth (htpasswd) config. |\n| `app_password_protection_update` | W | Enable/update HTTP Basic Auth protection. |\n| `app_wp_multisite_update` | W | Enable/update WordPress Multisite. |\n| `app_stack_update` | W | Switch stack (v1 Apache hybrid / v2 NGINX lightning). |\n| `app_object_cache_update` | W | Toggle WordPress Object Cache (OCP). |\n\n## Service Management\n\n| Tool | Flag | What it does |\n|------|------|--------------|\n| `service_status` | R | Status of all services on a server. |\n| `service_start` | W | Start a stopped service. |\n| `service_stop` | W | Stop a running service. |\n| `service_restart` | W | Restart a service (nginx, mysql, php-fpm…). |\n| `varnish_manage` | W | Enable/disable/purge Varnish at server level. |\n| `varnish_app_manage` | W | Enable/disable Varnish per application. |\n| `varnish_app_status` | R | Current Varnish status for an application. |\n\n> All 7 are live-verified. The tools article's endpoint-style aliases (`service_state_update`, `service_varnish_manage`, `service_app_varnish_get`) **do not exist** — use the names above.\n\n## Add-on Management\n\n| Tool | Flag | What it does |\n|------|------|--------------|\n| `addon_list` | R | List available add-ons (status + pricing). |\n| `addon_activate` | W | Activate an add-on on the account. |\n| `addon_deactivate` | W | Deactivate an account add-on. |\n| `addon_activate_on_server` | W | Enable an add-on on a server. |\n| `addon_deactivate_on_server` | W | Remove an add-on from a server. |\n\n## Cloudflare CDN\n\n| Tool | Flag | What it does |\n|------|------|--------------|\n| `cloudflare_add_domain` | W | Add a domain to Cloudflare CDN for an app. |\n| `cloudflare_get_details` | R | CDN status, configuration, usage. |\n| `cloudflare_get_txt_records` | R | DNS TXT records for domain verification. |\n\n## DNS Made Easy\n\n| Tool | Flag | What it does |\n|------|------|--------------|\n| `dns_made_easy_list_domains` | R | List managed domains. |\n| `dns_made_easy_add_domains` | W | Add domains for DNS management. |\n| `dns_made_easy_delete_domains` | W! | Delete domains from DNS Made Easy. |\n| `dns_made_easy_get_domain_status` | R | Check domain status. |\n| `dns_made_easy_list_records` | R | List DNS records for a domain. |\n| `dns_made_easy_add_records` | W | Add DNS records. |\n| `dns_made_easy_update_record` | W | Update a DNS record. |\n| `dns_made_easy_delete_records` | W! | Delete DNS records. |\n| `dns_made_easy_get_domain_usage` | R | DNS usage statistics. |\n\n## Server Settings\n\n| Tool | Flag | What it does |\n|------|------|--------------|\n| `server_settings_get` | R | View server + PHP configuration. |\n| `server_settings_update` | W | Update PHP and MySQL settings. |\n| `server_disk_cleanup_settings_get` | R | View disk-cleanup settings. |\n| `server_disk_cleanup_settings_update` | W | Configure automated cleanup. |\n| `server_disk_cleanup_execute` | W | Run disk cleanup manually. |\n\n## Monitoring & Analytics\n\n| Tool | Flag | What it does |\n|------|------|--------------|\n| `monitoring_server_summary` | R | Server bandwidth and disk usage. |\n| `monitoring_server_usage` | R | Refresh server usage statistics. |\n| `monitoring_server_graph` | R | Monitoring graphs (CPU, memory…). |\n| `monitoring_app_summary` | R | Application-level usage: `type: bw` for bandwidth, `type: db` for **disk** size (not the database — the live tool's own wording). |\n| `analytics_app_traffic` | R | Traffic patterns and sources. |\n| `analytics_app_traffic_details` | R | Detailed traffic data for custom ranges. |\n| `analytics_app_php` | R | PHP performance / slow pages. |\n| `analytics_app_mysql` | R | MySQL queries / performance. |\n| `analytics_app_cron` | R | Cron job execution analytics. |\n\n## Copilot Insights\n\n| Tool | Flag | What it does |\n|------|------|--------------|\n| `copilot_insights_list` | R | Insights, alerts, and recommendations for your infrastructure. |\n\n## Projects\n\n| Tool | Flag | What it does |\n|------|------|--------------|\n| `project_list` | R | List Projects (IDs, names, grouped servers/apps). |\n| `project_create` | W | Create a Project with initial app members. |\n| `project_update` | W | Rename / change a Project's members. |\n| `project_delete` | W! | Delete a Project (grouping only; resources untouched). |\n\n## Git Deployment\n\n| Tool | Flag | What it does |\n|------|------|--------------|\n| `git_generate_key` | W! | Generate a fresh SSH deploy key for an app — overwrites the previous deploy key, which stops working until the new public half is re-registered on the Git host. |\n| `git_key_get` | R | Retrieve the public deploy key (paste into GitHub/GitLab/Bitbucket). |\n| `git_branches_get` | R | Refresh/list branches in the linked repo. |\n| `git_clone` | W | Clone a repo/branch into the web root (initial link). |\n| `git_pull` | W | Pull latest commits and deploy. |\n| `git_history_get` | R | Recent deployment history (commit hashes, status). |\n\n## SSH Key Management\n\n| Tool | Flag | What it does |\n|------|------|--------------|\n| `ssh_key_create` | W | Add an SSH public key to a server/app/user. |\n| `ssh_key_update` | W | Rename an SSH key (label only). |\n| `ssh_key_delete` | W! | Revoke an SSH key by `ssh_key_id`. |\n\n## Toolset meta-tools\n\nThe server also exposes discovery/proxy tools for navigating its toolsets. Usually you call the named tools above directly; these are for when the client groups tools into on-demand toolsets.\n\n| Tool | Flag | What it does |\n|------|------|--------------|\n| `list_available_toolsets` | R | List the server's toolsets (categories of tools). |\n| `get_toolset_tools` | R | List the tools inside a given toolset. |\n| `execute_tool` | varies | Invoke a tool by name through the proxy (inherits that tool's R/W/W! risk). |\n\n---\n\n# New in MCP v1.2 (2026-07)\n\nEverything below was added in Cloudways MCP v1.2 and is documented in the [official tools article](https://support.cloudways.com/en/articles/15798823-cloudways-mcp-server-tools). These categories **close the gaps this catalog previously flagged as \"UI/direct-API only\"**: SSL / Let's Encrypt, SSH/MySQL/Adminer/Web-SSH IP whitelisting, and team-member management are now MCP tools. Most of these live in on-demand toolsets (invoked via `execute_tool`); R/W/W! risk tags below are this skill's operational assessment, not Cloudways labels.\n\n## Security — SSL & IP Access (new in v1.2)\n\n| Tool | Flag | What it does |\n|------|------|--------------|\n| `security_lets_encrypt_install` | W | Provision + install a Let's Encrypt SSL cert on an app (HTTP-01 or DNS-01 challenge). |\n| `security_lets_encrypt_renew` | W | Force an out-of-cycle renewal of an existing Let's Encrypt cert. |\n| `security_lets_encrypt_revoke` | W! | Revoke a Let's Encrypt cert — HTTPS falls back to no SSL (breaks production HTTPS). |\n| `security_lets_encrypt_auto_renewal` | W | Toggle Let's Encrypt auto-renewal on/off for an app. |\n| `security_remove_own_ssl` | W! | Remove a previously-installed custom (own) SSL cert from an app. |\n| `security_create_dns` | W | Create the DNS challenge (TXT) record required to issue a wildcard SSL cert. |\n| `security_verify_dns` | W | Verify the published DNS challenge record so the wildcard cert can be issued. |\n| `security_get_whitelisted_ips` | R | List IPs whitelisted for SSH/SFTP access on a server. |\n| `security_get_whitelisted_ips_mysql` | R | List IPs whitelisted for remote MySQL access. |\n| `security_update_whitelisted_ips` | W! | **Replace** the SSH/SFTP or MySQL IP whitelist — overrides the current list; a wrong list can lock you (or the client) out. Always read the current list first. |\n| `security_check_blacklisted_ip` | R | Check whether an IP is blacklisted on the server. |\n| `security_whitelist_ip_siab` | W | Whitelist an IP for Shell-in-a-Box (browser Web SSH). |\n| `security_whitelist_ip_adminer` | W | Whitelist an IP for Adminer (browser DB manager). |\n\n> **13 tools, all live-verified.** The tools article lists endpoint-style aliases for several of these (`security_dns_create`, `security_dns_verify`, `security_lets_encrypt_auto_renewal_update`, `security_mysql_whitelisted_ips_get`, `security_ssh_sftp_whitelisted_ips_get`, `security_whitelisted_ips_update`, `security_ip_blacklist_check`, `security_adminer_allow`, `security_siab_allow`) plus CSR tools (`security_csr_create` / `security_csr_get`) — **none of them exist on the live server.** The names in the table are the real ones.\n>\n> There is **no tool to install a custom SSL cert** (only `security_remove_own_ssl` to remove one), despite the toolset description claiming otherwise — the install/paste step is UI or direct-API only.\n\n## Security Suite — Anti-Malware / WAF (new in v1.2)\n\n| Tool | Flag | What it does |\n|------|------|--------------|\n| `security_suite_app_activate` | W | Enroll an app in the Security Suite (anti-malware, WAF, firewall). |\n| `security_suite_app_deactivate` | W | Unenroll an app from the Security Suite (removes protection). |\n| `security_suite_app_status_get` | R | Suite status for an app (active/inactive, last scan, threat count, tier). |\n| `security_suite_app_scans_list` | R | List all scans performed on an app. |\n| `security_suite_app_scan_start` | W | Start an on-demand malware / file-integrity scan. |\n| `security_suite_app_scan_status_get` | R | Status of ongoing/recent scans (queued/running/completed/failed). |\n| `security_suite_app_scan_details_get` | R | Detailed per-file findings for a scan. |\n| `security_suite_app_files_list` | R | List monitored / quarantined / affected files on an app. |\n| `security_suite_app_files_restore` | W! | Restore quarantined files — **can put malware back on the live site**; inspect the cleaned-vs-infected diff first. |\n| `security_suite_app_files_cleaned_diff_get` | R | Side-by-side cleaned-vs-infected file comparison. |\n| `security_suite_app_events_list` | R | Recent security events for an app (audit feed). |\n| `security_suite_app_incidents_list` | R | Detected incidents for an app (correlated events). |\n| `security_suite_app_ips_update` | W | Add/update IPs in the app's allowlist or blocklist. |\n| `security_suite_app_ips_delete` | W | Delete IPs from the app's allowlist or blocklist. |\n| `security_suite_server_incidents_list` | R | Incidents at server level (all apps). |\n| `security_suite_server_stats_get` | R | Server-level suite stats (threats, WAF alerts, brute-force, etc.). |\n| `security_suite_server_apps_list` | R | Which apps on a server are enrolled / unprotected. |\n| `security_suite_server_ips_get` | R | Server-level allowlist/blocklist IPs. |\n| `security_suite_server_ips_update` | W | Add/update server-level allowlist/blocklist IPs (affects every app). |\n| `security_suite_server_ips_delete` | W | Delete server-level allowlist/blocklist IPs. |\n| `security_suite_server_countries_blacklist_update` | W! | Block **all traffic** from selected countries (ISO codes) — server-wide traffic impact. |\n| `security_suite_server_countries_blacklist_delete` | W | Lift a country-level block. |\n| `security_suite_server_infected_domains_list` | R | Infected/compromised domains detected on a server. |\n| `security_suite_server_infected_domains_sync` | W | Force a re-sync of the infected-domains list. |\n| `security_suite_server_firewall_settings_get` | R | Current firewall config (rule sets, thresholds, geo-blocking). |\n| `security_suite_server_firewall_settings_update` | W | Update firewall config (rule sets, thresholds, geo-blocking). |\n\n## CloudwaysBot — Alerts & Integrations (new in v1.2)\n\n| Tool | Flag | What it does |\n|------|------|--------------|\n| `cloudways_bot_alert_mark_read` | W | Acknowledge one alert by `alert_id`. |\n| `cloudways_bot_alerts_mark_all_read` | W | Bulk-acknowledge all unread alerts. |\n| `cloudways_bot_integrations_list` | R | List configured alert channels (Slack, email, webhooks) + rules. |\n| `cloudways_bot_integration_channels_list` | R | Catalogue of supported channel types + event types. |\n| `cloudways_bot_integration_create` | W | Create a new alert channel. |\n| `cloudways_bot_integration_update` | W | Update an alert channel's rules/recipients/URL. |\n| `cloudways_bot_integration_delete` | W! | Delete an alert channel — alerts silently stop being delivered there. |\n\n## Staging Management (new in v1.2)\n\n| Tool | Flag | What it does |\n|------|------|--------------|\n| `staging_sync_tables` | W! | Sync/refresh database tables between staging and production — overwrites the target's data. |\n| `staging_sync_code` | W! | Sync code and/or DB between staging and deployed production (push to live or pull to staging) — a wrong direction overwrites production. Confirm **direction** explicitly. |\n| `staging_auth_status_update` | W | Enable/disable htaccess (Basic Auth) on a staging app. |\n| `staging_htaccess_update` | W | Update the staging app's htaccess credentials. |\n| `staging_logs_get` | R | Recent staging operation logs (syncs, refreshes, backups). |\n\n## Team Members (new in v1.2)\n\n| Tool | Flag | What it does |\n|------|------|--------------|\n| `team_member_list` | R | List sub-users with their roles and server/app access. |\n| `team_member_add` | W | Invite a new team member (email, role, access set). |\n| `team_member_update` | W! | Change a member's role or access — an over-grant is a security event; confirm the exact access set. |\n| `team_member_delete` | W! | Revoke a member's access to the account. |\n\n## Server Transfer (new in v1.2)\n\n| Tool | Flag | What it does |\n|------|------|--------------|\n| `server_transfer_request` | W! | Hand a server's **ownership** to another Cloudways account — after acceptance the server leaves this account entirely. |\n| `server_transfer_status_get` | R | Status of an in-flight transfer (pending/accepted/completed/cancelled/rejected). |\n| `server_transfer_cancel` | W | Cancel a not-yet-accepted transfer. |\n\n## Supervisord — Queues / Workers (new in v1.2)\n\n| Tool | Flag | What it does |\n|------|------|--------------|\n| `supervisord_list_queues` | R | List Supervisord-managed worker queues for an app (config included). |\n| `supervisord_queue_status` | R | Runtime status of the app's queues (running/stopped/fatal, pid, uptime). |\n| `supervisord_create_queue` | W | Create a worker queue (connection, procs, sleep, artisan path, …). |\n| `supervisord_restart_queue` | W | Restart a queue's workers (e.g. after a deploy). |\n| `supervisord_delete_queue` | W! | Delete a queue — stops its workers and removes the definition. |\n\n## Copilot — Subscription & Settings (expanded in v1.2)\n\n`copilot_insights_list` (R, above) existed before; the rest are new:\n\n| Tool | Flag | What it does |\n|------|------|--------------|\n| `copilot_plans_list` | R | Available Copilot plans and prices. |\n| `copilot_subscription_status` | R | Current subscription (plan, renewal, active/inactive). |\n| `copilot_billing_get` | R | Real-time Copilot billing/usage for the cycle. |\n| `copilot_subscribe` | W | Subscribe to a Copilot plan — **incurs cost**. |\n| `copilot_plan_change` | W | Upgrade/downgrade the Copilot plan — changes cost. |\n| `copilot_unsubscribe` | W! | Cancel the Copilot subscription. |\n| `copilot_server_settings_get` | R | Per-server Copilot settings (lists only servers where Copilot was disabled). |\n| `copilot_server_settings_update` | W | Toggle Copilot per server / adjust thresholds. |\n\n## AgencyOS (new in v1.2)\n\n| Tool | Flag | What it does |\n|------|------|--------------|\n| `agency_os_agency_create` | W | Create an AgencyOS agency (Stripe link optional, can be added later). |\n| `agency_os_agency_list` | R | List agencies under the account. |\n| `agency_os_client_create` / `agency_os_client_update` | W | Create / update an agency client record. |\n| `agency_os_client_list` / `agency_os_client_get` | R | List clients / client details. |\n| `agency_os_client_delete` | W! | Delete a client — cancels/archives their services and invoice history. |\n| `agency_os_service_create` / `agency_os_service_update` | W | Create / edit a service offering. |\n| `agency_os_service_list` / `agency_os_service_get` | R | List services / details. |\n| `agency_os_plan_price_create` | W | Attach a price plan to a service. |\n| `agency_os_plan_price_list` / `agency_os_plan_price_get` | R | List / get price plans. |\n| `agency_os_plan_update` | W | Rename / activate / deactivate a plan. |\n| `agency_os_client_subscriptions_get` | R | Services attached to a client. |\n| `agency_os_client_invoices_list` / `agency_os_invoices_list` | R | Client / agency-wide invoices. |\n| `agency_os_tax_rate_create` | W | Create a tax rate. |\n| `agency_os_tax_rate_list` | R | List tax rates. |\n| `agency_os_country_specs_get` | R | Supported countries + ISO codes (reference data). |\n| `agency_os_reports_list` | R | Client/service reports. |\n| `agency_os_report_archive` | W | Archive/unarchive a report. |\n\n## Cloudflare CDN — expanded in v1.2\n\nThe three tools above (`cloudflare_add_domain`, `cloudflare_get_details`, `cloudflare_get_txt_records`) existed before; v1.2 adds:\n\n| Tool | Flag | What it does |\n|------|------|--------------|\n| `cloudflare_delete_domain` | W! | Offboard a domain from Cloudflare CDN — proxying stops; can break production behavior. |\n| `cloudflare_transfer_domain` | W | Move a domain's CDN attachment between apps in the same account. |\n| `cloudflare_purge_domain` | W | Purge the Cloudflare edge cache for a domain. |\n| `cloudflare_get_dns_query` | R | Cloudflare DNS verification status for a domain. |\n| `cloudflare_verify_txt_records` | W | Trigger an immediate re-check of the published TXT records. |\n| `cloudflare_get_fpc_status` | R | Smart Cache Purge (FPC) deployment status. |\n| `cloudflare_deploy_fpc` | W | Configure/deploy Smart Cache Purge for a domain. |\n| `cloudflare_get_settings` | R | Per-domain Cloudflare settings (caching, minify, image optimization, …). |\n| `cloudflare_get_analytics` | R | Cache analytics (hit rate, bandwidth saved, edge requests). |\n| `cloudflare_get_security_analytics` | R | Security analytics (threats blocked, WAF events, bot mitigation). |\n| `cloudflare_get_logpush_analytics` | R | Raw Logpush request-log analytics. |\n| `cloudflare_get_logpush_security` | R | Raw Logpush security-event data. |\n\n## Add-on Management — expanded in v1.2\n\nIn addition to the five `addon_*` tools above:\n\n| Tool | Flag | What it does |\n|------|------|--------------|\n| `addon_upgrade` | W | Upgrade an activated add-on to a higher-tier package — changes cost. |\n| `addon_request` | W | Submit a per-app add-on request (for add-ons provisioned per application). |\n| `addon_elastic_list_domains` | R | List Elastic Email sender domains. |\n| `addon_elastic_verify_domain` | W | Verify a sender domain's DNS in Elastic Email. |\n| `addon_elastic_delete_domain` | W! | Delete a sender domain from Elastic Email — mail from that domain stops sending. |\n\n---\n\n## Still not exposed (use UI or direct API)\n\n- **SSH-key listing** — keys are managed via `ssh_key_create`/`ssh_key_update`/`ssh_key_delete`, but there is no dedicated read/list tool. Key metadata **does** come back inside the `server_get` payload — along with that server's **master credentials**, which is why an audit should read the roster in the Cloudways UI (Server → Security) or via the direct API and record a count. Reach for `server_get` when you need the metadata for one specific server and accept what comes with it (safety rule 7).\n- **SafeUpdate (managed WordPress updates)** — the `safe_update` toolset is declared but empty in the current build (0 tools); scheduling, run-now, history, and auto-rollback are UI-only.\n- **Custom SSL install** — `security_remove_own_ssl` removes a custom cert, but there is no install counterpart; paste the cert in the UI or use the direct API.\n- **Bot Protection, Client Billing, CloudwaysCDN (legacy), reference-data \"Lists API\", OAuth token minting** — documented in Cloudways' tools article but **absent from the live MCP**; UI or direct API only.\n- **Backup listing** — `app_backup_status_get` reports only an in-progress backup; available restore points are visible in the UI.\n- **Account/plan info** — no `customer_info` tool; plan/billing status of the Cloudways account itself is UI-only.\n\nFile v1.5.3:references/workflows-automation.md\n\n# Workflows — Automation & Integration\n\nHow to build automations around the Cloudways MCP — connecting to n8n, Make.com, Claude Code, or cron jobs. Suitable for any infrastructure management stack.\n\n> **Basic principle:** The MCP server is first and foremost an interface for Claude. For automation without human-in-the-loop, it's usually **better to call the Cloudways API directly** (curl/n8n HTTP node) rather than going through the MCP. The MCP adds overhead, and in automation it's not necessary.\n\n---\n\n## When to use MCP vs. direct API?\n\n| Scenario | Choose |\n|--------|-----|\n| Live conversation in Claude / Claude Code | MCP |\n| Daily report generated automatically | Direct API (n8n/Make) |\n| Alerting → automatic action | Direct API |\n| Audit one-off | MCP |\n| CI/CD trigger (post-deploy backup, etc.) | Direct API |\n| Claude Code headless running pipelines | MCP (if Claude is the orchestrator) |\n\nThe MCP saves time in an interactive context. In automation — overhead.\n\n---\n\n## 1. Cloudways API direct (for automation)\n\n### Authentication flow (current — Access Token, API v2)\n\nGenerate an **Access Token** at platform.cloudways.com → **API Integration** → Create Access Token (name + expiration + scope; use **Read-Only** for reporting loops, **Limited** for specific write workflows). Only the **primary account owner** can create/manage tokens — team-member accounts have no API Integration section. There is **no token-exchange step**: send the Access Token directly on every request.\n\n```bash\n# Current flow: one header, no exchange\ncurl -sH \"Authorization: Bearer $CLOUDWAYS_ACCESS_TOKEN\" \\\n     \"https://api.cloudways.com/api/v2/server\"\n```\n\n- **API v2 is the current API** ([announcement](https://www.cloudways.com/blog/introducing-cloudways-api-v2/)); the official migration rule is \"replace `v1` with `v2` in the API call URL structure\". v1 is deprecated (its docs remain up until **March 2026**).\n- Rate limit: 100 requests/minute (per the [access-tokens article](https://support.cloudways.com/en/articles/5136065)).\n- The token is long-lived per its configured expiration (1 day → never) — no hourly regeneration; rotate per your security policy, and revoke it the moment the integration is retired.\n\n### Common endpoints\n\nPaths are relative to `https://api.cloudways.com/api/v2` (same structure as v1 during the transition):\n\n| Endpoint | Method | What it is |\n|----------|--------|--------|\n| `/server` | GET | list servers |\n| `/server/{id}` | GET | server details |\n| `/app/manage/varnish` | POST | Varnish operations |\n| `/app/manage/backup` | POST | trigger backup |\n| `/app/letsencrypt_renew` | POST | renew SSL |\n| `/app/analytics/visitor` | GET | traffic |\n| `/app/manage/cache` | POST | clear cache |\n\nFull documentation: `https://developers.cloudways.com/docs/` (Redocly portal + API Playground; the Playground runs against your **real** account — prefer a test server).\n\n### Legacy authentication (works until 2026-10-15 only)\n\n> The `email` + `api_key` OAuth exchange authenticates existing, unmigrated automations. **The API key stops working on October 15, 2026** — migrate to an Access Token before then. Do not build anything new on this flow.\n\n```bash\n# LEGACY: exchange email+api_key for a short-lived (~1h) bearer token\nTOKEN=$(curl -sX POST \"https://api.cloudways.com/api/v1/oauth/access_token\" \\\n  -H \"Content-Type: application/x-www-form-urlencoded\" \\\n  -d \"email=$CLOUDWAYS_EMAIL&api_key=$CLOUDWAYS_API_KEY\" \\\n  | jq -r '.access_token')\n\ncurl -sH \"Authorization: Bearer $TOKEN\" \\\n     \"https://api.cloudways.com/api/v1/server\"\n```\n\n---\n\n## 2. n8n workflows\n\n### Workflow: Daily account health check\n\n**Trigger:** Cron, every day 8:00 AM Israel time\n\n```\n┌─ Cron (08:00 Asia/Jerusalem)\n├─ HTTP: GET /api/v2/server  (Authorization: Bearer <access-token>)  → list of servers\n├─ Loop over servers:\n│   ├─ HTTP: GET /server/{id}      → details\n│   ├─ HTTP: GET /alerts           → alerts\n│   ├─ Filter: status != Running OR alerts > 0\n│   └─ Continue if filter passed\n├─ Aggregate: Build summary message\n└─ Slack/Email: Send to team\n```\n\n**Note:** The API doesn't consistently return alerts in a separate endpoint — sometimes they're part of the server response. Check with direct curl first.\n\n### Workflow: SSL expiry monitoring\n\n**Trigger:** Cron, every Sunday\n\n```\n┌─ Cron (Sunday 09:00)\n├─ ONE step — request AND projection together:\n│   ├─ GET /api/v2/server                        → servers\n│   ├─ for each app: GET /app/{id}               → certificate fields + DB credentials\n│   └─ return ONLY { label, app_fqdn, ssl_* }    ← nothing else leaves this step\n├─ IF expiry < 30 days → \"needs attention\"\n├─ Aggregate\n└─ Send report\n```\n\n> **The projection has to happen inside the step that makes the request.** `GET /app/{id}`\n> returns that application's database credentials beside the certificate fields, and on n8n or\n> Make **every node's output is persisted in the execution record** — so an HTTP node that emits\n> the whole payload has already retained every app's DB password, and a filter node after it\n> cannot take that back. A later \"select these fields\" step protects the *report*, not the\n> platform's own log; this is the same mistake as telling an agent to keep secrets out of its\n> summary.\n>\n> Three shapes that actually work, in order of preference:\n>\n> 1. **A plain script** — cron + `curl` + `jq`, doing its own requests and printing only the\n>    allowlisted fields. The projection happens in the same process; nothing is persisted\n>    anywhere. Note that this means a **script**, not `claude -p`: the daily-summary job below\n>    delivers its report with `curl`, but its body is an agent, and an agent asked for expiry\n>    dates has only the credential-bearing `app_get` to get them with.\n> 2. **One n8n Code node** that performs the requests itself (`this.helpers.httpRequest`) and\n>    returns only the allowlisted fields. One node, one output, and that output is the filtered\n>    one.\n> 3. **A platform whose execution logging you have turned off or redacted for this scenario** —\n>    verify it on a test run before trusting it, because \"logs expire in 30 days\" is retention,\n>    not absence.\n>\n> If your automation platform emits the raw response from a node you cannot collapse, and you\n> cannot disable that node's logging, do not run this job there. This is the job\n> `workflows-monitoring.md` §5 points at: the agent gets names and dates from here, never the\n> payloads.\n\n### Workflow: Disk space alerting\n\n**Trigger:** Cron, every 4 hours\n\n```\n┌─ Cron (every 4h)\n├─ HTTP: GET /api/v2/server  (Authorization: Bearer <access-token>)\n├─ Loop servers:\n│   ├─ HTTP: GET /server/{id}/disk_usage\n│   ├─ IF usage > 85%:\n│   │   ├─ Slack alert: \"[Server X] disk 87% — investigate\"\n│   │   └─ (optional) IF usage > 95%: PagerDuty trigger\n```\n\n### Workflow: Auto-backup before deployment\n\n**Trigger:** Webhook from GitHub Actions / GitLab CI\n\n```\n┌─ Webhook IN (with: app_id, deployment_sha)\n├─ HTTP: POST /api/v2/app/manage/backup  (Authorization: Bearer <access-token>)  → trigger backup\n├─ Poll: get backup status until complete\n├─ Save: backup_id, timestamp → Airtable / DB\n└─ Webhook OUT → continue deployment\n```\n\n---\n\n## 3. Make.com (Integromat) scenarios\n\nA dedicated Make.com Custom App (if built) can wrap the API in more convenient modules. But even without a custom app, the HTTP module works.\n\n### Basic template:\n\n```\n[HTTP — Make a request]\n  URL: https://api.cloudways.com/api/v2/server\n  Method: GET\n  Headers: Authorization: Bearer <access-token from Make's credential store>\n  → iterate\n  (no token-exchange step — the Access Token is sent directly; the legacy\n   email+api_key exchange works only until 2026-10-15, see §1)\n\n[Iterator]\n  → for each server:\n\n[Router]\n  → branch by status / size / region\n\n[Slack / Email / Airtable]\n  → output\n```\n\n---\n\n## 4. Claude Code headless\n\nIn automation via `claude -p` (headless mode), the MCP server keeps working as usual. Useful when:\n\n- You want Claude to decide what to do (not just rules-based)\n- The automation involves textual analysis (summarizing a report, drafting an email)\n- There's value in natural language interpretation of the data\n\n**Example: Daily summary in production**\n\n```bash\n#!/bin/bash\n# scripts/cw-daily-summary.sh\n#\n# Needs: claude (Claude Code, with the Cloudways MCP connection configured for\n# the user running the cron) and jq. jq is NOT installed by default on macOS -\n# `brew install jq` - and set -e means the job would otherwise die at the\n# encode step after doing all the work.\nset -euo pipefail\ncommand -v jq >/dev/null || { echo \"this job needs jq (brew install jq / apt install jq)\" >&2; exit 1; }\n\n# A private temp file, not a fixed path. /tmp is shared: a fixed name can be\n# pre-created by another user as a symlink, so the report either overwrites\n# whatever it points at or is read back by whoever owns it.\n#\n# The X's must be at the END of the template. BSD mktemp (macOS) does not\n# substitute them anywhere else - and it does not fail either: it creates a file\n# called literally \"cw-summary.XXXXXX.md\", which is exactly the predictable name\n# this line exists to avoid, with a successful exit status hiding it. Measured,\n# not assumed: on macOS 26.3, `mktemp ./cw-summary.XXXXXX.md` exits 0 and leaves\n# a 0600 file of that literal name, so `set -e` never fires and nothing warns.\numask 077\nOUT=$(mktemp \"${TMPDIR:-/tmp}/cw-summary.md.XXXXXX\")\ntrap 'rm -f \"$OUT\"' EXIT\n\n# Here Claude calls the MCP tools itself and generates a summary\n# Certificate expiry is deliberately NOT asked for here. The only tool that returns it is\n# app_get, which also returns that application's database credentials - so an agent asked for\n# expiry dates across the fleet has no way to answer except the sweep this skill refuses\n# (workflows-monitoring.md section 5). SSL runs as its own direct-API job above, which projects\n# the response before anything reads it. Ask an agent only for what a credential-free tool\n# answers.\nclaude -p \"\nGenerate today's Cloudways health summary.\nCheck all servers (server_list), get alerts (copilot_insights_list), and identify:\n1. Any server not in Running status\n2. Any disk > 80%\n3. Top 3 apps by traffic in the past 24h\n\nCall no credential-returning tool: not server_get, not app_get, not app_credentials.\nTelling you to leave secrets out of the summary would not help - by then they are in this\ntranscript, and the transcript outlives the summary.\n\nOutput in Hebrew, markdown format, written to $OUT\n\"\n\n# Failing on an HTTP error is the point: without one of these flags curl exits\n# 0 on a rejected webhook (bad URL, payload too large), so the cron looks like\n# it succeeded - and the trap below has already deleted the only copy of the\n# report. --fail-with-body keeps the server's reason in the output, but it is\n# curl >= 7.76.0; Ubuntu 20.04 ships 7.68, where it is an unknown option, curl\n# exits 2 having posted NOTHING, and every run fails the same silent way. So\n# ask the installed curl rather than assuming: an unsupported option makes curl\n# exit non-zero before it ever reaches --version.\nif curl --fail-with-body --version >/dev/null 2>&1; then\n  CURL_FAIL=--fail-with-body          # curl >= 7.76.0: status AND the reason\nelse\n  CURL_FAIL=--fail                    # older curl: status only, body discarded\nfi\n\n# Build the JSON with a real encoder. Interpolating the file into a JSON\n# string breaks on the first quote, backslash or newline in the report - and a\n# report is generated text, so it WILL contain them.\njq -Rs '{text: .}' < \"$OUT\" \\\n  | curl \"$CURL_FAIL\" --silent --show-error \\\n         -X POST -H 'Content-type: application/json' --data-binary @- \"$SLACK_WEBHOOK_URL\"\n```\n\n> **No jq?** Any real JSON encoder will do — `python3 -c 'import json,sys; print(json.dumps({\"text\": sys.stdin.read()}))' < \"$OUT\"` is the same thing. What must not come back is\n> building the payload by interpolating the file into a string.\n\n> **What leaves the machine.** The summary goes to a channel with its own membership and\n> retention, so keep infrastructure detail out of it: status, counts and names are the point;\n> credentials, IPs and tokens are not. The `trap` removes the file even if `curl` fails.\n\n**Note:** Headless requires Claude Code to have the Cloudways MCP connection configured. Make sure the MCP config is set in the `~/.claude.json` of the user running the cron.\n\n---\n\n## 5. Airtable as state store\n\nFor automations that generate a lot of data (audit results, alerts log, deployment history), Airtable is a good state store. Pattern:\n\n**Table: cloudways_servers**\n\n| Field | Type |\n|-------|------|\n| cw_id | Number (PK) |\n| label | Text |\n| provider | Single select |\n| region | Text |\n| size | Text |\n| status | Single select |\n| last_check | DateTime |\n| client | Linked to Clients table |\n| monthly_cost_usd | Number (formula or manual) |\n\n**Table: cloudways_alerts**\n\n| Field | Type |\n|-------|------|\n| date | DateTime |\n| server | Linked |\n| app | Linked |\n| severity | Single select (P0/P1/P2) |\n| issue | Text |\n| status | Single select (open/investigating/resolved) |\n| resolution_notes | Long text |\n\n**Sync:** n8n / Make scenario every hour: pull state from Cloudways → upsert to Airtable. The team gets a live view.\n\n> **Map an explicit field allowlist per table — never the whole API response.** One list per\n> destination table, and nothing outside it:\n>\n> - `cloudways_servers`: `cw_id`, `label`, `provider`, `region`, `size`, `status`, `last_check`,\n>   `client`, `monthly_cost_usd`\n> - `cloudways_alerts`: `date`, `server`, `app`, `severity`, `issue`, `status`,\n>   `resolution_notes`\n>\n> Every field the tables above define, and no credential field from any payload. Copying a\n> response wholesale carries master credentials, database passwords and SFTP access out of\n> Cloudways into a third-party store with its own sharing, export and retention — and\n> `server_get` / `app_get` return those fields whether or not the sync asked for them. Pick\n> fields by name in the mapping step; do not pass the object through. The same applies to the\n> Slack and email destinations elsewhere in this file, and to anything that keeps history: a\n> credential written to a state store is still there after it has been rotated, and after the\n> engagement has ended.\n\n---\n\n## 6. Slack notifications — recommended patterns\n\n**Slack message types:**\n\n| Severity | Format | Expected response |\n|--------|--------|--------------|\n| P0 (server down, SSL expired) | `<!channel>` + 🚨 | response within 30 minutes |\n| P1 (disk > 90%, SSL < 7d) | `<!here>` + ⚠️ | response within 4 hours |\n| P2 (info: backup completed) | regular + ✅ | no response needed |\n\n**Payload example:**\n\n```json\n{\n  \"text\": \"🚨 P0: Cloudways alert\",\n  \"blocks\": [\n    {\n      \"type\": \"section\",\n      \"text\": {\n        \"type\": \"mrkdwn\",\n        \"text\": \"*Server:* prod-shop-il (1234567)\\n*Issue:* SSL expired 2 hours ago\\n*Affected apps:* shop.example.co.il, admin.example.co.il\\n*Action:* renew via `security_lets_encrypt_renew` (W) or the Cloudways UI — pending human approval\"\n      }\n    },\n    {\n      \"type\": \"actions\",\n      \"elements\": [\n        {\n          \"type\": \"button\",\n          \"text\": {\"type\": \"plain_text\", \"text\": \"Open Cloudways\"},\n          \"url\": \"https://platform.cloudways.com/server/1234567\"\n        }\n      ]\n    }\n  ]\n}\n```\n\n---\n\n## 7. Multi-account management\n\nIf you manage multiple Cloudways accounts (different clients, separate accounts) — don't keep all the keys in one place.\n\n**Strategy:**\n\n1. **Account-per-client:** each client's credentials in a separate secrets-manager project (e.g. a vault project)\n2. **Connection per account:** one MCP connection per account in Claude, each with its own credentials (see `installation.md` → Multi-account configuration)\n3. **n8n credentials:** define them in the n8n credentials store, not in the workflow\n4. **Make.com connections:** same thing in Make\n\n**Multi-account Claude config example:**\n\n```json\n{\n  \"mcpServers\": {\n    \"cloudways-clientA\": { \"__\": \"official MCP connection, clientA credentials (per the support article)\" },\n    \"cloudways-clientB\": { \"__\": \"official MCP connection, clientB credentials (per the support article)\" }\n  }\n}\n```\n\nClaude will see each of them as a separate MCP with its own prefix.\n\n---\n\n## 8. Rate limiting considerations\n\nThe MCP is a proxy in front of the Cloudways API, so it inherits whatever rate limits the underlying Cloudways API enforces. There is no documented, fixed per-minute MCP request budget, and there is no `rate_limit_status` tool — don't assume a specific number. In automation, be mindful:\n\n- **Daily summary** of 50 servers + 200 apps = ~250 reads. Under normal conditions — fine.\n- **Bulk audit** of a massive account — large bursts of requests can run into the upstream Cloudways API limits. Spread work out, batch sensibly, and add backoff/retry on errors rather than hammering.\n- **For very high-volume automation, prefer the direct Cloudways API** (`https://developers.cloudways.com/`). It's the supported path for heavy programmatic use and avoids the extra hop through the MCP.\n\n---\n\n## Anti-patterns (don't do)\n\n❌ **Auto-execute write operations** without human-in-the-loop. Even if it looks safe, don't.\n\n❌ **Logging credentials.** Make sure the n8n / Make logs don't display the Access Token (or legacy API key). Use the internal credential store.\n\n❌ **Credentials in shared or committed config.** Keep each account's Access Token in a secrets manager / local git-ignored config — never in a workflow body or a committed file. Scope automation tokens to the minimum role (READ for reporting loops).\n\n❌ **Use MCP for monitoring loops.** For continuous monitoring (every 30 seconds), switch to the direct API. The MCP overhead isn't worth it.\n\n❌ **Sharing one account's credentials across the team.** Each person should connect with their own Cloudways credentials (or via a proper SSO/proxy). Sharing a connection = sharing credentials.\n\nFile v1.5.3:references/workflows-maintenance.md\n\n# Workflows — Maintenance (write operations)\n\nMaintenance scenarios requiring write operations. **Every operation here requires explicit confirmation from the user before execution**, per the pattern in `SKILL.md`.\n\n> **Basic rule:** Read before Write. Always read the current state before you change it. Both to verify the operation is needed, and so you have a baseline to roll back to.\n\n---\n\n## Standard confirmation pattern\n\nBefore **every** call to a W tool, display a block like this to the user and wait for an explicit \"yes\":\n\n```\n🔒 Confirm execution?\n   tool: <tool_name>\n   target: <server name + ID or app name + URL>\n   parameters: <key params>\n   expected impact: <what happens>\n   risks: <what could go wrong>\n   Continue? (yes / no / pause)\n```\n\nIf the user wrote \"yes\" — execute. If anything else — ask for clarification.\n\nFor especially dangerous operations (W!): add a **second step**: \"Type the server/application name to confirm\". This ensures they are reading and not approving automatically.\n\n---\n\n> **Confirming a target: the narrowest fetch that gives you name + URL, and often no fetch at\n> all.** Every sequence below starts by making sure the right application is in hand, and the\n> confirmation block above requires its name + URL — **an id alone is never a confirmation**: a\n> mistyped id that happens to belong to another application is still a valid id, so a write\n> confirmed against nothing but a number can land on the wrong site (`app_restore` is the one\n> that cannot be undone). Resolve in this order, and stop at the first rung you can use:\n>\n> 1. **The roster you already hold** from this conversation — zero calls. This is the usual case.\n> 2. **You know the server and the app id, hold no roster** — read the name + URL **outside the\n>    conversation** (the application's page in the Cloudways UI, or a direct API call through a\n>    field filter): zero secrets in the transcript. If it has to be the API from here, **one\n>    `app_get` for that one app** is the narrowest call there is — it returns that app's\n>    database credentials, and nothing else's. Do **not** reach for `app_list` to confirm one\n>    known id: rule 7 describes its payload, and it covers **every** application on the server,\n>    so it exposes strictly more than the `app_get` it would be standing in for.\n> 3. **You know the server and only a name or URL.** If it is the application's **primary**\n>    domain (or its label), `app_list` on that server is the one API route — a single call;\n>    take the one row you came for and paste none of it — or the same external filtered roster\n>    as rung 2. If it is a **secondary** domain — an alias — `app_list` cannot resolve it: the\n>    payload carries the primary `domain` only, and aliases have no read tool at all (see\n>    `workflows-onboarding.md`, the domains note). An alias lookup through `app_list` would pay\n>    the roster's cost and find nothing. Resolve an alias in the UI (the application's Domain\n>    Management page) or through a filtered direct API call, which gives you the primary — and\n>    only then, if you still need the id, is there something for `app_list` to match.\n> 4. **You do not know the server** — stop and ask which server, or for the name/URL. There is\n>    no lookup from an app id to its server: `app_list` and `app_get` both take a `server_id`,\n>    and the only API route from a bare id is reading every server's roster, which is the sweep\n>    this skill refuses.\n>\n> What `app_get` is never for is **habit**: reaching for it as the opening step of every job,\n> for a label a held roster already gives you, was the finding this section exists to close.\n>\n> **Certificate state is read from the outside, and the verdict and the dates are two different\n> commands.** The verdict is `env -u CURL_CA_BUNDLE -u SSL_CERT_FILE -u SSL_CERT_DIR curl -q --cacert \"$HOME/.config/cloudways-mcp/cacert.pem\" -sS -o /dev/null --max-time 15 --noproxy '*' https://<domain>/`: exit **0** means the chain, the hostname and the validity\n> period all passed the OS trust store — what a browser checks — and exit **60** means one of\n> them did not. Two guards on that command are not optional. `-q`, which must come **first**,\n> stops curl reading `~/.curlrc`: a machine whose curlrc says `insecure` would otherwise pass\n> an expired, self-signed or wrong-host certificate with exit 0 — measured against\n> `expired.badssl.com` with such a file, exit 0 without `-q`, exit 60 with it. And the\n> `env -u CURL_CA_BUNDLE -u SSL_CERT_FILE -u SSL_CERT_DIR` prefix clears curl's environment\n> equivalents of `--cacert`/`--capath`, which `-q` does not touch: measured against\n> `self-signed.badssl.com` with `CURL_CA_BUNDLE` pointed at its own certificate, exit 0 — the\n> gate passes a certificate no browser trusts — and exit 60 again under `env -u`. And the\n> third guard is the `--cacert` pointing at a **pinned copy of Mozilla's public roots**, because\n> the system trust store is not the public one: a corporate or user-installed CA sits in it\n> exactly where `env -u` cannot reach, and an origin certificate signed only by that CA exits 0\n> on this machine while every ordinary visitor rejects it. The verdict has to come from the\n> roots browsers ship with. One-time setup, beside `headers.txt` — and the digest it is checked\n> against is **this one, recorded here**, not one fetched beside the file:\n>\n> ```\n> sha256  f66dff1bdf8f96060b8177976f8b7d9254bc89bc4db933d769f7384d28480bc9\n>         Mozilla certificate data as of Thu Aug 13 03:12:01 2026 GMT, 188 900 bytes\n> ```\n>\n> ```bash\n> umask 077; mkdir -p ~/.config/cloudways-mcp && cd ~/.config/cloudways-mcp\n> curl -q -sS -o cacert.pem.new https://curl.se/ca/cacert-2026-08-13.pem\n> [ \"$(shasum -a 256 cacert.pem.new | cut -d' ' -f1)\" = f66dff1bdf8f96060b8177976f8b7d9254bc89bc4db933d769f7384d28480bc9 ] && mv -f cacert.pem.new cacert.pem && echo OK || { echo 'digest MISMATCH against the value recorded in the skill - not installed'; rm -f cacert.pem.new; }\n> ```\n>\n> The URL is the **dated** artifact, not `cacert.pem`: curl.se serves every Mozilla revision at\n> `cacert-YYYY-MM-DD.pem` and moves the undated name to the newest, so an undated fetch would\n> stop matching the recorded digest the day Mozilla revises — every new setup failing, for no\n> reason anyone changed. And the download lands in a temporary name and is moved into place\n> only after it verifies, so a mismatch leaves a working installation's existing bundle exactly\n> where it was. Bumping is one commit that changes the date in the URL and the digest beside\n> it, together.\n>\n> Why the digest lives here and not in `cacert.pem.sha256` next to the download: that file\n> comes from the same origin over the same trust path as the bundle, so whatever can replace\n> one can replace the other in the same breath — a TLS-inspecting proxy or a compromised\n> system CA, which is exactly the situation this bundle exists to defend against. A same-origin\n> checksum proves a transfer was not corrupted and nothing more. A digest recorded here moves\n> the trust from the download path to the commit that recorded it (the same arrangement as\n> `mcp-remote`'s tarball digest in `installation.md`): bumping the bundle means recording the\n> new digest in the same commit. On a machine whose network or trust store you do not trust at\n> all, obtain the bundle through an independent channel — a machine you do trust, or your OS\n> vendor's `ca-certificates` package — and compare against the recorded digest there. Measured\n> on this curl build: with that bundle a good host exits 0 and `self-signed.badssl.com` exits 60;\n> with an **empty** bundle the good host exits 77 — proof that the file, not the OS store, is\n> what the verdict trusts. Mozilla revises the bundle a few times a year; a newer one is\n> adopted by updating this record, never by fetching the undated name.\n>\n> **And there may be two certificates.** Through public DNS that command validates whatever\n> answers for the name — behind Cloudflare or any reverse proxy, that is the **edge**\n> certificate, not the one installed on the Cloudways application. The **origin** is checked\n> by pinning the name to the server's IP (from the `server_list` row you already hold, the\n> server's page in the UI, or one `server_get` for that server — never a fresh `server_list`\n> to read one address):\n> `env -u CURL_CA_BUNDLE -u SSL_CERT_FILE -u SSL_CERT_DIR curl -q --cacert \"$HOME/.config/cloudways-mcp/cacert.pem\" -sS -o /dev/null --max-time 15 --noproxy '*' --resolve <domain>:443:<server-ip> https://<domain>/` — `--resolve` keeps the hostname for SNI and verification and only changes where the\n> connection goes. `--noproxy '*'` is part of the command: with `HTTPS_PROXY` / `https_proxy` /\n> `ALL_PROXY` set in the environment, curl hands the request to that proxy, which resolves\n> `<domain>` through its own DNS and reaches the CDN edge — so the \"origin\" check would be\n> validating the edge certificate after all (measured: with a proxy variable set, the\n> `--resolve` form connects to the proxy address, not the server, until `--noproxy '*'` is\n> added). **Whether something is in front is a DNS question, not a certificate one**:\n> `R4=$(dig @1.1.1.1 +noall +comments +answer <domain> A) && R6=$(dig @1.1.1.1 +noall +comments +answer <domain> AAAA) && printf '%s\\n%s\\n' \"$R4\" \"$R6\" | grep -c 'status: NOERROR' | grep -qx 2 && A=$(printf '%s\\n%s\\n' \"$R4\" \"$R6\" | awk '$4==\"A\"||$4==\"AAAA\"{print $5}') && [ -n \"$A\" ] && printf '%s\\n' \"$A\" || { echo 'DNS gate FAILED: lookup error, non-NOERROR rcode, or no address' >&2; false; }` against the server's addresses (same source as the IP above) — **every** routable answer, both\n> record types, must be the server. `@1.1.1.1` (or any public resolver) is not decoration: on\n> a VPN or an office network with **split-horizon DNS**, the local resolver can answer with the\n> Cloudways origin while the public one answers with a Flexible-mode CDN — every check then\n> exercises the origin, \"no proxy\" is concluded, and the redirect loops for every visitor\n> outside that network. The gate has to believe what a visitor's resolver says. The same\n> applies to the redirect-chain checks, which cannot pin hops to other hostnames: on a network\n> whose resolver disagrees with the public one, run them from **outside** it, or with\n> `--resolve <hostname>:443:<answer>` for **each** public answer in turn. Each, not one: a\n> request with no pin exercises **one** address — curl races the answers and keeps the first\n> connection to succeed (measured: three plain requests to a dual-stack hostname all connected\n> to the same IPv6 address; its IPv4 answer was never touched) — so a hostname with an A and\n> an AAAA record is two checks, and a family whose edge serves an expired certificate hides\n> behind the family that works. An IPv6 answer goes into `--resolve` as it is (measured:\n> accepted with and without brackets).\n> The `awk`s keep only addresses: for a CNAME — an ordinary\n> `www` alias — `dig +short` prints the canonical name on its own line before the address\n> (`github.com.` then `20.217.135.5`, measured), and comparing that line against a server IP\n> would call every alias a proxy. `awk` rather than `grep` because a name with no AAAA record\n> is normal, and `grep` with nothing to select exits 1 — under `set -e`, or a runner that\n> surfaces non-zero commands, that would fail the check on a perfectly valid setup; `awk`\n> prints the same lines and exits 0 with nothing to print (measured on an IPv4-only name).\n> Which is why the guard around it exists: an empty **family** is fine, an empty **answer** is\n> not. With the public resolver blocked, filtered or erroring, `dig` exits 9 but the pipeline\n> prints nothing and exits 0 (measured), and \"every answer matches the origin\" is then true of\n> no answers — the proxy-mode check would be skipped on the very networks where it matters.\n> Each `dig` is therefore checked on **its own exit status** before anything is filtered — a\n> failed lookup for one family, hidden behind the other family's good answer, would otherwise\n> pass an aggregate check while being the very family that resolves publicly to a proxy\n> (measured: A good, AAAA against an unreachable resolver — an aggregate non-empty guard passed,\n> the per-lookup guard failed). And the exit status is not enough either: `dig +short` prints\n> nothing and exits **0** on a `SERVFAIL` (measured against `dnssec-failed.org` at 1.1.1.1), so\n> a family whose lookup the resolver *refused* looked exactly like a family with no records.\n> Each family's **RCODE** is therefore read from `+comments` and must be `NOERROR` — only then\n> is an empty family a real no-data answer — and the addresses are taken from the answer\n> section by record type. Three cases, stated: `NOERROR` with no records for either family\n> passes; anything other than `NOERROR` for either family fails (a `SERVFAIL`, and an\n> `NXDOMAIN` — a hostname the app supposedly serves that does not resolve is not a served\n> hostname); and no addresses at all fails. No output, and no successful pair of lookups, is\n> an unanswered gate — which is a failed gate, never a passed one. Any other address is a CDN or proxy, whatever certificate\n> it shows; and a proxy reachable only over IPv6 (an A record at the origin, an AAAA at the\n> edge) is still a proxy for every IPv6 client, so an A-only check is not a check. Comparing\n> issuers proves nothing,\n> because the edge and the origin can both hold Let's Encrypt certificates and still be two\n> different machines with a Flexible-mode HTTP hop between them. And the origin check is for a\n> site visitors reach **directly**: behind a proxy in Full or Full (strict) mode the origin may\n> hold a certificate only the proxy trusts (Cloudflare Origin CA is the ordinary case), the\n> local trust store calls it invalid, and the certificate that has to pass is the edge's — for\n> the visitors who reach the edge. Proxying is a property of each DNS **answer**, not of the\n> hostname: an origin A record beside a proxied AAAA record means IPv4 visitors get the origin's\n> certificate and IPv6 visitors get the edge's, and both have to pass — see §9 step 1 for the\n> branching. Let's Encrypt renewals happen\n> at the origin, so a renewal is verified there; and enforcing HTTPS at the origin behind a\n> proxy in **Flexible** mode (proxy speaks HTTPS to the browser, HTTP to the origin) makes the\n> origin redirect every proxied request back to HTTPS — a loop. Both checks are measured below\n> where they matter. The dates for a report come from `openssl s_client -servername <domain> -connect <domain>:443 </dev/null 2>/dev/null | openssl x509 -noout -issuer -dates`, and that line is **informational\n> only**: measured against `expired.badssl.com`, `self-signed.badssl.com` and\n> `wrong.host.badssl.com`, it prints issuer and dates and exits 0 for all three, while `curl`\n> exits 60 for each. Nothing below decides anything on the openssl line.\n\n## 1. Cache clear — basic\n\n**When:** \"The site isn't updating after a change\" / \"Admin screen shows an old version\"\n\n**Sequence:**\n\n1. Confirm the target — name + URL, from the first rung of the ladder at the top you can use (held roster → external lookup or one `app_get` for a known id → `app_list` for a name → ask); an id alone is not a confirmation\n2. `app_varnish_settings_get` — see if Varnish is active\n3. **CONFIRM:** `app_purge_cache` (W)\n4. If Varnish is active: **CONFIRM:** `varnish_app_manage` with action=purge (W)\n5. Check the site in a browser (curl or manually)\n\n**If the problem persists:**\n- Check plugin caches (W3 Total Cache, WP Rocket, LiteSpeed) — these are not in Cloudways, they must be cleared from WP-Admin\n- Check Cloudflare cache if in use — `purge everything` in the Cloudflare UI\n- CDN caches\n\n---\n\n## 2. SSL — Let's Encrypt renewal\n\n**When:** SSL is approaching expiry and there is no auto-renewal / auto-renewal failed\n\n> **Covered by the MCP as of v1.2** via the security toolset: `security_lets_encrypt_install` (W), `security_lets_encrypt_renew` (W), `security_lets_encrypt_auto_renewal` (W), `security_lets_encrypt_revoke` (W!). For wildcard certs: `security_create_dns` + `security_verify_dns` handle the DNS-01 challenge.\n\n**Sequence:**\n\n1. Domain from the roster you hold, server IP from the `server_list` row you hold (or the UI, or\n   one `server_get` for that server — see the note at the top). The certificate being renewed\n   lives on the **origin**, so check that one: `env -u CURL_CA_BUNDLE -u SSL_CERT_FILE -u SSL_CERT_DIR curl -q --cacert \"$HOME/.config/cloudways-mcp/cacert.pem\" -sS -o /dev/null --max-time 15 --noproxy '*' --resolve <domain>:443:<server-ip> https://<domain>/` for the verdict (exit 0 / 60), and\n   `openssl s_client -servername <domain> -connect <server-ip>:443 </dev/null 2>/dev/null | openssl x509 -noout -issuer -dates`\n   for the issuer and `notAfter` on record. If `R4=$(dig @1.1.1.1 +noall +comments +answer <domain> A) && R6=$(dig @1.1.1.1 +noall +comments +answer <domain> AAAA) && printf '%s\\n%s\\n' \"$R4\" \"$R6\" | grep -c 'status: NOERROR' | grep -qx 2 && A=$(printf '%s\\n%s\\n' \"$R4\" \"$R6\" | awk '$4==\"A\"||$4==\"AAAA\"{print $5}') && [ -n \"$A\" ] && printf '%s\\n' \"$A\" || { echo 'DNS gate FAILED: lookup error, non-NOERROR rcode, or no address' >&2; false; }` answers with anything that is not one\n   of the server's own addresses, a proxy is in front — the renewal still happens here, at the origin, and\n   the browser will keep showing you the proxy's certificate afterwards.\n   Nothing in this step needs the database credentials `app_get` would add.\n2. Check that the DNS still points to the server (critical for LE validation)\n3. **CONFIRM:** `security_lets_encrypt_renew` (W) — or `security_lets_encrypt_install` (W) if no cert was issued yet. For a wildcard domain: **CONFIRM** `security_create_dns` (W), publish the returned TXT record at the DNS host, then **CONFIRM** `security_verify_dns` (W).\n4. **CONFIRM:** `security_lets_encrypt_auto_renewal` (W) — turn auto-renewal on if it wasn't active.\n5. Verify at the origin — the `--resolve` form of the check must exit **0** now, and the\n   openssl line against `<server-ip>:443` should show the new `notAfter` — then load the site\n   in a browser (which, behind a proxy, shows you the edge certificate, not this one). A re-read of `app_get` would confirm\n   nothing the verified handshake does not, at the price of a second credential payload.\n\n**If renewal fails:**\n- Most common problem: DNS doesn't point correctly, or wildcard domains aren't configured\n- Second: Let's Encrypt rate limit (5 attempts per week per domain)\n- Third: HTTP-01 validation fails because the site is behind a Cloudflare proxy → resolve with DNS-01 or disable the proxy temporarily\n\n---\n\n## 3. SSL — installing a custom cert\n\n**When:** The client purchased a cert from another CA (DigiCert, Sectigo, etc.), not Let's Encrypt\n\n> **Barely covered by the MCP** (live-verified 2026-07-20): the only custom-SSL tool is `security_remove_own_ssl` (W!), which *removes* an installed cert. There is **no install tool and no CSR tool** — the article's `security_csr_create` / `security_csr_get` do not exist on the live server, and the `security` toolset description claiming a custom-cert install is wrong. Generate the CSR on the CA's side (or via `openssl`), then paste cert + key in the **Cloudways Platform UI** (Application → SSL Certificate → Custom SSL) or use the [direct API](https://developers.cloudways.com/).\n\n**Sequence:**\n\n1. Collect from the client: certificate, private key, ca bundle. If the CA still needs a CSR, generate it outside Cloudways (the MCP exposes no CSR tool) — and **name the key explicitly**, because the cert is only installable with the exact key that signed the CSR:\n\n   ```bash\n   # Signing with the key you already have:\n   openssl req -new -key privkey.pem -out request.csr\n\n   # Or generating a new key + CSR together (-nodes leaves the key\n   # unencrypted, which the Cloudways UI requires):\n   openssl req -new -newkey rsa:2048 -nodes -keyout privkey.pem -out request.csr\n   ```\n\n   Keep `privkey.pem` — a bare `openssl req -new` writes an encrypted key to whatever path the local OpenSSL config picks, and losing it makes the issued certificate unusable.\n2. Confirm the target — name + URL, by the ladder at the top (held roster first; an id alone is not a confirmation)\n3. **Install the custom cert in the Cloudways UI** (paste cert + key) — manual by necessity; no MCP tool covers this step.\n4. Check SSL from the browser (SSL Labs grade A+ preferred)\n5. If Let's Encrypt was active — decide: keep as backup or revoke (`security_lets_encrypt_revoke`, W! — double-confirm)\n\n> Warning: Installing a custom cert **cancels** the Let's Encrypt cert if one was active. Make sure you have the custom cert in hand **before** you start.\n\n---\n\n## 4. Backup before a change\n\n**When:** Before a migration, restore, significant plugin update, or any \"I'm not sure what this will do\"\n\n**Sequence:**\n\n1. Confirm the target — name + URL, by the ladder at the top (held roster first; an id alone is not a confirmation)\n2. `monitoring_app_summary` — before: snapshot of state\n3. **CONFIRM:** `app_backup` (W)\n4. Check that the backup is progressing (`app_backup_status_get` for in-progress state, or via the UI). Note: there is no general \"list backups\" tool — the available restore points are visible in the Cloudways UI.\n5. Record the backup timestamp — you'll need it for restore if something goes wrong\n\n**Server-level backup:**\n- `server_backup` (W) — slower, includes everything, more expensive\n- Useful before a server-wide change (PHP upgrade, OS upgrade, package change)\n\n---\n\n## 5. Restore after an error\n\n**When:** Something went wrong (bad deployment, hack, accidental delete)\n\n**Sequence — critical to follow in order:**\n\n1. **STOP** — don't do anything until you understand the scope of the problem.\n2. The app's identity — name + URL — by the ladder at the top (a bare id is not an identity,\n   and step 5 below has to be checked against something), then `monitoring_app_summary` for\n   what it is doing right now — the current state a restore decision needs\n3. Check the list of available backups (via the Cloudways UI — there is no MCP \"list backups\" tool; `app_backup_status_get` only reports in-progress backup status)\n4. **CONFIRM step 1:** \"Is the backup from date X the point you want to roll back to?\"\n5. **CONFIRM step 2:** Type the app name to confirm restore\n6. **CONFIRM:** `app_restore` (W!) — full overwrite of the current state\n7. Check that the site works\n8. If the restore itself made things worse: **CONFIRM (W!):** `app_restore_rollback` — returns the app to its pre-restore files + database. This is available only within the limited rollback window (see below); after that, fall back to restoring an earlier backup or the Cloudways UI.\n\n> **Limited rollback window.** `app_restore_rollback` only works for a short time after the restore (a few hours / a day) — after that the pre-restore local snapshot is gone and rollback is no longer possible. Make sure the site works **on the same day** as the restore. (Note: `app_local_backup_delete` deletes that pre-restore snapshot immediately, which also forecloses the rollback.)\n\n---\n\n## 6. Restart server / service\n\n**When:** memory leak, services stuck, or troubleshooting\n\n**Priority order — try the quietest one first:**\n\n1. `app_purge_cache` (W) per app — sometimes that's all it takes\n2. `service_restart` (W) on a single service (e.g. restart MySQL only)\n3. If that doesn't help: `server_restart` (W) — 1-5 minutes downtime for all the apps\n\n**Before server_restart:**\n\n1. **Mandatory:** `app_list` → the applications about to go offline (`server_get` returns the same roster plus the server's master credentials; the roster is all this preflight needs)\n2. **Mandatory:** count active users (if relevant — a store site with open carts?)\n3. **Double CONFIRM:** \"The server hosts X applications — Y, Z, W. Each of them will be offline for X minutes. Continue?\"\n4. Execute\n5. **VERIFY:** `service_status` after the restart\n\n---\n\n## 7. IP whitelist — SSH/MySQL\n\n**When:** Adding a key for a team member, or removing access for an old IP\n\n> **Covered by the MCP as of v1.2** via the security toolset: `security_get_whitelisted_ips` (R, SSH/SFTP), `security_get_whitelisted_ips_mysql` (R), and `security_update_whitelisted_ips` (W! — **replaces** the whole list, not an append). Web-SSH / Adminer access: `security_whitelist_ip_siab` / `security_whitelist_ip_adminer` (W).\n\n**Sequence:**\n\n1. `security_get_whitelisted_ips` (or `_mysql`) — read what's there **now**\n2. Plan the new list — **including your own IP** (the update **replaces** the list; anything you omit is removed)\n3. **CONFIRM:** Show the user: \"The new list is: [...]. Does your IP X.X.X.X stay on the list? yes/no\"\n4. If the user is missing from the list — **stop and clarify**\n5. **Double CONFIRM (W!):** `security_update_whitelisted_ips` with the complete new list\n6. **VERIFY:** re-read the list, then try SSH immediately (if it doesn't work — Cloudways support to restore)\n\n> **Nightmare scenario to avoid:** updating the whitelist + removing your own IP + no alternative SSH. The only way out — the Cloudways UI (panic) or a support ticket (time). Be careful.\n\n---\n\n## 8. Disk cleanup\n\n**When:** disk usage > 80%\n\n**Priority order:**\n\n1. `server_disk_usage_fetch` (init) then `monitoring_server_summary` (read) — where's the space?\n2. **CONFIRM:** `app_purge_cache` (W) for all the suspect apps\n3. If not enough: **CONFIRM:** `server_disk_cleanup_*` (W) — Cloudways magic cleanup\n4. If not enough: upgrade size (UI only) or manual SSH to clean logs\n5. **VERIFY:** `server_disk_usage_fetch` + `monitoring_server_summary` again\n\n> `server_disk_cleanup_*` may delete logs. If the client must keep logs (compliance, debugging), export them manually first.\n\n---\n\n## 9. Enforce HTTPS\n\n**When:** An old site still running on HTTP / client wants an SEO/security boost\n\n**Sequence:**\n\n0. **List every hostname the application serves, because the write covers all of them.**\n   `app_enforce_https_update` turns on the redirect for the application, not for one domain —\n   and steps 1 and 2 below are per **hostname**. The primary domain is in the roster you hold;\n   the aliases are not: `app_list` carries the primary `domain` only, and the alias tools\n   (`app_cname_update`, `app_aliases_update`) are writes with no read counterpart. Read them\n   from the application's Domain Management page in the Cloudways UI, or a filtered direct API\n   call. Then run steps 1 and 2 **once per hostname**. An alias with no matching origin\n   certificate, or one behind a Flexible-mode proxy while the primary is not, passes nothing\n   and breaks — certificate errors, or the loop — the moment the redirect goes on. The write in\n   step 4 waits until every hostname has passed both.\n1. **Is anything in front, and does the certificate visitors will meet pass?** The handshake\n   only — what the application answers *after* it is step 2's job — and both must pass, **for\n   every hostname from step 0** (`<hostname>` below is each of them in turn), before step 4.\n   The DNS gate comes **first**, because it decides which certificate matters:\n   `R4=$(dig @1.1.1.1 +noall +comments +answer <hostname> A) && R6=$(dig @1.1.1.1 +noall +comments +answer <hostname> AAAA) && printf '%s\\n%s\\n' \"$R4\" \"$R6\" | grep -c 'status: NOERROR' | grep -qx 2 && A=$(printf '%s\\n%s\\n' \"$R4\" \"$R6\" | awk '$4==\"A\"||$4==\"AAAA\"{print $5}') && [ -n \"$A\" ] && printf '%s\\n' \"$A\" || { echo 'DNS gate FAILED: lookup error, non-NOERROR rcode, or no address' >&2; false; }`\n   — compare **every** answer, A and AAAA both, against the server's own addresses (from the\n   `server_list` row you hold), and classify **per answer**, not per hostname: an answer that\n   is the server means clients on that family reach the origin directly; an answer that is\n   anything else means clients on that family go through a CDN or reverse proxy, regardless of\n   what certificate it presents and even if its issuer matches the origin's. A hostname whose\n   A record is the origin and whose AAAA record is a proxy is **both**, and both branches\n   below apply to it — every IPv4 client reaches the origin, every IPv6 client reaches the\n   edge, and each path has to be right on its own.\n   - **Any answer is the server — some or all visitors reach the origin directly.** Then the\n     origin's certificate is one that browsers will be handed, and it must pass the pinned\n     public roots: `env -u CURL_CA_BUNDLE -u SSL_CERT_FILE -u SSL_CERT_DIR curl -q --cacert \"$HOME/.config/cloudways-mcp/cacert.pem\" -sS -o /dev/null --max-time 15 --noproxy '*' --resolve <hostname>:443:<server-ip> https://<hostname>/` must exit\n     **0** — chain + hostname + dates, at the Cloudways server itself. Exit 60 means there is\n     no certificate browsers accept here **yet**, and which of two things that is decides where\n     you go: if none was ever issued (a fresh app answers with Cloudways' self-signed default),\n     go to step 3 and **install one**, then come back and re-run; if one is installed and\n     failing (expired, wrong host), fix or reissue it first (sections 2 and 3) and re-run. What\n     exit 60 never permits is step 4 — enforcing HTTPS now would redirect production traffic\n     onto a certificate browsers reject.\n   - **Any answer is not the server — some or all visitors go through a proxy.** Its origin\n     mode has to be **Full (strict)**, or at least Full,\n     confirmed in that proxy's own settings before this write; in Flexible mode the proxy\n     reaches the origin over HTTP, the origin's new redirect sends it back to HTTPS, and the\n     site loops. The origin's certificate is then judged by **the proxy's origin policy, not by\n     this machine's trust store**: in Full it may be anything the proxy accepts, an\n     origin-CA certificate included; in Full (strict) it must be publicly trusted **or** issued\n     by that proxy's own origin CA — a Cloudflare Origin CA certificate is the normal, correct\n     case here, and the origin command above would call it invalid (exit 60) while the proxy\n     trusts it and visitors on that path never see it. Do not run the origin check against a\n     site whose **every** answer is a proxy and read exit 60 as \"replace the certificate\" —\n     but if the hostname also has a direct answer (the mixed case above), that allowance is\n     gone: the origin certificate *is* handed to the direct family's browsers, and the direct\n     branch's browser-trust check applies to it as well. The certificate visitors **will** be handed\n     is the edge's, so that is what must pass the trust store — at **every** answer, pinned one\n     at a time, since a request with no pin tests one address only (curl keeps the first\n     connection to succeed; measured, three plain requests to a dual-stack hostname all landed\n     on the same IPv6 address and never touched the IPv4 answer, so an edge family with an\n     expired certificate hides behind a healthy one). With `$A` from this hostname's gate:\n     `printf '%s\\n' \"$A\" | while read -r ip; do env -u CURL_CA_BUNDLE -u SSL_CERT_FILE -u SSL_CERT_DIR curl -q --cacert \"$HOME/.config/cloudways-mcp/cacert.pem\" -sS -o /dev/null --max-time 15 --noproxy '*' --resolve \"<hostname>:443:$ip\" -w \"$ip %{http_code}\\n\" https://<hostname>/ || echo \"$ip FAILED (curl exit $?)\"; done`\n     — every line must carry a status that is **not 5xx** and no line may say `FAILED`\n     (a failed handshake prints `<ip> 000` and then `<ip> FAILED (curl exit 60)`, measured).\n     An answer that is the server gets the direct branch's check again here, same command,\n     same verdict. 525/526 is the edge admitting it cannot complete TLS to the origin, which is the origin-policy failure the paragraph above is about, arriving as an HTTP status.\n   Do not read any of this off `openssl x509 -dates`, which prints dates for a broken\n   certificate just as happily.\n2. **The HTTPS answer must not send anyone back to HTTP — at any hop.** Step 1 validated the\n   handshake and nothing after it: a valid certificate in front of an application that answers\n   `https://` with `301 Location: http://…` still exits 0, and so does one whose first hop is a\n   harmless `https://www.` canonical redirect while the **second** hop goes back to `http://`.\n   So follow the whole chain, the way a browser will, **before** the write — and refuse\n   **any** hop to HTTP, not just an HTTP ending, because `%{url_effective}` reports only the\n   final URL and a chain that dips to `http://` and climbs back to `https://` would otherwise\n   pass:\n   `printf '%s\\n' \"$A\" | while read -r ip; do env -u CURL_CA_BUNDLE -u SSL_CERT_FILE -u SSL_CERT_DIR curl -q --cacert \"$HOME/.config/cloudways-mcp/cacert.pem\" -sS -o /dev/null --max-time 15 --noproxy '*' --resolve \"<hostname>:443:$ip\" -L --max-redirs 5 --proto-redir '=https' -w \"$ip %{http_code} %{url_effective} %{num_redirects}\\n\" https://<hostname>/ || echo \"$ip FAILED (curl exit $?)\"; done`\n   — one run per public answer (`$A` from this hostname's gate in step 1), for the reason\n   step 1 gives: a request with no pin follows the chain at **one** address, and the\n   application behind the other family may answer differently.\n   (Quote `'=https'` — in zsh, macOS's default shell, a bare `=https` is expanded as a\n   command lookup and the line fails with `https not found`. `--noproxy '*'` is here for the\n   same reason as on the origin check: with `HTTPS_PROXY` set, an intercepting proxy's block\n   or login page would pass this check — any status, small hop count — without the\n   application's redirects ever being seen. It stops curl using a configured proxy and nothing\n   else: DNS still resolves publicly, so the check still reaches the site's CDN as a visitor\n   would.) Pass is, on **every** line: no `FAILED`, an\n   `https://` effective URL, a small hop count — and a terminal status that is **not 5xx**. A\n   `401` from Basic Auth on the root, a `403` from a WAF, a `204`/`404` from an API root are\n   all fine answers over HTTPS: this check is about the path, not the application's opinion of\n   the request. But a **5xx** over HTTPS is a broken HTTPS path, and behind a proxy the\n   specific codes matter: Cloudflare's **525** (SSL handshake to the origin failed) and **526**\n   (origin certificate invalid) — and the 52x family generally — are the *edge* reporting that\n   it could not reach the origin over TLS, behind a perfectly valid edge certificate. `-sS`\n   without `--fail` leaves curl's exit code to the transport, so these arrive as `exit 0` with\n   the status in the `-w` output (measured: 525, 526 and 502 all exit 0) — read the status.\n   Redirecting a working HTTP path onto any of them is the outage this step exists to prevent. `curl: (1) Protocol \"http\" disabled (in redirect)`\n   (measured: exit 1, before curl ever connects to the HTTP target — the line reads\n   `<ip> FAILED (curl exit 1)`) or an effective URL\n   beginning `http://` means the app itself is pushing HTTPS visitors back to HTTP somewhere\n   in its chain; on WordPress that is `WP_HOME` / `WP_SITEURL` still set to `http://`, the\n   usual cause on a site that has never had HTTPS enforced. Enforcing now produces the loop\n   the audit warned about: the server redirects `http→https`, the app redirects `https→http`,\n   and every browser bounces between them until it gives up.\n   `curl: (47) Maximum (5) redirects followed` is a **different** failure and must not be sent\n   to the same repair: with `--proto-redir '=https'` an HTTP hop is never followed, so `47` can\n   only be an **HTTPS-only** loop or a finite chain longer than five hops — `www`/apex\n   ping-pong, an authentication redirect that never settles, a plugin's canonical rule\n   fighting a server rule. Changing `home`/`siteurl` for that changes the scheme of a site\n   whose problem is not the scheme. Diagnose it hop by hop instead — `--max-redirs 0 -w\n   '%{http_code} %{redirect_url}\\n'` on the start URL, then on the URL it named, and so on\n   until the pair that bounces is in front of you — and fix that rule where it lives. Either\n   way, step 4 waits until this step passes.\n   **For the downgrade case, fix the application first**, then re-run this step until it\n   passes — and that repair is a write of its own, on the site's database, with its own\n   confirmation:\n   - Read before writing: `wp option get home; wp option get siteurl`. The hostname in those\n     values is the site's canonical host and **stays**; only the scheme changes. Never write\n     `https://<hostname>` from this step's placeholder — when `<hostname>` is an alias, that\n     would promote the alias to canonical and change every generated URL on the site.\n   - Back up first (§4, `app_backup`, confirmed) — a wrong `siteurl` locks `wp-admin` out\n     immediately.\n   - **CONFIRM:** with the standard block (`tool: wp option update`, `target`: the app and its\n     current `home`/`siteurl`, `expected impact`: scheme `http://` → `https://`, hostname\n     unchanged), then:\n     `wp option update home \"$(wp option get home | sed 's#^http://#https://#')\" && wp option update siteurl \"$(wp option get siteurl | sed 's#^http://#https://#')\"`\n     (or edit the two constants in `wp-config.php` the same way). This confirmation is for this\n     write; step 4 has its own. The pin covers `<hostname>` only, on purpose: a hop to another\n   hostname (`www.`) resolves publicly, and that is right — that hostname is in step 0's list\n   and gets its own gate and its own per-answer pass; the origin itself was already checked in\n   step 1.\n3. If there's no SSL: install one first — `security_lets_encrypt_install` (W, see sections 2 and 3). Enforcing HTTPS without a valid cert will break the site.\n4. **CONFIRM:** `app_enforce_https_update` (W) — toggles the HTTP→HTTPS redirect (this is separate from installing the cert)\n5. Verify the **whole chain** the way a browser walks it, not the first hop — **for every\n   hostname from step 0**, since an alias can loop for host-specific CDN or origin reasons\n   while the primary passes:\n   `printf '%s\\n' \"$A\" | while read -r ip; do env -u CURL_CA_BUNDLE -u SSL_CERT_FILE -u SSL_CERT_DIR curl -q --cacert \"$HOME/.config/cloudways-mcp/cacert.pem\" -sS -o /dev/null --max-time 15 --noproxy '*' --resolve \"<hostname>:80:$ip\" --resolve \"<hostname>:443:$ip\" -L --max-redirs 6 --proto-redir '=https' -w \"$ip %{http_code} %{url_effective} %{num_redirects}\\n\" http://<hostname>/ || echo \"$ip FAILED (curl exit $?)\"; done`\n   — per public answer, `$A` from this hostname's gate (re-run the gate line if the shell has\n   moved on), and **both** ports pinned: `--resolve` is per host:port, so pinning `:443` alone\n   leaves the `http://` hop — the one this write changed — free to land on whichever address\n   curl reaches first (measured). The limit is **6**, not step 2's 5, on purpose: step 2\n   passed a chain of up to five hops starting at `https://`, and this check starts one hop\n   earlier — the `http→https` hop the write just added — so a chain that was exactly five\n   hops and healthy would be six here, and `--max-redirs 5` would call it a loop (measured:\n   a six-hop chain exits 47 at `--max-redirs 5` and 0 at 6). One more than the preflight\n   allowed, and no more.\n   The start is `http://` on purpose — that is what the new redirect acts on — and\n   `--proto-redir` governs only the hops afte\n\nArchive v1.5.2: 11 files, 66831 bytes\n\nFiles: bridge/package-lock.json (35638b), bridge/package.json (365b), references/installation.md (30469b), references/tools-catalog.md (32047b), references/workflows-automation.md (18248b), references/workflows-maintenance.md (12394b), references/workflows-monitoring.md (9207b), references/workflows-onboarding.md (15537b), skill-card.md (3307b), SKILL.md (19022b), _meta.json (132b)\n\nArchive v1.5.1: 11 files, 62122 bytes\n\nFiles: bridge/package-lock.json (36177b), bridge/package.json (324b), references/installation.md (22509b), references/tools-catalog.md (32047b), references/workflows-automation.md (15837b), references/workflows-maintenance.md (12394b), references/workflows-monitoring.md (7662b), references/workflows-onboarding.md (15449b), skill-card.md (3496b), SKILL.md (19022b), _meta.json (132b)\n\nArchive v1.5.0: 9 files, 45210 bytes\n\nFiles: references/installation.md (17122b), references/tools-catalog.md (31834b), references/workflows-automation.md (12007b), references/workflows-maintenance.md (12256b), references/workflows-monitoring.md (7046b), references/workflows-onboarding.md (10609b), skill-card.md (3590b), SKILL.md (17295b), _meta.json (132b)\n\nArchive v1.4.1: 9 files, 43251 bytes\n\nFiles: references/installation.md (12286b), references/tools-catalog.md (31834b), references/workflows-automation.md (12007b), references/workflows-maintenance.md (12256b), references/workflows-monitoring.md (7046b), references/workflows-onboarding.md (10609b), skill-card.md (3554b), SKILL.md (17295b), _meta.json (132b)\n\nArchive v1.4.0: 9 files, 42638 bytes\n\nFiles: references/installation.md (10939b), references/tools-catalog.md (31834b), references/workflows-automation.md (12007b), references/workflows-maintenance.md (12256b), references/workflows-monitoring.md (7046b), references/workflows-onboarding.md (10609b), skill-card.md (3343b), SKILL.md (17295b), _meta.json (132b)\n\nArchive v1.3.1: 9 files, 41981 bytes\n\nFiles: references/installation.md (9579b), references/tools-catalog.md (31834b), references/workflows-automation.md (12007b), references/workflows-maintenance.md (12256b), references/workflows-monitoring.md (7046b), references/workflows-onboarding.md (10609b), skill-card.md (2918b), SKILL.md (17295b), _meta.json (132b)\n\nArchive v1.2.2: 9 files, 32449 bytes\n\nFiles: references/installation.md (6811b), references/tools-catalog.md (14354b), references/workflows-automation.md (10263b), references/workflows-maintenance.md (11281b), references/workflows-monitoring.md (6386b), references/workflows-onboarding.md (9197b), skill-card.md (3197b), SKILL.md (15094b), _meta.json (132b)\n\nArchive v1.2.1: 9 files, 30949 bytes\n\nFiles: references/installation.md (6811b), references/tools-catalog.md (12910b), references/workflows-automation.md (10263b), references/workflows-maintenance.md (10874b), references/workflows-monitoring.md (6386b), references/workflows-onboarding.md (9197b), skill-card.md (3510b), SKILL.md (12506b), _meta.json (132b)\n\nArchive v1.2.0: 9 files, 30432 bytes\n\nFiles: references/installation.md (6811b), references/tools-catalog.md (12296b), references/workflows-automation.md (10263b), references/workflows-maintenance.md (10874b), references/workflows-monitoring.md (6386b), references/workflows-onboarding.md (9197b), skill-card.md (2931b), SKILL.md (12493b), _meta.json (132b)","readmeExcerpt":"Skill: Cloudways MCP Owner: benkalsky Summary: Operational guide for managing Cloudways servers and applications, across one or several Cloudways accounts, via the official Cloudways MCP server (Cloudways' hosted MCP / Remote MCP, per their support docs). Use whenever the user mentions Cloudways, a Cloudways server or app, server monitoring, app monitoring, bandwidth, disk usage, PHP/MySQL/traffic analytics, Varnish ","codeSnippets":[],"executableExamples":[{"language":"text","snippet":"🔒 Confirm operation execution?\n   Account: clientA (mcp__cloudways-clientA)\n   Tool: server_stop\n   Server: production-shop-il (ID: 1234567)\n   Applications affected: woocommerce-prod, staging-clone, admin-tools\n   Impact: all 3 applications will be offline until a manual restart\n   Proceed? (yes / no / pause and check backup first)"},{"language":"text","snippet":"mcp__cloudways-clientA__server_list\nmcp__cloudways-clientB__server_list\nmcp__cloudways-internal__server_list"},{"language":"text","snippet":"Example tagging in the response:\n| Account  | Server           | disk |\n|----------|------------------|------|\n| clientA  | prod-shop-il     | 87%  |\n| clientB  | prod-blog        | 41%  |\n| internal | ops-tools        | 63%  |"},{"language":"text","snippet":"1. server_list               → list of all servers\n2. copilot_insights_list     → active insights/alerts"},{"language":"text","snippet":"1. server_list                  → the fleet (server_get would add master credentials)\n2. monitoring_server_graph      → metrics (CPU/mem/etc.)\n3. app_list                     → per server, the application roster. server_list returns an\n                                  app COUNT, not the IDs step 4 needs. One call per server,\n                                  and rule 7 on what that one payload may carry\n4. monitoring_app_summary       → for each application from step 3\n5. copilot_insights_list        → open insights/alerts\n6. monitoring_server_summary    → disk/bandwidth; if disk > 80% — red flag"},{"language":"text","snippet":"1. app_list                  → find the app: id, label, type, version, domain\n2. app_settings_get          → its setting flags (XML-RPC, GEO-IP, password protection, …)\n3. monitoring_app_summary    → what it is doing right now"}],"parameters":null,"dependencies":[],"permissions":[],"extractedFiles":[{"path":"SKILL.md","content":"---\nname: cloudways-mcp\nversion: 1.5.3\nlicense: MIT\ndescription: |\n  Operational guide for managing Cloudways servers and applications, across one or several Cloudways accounts, via the official Cloudways MCP server (Cloudways' hosted MCP / Remote MCP, per their support docs).\n  Use whenever the user mentions Cloudways, a Cloudways server or app, server monitoring, app monitoring, bandwidth, disk usage, PHP/MySQL/traffic analytics, Varnish cache, app cloning, backups/restore on Cloudways, Git deployments on Cloudways, SSL/Let's Encrypt on Cloudways, malware scans / Security Suite, staging sync, team members, AgencyOS client billing, or running an audit/onboarding on a Cloudways-hosted client site.\n  Any write operation (start/stop/restart server, backup, restore, update CNAME, purge cache, change service state, git pull, SSL install/revoke, IP whitelist update, staging sync, team/billing changes, delete server/app) requires explicit confirmation of target server/app and intended action before execution.\n---\n\n# Cloudways MCP — Operational Skill\n\nManaging Cloudways infrastructure through the Cloudways MCP server.\n\n> **Connection:** This skill targets the **official Cloudways (Remote) MCP** — an MCP hosted by Cloudways at `https://mcp.cloudways.com/mcp/` that you connect to directly. The source of truth for connecting is the **official article**: `support.cloudways.com/en/articles/14654372`. See `references/installation.md`.\n>\n> **Tool names match the official articles.** The tool catalog and workflows in this skill use the official Cloudways MCP tool names (verified against the setup article and the dedicated [tools article](https://support.cloudways.com/en/articles/15798823-cloudways-mcp-server-tools)). Always treat the live `mcp__cloudways*__*` tools as the source of truth if Cloudways changes them (see \"Versioning and source of truth\" below).\n\n> **Context:** The skill is built for day-to-day work managing clients/environments on Cloudways — monitoring, routine maintenance, onboarding/audit for new clients, and automations. All monetary values reported by the API are in $ (USD), not ₪.\n\n---\n\n## Quick Route\n\n| Intent | Load |\n|--------|------|\n| Initial installation/configuration of the MCP server | `references/installation.md` |\n| Don't know which tool exists / searching for a tool by name | `references/tools-catalog.md` |\n| Monitoring, status check, bandwidth, analytics | `references/workflows-monitoring.md` |\n| Cache clear, SSL, backup, restart, IP whitelist | `references/workflows-maintenance.md` |\n| New audit / onboarding a new client | `references/workflows-onboarding.md` |\n| Building an automated workflow for n8n/Make/Claude Code | `references/workflows-automation.md` |\n| Multiple Cloudways accounts / multi-account configuration | `references/installation.md` (Multi-account section) |\n\n**Load only what's needed.** Maximum 2-3 references per task. If the user just asks \"show me my servers\", don't load the entire catalog — call `server_list` "},{"path":"_meta.json","content":"{\n  \"ownerId\": \"kn7afv05r120atbc75whrv1zkx825tt5\",\n  \"slug\": \"cloudways-mcp\",\n  \"version\": \"1.5.3\",\n  \"publishedAt\": 1789340302484\n}"},{"path":"references/installation.md","content":"# Installation — Cloudways MCP Server\n\nThis skill targets the **official Cloudways (Remote) MCP** — a Cloudways-hosted MCP at `https://mcp.cloudways.com/mcp/` that you connect to directly, without self-hosting.\n\n> **Source of truth:** [How to Use Cloudways MCP Server for AI-Based Server Management](https://support.cloudways.com/en/articles/14654372-how-to-use-cloudways-mcp-server-for-ai-based-server-management). The endpoint, headers, and steps below are from that article; if Cloudways changes them, the article wins.\n\n**Prerequisites**\n\n- A valid Cloudways account with API access.\n- A Cloudways **Access Token** (see Step 1).\n- **Node.js v24.14.1+** (only for the Claude Desktop path, which uses the `mcp-remote` bridge). Claude Code connects over native HTTP and does not need it.\n\n---\n\n## Step 1 — Generate an Access Token\n\n1. Log in to [platform.cloudways.com](https://platform.cloudways.com).\n2. Open the **API** section (bottom-left of the platform — the same place the legacy API key lived).\n3. Generate an **Access Token** (Access Token Details → Create Access Token). Note: only the **primary account owner** can create/manage API credentials — team-member accounts have no API Integration section. Give the token a name, an expiration period (1 day → never; shortest that works), and a **role**:\n   - **READ** — look-ups only (status, config, monitoring). Recommended starting point for every new integration, and for monitoring-only connections.\n   - **LIMITED** — only the endpoint groups you select.\n   - **FULL ACCESS** — everything the account can do, including destructive actions. Only for connections that genuinely need to make changes.\n4. Copy the token.\n\n> Treat the token like a password — never commit it or print it in responses. Prefer one token **per integration** (per MCP connection), each with the minimum role it needs, so tokens can be revoked individually.\n\n> **Legacy API key — deprecated.** The old flow (API key + `X-CW-Email`/`X-CW-Api-Key` headers) still works but the API key **stops working on October 15, 2026**. If you have an existing connection using the old headers, regenerate as an Access Token and update the connection before then. Until migrated, a legacy connection retains **unrestricted full-account access** — the RBAC roles apply only to Access Tokens. And note there is still **no per-tool permission control at the MCP layer** beyond the token's role: a FULL ACCESS token can call every tool.\n\n**Required headers** (every request; header names are case-sensitive):\n\n| Header | Value |\n|--------|-------|\n| `X-Access-Token` | your Cloudways Access Token |\n| `X-Mcp-Host` | the client you connect from — official values: `claude-code`, `claude-desktop`, `cursor`, `Devin` (the client formerly called Windsurf — the article's own snippet sends the capitalised `Devin`, and header values are case-sensitive), `vs-code`, `gemini-cli`, `codex`, `codex-cli` |\n\n---\n\n## Step 2 — Connect Claude to the MCP\n\n### Env var (zero-config — devices and c"},{"path":"references/tools-catalog.md","content":"# Tools Catalog — Cloudways MCP\n\nThe official tool catalog for the **Cloudways (Remote) MCP** (`https://mcp.cloudways.com/mcp/`), taken from the official support articles: [Cloudways MCP Server Tools](https://support.cloudways.com/en/articles/15798823-cloudways-mcp-server-tools) (the dedicated tool reference) and [How to Use Cloudways MCP Server](https://support.cloudways.com/en/articles/14654372-how-to-use-cloudways-mcp-server-for-ai-based-server-management) (setup). Tools appear in Claude as `mcp__cloudways__<tool>` (or `mcp__cloudways-<client>__<tool>` per account).\n\n**MCP v1.2 scale:** **244 tools** — 241 spread across 22 toolsets plus the 3 meta-tools, live-verified 2026-07-20 (see the toolset table below). This matches the [v1.2 announcement](https://www.cloudways.com/blog/cloudways-mcp-v1-2-112-new-tools-role-based-access-tokens-and-full-cloudways-api-coverage/) exactly (112 added in v1.2, covering essentially the full Cloudways API). Your client initially sees only **65 tools** (62 high-frequency direct tools + the 3 meta-tools below). Those 62 are **members of the toolsets**, not a separate tier, so the hidden remainder is **179** (241 − 62) — discovered and invoked on demand through the meta-tools.\n\n> **The official tools article over-lists.** It documents endpoint-style aliases for the Security and Service categories (`security_dns_create`, `service_state_update`, …) plus whole categories — Bot Protection, Client Billing, CloudwaysCDN legacy, a \"Lists API\", `oauth_access_token_generate` — that **have no live counterpart**. Live re-enumeration on 2026-07-20 found 64 such phantom identifiers and **zero** real alias mismatches: every name in the tables below matches the live `tool_name` exactly. They have been removed from this catalog; if you see them in Cloudways' docs, don't build automation on them.\n\nFlags: **R** = read-only · **W** = write (requires confirmation) · **W!** = destructive / irreversible (requires double confirmation).\n\n> The **live server is the source of truth.** Every section of this file — including the \"New in MCP v1.2\" half — was reconciled against the live MCP on 2026-07-20 via `list_available_toolsets` + `get_toolset_tools` on a connected account: all 22 toolsets enumerated, every tool name verified byte-for-byte, phantom entries removed. You do **not** need to memorize tool names to use the MCP (it resolves natural language), but knowing them sharpens prompts and lets you confirm an action maps to the tool you expect.\n>\n> **Most tools are grouped into on-demand toolsets.** Only a subset appears in your default tool list; the rest live inside toolsets (`apps`, `servers`, `git`, `ssh_keys`, `projects`, `staging_management`, `dns_made_easy`, `cloudflare`, and the v1.2 additions) and are invoked through the `execute_tool` proxy. A tool being absent from the default list does **not** mean it is absent from the MCP — `git_*`, `project_*`, `ssh_key_*`, `app_restore_rollback`, `app_db_password_update`, `server_local_bac"},{"path":"references/workflows-automation.md","content":"# Workflows — Automation & Integration\n\nHow to build automations around the Cloudways MCP — connecting to n8n, Make.com, Claude Code, or cron jobs. Suitable for any infrastructure management stack.\n\n> **Basic principle:** The MCP server is first and foremost an interface for Claude. For automation without human-in-the-loop, it's usually **better to call the Cloudways API directly** (curl/n8n HTTP node) rather than going through the MCP. The MCP adds overhead, and in automation it's not necessary.\n\n---\n\n## When to use MCP vs. direct API?\n\n| Scenario | Choose |\n|--------|-----|\n| Live conversation in Claude / Claude Code | MCP |\n| Daily report generated automatically | Direct API (n8n/Make) |\n| Alerting → automatic action | Direct API |\n| Audit one-off | MCP |\n| CI/CD trigger (post-deploy backup, etc.) | Direct API |\n| Claude Code headless running pipelines | MCP (if Claude is the orchestrator) |\n\nThe MCP saves time in an interactive context. In automation — overhead.\n\n---\n\n## 1. Cloudways API direct (for automation)\n\n### Authentication flow (current — Access Token, API v2)\n\nGenerate an **Access Token** at platform.cloudways.com → **API Integration** → Create Access Token (name + expiration + scope; use **Read-Only** for reporting loops, **Limited** for specific write workflows). Only the **primary account owner** can create/manage tokens — team-member accounts have no API Integration section. There is **no token-exchange step**: send the Access Token directly on every request.\n\n```bash\n# Current flow: one header, no exchange\ncurl -sH \"Authorization: Bearer $CLOUDWAYS_ACCESS_TOKEN\" \\\n     \"https://api.cloudways.com/api/v2/server\"\n```\n\n- **API v2 is the current API** ([announcement](https://www.cloudways.com/blog/introducing-cloudways-api-v2/)); the official migration rule is \"replace `v1` with `v2` in the API call URL structure\". v1 is deprecated (its docs remain up until **March 2026**).\n- Rate limit: 100 requests/minute (per the [access-tokens article](https://support.cloudways.com/en/articles/5136065)).\n- The token is long-lived per its configured expiration (1 day → never) — no hourly regeneration; rotate per your security policy, and revoke it the moment the integration is retired.\n\n### Common endpoints\n\nPaths are relative to `https://api.cloudways.com/api/v2` (same structure as v1 during the transition):\n\n| Endpoint | Method | What it is |\n|----------|--------|--------|\n| `/server` | GET | list servers |\n| `/server/{id}` | GET | server details |\n| `/app/manage/varnish` | POST | Varnish operations |\n| `/app/manage/backup` | POST | trigger backup |\n| `/app/letsencrypt_renew` | POST | renew SSL |\n| `/app/analytics/visitor` | GET | traffic |\n| `/app/manage/cache` | POST | clear cache |\n\nFull documentation: `https://developers.cloudways.com/docs/` (Redocly portal + API Playground; the Playground runs against your **real** account — prefer a test server).\n\n### Legacy authentication (works until 2026-10-15 only)\n\n> The `email` + `api_key` OAuth exch"}],"languages":[],"docsSourceLabel":"CLAWHUB","editorialOverview":null,"editorialQuality":{"score":100,"threshold":65,"status":"thin","wordCount":2562,"uniquenessScore":35,"reasons":["uniqueness-below-45"]}},"media":{"evidence":{"source":"no-media","verified":false,"confidence":"low","updatedAt":"2026-10-10T05:20:18.733Z","emptyReason":"No screenshots, media assets, or demo links are available."},"primaryImageUrl":null,"mediaAssetCount":0,"assets":[],"demoUrl":null},"ownerResources":{"evidence":{"source":"unclaimed","verified":false,"confidence":"low","updatedAt":"2026-10-10T05:20:18.733Z","emptyReason":"This page has not been claimed by the agent owner."},"hasCustomPage":false,"customPageUpdatedAt":null,"customLinks":[],"structuredLinks":{"docsUrl":null,"demoUrl":null,"supportUrl":null,"pricingUrl":null,"statusUrl":null},"customPage":null},"relatedAgents":{"evidence":{"source":"protocol-neighbors","verified":false,"confidence":"medium","updatedAt":"2026-10-10T10:43:16.292Z","emptyReason":null},"items":[{"id":"8ebccd8e-3863-4187-8355-c3f14e1f9edf","entityType":"agent","canonicalPath":"/agent/iofficeai-aionui","slug":"iofficeai-aionui","name":"AionUi","description":"Free, local, open-source 24/7 Cowork app and OpenClaw for Gemini CLI, Claude Code, Codex, OpenCode, Qwen Code, Goose CLI, Auggie, and more | 🌟 Star if you like it!","url":"https://github.com/iOfficeAI/AionUi","homepage":"https://www.aionui.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-10-09T19:11:12.944Z","createdAt":"2026-02-25T03:38:16.584Z","downloads":null},{"id":"b917f68a-ebff-438e-84f8-3f4b2494c0bc","entityType":"agent","canonicalPath":"/agent/activepieces-activepieces","slug":"activepieces-activepieces","name":"activepieces","description":"AI Agents & MCPs & AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows & AI Agents • MCPs for AI Agents","url":"https://github.com/activepieces/activepieces","homepage":"https://www.activepieces.com","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-15T02:22:12.426Z","createdAt":"2026-02-25T03:38:12.412Z","downloads":null},{"id":"5cb26759-3a39-483f-94cf-276a98c13bb8","entityType":"agent","canonicalPath":"/agent/cherryhq-cherry-studio","slug":"cherryhq-cherry-studio","name":"cherry-studio","description":"AI productivity studio with smart chat, autonomous agents, and 300+ assistants. Unified access to frontier LLMs","url":"https://github.com/CherryHQ/cherry-studio","homepage":"https://cherry-ai.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-11T14:38:40.986Z","createdAt":"2026-02-25T03:38:19.379Z","downloads":null},{"id":"6f6582d0-5d76-4f0f-b81d-86520247950b","entityType":"agent","canonicalPath":"/agent/copilotkit-copilotkit","slug":"copilotkit-copilotkit","name":"CopilotKit","description":"The Frontend for Agents & Generative UI. React + Angular","url":"https://github.com/CopilotKit/CopilotKit","homepage":"https://docs.copilotkit.ai","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-03-25T09:50:57.846Z","createdAt":"2026-02-25T03:39:14.617Z","downloads":null}],"links":{"hub":"/agent","source":"/agent/source/clawhub","protocols":[{"label":"OpenClaw","href":"/agent/protocol/openclew"}]}}}