From 803e3d1e727accb275cd660d04cc84d12c76e311 Mon Sep 17 00:00:00 2001 From: Arnas Donauskas Date: Wed, 30 Sep 2026 15:08:13 +0300 Subject: [PATCH] feat: ship the web hosting skills and catch up with hostinger-api-mcp 2.5.0 Replace the five hand-written skills with the seven published by hostinger/api-mcp-server, synced by the new scripts/sync-skills.mjs. Register the agency-hosting server, move the rules, agent, command and README to the current operation names and the search/execute server model, and drop the superseded nodejs-deployments rule. --- .cursor-plugin/plugin.json | 2 +- .github/workflows/ci.yml | 23 + CHANGELOG.md | 27 + README.md | 87 ++- agents/hostinger-deployment-reviewer.md | 37 +- commands/hostinger-status.md | 27 +- mcp.json | 21 +- rules/confirm-destructive-actions.mdc | 60 +- rules/nodejs-deployments.mdc | 58 -- rules/prefer-mcp-tools.mdc | 43 +- scripts/check-tool-names.mjs | 9 +- scripts/mcp-tools.json | 696 ++++++++++-------- scripts/sync-skills.mjs | 69 ++ skills/audit-hosting/SKILL.md | 83 +++ skills/connect-domain/SKILL.md | 69 ++ skills/deploy-nodejs-app/SKILL.md | 51 -- skills/deploy-to-hosting/SKILL.md | 90 +++ skills/diagnose-build-failure/SKILL.md | 55 -- skills/hostinger-headless/SKILL.md | 56 ++ .../hostinger-headless/references/DATABASE.md | 49 ++ .../references/DEPLOYMENT.md | 34 + skills/hostinger-headless/references/SETUP.md | 34 + skills/hostinger-headless/references/STORE.md | 52 ++ .../references/WORDPRESS.md | 51 ++ skills/maintain-wordpress/SKILL.md | 74 ++ skills/manage-dns-records/SKILL.md | 74 -- skills/migrate-to-hosting/SKILL.md | 78 ++ skills/query-deployment-logs/SKILL.md | 53 -- skills/troubleshoot-website/SKILL.md | 104 +++ skills/troubleshoot-wordpress/SKILL.md | 69 -- 30 files changed, 1436 insertions(+), 799 deletions(-) delete mode 100644 rules/nodejs-deployments.mdc create mode 100755 scripts/sync-skills.mjs create mode 100644 skills/audit-hosting/SKILL.md create mode 100644 skills/connect-domain/SKILL.md delete mode 100644 skills/deploy-nodejs-app/SKILL.md create mode 100644 skills/deploy-to-hosting/SKILL.md delete mode 100644 skills/diagnose-build-failure/SKILL.md create mode 100644 skills/hostinger-headless/SKILL.md create mode 100644 skills/hostinger-headless/references/DATABASE.md create mode 100644 skills/hostinger-headless/references/DEPLOYMENT.md create mode 100644 skills/hostinger-headless/references/SETUP.md create mode 100644 skills/hostinger-headless/references/STORE.md create mode 100644 skills/hostinger-headless/references/WORDPRESS.md create mode 100644 skills/maintain-wordpress/SKILL.md delete mode 100644 skills/manage-dns-records/SKILL.md create mode 100644 skills/migrate-to-hosting/SKILL.md delete mode 100644 skills/query-deployment-logs/SKILL.md create mode 100644 skills/troubleshoot-website/SKILL.md delete mode 100644 skills/troubleshoot-wordpress/SKILL.md diff --git a/.cursor-plugin/plugin.json b/.cursor-plugin/plugin.json index dbda907..4762d5b 100644 --- a/.cursor-plugin/plugin.json +++ b/.cursor-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "hostinger-connector", "displayName": "Hostinger Connector", - "version": "0.2.0", + "version": "0.3.0", "description": "MCP plugin to deploy and manage Hostinger websites, WordPress, domains, DNS, VPS, and subscriptions from inside Cursor.", "author": "Hostinger", "license": "MIT", diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4ff3c96..c0e1a40 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -61,3 +61,26 @@ jobs: echo "Run 'node scripts/sync-mcp-tools.mjs' and commit the result." git --no-pager diff --stat -- scripts/mcp-tools.json fi + + skills-drift: + runs-on: ubuntu-latest + continue-on-error: true + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 22 + + - name: Regenerate skills from the latest published server + run: node scripts/sync-skills.mjs + + - name: Report drift + run: | + if [ -z "$(git status --porcelain -- skills)" ]; then + echo "Skills are up to date with hostinger-api-mcp@latest." + else + echo "::warning::skills/ is behind hostinger-api-mcp@latest." + echo "Run 'node scripts/sync-skills.mjs' and commit the result." + git status --short -- skills + fi diff --git a/CHANGELOG.md b/CHANGELOG.md index db94af1..6cf6400 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,32 @@ # Changelog +## [0.3.0] - 2026-09-30 + +Catches the plugin up with `hostinger-api-mcp` 2.5.0, which renamed every operation after the CLI commands (2.1.0) and consolidated each server to `search`, `execute` and `multi-execute` (2.0.0). + +### Fixed + +- **Every operation name in the rules, agent, command and README had been renamed upstream.** Names such as `hosting_listWebsitesV1`, `DNS_updateDNSRecordsV1` and `hosting_deployJsApplication` no longer exist; the docs now use the current ones (`hosting_websites_list`, `dns_records_update`, `hosting_deploy-js-application`, …), checked against a refreshed catalog of 403 operations. +- **Agency Plan sites could not be managed.** `mcp.json` now registers `hostinger-agency-hosting-mcp` alongside the other eight servers. +- **The guidance said Node.js environment variables cannot be set through the API.** They can (`hosting_nodejs_replace-environment-variables`); the claim is removed from the rules and README. + +### Changed + +- **Skills now come from `hostinger/api-mcp-server`.** The five hand-written skills (`deploy-nodejs-app`, `diagnose-build-failure`, `manage-dns-records`, `query-deployment-logs`, `troubleshoot-wordpress`) are replaced by the seven the MCP server publishes: `troubleshoot-website`, `connect-domain`, `deploy-to-hosting`, `maintain-wordpress`, `audit-hosting`, `migrate-to-hosting` and `hostinger-headless`. +- `rules/prefer-mcp-tools.mdc` explains the three-tool server model and the current operation prefixes, including `wordpress_`, `agency-hosting_`, `dns_` and `vps_`. +- `rules/confirm-destructive-actions.mdc` keys confirmation off the `destructive` hint that `search` returns, and keeps a short list of the highest-risk operations. +- `/hostinger-status` covers Agency Plan sites and reads the latest Node.js build per site. +- `scripts/check-tool-names.mjs` checks only operation-prefixed names inside the synced `skills/`, since those are verified upstream and mention many request fields. + +### Removed + +- `rules/nodejs-deployments.mdc` — the `deploy-to-hosting` skill covers deploy methods, archive rules and build settings, with the current `app_type` list. + +### Added + +- `scripts/sync-skills.mjs` — replaces `skills/` with the skills from a published `hostinger-api-mcp` version or a local skills directory, leaving out the cold-start `entry/` bootstrap. +- An advisory `skills-drift` CI job that flags when `skills/` falls behind `hostinger-api-mcp@latest`. + ## [0.2.0] - 2026-08-06 Brings the plugin back in line with `hostinger-api-mcp` (now 1.29.0) and with the product surface of the Hostinger VS Code extension (1.3.2). The plugin was last touched on 2026-05-27, two days before OAuth shipped. diff --git a/README.md b/README.md index 6c09cb5..383cd4e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Hostinger Connector for Cursor -Official Cursor plugin for [Hostinger](https://hostinger.com/) — deploy and manage Hostinger websites, WordPress, domains, DNS, VPS, and subscriptions without leaving Cursor. +Official Cursor plugin for [Hostinger](https://hostinger.com/) — deploy and manage Hostinger websites, WordPress, Agency Plan sites, domains, DNS, VPS, and subscriptions without leaving Cursor. The plugin wires the official [`hostinger-api-mcp`](https://www.npmjs.com/package/hostinger-api-mcp) servers into Cursor, plus a set of skills, rules, an agent, and a command so the agent can take real actions on your Hostinger account. @@ -8,13 +8,13 @@ The plugin wires the official [`hostinger-api-mcp`](https://www.npmjs.com/packag ## What it does -- Deploy Node.js and static sites to Hostinger, then follow the server-side build to completion. -- Diagnose failed builds from real Hostinger build logs. -- Read, validate, and update DNS zone records, with snapshot-based rollback. -- Manage Hostinger-hosted WordPress: installations, plugins, themes, core, caches, PHP settings. -- Manage domains — availability, registration, transfers, locks, forwarding, WHOIS. -- Inspect VPS state, firewalls, snapshots, and metrics. -- Review subscriptions, renewals, and payment methods. +- Troubleshoot a site that is down, slow, erroring or failing to build, and apply the fix. +- Connect a domain end to end — DNS without losing email records, SSL, HTTPS redirect. +- Deploy static sites, Node.js apps, PHP apps and WordPress plugins or themes; set up Git auto-deploy, environment variables and databases. +- Keep WordPress updated and secure across one site or all of them. +- Audit the whole hosting account and get a prioritised to-do list. +- Migrate a site from another host, testing it before DNS moves. +- Manage domains, DNS, VPS, subscriptions, ecommerce and email marketing. --- @@ -24,14 +24,14 @@ The plugin wires the official [`hostinger-api-mcp`](https://www.npmjs.com/packag [Install Hostinger Connector in Cursor](cursor://anysphere.cursor-deeplink/plugin/install?repo=hostinger/hostinger-cursor-plugin) -### From an `/add-plugin` URL - -In Cursor chat, run: +### From Cursor chat ``` -/add-plugin https://github.com/hostinger/hostinger-cursor-plugin +/add-plugin hostinger-cursor-plugin ``` +Install from the marketplace rather than from the GitHub URL: an `/add-plugin https://github.com/...` install can stay on the commit it was installed from and miss later updates. + ### Requirements Node.js 20 or newer must be on your `PATH`, since the MCP servers run via `npx`. Check with `node --version`. @@ -75,26 +75,27 @@ Never commit a token. The plugin ships a `beforeShellExecution` hook that inspec ## MCP servers -Each product area runs as its own MCP server. This keeps the tool count per server small instead of loading all 289 tools into context at once, and it lets you disable areas you don't use from Cursor's MCP settings. +Each product area runs as its own MCP server, so you can disable areas you don't use from Cursor's MCP settings. Every server exposes the same three tools — `search`, `execute` and `multi-execute` — over its own operations. -| Server | Binary | Tools | Covers | +| Server | Binary | Operations | Covers | |---|---|---:|---| -| `hostinger-hosting` | `hostinger-hosting-mcp` | 48 | Websites, Node.js builds and deployments, databases, cron, PHP, subdomains | -| `hostinger-wordpress` | `hostinger-wordpress-mcp` | 35 | WordPress installations, plugins, themes, core, LiteSpeed cache, maintenance mode | -| `hostinger-domains` | `hostinger-domains-mcp` | 36 | Availability, registration, transfers, locks, forwarding, WHOIS | +| `hostinger-hosting` | `hostinger-hosting-mcp` | 75 | Shared and Cloud websites, Node.js builds and deployments, databases, cron, PHP, SSL, files, Git | +| `hostinger-wordpress` | `hostinger-wordpress-mcp` | 38 | WordPress installations, plugins, themes, core, LiteSpeed cache, maintenance mode | +| `hostinger-agency-hosting` | `hostinger-agency-hosting-mcp` | 42 | Agency Plan websites, deploys, databases, SSL, PHP, metrics | +| `hostinger-domains` | `hostinger-domains-mcp` | 42 | Availability, registration, transfers, locks, forwarding, WHOIS | | `hostinger-dns` | `hostinger-dns-mcp` | 8 | Zone records, snapshots, validation | | `hostinger-billing` | `hostinger-billing-mcp` | 9 | Subscriptions, auto-renewal, payment methods, catalog, orders | -| `hostinger-reach` | `hostinger-reach-mcp` | 12 | Contacts, segments, email marketing profiles | -| `hostinger-ecommerce` | `hostinger-ecommerce-mcp` | 12 | Stores, products, sales channels, shipping | -| `hostinger-vps` | `hostinger-vps-mcp` | 62 | Virtual machines, firewalls, snapshots, backups, SSH keys, metrics | +| `hostinger-reach` | `hostinger-reach-mcp` | 52 | Contacts, segments, email marketing profiles | +| `hostinger-ecommerce` | `hostinger-ecommerce-mcp` | 29 | Stores, products, sales channels, shipping | +| `hostinger-vps` | `hostinger-vps-mcp` | 64 | Virtual machines, firewalls, snapshots, backups, SSH keys, metrics | -These eight mirror the product groups in the [Hostinger VS Code extension](https://open-vsx.org/extension/hostinger/hostinger-connector). `hostinger-api-mcp` also publishes `hostinger-mail-mcp`, `hostinger-agency-hosting-mcp`, and `hostinger-horizons-mcp`, which neither the plugin nor the extension wires up yet. +`hostinger-api-mcp` also publishes `hostinger-mail-mcp` and `hostinger-horizons-mcp`, which the plugin doesn't wire up. -Tool names are Hostinger's OpenAPI operation IDs — `hosting_listWebsitesV1`, `DNS_getDNSRecordsV1`, `billing_getSubscriptionListV1` — and the prefix tells you which server owns the call. WordPress tools share the `hosting_` prefix despite living on their own server. For the full catalog, see [`scripts/mcp-tools.json`](scripts/mcp-tools.json) or [hostinger/api-mcp-server](https://github.com/hostinger/api-mcp-server). +Operation names follow the API — `hosting_websites_list`, `dns_records_list`, `billing_subscriptions_list` — and the prefix tells you which server owns the operation. For the full catalog, see [`scripts/mcp-tools.json`](scripts/mcp-tools.json) or [hostinger/api-mcp-server](https://github.com/hostinger/api-mcp-server). ### Already using the VS Code extension? -The extension writes these same servers into `~/.cursor/mcp.json` for whichever IDE it detects. If you run both the extension and this plugin in Cursor, you'll get two copies of every server and roughly 200 duplicate tools. Pick one: keep the plugin for Cursor, or disconnect the extension from Cursor's config. +The extension writes these same servers into `~/.cursor/mcp.json` for whichever IDE it detects. If you run both the extension and this plugin in Cursor, you'll get two copies of every server. Pick one: keep the plugin for Cursor, or disconnect the extension from Cursor's config. --- @@ -102,28 +103,33 @@ The extension writes these same servers into `~/.cursor/mcp.json` for whichever Worth knowing up front, because the agent will tell you rather than inventing a tool: -- **No raw access or PHP error logs.** Build, deployment, and cron logs are available; HTTP access logs and PHP error logs live in hPanel only. -- **No Node.js environment variables.** Set them in hPanel — there is no API for it. -- **No shared-hosting backups.** VPS backups and snapshots are exposed; shared-hosting backups are not. +- **No raw access or PHP error logs.** Build, Node.js runtime and cron output are available; HTTP access logs and PHP error logs live in hPanel only. +- **No website backups.** VPS backups and snapshots are exposed; backups for Shared, Cloud and Agency websites are not. +- **No CPU or memory metrics for Shared and Cloud plans.** Agency Plan orders have them. - **No on-demand DNS snapshots.** Hostinger creates them automatically. You can list, read, and restore them, but not trigger one. --- ## Available skills +Invoke a skill with `/` in chat, or let the agent pick it from your request. + | Skill | What it does | |---|---| -| [`deploy-nodejs-app`](skills/deploy-nodejs-app/SKILL.md) | Pick the right deploy tool, build a clean archive, follow the build to completion. | -| [`diagnose-build-failure`](skills/diagnose-build-failure/SKILL.md) | Pull build logs, match the failure signature, propose a concrete fix. | -| [`manage-dns-records`](skills/manage-dns-records/SKILL.md) | Read, validate, and update zone records; roll back via snapshots. | -| [`troubleshoot-wordpress`](skills/troubleshoot-wordpress/SKILL.md) | Diagnose WP issues: PHP settings, plugin/theme conflicts, caches, maintenance mode. | -| [`query-deployment-logs`](skills/query-deployment-logs/SKILL.md) | Pull and summarize build, deployment, and cron logs. | +| [`troubleshoot-website`](skills/troubleshoot-website/SKILL.md) | One site, one symptom — down, slow, 5xx, SSL warning, failed build — to a named cause and a fix. | +| [`connect-domain`](skills/connect-domain/SKILL.md) | Attach a domain, point DNS at Hostinger without losing `MX`/`TXT` records, install SSL, verify. | +| [`deploy-to-hosting`](skills/deploy-to-hosting/SKILL.md) | Deploy an existing project the right way; Git auto-deploy, environment variables, databases. | +| [`maintain-wordpress`](skills/maintain-wordpress/SKILL.md) | Check core, plugins and themes for updates and vulnerabilities; update safely, site by site. | +| [`audit-hosting`](skills/audit-hosting/SKILL.md) | Read-only review of the whole hosting account with a prioritised to-do list. | +| [`migrate-to-hosting`](skills/migrate-to-hosting/SKILL.md) | Move a site from another host; test the copy before DNS moves. | +| [`hostinger-headless`](skills/hostinger-headless/SKILL.md) | Build a new site from a prompt — hosting, domain, optional store or WordPress backend, deploy. | + +The skills come from [hostinger/api-mcp-server](https://github.com/hostinger/api-mcp-server) and are synced into `skills/` by `scripts/sync-skills.mjs` — change them upstream, not here. ## Rules -- [`prefer-mcp-tools`](rules/prefer-mcp-tools.mdc) — use MCP tools instead of `curl` / `ssh` / one-off scripts, and which server owns what. Always on. -- [`confirm-destructive-actions`](rules/confirm-destructive-actions.mdc) — require explicit confirmation before any mutating tool call. Always on. -- [`nodejs-deployments`](rules/nodejs-deployments.mdc) — which deploy tool to use, how to build the archive, and the accepted build override values. +- [`prefer-mcp-tools`](rules/prefer-mcp-tools.mdc) — use the MCP servers instead of `curl` / `ssh` / one-off scripts, how `search` / `execute` / `multi-execute` work, and which server owns what. Always on. +- [`confirm-destructive-actions`](rules/confirm-destructive-actions.mdc) — require explicit confirmation before any mutating operation. Always on. ## Agents @@ -131,7 +137,7 @@ Worth knowing up front, because the agent will tell you rather than inventing a ## Commands -- [`/hostinger-status`](commands/hostinger-status.md) — one-screen snapshot of websites, deployments, domains, VPS, and subscriptions. +- [`/hostinger-status`](commands/hostinger-status.md) — one-screen snapshot of websites, builds, domains, VPS, and subscriptions. --- @@ -141,14 +147,19 @@ Worth knowing up front, because the agent will tell you rather than inventing a # Validate the plugin manifest and component frontmatter node scripts/validate-template.mjs -# Assert every MCP tool named in the docs actually exists +# Assert every MCP operation named in the docs actually exists node scripts/check-tool-names.mjs -# Refresh the tool catalog after the MCP server ships new tools +# Refresh the operation catalog after the MCP server ships new operations node scripts/sync-mcp-tools.mjs + +# Refresh skills/ from the latest published server, a version, or a local skills directory +node scripts/sync-skills.mjs +node scripts/sync-skills.mjs 2.5.0 +node scripts/sync-skills.mjs ../public-api-generator/mcp/assets/skills ``` -`scripts/mcp-tools.json` is a checked-in snapshot of the published server's tool catalog, so `check-tool-names.mjs` runs offline in CI. Regenerate and commit it whenever the server adds tools. CI also runs an advisory job that flags when the snapshot has fallen behind `hostinger-api-mcp@latest`. +`scripts/mcp-tools.json` is a checked-in snapshot of the published server's operation catalog, so `check-tool-names.mjs` runs offline in CI. `skills/` is generated the same way and is replaced wholesale on every sync. CI runs advisory jobs that flag when either has fallen behind `hostinger-api-mcp@latest`. --- diff --git a/agents/hostinger-deployment-reviewer.md b/agents/hostinger-deployment-reviewer.md index 969c7fa..2a0db80 100644 --- a/agents/hostinger-deployment-reviewer.md +++ b/agents/hostinger-deployment-reviewer.md @@ -7,37 +7,42 @@ description: Pre-flight review of a planned Hostinger deployment. Reads the proj ## When to invoke -- Before calling `hosting_deployJsApplication` or `hosting_createNodeJSBuildFromArchiveV1` for a non-trivial deploy. +- Before `hosting_deploy-js-application` or `hosting_nodejs_start-build` for a non-trivial deploy. - When the user asks "is this ready to ship to Hostinger?". - After a build failure, before retrying. +The `deploy-to-hosting` skill runs the deploy itself; this agent only reviews. + ## What to check -1. **Deploy tool** — is the right one selected? Anything with a `package.json` and a build script needs `hosting_deployJsApplication` or `hosting_createNodeJSBuildFromArchiveV1`, never `hosting_deployStaticWebsite`. -2. **Node version** — does `engines.node` resolve to `18`, `20`, `22`, or `24`? Anything else needs an explicit `node_version` override. -3. **Overrides** — if `app_type` is being set, is the value in the accepted enum (`create-react-app`, `vite`, `angular`, `react`, `vue`, `parcel`, `express`, `fastify`, `nest`)? For any other framework, `app_type` must be omitted and auto-detection allowed to run. -4. **Output directory** — does the build's real output path match `output_directory`? A mismatch builds cleanly and then serves a 404. -5. **Root directory** — in a monorepo, does `root_directory` point at the folder holding `package.json`? -6. **Package manager** — does the committed lockfile match `package_manager`? -7. **Start command / PORT** — does the entry point bind to `process.env.PORT`? A hardcoded port receives no traffic. -8. **Env vars** — list every `process.env.X` the code reads. These cannot be set through the API, so flag them for the user to add in hPanel before the first request. -9. **Archive hygiene** — is `node_modules/`, build output, `.git/`, and `.env` excluded? Is the archive under 50 MB? -10. **Secrets** — is anything that looks like a token committed in source? -11. **Domain state** — does `hosting_listWebsitesV1` show the target domain, and does `DNS_getDNSRecordsV1` point it at Hostinger? +1. **Deploy method** — anything with a `package.json` and a build script needs `hosting_deploy-js-application` (or `hosting_nodejs_start-build`), never `hosting_deploy-static-website`, which serves the archive as-is. +2. **Plan** — Node.js apps run on Business and Cloud plans; `hosting_orders_list` shows the plan behind the target website's `order_id`. +3. **Node version** — does `engines.node` resolve to `18`, `20`, `22` or `24`? Anything else needs an explicit `node_version`. +4. **Framework** — if `app_type` is set, is it one of `create-react-app`, `gatsby`, `vite`, `angular`, `react`, `vue`, `parcel`, `next`, `nuxt`, `nest`, `express`, `fastify`, `astro`, `svelte`, `svelte-kit`, `hono`, `react-router`, `nitro`, `other`? Otherwise omit it and let detection run. +5. **Entry file** — express, fastify, nest, nuxt and hono apps need `entry_file`. +6. **Output directory** — does the build's real output path match `output_directory`? A mismatch builds cleanly and then serves a 404. +7. **Root directory** — in a monorepo, does `root_directory` point at the folder holding `package.json`? +8. **Package manager** — does the committed lockfile match `package_manager`? +9. **Start command / PORT** — does the entry point bind to `process.env.PORT`? A hardcoded port receives no traffic. +10. **Env vars** — list every `process.env.X` the code reads and compare with the keys from `hosting_nodejs_list-environment-variables`. Missing ones are set with the `deploy-to-hosting` skill's environment step; build-time values (Next.js, Vite) need a rebuild after they change. +11. **Archive hygiene** — are `node_modules/`, build output, `.git/` and `.env` excluded? Is the archive under 50 MB? +12. **Secrets** — is anything that looks like a token committed in source? +13. **Domain state** — does `hosting_websites_list` show the target domain, has a new site's setup finished (`hosting_websites_list-setups`), and does the domain resolve to Hostinger? ## Output Reply with a checklist: ``` -- [x] Deploy tool: hosting_deployJsApplication (build required) +- [x] Deploy method: hosting_deploy-js-application (build required) +- [x] Plan: Business - [x] Node version: 22 (supported) -- [x] app_type: omitted — SvelteKit isn't in the enum, auto-detect will run +- [x] app_type: svelte-kit - [ ] PORT: hardcoded in server.js:42 — change to process.env.PORT -- [ ] Env vars: DATABASE_URL, STRIPE_SECRET_KEY must be set in hPanel (not settable via API) +- [ ] Env vars: DATABASE_URL, STRIPE_SECRET_KEY missing on the website - [x] Archive: node_modules, dist, .git, .env excluded — 3.1 MB - [x] Secrets: clean -- [x] Domain: example.com listed, apex A record points to Hostinger +- [x] Domain: example.com listed, setup completed, resolves to Hostinger ``` End with a one-line verdict: "Ready to deploy" or "Block: issues to fix first". diff --git a/commands/hostinger-status.md b/commands/hostinger-status.md index b6544f9..17e3c50 100644 --- a/commands/hostinger-status.md +++ b/commands/hostinger-status.md @@ -5,18 +5,18 @@ description: Print a one-screen summary of the user's Hostinger account — webs # /hostinger-status -Run a fast, read-only snapshot of the user's Hostinger account. +Run a fast, read-only snapshot of the user's Hostinger account. For a deeper web hosting review (SSL, WordPress vulnerabilities, quotas), use the `audit-hosting` skill instead. ## Steps -1. Call these tools in parallel where possible: - - `hosting_listWebsitesV1` → domain, username, enabled state. - - `hosting_listJsDeployments` per domain that has deployments → state (`pending`, `running`, `completed`, `failed`) and timestamp. Skip domains with none rather than erroring. - - `domains_getDomainListV1` → registered domains and expiry. - - `VPS_getVirtualMachinesV1` → hostname and power state. - - `billing_getSubscriptionListV1` → next renewal date and auto-renewal flag. +1. Run one `multi-execute` batch per server, in parallel: + - `hostinger-hosting`: `hosting_websites_list` → domain, `website_type`, enabled state. Then, for Node.js websites, `hosting_nodejs_list-builds` with `per_page: 1` → latest build state (`pending`, `running`, `completed`, `failed`) and time. + - `hostinger-agency-hosting`: `agency-hosting_websites_list-plan` → Agency Plan sites and their state. + - `hostinger-domains`: `domains_portfolio_list` → registered domains and expiry. + - `hostinger-vps`: `vps_virtual-machines_list` → hostname and power state. + - `hostinger-billing`: `billing_subscriptions_list` → next renewal date and auto-renewal flag. 2. Render as compact sections — do not paginate. -3. Flag anything that needs attention with `[!]`: a failed deployment, a disabled website, a stopped VPS, or a subscription renewing within 30 days without auto-renewal. +3. Flag anything that needs attention with `[!]`: a failed build, a disabled website, a stopped VPS, or a subscription renewing within 30 days without auto-renewal. If a server isn't enabled in the user's Cursor MCP settings, its section will error. Note the section as unavailable and carry on — don't abort the whole snapshot. @@ -24,16 +24,17 @@ If a server isn't enabled in the user's Cursor MCP settings, its section will er ``` Websites (N) -- example.com — enabled -- shop.example — disabled [!] +- example.com — WordPress, enabled +- shop.example — Node.js, disabled [!] -Recent deployments -- example.com — completed, 2h ago +Agency Plan sites (N) +- client-a.com — active + +Recent builds - api.example — failed, 6h ago [!] Domains (N) - example.com — expires 2027-03-14 -- shop.example — expires 2026-09-02 VPS (N) - srv-1 — running diff --git a/mcp.json b/mcp.json index 651a87d..316805f 100644 --- a/mcp.json +++ b/mcp.json @@ -3,42 +3,47 @@ "hostinger-hosting": { "command": "npx", "args": ["--yes", "--package=hostinger-api-mcp@latest", "hostinger-hosting-mcp"], - "env": { "USER_AGENT": "plugin;cursor;0.2.0" } + "env": { "USER_AGENT": "plugin;cursor;0.3.0" } }, "hostinger-wordpress": { "command": "npx", "args": ["--yes", "--package=hostinger-api-mcp@latest", "hostinger-wordpress-mcp"], - "env": { "USER_AGENT": "plugin;cursor;0.2.0" } + "env": { "USER_AGENT": "plugin;cursor;0.3.0" } + }, + "hostinger-agency-hosting": { + "command": "npx", + "args": ["--yes", "--package=hostinger-api-mcp@latest", "hostinger-agency-hosting-mcp"], + "env": { "USER_AGENT": "plugin;cursor;0.3.0" } }, "hostinger-domains": { "command": "npx", "args": ["--yes", "--package=hostinger-api-mcp@latest", "hostinger-domains-mcp"], - "env": { "USER_AGENT": "plugin;cursor;0.2.0" } + "env": { "USER_AGENT": "plugin;cursor;0.3.0" } }, "hostinger-dns": { "command": "npx", "args": ["--yes", "--package=hostinger-api-mcp@latest", "hostinger-dns-mcp"], - "env": { "USER_AGENT": "plugin;cursor;0.2.0" } + "env": { "USER_AGENT": "plugin;cursor;0.3.0" } }, "hostinger-billing": { "command": "npx", "args": ["--yes", "--package=hostinger-api-mcp@latest", "hostinger-billing-mcp"], - "env": { "USER_AGENT": "plugin;cursor;0.2.0" } + "env": { "USER_AGENT": "plugin;cursor;0.3.0" } }, "hostinger-reach": { "command": "npx", "args": ["--yes", "--package=hostinger-api-mcp@latest", "hostinger-reach-mcp"], - "env": { "USER_AGENT": "plugin;cursor;0.2.0" } + "env": { "USER_AGENT": "plugin;cursor;0.3.0" } }, "hostinger-ecommerce": { "command": "npx", "args": ["--yes", "--package=hostinger-api-mcp@latest", "hostinger-ecommerce-mcp"], - "env": { "USER_AGENT": "plugin;cursor;0.2.0" } + "env": { "USER_AGENT": "plugin;cursor;0.3.0" } }, "hostinger-vps": { "command": "npx", "args": ["--yes", "--package=hostinger-api-mcp@latest", "hostinger-vps-mcp"], - "env": { "USER_AGENT": "plugin;cursor;0.2.0" } + "env": { "USER_AGENT": "plugin;cursor;0.3.0" } } } } diff --git a/rules/confirm-destructive-actions.mdc b/rules/confirm-destructive-actions.mdc index ceaf33d..1e31593 100644 --- a/rules/confirm-destructive-actions.mdc +++ b/rules/confirm-destructive-actions.mdc @@ -5,57 +5,19 @@ alwaysApply: true # Confirm destructive Hostinger actions before executing -Before invoking any Hostinger MCP tool that **mutates or destroys** state, show the user the exact change and wait for an explicit "yes" / "go" / equivalent confirmation in the same turn. +Before running any Hostinger operation that **mutates or destroys** state, show the user the exact change and wait for an explicit "yes" / "go" / equivalent confirmation in the same turn. -## Always confirm +## What needs confirmation -**Websites & hosting** +Every operation that `search` reports with `destructive: true`, every operation that spends money, and every write the user did not ask for by name. `readOnly: true` operations never need confirmation. -- `hosting_deleteWebsiteV1`, `hosting_deleteWebsiteSubdomainV1`, `hosting_deleteWebsiteParkedDomainV1` -- `hosting_deployJsApplication`, `hosting_deployStaticWebsite`, `hosting_createNodeJSBuildFromArchiveV1` — these overwrite what is currently live -- `hosting_restartNode_jsApplicationV1`, `hosting_patchNode_jsVulnerabilitiesV1` -- `hosting_updatePHPVersionV1`, `hosting_updatePHPOptionsV1`, `hosting_updatePHPExtensionsV1`, `hosting_resetPHPExtensionsV1` -- `hosting_deleteAccountDatabaseV1`, `hosting_changeDatabasePasswordV1`, `hosting_repairDatabaseV1`, `hosting_deleteDatabaseRemoteConnectionV1` -- `hosting_deleteAccountCronJobV1` +The ones that do the most damage when wrong — confirm these even inside a flow the user already approved: -**WordPress** - -- `hosting_deleteWordPressInstallationV1`, `hosting_updateWordPressCoreV1` -- `hosting_uninstallWordPressPluginsV1`, `hosting_deactivateWordPressPluginV1`, `hosting_updateWordPressPluginsV1` -- `hosting_uninstallWordPressThemesV1`, `hosting_activateWordPressThemeV1`, `hosting_updateWordPressThemesV1` -- `hosting_toggleMaintenanceModeV1` on a production site - -**Domains** - -- `domains_purchaseNewDomainV1` (spends money), `domains_disableDomainLockV1`, `domains_updateDomainNameserversV1` -- `domains_createDomainForwardingV1`, `domains_updateDomainForwardingV1`, `domains_deleteDomainForwardingV1` -- `domains_startOutgoingDomainMoveV1`, `domains_acceptIncomingDomainMoveV1`, `domains_rejectIncomingDomainMoveV1` -- `domains_deleteWHOISProfileV1`, `domains_disablePrivacyProtectionV1` - -**DNS** - -- `DNS_updateDNSRecordsV1` — always, and especially with `overwrite: true`, which replaces matching records -- `DNS_deleteDNSRecordsV1`, `DNS_resetDNSRecordsV1` (resets the whole zone to defaults), `DNS_restoreDNSSnapshotV1` -- Any change to apex `A`/`AAAA`, `NS`, or `MX` records — these break live traffic or mail delivery - -**VPS** - -- `VPS_stopVirtualMachineV1`, `VPS_restartVirtualMachineV1`, `VPS_recreateVirtualMachineV1` (destroys all data), `VPS_purchaseNewVirtualMachineV1` -- `VPS_restoreSnapshotV1`, `VPS_deleteSnapshotV1`, `VPS_restoreBackupV1` -- `VPS_setRootPasswordV1`, `VPS_setPanelPasswordV1`, `VPS_deletePublicKeyV1` -- `VPS_deleteFirewallV1`, `VPS_deleteFirewallRuleV1`, `VPS_deactivateFirewallV1`, `VPS_syncFirewallV1` -- `VPS_deleteProjectV1`, `VPS_stopProjectV1`, `VPS_restartProjectV1` -- `VPS_startRecoveryModeV1`, `VPS_deletePTRRecordV1`, `VPS_uninstallMonarxV1` - -**Subscriptions & payments** - -- `billing_createPurchaseOrderV1`, `billing_renewSubscriptionV1` (both spend money) -- `billing_disableAutoRenewalV1` on an active service, `billing_deletePaymentMethodV1` - -**Ecommerce & email marketing** - -- `ecommerce_deleteStoreV1`, `ecommerce_updateSalesChannelV1`, `ecommerce_setStoreShippingV1` -- `reach_deleteAContactV1` +- **Spends money:** `domains_portfolio_purchase`, `vps_virtual-machines_purchase`, `billing_orders_create-purchase`, `billing_subscriptions_renew`. +- **Deletes data for good:** `hosting_websites_delete`, `agency-hosting_websites_delete`, `hosting_databases_delete`, `agency-hosting_databases_delete-website`, `wordpress_installations_delete`, `vps_virtual-machines_recreate`. +- **Overwrites what is live:** `hosting_deploy-static-website`, `hosting_deploy-js-application`, `hosting_websites_deploy-static-site-archive`, `hosting_nodejs_start-build`, `hosting_import-wordpress-website`, `wordpress_installations_import-website`, `agency-hosting_deploy-php-application`, `agency-hosting_deploy-node-static-website`, `vps_snapshots_restore`, `vps_backups_restore`. +- **Replaces a whole set:** `hosting_nodejs_replace-environment-variables` (every variable not sent is deleted), `agency-hosting_php_replace-website-options`, `agency-hosting_php_replace-website-extensions`. +- **Breaks traffic or mail:** `dns_records_update` (its `overwrite` defaults to `true`), `dns_records_delete`, `dns_records_reset`, `dns_snapshots_restore`, `domains_portfolio_update-nameservers`, `agency-hosting_domains_change-website`, and any change to apex `A`/`AAAA`, `NS` or `MX` records. ## Confirmation format @@ -63,11 +25,11 @@ Reply with: 1. **Action** — one sentence ("I'm about to delete the `mail` A record on `example.com`."). 2. **Impact** — one or two sentences on what breaks if this is wrong. -3. **Recovery** — how to undo, if it's reversible (e.g. the DNS snapshot ID from `DNS_getDNSSnapshotListV1`, a VPS snapshot ID, etc.). +3. **Recovery** — how to undo, if it's reversible (e.g. the newest DNS snapshot ID from `dns_snapshots_list`, or a VPS snapshot). 4. End with a clear question: "Proceed? (yes / no)". Do not chain destructive operations — confirm each one separately, even if the user already approved a related action earlier in the conversation. ## Read-only is safe -Tools whose names read as `list*`, `get*`, `show*`, `check*`, or `validate*` never need confirmation. `DNS_validateDNSRecordsV1` is safe by design — it is the dry run for `DNS_updateDNSRecordsV1`, so prefer calling it first rather than asking the user to approve an unvalidated change. +`readOnly: true` operations never need confirmation. `dns_records_validate` is the dry run for `dns_records_update` — call it first rather than asking the user to approve an unvalidated change. diff --git a/rules/nodejs-deployments.mdc b/rules/nodejs-deployments.mdc deleted file mode 100644 index b85fc8a..0000000 --- a/rules/nodejs-deployments.mdc +++ /dev/null @@ -1,58 +0,0 @@ ---- -description: Pick the right Hostinger deployment tool and build overrides when shipping Node.js or static sites -alwaysApply: false ---- - -# Deploying to Hostinger - -Hostinger deploys from an **uploaded archive**, and the build runs server-side. There is no framework "preset" parameter — build settings are auto-detected from `package.json`, and you may override individual fields. Pick the tool that matches the job. - -## Which tool - -| Situation | Tool | -|---|---| -| Node.js / JS app, let Hostinger auto-detect everything | `hosting_deployJsApplication` | -| Node.js app where you must override Node version, entry file, build script, or output dir | `hosting_createNodeJSBuildFromArchiveV1` | -| Pre-built static files, no build step at all | `hosting_deployStaticWebsite` | - -`hosting_deployJsApplication` is the default choice — it takes just `domain` and `archivePath` and resolves the hosting username itself. Reach for `hosting_createNodeJSBuildFromArchiveV1` only when an override is actually needed; it additionally requires `username`, which you can get from `hosting_listWebsitesV1`. - -Do not use `hosting_deployStaticWebsite` for anything with a `package.json` or a build command — it extracts the archive verbatim and serves it, with no install or build step. - -## Building the archive - -Applies to all three tools: - -- Include **source only**. Exclude `node_modules/` and build output (`dist/`, `build/`, `.next/`, `.svelte-kit/`, `.output/`) — the server runs the install and build itself, and shipping them just bloats the upload. -- Also exclude anything matched by `.gitignore` when that file exists. -- Never include `.env` or any file holding credentials. -- Supported formats: `zip`, `tar`, `tar.gz`, `tgz`, `7z`, `gz`. `hosting_createNodeJSBuildFromArchiveV1` accepts `.zip`, `.tar.gz`, `.tgz` only, and caps the archive at **50 MB**. -- For `hosting_deployStaticWebsite`, name the archive exactly `_YYYYMMDD_HHMMSS.zip` (e.g. `mystaticwebsite_20260806_143022.zip`). - -```bash -zip -r myapp.zip . --exclude "node_modules/*" --exclude "dist/*" --exclude ".git/*" --exclude ".env" -``` - -## Valid override values - -Only these values are accepted — do not invent others. - -- `node_version`: `18`, `20`, `22`, `24`. Omit to auto-detect from `engines.node`. -- `app_type`: `create-react-app`, `vite`, `angular`, `react`, `vue`, `parcel`, `express`, `fastify`, `nest`. Omit to auto-detect. -- `package_manager`: `npm`, `yarn`, `pnpm`. Omit to auto-detect from the lockfile. -- `root_directory` — where `package.json` lives, relative to `public_html`. Needed for monorepos. -- `output_directory` — build output, relative to the root directory. -- `build_script`, `entry_file` — free-form string overrides. - -If the project's framework isn't in the `app_type` list (SvelteKit, Remix, Astro, Hono, Nuxt, and so on), **omit `app_type` entirely** and let auto-detection handle it, overriding `build_script`, `entry_file`, and `output_directory` if the detected values are wrong. Do not force an unrelated `app_type` value as a stand-in. - -## After submitting - -1. `hosting_listJsDeployments` (or `hosting_listNodeJSBuildsV1`) to get the build `uuid` and state — `pending`, `running`, `completed`, or `failed`. -2. Poll logs while the state is `running`: `hosting_showJsDeploymentLogs` for `deployJsApplication` builds, or `hosting_getNodeJSBuildLogsV1` for archive builds. Pass the previously returned line count as `fromLine` / `from_line` so you only fetch new output. -3. On `failed`, hand off to the `diagnose-build-failure` skill. - -## Runtime expectations - -- The app must bind to `process.env.PORT`. A hardcoded port will not receive traffic. -- Node.js application environment variables cannot be set through the API. If the app needs them, tell the user to add them in hPanel before the first request — don't pretend an MCP tool can do it. diff --git a/rules/prefer-mcp-tools.mdc b/rules/prefer-mcp-tools.mdc index c942d8f..d9b273d 100644 --- a/rules/prefer-mcp-tools.mdc +++ b/rules/prefer-mcp-tools.mdc @@ -5,7 +5,7 @@ alwaysApply: true # Use the Hostinger MCP servers, not shell hacks -When the user asks about anything on Hostinger — websites, WordPress, domains, DNS, VPS, subscriptions, email marketing, ecommerce — call a tool on one of this plugin's Hostinger MCP servers instead of: +When the user asks about anything on Hostinger — websites, WordPress, Agency Plan sites, domains, DNS, VPS, subscriptions, email marketing, ecommerce — use one of this plugin's Hostinger MCP servers instead of: - `curl` / `wget` against `https://developers.hostinger.com` - `ssh` into a host to read files or run commands @@ -14,42 +14,45 @@ When the user asks about anything on Hostinger — websites, WordPress, domains, ## Why -- The MCP tools are typed and validated against Hostinger's OpenAPI spec. +- The operations are typed and validated against Hostinger's OpenAPI spec. - They handle authentication consistently (OAuth by default, `HOSTINGER_API_TOKEN` when set). - They surface structured errors that the agent can act on. - They don't leak tokens into shell history. -## Which server to use +## How the servers work -The plugin registers one MCP server per product area. Tool names are prefixed by area, so the prefix tells you which server owns the call: +Every server exposes the same three tools: `search` finds operations by keyword and returns each one's name, `readOnly` / `destructive` hints and `inputSchema`; `execute` runs one operation; `multi-execute` runs up to 20 in order, passing values between steps as `$steps..`. The `inputSchema` from `search` is authoritative — read it before the first call of an operation. -| Server | Tool prefix | Covers | +The plugin registers one server per product area. The operation prefix tells you which server owns it, and a `multi-execute` batch can only chain operations from one server: + +| Server | Operation prefix | Covers | |---|---|---| -| `hostinger-hosting` | `hosting_` | Websites, Node.js builds & deployments, databases, cron, PHP, subdomains | -| `hostinger-wordpress` | `hosting_` | WordPress installations, plugins, themes, core, LiteSpeed cache, maintenance mode | +| `hostinger-hosting` | `hosting_` | Shared and Cloud websites, Node.js builds and deployments, databases, cron, PHP, SSL, files, Git | +| `hostinger-wordpress` | `wordpress_` | WordPress installations, plugins, themes, core, LiteSpeed cache, maintenance mode | +| `hostinger-agency-hosting` | `agency-hosting_` | Agency Plan websites, deploys, databases, SSL, PHP, metrics | | `hostinger-domains` | `domains_` | Availability, registration, transfers, locks, forwarding, WHOIS | -| `hostinger-dns` | `DNS_` | Zone records, snapshots, validation | +| `hostinger-dns` | `dns_` | Zone records, snapshots, validation | | `hostinger-billing` | `billing_` | Subscriptions, auto-renewal, payment methods, catalog, orders | | `hostinger-reach` | `reach_` | Contacts, segments, email marketing profiles | | `hostinger-ecommerce` | `ecommerce_` | Stores, products, sales channels, shipping | -| `hostinger-vps` | `VPS_` | Virtual machines, firewalls, snapshots, backups, SSH keys, metrics | - -Note that WordPress tools also use the `hosting_` prefix — they live on a separate server but share the namespace. +| `hostinger-vps` | `vps_` | Virtual machines, firewalls, snapshots, backups, SSH keys, metrics | -## Tool selection +## Operation selection -- Discover before you guess. Tool names follow Hostinger's OpenAPI operation IDs — `hosting_listWebsitesV1`, `DNS_getDNSRecordsV1`, `billing_getSubscriptionListV1` — not generic verbs of the *list_websites* or *create_deployment* shape. If you are unsure of an exact name, list the server's tools rather than inventing one. -- Several Hostinger endpoints are deliberately **batch-oriented**. Do not look for a per-item tool that does not exist — for example DNS has no single-record create/update tool; `DNS_updateDNSRecordsV1` takes a whole zone array and an `overwrite` flag. -- Read before you write: pair a `get`/`list`/`show` call with every mutation so you can report the before/after state. +- Search before you guess. Names follow the API: `hosting_websites_list`, `dns_records_list`, `billing_subscriptions_list`. If `search` returns nothing useful, try other words for what the step does rather than inventing a name. +- Several endpoints are **batch-oriented**. DNS has no single-record create call: `dns_records_update` takes a zone array, and its `overwrite` flag defaults to `true`, which replaces every record with the same name and type. +- Read before you write: pair a read with every change so you can report the before and after state. +- For multi-step web hosting work — troubleshooting a site, connecting a domain, deploying, WordPress maintenance, an account audit, a migration — follow the matching skill in this plugin. ## Not covered by the API -Some things users ask for have no MCP tool. Say so plainly instead of substituting a plausible-sounding call: +Say so plainly instead of substituting a plausible-sounding call: -- Raw HTTP access logs and PHP error logs are not exposed by the API — point the user to hPanel. Build and deployment logs *are* available (see the `query-deployment-logs` skill). -- Node.js application environment variables cannot be managed through the API; they are set in hPanel. +- HTTP access logs and PHP error logs — hPanel only. Build, runtime (Node.js) and cron output are available. +- Backups and restores for Shared, Cloud and Agency websites — hPanel only. +- CPU and memory usage for Shared and Cloud plans — hPanel only; Agency Plan orders do have metrics. ## Exceptions -- Read-only `dig` / `nslookup` against public DNS is fine for *verification* after an MCP-driven change, never as the primary read path. -- If a needed capability genuinely isn't covered by any MCP tool, tell the user explicitly before falling back to the raw API — don't quietly skip the MCP layer. +- Read-only `dig` / `nslookup` / `curl` against the public site is fine for *verification* after an MCP-driven change, never as the primary read path. +- If a needed capability genuinely isn't covered by any operation, tell the user explicitly before falling back to the raw API — don't quietly skip the MCP layer. diff --git a/scripts/check-tool-names.mjs b/scripts/check-tool-names.mjs index 5c99f37..db362ba 100644 --- a/scripts/check-tool-names.mjs +++ b/scripts/check-tool-names.mjs @@ -44,6 +44,7 @@ const NON_TOOL_IDENTIFIERS = new Set([ "order_id", "per_page", "snapshot_id", + "website_type", // package.json / npm / filesystem "create-react-app", "engines.node", @@ -73,9 +74,7 @@ function stripFencedBlocks(text) { /** * A backticked token is "tool-shaped" if it could plausibly be read as an MCP * tool name: a bare identifier containing an underscore, no whitespace, and no - * path or call syntax. Hostinger's own names are mixed-case with an underscore - * separator (hosting_listWebsitesV1), and the invented ones were snake_case — - * both land here. + * path or call syntax. */ function isToolShaped(token) { if (!token.includes("_")) return false; @@ -114,14 +113,18 @@ const targets = ["rules", "skills", "agents", "commands", "README.md"].flatMap(( const problems = []; const seen = new Set(); +const groupPrefix = new RegExp(`^(?:${Object.keys(catalog.groups).join("|")})_`); +const syncedSkillsDir = `skills${path.sep}`; for (const file of targets) { const relative = path.relative(repoRoot, file); + const synced = relative.startsWith(syncedSkillsDir); const lines = stripFencedBlocks(readFileSync(file, "utf8")).split("\n"); lines.forEach((line, index) => { for (const [, token] of line.matchAll(/`([^`\n]+)`/g)) { if (!isToolShaped(token)) continue; + if (synced && !groupPrefix.test(token)) continue; if (NON_TOOL_IDENTIFIERS.has(token)) continue; if (knownTools.has(token)) { seen.add(token); diff --git a/scripts/mcp-tools.json b/scripts/mcp-tools.json index b88f8bc..d28bedc 100644 --- a/scripts/mcp-tools.json +++ b/scripts/mcp-tools.json @@ -1,318 +1,432 @@ { "package": "hostinger-api-mcp", - "version": "1.29.0", - "total": 289, + "version": "2.5.0", + "total": 403, "groups": { "agency-hosting": [ - "agency-hosting_buildAgencyPlanWebsiteNodeJSAssetsV1", - "agency-hosting_changeAgencyPlanWebsiteDomainV1", - "agency-hosting_changeAgencyPlanWebsiteWordPressCoreVersionV1", - "agency-hosting_clearAgencyPlanWebsiteCacheV1", - "agency-hosting_createAgencyPlanWebsiteCronJobV1", - "agency-hosting_createAgencyPlanWebsiteDatabaseUserV1", - "agency-hosting_createAgencyPlanWebsiteDatabaseV1", - "agency-hosting_deleteAgencyPlanWebsiteCronJobV1", - "agency-hosting_deleteAgencyPlanWebsiteDatabaseUserV1", - "agency-hosting_deleteAgencyPlanWebsiteDatabaseV1", - "agency-hosting_deleteAgencyPlanWebsiteV1", - "agency-hosting_getAgencyPlanWebsiteDetailsV1", - "agency-hosting_getAgencyPlanWebsiteSetupStatusV1", - "agency-hosting_getAgencyPlanWebsiteWordPressSettingsV1", - "agency-hosting_importAgencyPlanWebsiteFromArchiveV1", - "agency-hosting_linkDomainToAgencyPlanWebsiteV1", - "agency-hosting_listAgencyPlanDomainsV1", - "agency-hosting_listAgencyPlanOrdersV1", - "agency-hosting_listAgencyPlanWebsiteCronJobsV1", - "agency-hosting_listAgencyPlanWebsiteDatabasesV1", - "agency-hosting_listAvailableDatacentersForAnAgencyPlanOrderV1", - "agency-hosting_listAvailableWordPressVersionsForAnAgencyPlanWebsiteV1", - "agency-hosting_listRunningAgencyPlanWebsiteProcessesV1", - "agency-hosting_provisionANewAgencyPlanWebsiteV1", - "agency-hosting_unlinkDomainFromAgencyPlanWebsiteV1", - "agencyHosting_deployNodeStaticWebsite", - "agencyHosting_deployPhpApplication" + "agency-hosting_cache_clear-website", + "agency-hosting_cron-jobs_create-website", + "agency-hosting_cron-jobs_delete-website", + "agency-hosting_cron-jobs_list-website", + "agency-hosting_databases_create-website", + "agency-hosting_databases_create-website-user", + "agency-hosting_databases_delete-website", + "agency-hosting_databases_delete-website-user", + "agency-hosting_databases_list-website", + "agency-hosting_datacenters_list", + "agency-hosting_deploy-node-static-website", + "agency-hosting_deploy-php-application", + "agency-hosting_domains_change-website", + "agency-hosting_domains_link-to-website", + "agency-hosting_domains_list", + "agency-hosting_domains_unlink-from-website", + "agency-hosting_files_generate-upload-url", + "agency-hosting_files_import-website-from-archive", + "agency-hosting_metrics_list-order-resource-usage", + "agency-hosting_metrics_list-plan-order-disk-usage", + "agency-hosting_orders_list", + "agency-hosting_php_list-extensions-for-website", + "agency-hosting_php_list-options-for-website", + "agency-hosting_php_list-versions-for-order", + "agency-hosting_php_list-versions-for-website", + "agency-hosting_php_replace-website-extensions", + "agency-hosting_php_replace-website-options", + "agency-hosting_php_update-website-version", + "agency-hosting_ssl_install-website", + "agency-hosting_ssl_reinstall-website", + "agency-hosting_ssl_uninstall-website", + "agency-hosting_ssl_website-status", + "agency-hosting_website-setups_create", + "agency-hosting_website-setups_status", + "agency-hosting_websites_build-nodejs-assets", + "agency-hosting_websites_delete", + "agency-hosting_websites_get", + "agency-hosting_websites_list-plan", + "agency-hosting_websites_list-processes", + "agency-hosting_wordpress_change-version", + "agency-hosting_wordpress_list-versions", + "agency-hosting_wordpress_settings" ], "billing": [ - "billing_createPurchaseOrderV1", - "billing_deletePaymentMethodV1", - "billing_disableAutoRenewalV1", - "billing_enableAutoRenewalV1", - "billing_getCatalogItemListV1", - "billing_getPaymentMethodListV1", - "billing_getSubscriptionListV1", - "billing_renewSubscriptionV1", - "billing_setDefaultPaymentMethodV1" + "billing_catalog_list", + "billing_orders_create-purchase", + "billing_payment-methods_delete", + "billing_payment-methods_list", + "billing_payment-methods_set-default", + "billing_subscriptions_disable-auto-renewal", + "billing_subscriptions_enable-auto-renewal", + "billing_subscriptions_list", + "billing_subscriptions_renew" ], "dns": [ - "DNS_deleteDNSRecordsV1", - "DNS_getDNSRecordsV1", - "DNS_getDNSSnapshotListV1", - "DNS_getDNSSnapshotV1", - "DNS_resetDNSRecordsV1", - "DNS_restoreDNSSnapshotV1", - "DNS_updateDNSRecordsV1", - "DNS_validateDNSRecordsV1" + "dns_records_delete", + "dns_records_list", + "dns_records_reset", + "dns_records_update", + "dns_records_validate", + "dns_snapshots_get", + "dns_snapshots_list", + "dns_snapshots_restore" ], "domains": [ - "domains_acceptIncomingDomainMoveV1", - "domains_cancelOutgoingDomainMoveV1", - "domains_cancelPendingIRTPVerificationV1", - "domains_changeWHOISProfileForDomainV1", - "domains_checkDomainAvailabilityV1", - "domains_createDomainForwardingV1", - "domains_createWHOISProfileV1", - "domains_deleteDomainForwardingV1", - "domains_deleteWHOISProfileV1", - "domains_disableDomainLockV1", - "domains_disablePrivacyProtectionV1", - "domains_enableDomainLockV1", - "domains_enablePrivacyProtectionV1", - "domains_getDomainAuthorizationCodeV1", - "domains_getDomainDetailsV1", - "domains_getDomainForwardingV1", - "domains_getDomainListV1", - "domains_getDomainRenewalInformationV1", - "domains_getIncomingDomainMoveListV1", - "domains_getIncomingDomainMoveV1", - "domains_getOutgoingDomainMoveListV1", - "domains_getOutgoingDomainMoveV1", - "domains_getPendingIRTPVerificationV1", - "domains_getTransferListV1", - "domains_getTransferV1", - "domains_getWHOISProfileListV1", - "domains_getWHOISProfileUsageV1", - "domains_getWHOISProfileV1", - "domains_purchaseNewDomainV1", - "domains_rejectIncomingDomainMoveV1", - "domains_setWHOISProfileAsDefaultV1", - "domains_startOutgoingDomainMoveV1", - "domains_unsetDefaultWHOISProfileV1", - "domains_updateDomainForwardingV1", - "domains_updateDomainNameserversV1", - "v2_getDomainVerificationsDIRECT" + "domains_availability_check", + "domains_availability_suggest-names-from", + "domains_availability_suggest-names-from-description", + "domains_forwarding_create", + "domains_forwarding_delete", + "domains_forwarding_get", + "domains_forwarding_update", + "domains_move_accept-incoming", + "domains_move_cancel-outgoing", + "domains_move_incoming", + "domains_move_incoming-list", + "domains_move_outgoing", + "domains_move_outgoing-list", + "domains_move_reject-incoming", + "domains_move_start-outgoing", + "domains_portfolio_authorization-code", + "domains_portfolio_claim-free", + "domains_portfolio_complete-setup", + "domains_portfolio_disable-lock", + "domains_portfolio_disable-privacy-protection", + "domains_portfolio_enable-lock", + "domains_portfolio_enable-privacy-protection", + "domains_portfolio_get", + "domains_portfolio_list", + "domains_portfolio_purchase", + "domains_portfolio_renewal-information", + "domains_portfolio_update-nameservers", + "domains_transfer_claim-free", + "domains_transfer_get", + "domains_transfer_list", + "domains_transfer_start", + "domains_verifications_direct", + "domains_whois_cancel-pending-irtp-verification", + "domains_whois_change-for", + "domains_whois_create", + "domains_whois_delete", + "domains_whois_get", + "domains_whois_list", + "domains_whois_pending-irtp-verification", + "domains_whois_set-as-default", + "domains_whois_unset-default", + "domains_whois_usage" ], "ecommerce": [ - "ecommerce_createCustomSalesChannelV1", - "ecommerce_createDigitalProductV1", - "ecommerce_createPhysicalProductV1", - "ecommerce_createStoreV1", - "ecommerce_deleteStoreV1", - "ecommerce_enableManualPaymentMethodV1", - "ecommerce_getCustomStorefrontSetupInstructionsV1", - "ecommerce_getStoreMetadataV1", - "ecommerce_getStoresV1", - "ecommerce_listSalesChannelsV1", - "ecommerce_setStoreShippingV1", - "ecommerce_updateSalesChannelV1" + "ecommerce_discounts_create", + "ecommerce_discounts_list", + "ecommerce_miscellaneous_custom-storefront-setup-instructions", + "ecommerce_orders_cancel", + "ecommerce_orders_fulfil", + "ecommerce_orders_list-store", + "ecommerce_orders_retrieve", + "ecommerce_payments_create-provider-connect-link", + "ecommerce_payments_enable-manual-method", + "ecommerce_payments_list-store-providers", + "ecommerce_product-variants_create", + "ecommerce_product-variants_delete", + "ecommerce_product-variants_list", + "ecommerce_product-variants_update-in-batch", + "ecommerce_products_create-digital", + "ecommerce_products_create-image-upload-url", + "ecommerce_products_create-physical", + "ecommerce_products_delete", + "ecommerce_products_list", + "ecommerce_products_update", + "ecommerce_products_upload-and-attach-image", + "ecommerce_sales-channels_create", + "ecommerce_sales-channels_list", + "ecommerce_sales-channels_update", + "ecommerce_shipping_set-store", + "ecommerce_stores_create", + "ecommerce_stores_delete", + "ecommerce_stores_list", + "ecommerce_stores_metadata" ], "horizons": [ - "horizons_createWebsiteV1", - "horizons_getWebsiteV1" + "horizons_websites_clone", + "horizons_websites_create", + "horizons_websites_edit", + "horizons_websites_get", + "horizons_websites_list", + "horizons_websites_publish" ], "hosting": [ - "hosting_changeDatabasePasswordV1", - "hosting_clearWebsiteCacheV1", - "hosting_createAccountCronJobV1", - "hosting_createAccountDatabaseV1", - "hosting_createDatabaseRemoteConnectionV1", - "hosting_createNodeJSBuildFromArchiveV1", - "hosting_createWebsiteParkedDomainV1", - "hosting_createWebsiteSubdomainV1", - "hosting_createWebsiteV1", - "hosting_deleteAccountCronJobV1", - "hosting_deleteAccountDatabaseV1", - "hosting_deleteDatabaseRemoteConnectionV1", - "hosting_deleteWebsiteParkedDomainV1", - "hosting_deleteWebsiteSubdomainV1", - "hosting_deleteWebsiteV1", - "hosting_deployJsApplication", - "hosting_deployStaticWebsite", - "hosting_deployWordpressPlugin", - "hosting_deployWordpressTheme", - "hosting_generateAFreeSubdomainV1", - "hosting_getCronJobOutputV1", - "hosting_getNodeJSBuildLogsV1", - "hosting_getPHPDetailsV1", - "hosting_getPHPInfoV1", - "hosting_getPhpMyAdminLinkV1", - "hosting_importWordpressWebsite", - "hosting_listAccountCronJobsV1", - "hosting_listAccountDatabasesV1", - "hosting_listAvailableDatacentersV1", - "hosting_listDatabaseRemoteConnectionsV1", - "hosting_listJsDeployments", - "hosting_listNodeJSBuildsV1", - "hosting_listNode_jsVulnerabilitiesV1", - "hosting_listOrdersV1", - "hosting_listWebsiteParkedDomainsV1", - "hosting_listWebsiteSubdomainsV1", - "hosting_listWebsitesV1", - "hosting_patchNode_jsVulnerabilitiesV1", - "hosting_repairDatabaseV1", - "hosting_resetPHPExtensionsV1", - "hosting_restartNode_jsApplicationV1", - "hosting_showJsDeploymentLogs", - "hosting_toggleCachelessModeV1", - "hosting_toggleWebsiteCacheV1", - "hosting_updatePHPExtensionsV1", - "hosting_updatePHPOptionsV1", - "hosting_updatePHPVersionV1", - "hosting_verifyDomainOwnershipV1" + "hosting_cache_clear-website", + "hosting_cache_toggle-cacheless", + "hosting_cache_toggle-website", + "hosting_cron-jobs_create", + "hosting_cron-jobs_delete", + "hosting_cron-jobs_list", + "hosting_cron-jobs_output", + "hosting_databases_change-password", + "hosting_databases_create", + "hosting_databases_create-remote-connection", + "hosting_databases_delete", + "hosting_databases_delete-remote-connection", + "hosting_databases_list", + "hosting_databases_list-remote-connections", + "hosting_databases_phpmyadmin-link", + "hosting_databases_repair", + "hosting_databases_setup-website", + "hosting_datacenters_list", + "hosting_deploy-js-application", + "hosting_deploy-static-website", + "hosting_deploy-wordpress-plugin", + "hosting_deploy-wordpress-theme", + "hosting_domains_create-website-parked", + "hosting_domains_create-website-subdomain", + "hosting_domains_delete-website-parked", + "hosting_domains_delete-website-subdomain", + "hosting_domains_generate-free-subdomain", + "hosting_domains_list-website-parked", + "hosting_domains_list-website-subdomains", + "hosting_domains_verify-ownership", + "hosting_files_generate-upload-url", + "hosting_files_list-website-and-directories", + "hosting_files_website-content", + "hosting_git_auto-deployment-settings", + "hosting_git_delete-auto-deployment-settings", + "hosting_git_list-installation-repositories", + "hosting_git_list-installations", + "hosting_git_update-auto-deployment-settings", + "hosting_import-wordpress-website", + "hosting_list-js-deployments", + "hosting_nodejs_analyse-failed-build", + "hosting_nodejs_build", + "hosting_nodejs_build-logs", + "hosting_nodejs_build-settings", + "hosting_nodejs_build-settings-from-archive", + "hosting_nodejs_clear-runtime-logs", + "hosting_nodejs_list-builds", + "hosting_nodejs_list-environment-variables", + "hosting_nodejs_list-vulnerabilities", + "hosting_nodejs_patch-vulnerabilities", + "hosting_nodejs_replace-environment-variables", + "hosting_nodejs_restart-application", + "hosting_nodejs_runtime-logs", + "hosting_nodejs_start-build", + "hosting_nodejs_update-build-settings", + "hosting_orders_list", + "hosting_php_get", + "hosting_php_info", + "hosting_php_reset-extensions", + "hosting_php_update-extensions", + "hosting_php_update-options", + "hosting_php_update-version", + "hosting_redirects_create-website", + "hosting_redirects_delete-website", + "hosting_redirects_list-website", + "hosting_show-js-deployment-logs", + "hosting_ssl_install", + "hosting_ssl_status", + "hosting_ssl_toggle-https-redirect", + "hosting_ssl_uninstall", + "hosting_websites_create", + "hosting_websites_delete", + "hosting_websites_deploy-static-site-archive", + "hosting_websites_list", + "hosting_websites_list-setups" ], "mail": [ - "mail_changeMailboxPasswordV1", - "mail_createAPITokenV1", - "mail_createAliasV1", - "mail_createAutoreplyV1", - "mail_createCatchAllV1", - "mail_createForwarderV1", - "mail_createMailboxV1", - "mail_createWebhookV1", - "mail_deleteAliasV1", - "mail_deleteAutoreplyV1", - "mail_deleteCatchAllV1", - "mail_deleteForwarderV1", - "mail_deleteMailboxV1", - "mail_deleteWebhookV1", - "mail_getOrderPlanV1", - "mail_getWebhookV1", - "mail_listAPITokensV1", - "mail_listAccessLogsV1", - "mail_listActionLogsV1", - "mail_listAliasesV1", - "mail_listAutorepliesV1", - "mail_listCatchAllsV1", - "mail_listForwardersV1", - "mail_listInboundLogsV1", - "mail_listMailboxActionLogsV1", - "mail_listMailboxesV1", - "mail_listOrdersV1", - "mail_listOutboundLogsV1", - "mail_listWebhookDeliveryLogsV1", - "mail_listWebhooksV1", - "mail_regenerateWebhookSecretV1", - "mail_resendCatchAllConfirmationV1", - "mail_resendForwarderConfirmationV1", - "mail_revokeAPITokenV1", - "mail_testWebhookV1", - "mail_updateAutoreplyV1", - "mail_updateForwarderKeepCopySettingV1", - "mail_updateWebhookV1" + "mail_aliases_create-alias", + "mail_aliases_delete-alias", + "mail_aliases_list", + "mail_api-tokens_create", + "mail_api-tokens_list", + "mail_api-tokens_revoke", + "mail_autoreplies_create", + "mail_autoreplies_delete", + "mail_autoreplies_list", + "mail_autoreplies_update", + "mail_catchalls_create-catch-all", + "mail_catchalls_delete-catch-all", + "mail_catchalls_list-catch-alls", + "mail_catchalls_resend-catch-all-confirmation", + "mail_forwarders_create", + "mail_forwarders_delete", + "mail_forwarders_list", + "mail_forwarders_resend-confirmation", + "mail_forwarders_update-keep-copy-setting", + "mail_logs_list-access", + "mail_logs_list-action", + "mail_logs_list-inbound", + "mail_logs_list-mailbox-action", + "mail_logs_list-outbound", + "mail_mailboxes_change-mailbox-password", + "mail_mailboxes_create-mailbox", + "mail_mailboxes_delete-mailbox", + "mail_mailboxes_list", + "mail_orders_list", + "mail_orders_plan", + "mail_webhooks_create", + "mail_webhooks_delete", + "mail_webhooks_get", + "mail_webhooks_list", + "mail_webhooks_list-delivery-logs", + "mail_webhooks_regenerate-secret", + "mail_webhooks_test", + "mail_webhooks_update" ], "reach": [ - "reach_createANewContactSegmentV1", - "reach_createANewContactV1", - "reach_createNewContactsV1", - "reach_deleteAContactV1", - "reach_getProfileDomainDNSStatusV1", - "reach_getSegmentDetailsV1", - "reach_listContactGroupsV1", - "reach_listContactsV1", - "reach_listProfileSegmentContactsV1", - "reach_listProfilesV1", - "reach_listSegmentContactsV1", - "reach_listSegmentsV1" + "reach_automations_get", + "reach_automations_list", + "reach_automations_list-steps", + "reach_campaigns_create-draft", + "reach_campaigns_get", + "reach_campaigns_list", + "reach_campaigns_performance", + "reach_contact-fields_create", + "reach_contact-fields_delete", + "reach_contact-fields_list", + "reach_contact-fields_update", + "reach_contacts_create", + "reach_contacts_create-bulk", + "reach_contacts_create-in-bulk", + "reach_contacts_delete", + "reach_contacts_delete-profile", + "reach_contacts_get", + "reach_contacts_list", + "reach_contacts_list-groups", + "reach_contacts_list-profile", + "reach_contacts_update", + "reach_forms_delete", + "reach_forms_get", + "reach_forms_list", + "reach_profiles_connected-sending-domain", + "reach_profiles_domain-dns-status", + "reach_profiles_list", + "reach_profiles_list-plan-feature-access", + "reach_profiles_remaining-plan-limits", + "reach_segments_count-profile-contacts", + "reach_segments_create", + "reach_segments_create-profile", + "reach_segments_delete-profile", + "reach_segments_get", + "reach_segments_list", + "reach_segments_list-contacts", + "reach_segments_list-filter-attributes", + "reach_segments_list-profile", + "reach_segments_list-profile-contacts", + "reach_segments_preview-contacts-matching-conditions", + "reach_segments_profile", + "reach_segments_update-profile", + "reach_tags_assign-contact-to", + "reach_tags_assign-contacts-to", + "reach_tags_create-or-find", + "reach_tags_delete", + "reach_tags_list-profile", + "reach_tags_remove-contact-from", + "reach_tags_remove-contacts-from", + "reach_tags_rename", + "reach_templates_create-email", + "reach_templates_list-email" ], "vps": [ - "VPS_activateFirewallV1", - "VPS_attachPublicKeyV1", - "VPS_createFirewallRuleV1", - "VPS_createNewFirewallV1", - "VPS_createNewProjectV1", - "VPS_createPTRRecordV1", - "VPS_createPostInstallScriptV1", - "VPS_createPublicKeyV1", - "VPS_createSnapshotV1", - "VPS_deactivateFirewallV1", - "VPS_deleteFirewallRuleV1", - "VPS_deleteFirewallV1", - "VPS_deletePTRRecordV1", - "VPS_deletePostInstallScriptV1", - "VPS_deleteProjectV1", - "VPS_deletePublicKeyV1", - "VPS_deleteSnapshotV1", - "VPS_getActionDetailsV1", - "VPS_getActionsV1", - "VPS_getAttachedPublicKeysV1", - "VPS_getBackupsV1", - "VPS_getDataCenterListV1", - "VPS_getFirewallDetailsV1", - "VPS_getFirewallListV1", - "VPS_getMetricsV1", - "VPS_getPostInstallScriptV1", - "VPS_getPostInstallScriptsV1", - "VPS_getProjectContainersV1", - "VPS_getProjectContentsV1", - "VPS_getProjectListV1", - "VPS_getProjectLogsV1", - "VPS_getPublicKeysV1", - "VPS_getScanMetricsV1", - "VPS_getSnapshotV1", - "VPS_getTemplateDetailsV1", - "VPS_getTemplatesV1", - "VPS_getVirtualMachineDetailsV1", - "VPS_getVirtualMachinesV1", - "VPS_installMonarxV1", - "VPS_purchaseNewVirtualMachineV1", - "VPS_recreateVirtualMachineV1", - "VPS_resetHostnameV1", - "VPS_restartProjectV1", - "VPS_restartVirtualMachineV1", - "VPS_restoreBackupV1", - "VPS_restoreSnapshotV1", - "VPS_setHostnameV1", - "VPS_setNameserversV1", - "VPS_setPanelPasswordV1", - "VPS_setRootPasswordV1", - "VPS_setupPurchasedVirtualMachineV1", - "VPS_startProjectV1", - "VPS_startRecoveryModeV1", - "VPS_startVirtualMachineV1", - "VPS_stopProjectV1", - "VPS_stopRecoveryModeV1", - "VPS_stopVirtualMachineV1", - "VPS_syncFirewallV1", - "VPS_uninstallMonarxV1", - "VPS_updateFirewallRuleV1", - "VPS_updatePostInstallScriptV1", - "VPS_updateProjectV1" + "vps_actions_get", + "vps_actions_list", + "vps_backups_list", + "vps_backups_restore", + "vps_data-centers_list", + "vps_docker_containers", + "vps_docker_create", + "vps_docker_delete", + "vps_docker_get", + "vps_docker_list", + "vps_docker_logs", + "vps_docker_restart", + "vps_docker_start", + "vps_docker_stop", + "vps_docker_update", + "vps_firewall_activate", + "vps_firewall_create", + "vps_firewall_create-rule", + "vps_firewall_deactivate", + "vps_firewall_delete", + "vps_firewall_delete-rule", + "vps_firewall_get", + "vps_firewall_list", + "vps_firewall_replace-all-rules-in-group", + "vps_firewall_sync", + "vps_firewall_sync-to-all-assigned-v-ms", + "vps_firewall_update-rule", + "vps_monarx_install", + "vps_monarx_scan-metrics", + "vps_monarx_uninstall", + "vps_post-install-scripts_create", + "vps_post-install-scripts_delete", + "vps_post-install-scripts_get", + "vps_post-install-scripts_list", + "vps_post-install-scripts_update", + "vps_ptr_create", + "vps_ptr_delete", + "vps_public-keys_attach", + "vps_public-keys_create", + "vps_public-keys_delete", + "vps_public-keys_list", + "vps_recovery_start", + "vps_recovery_stop", + "vps_snapshots_create", + "vps_snapshots_delete", + "vps_snapshots_get", + "vps_snapshots_restore", + "vps_templates_get", + "vps_templates_list", + "vps_virtual-machines_attached-public-keys", + "vps_virtual-machines_get", + "vps_virtual-machines_list", + "vps_virtual-machines_metrics", + "vps_virtual-machines_purchase", + "vps_virtual-machines_recreate", + "vps_virtual-machines_reset-hostname", + "vps_virtual-machines_restart", + "vps_virtual-machines_set-hostname", + "vps_virtual-machines_set-nameservers", + "vps_virtual-machines_set-panel-password", + "vps_virtual-machines_set-root-password", + "vps_virtual-machines_setup", + "vps_virtual-machines_start", + "vps_virtual-machines_stop" ], "wordpress": [ - "hosting_activateWordPressPluginV1", - "hosting_activateWordPressThemeV1", - "hosting_checkIfWooCommerceIsInstalledV1", - "hosting_checkIfWordPressInstallationsAreValidV1", - "hosting_createLoginLinksV1", - "hosting_deactivateWordPressPluginV1", - "hosting_deleteWordPressInstallationV1", - "hosting_detectWordPressInstallationsV1", - "hosting_getInstallationJWTTokenV1", - "hosting_installWordPressPluginsV1", - "hosting_installWordPressThemeV1", - "hosting_installWordPressV1", - "hosting_listAvailableWordPressCoreUpdatesV1", - "hosting_listAvailableWordPressPluginsV1", - "hosting_listInstalledWordPressPluginsV1", - "hosting_listInstalledWordPressThemesV1", - "hosting_listSuggestedWordPressPluginsV1", - "hosting_listWordPressInstallationsV1", - "hosting_listWordPressThemesV1", - "hosting_purgeLiteSpeedCacheV1", - "hosting_searchWordPressPluginsV1", - "hosting_setAIOptionStatusV1", - "hosting_showAIOptionStatusV1", - "hosting_showLiteSpeedCacheStatusV1", - "hosting_showMaintenanceStatusV1", - "hosting_showMemcachedObjectCacheStatusV1", - "hosting_showWordPressCoreVersionV1", - "hosting_toggleMaintenanceModeV1", - "hosting_toggleMemcachedObjectCacheV1", - "hosting_uninstallWordPressPluginsV1", - "hosting_uninstallWordPressThemesV1", - "hosting_updateHostingerWordPressPluginV1", - "hosting_updateWordPressCoreV1", - "hosting_updateWordPressPluginsV1", - "hosting_updateWordPressThemesV1" + "wordpress_ai-tools_set-option-status", + "wordpress_ai-tools_show-option-status", + "wordpress_installations_check-if-are-valid", + "wordpress_installations_delete", + "wordpress_installations_detect", + "wordpress_installations_import-website", + "wordpress_installations_install", + "wordpress_installations_jwt-token", + "wordpress_installations_list", + "wordpress_installations_list-core-updates", + "wordpress_installations_show-core-version", + "wordpress_installations_update-core", + "wordpress_litespeed-cache_purge-lite-speed", + "wordpress_litespeed-cache_show-lite-speed-status", + "wordpress_login_create-links", + "wordpress_maintenance_show-status", + "wordpress_maintenance_toggle", + "wordpress_object-cache_show-memcached-status", + "wordpress_object-cache_toggle-memcached", + "wordpress_plugins_activate", + "wordpress_plugins_check-if-woo-commerce-is-installed", + "wordpress_plugins_deactivate", + "wordpress_plugins_deploy", + "wordpress_plugins_install", + "wordpress_plugins_list", + "wordpress_plugins_list-installed", + "wordpress_plugins_list-suggested", + "wordpress_plugins_search", + "wordpress_plugins_uninstall", + "wordpress_plugins_update", + "wordpress_plugins_update-hostinger", + "wordpress_themes_activate", + "wordpress_themes_deploy", + "wordpress_themes_install", + "wordpress_themes_list", + "wordpress_themes_list-installed", + "wordpress_themes_uninstall", + "wordpress_themes_update" ] } } diff --git a/scripts/sync-skills.mjs b/scripts/sync-skills.mjs new file mode 100755 index 0000000..292d70a --- /dev/null +++ b/scripts/sync-skills.mjs @@ -0,0 +1,69 @@ +#!/usr/bin/env node + +import { execFileSync } from "node:child_process"; +import { cpSync, existsSync, mkdtempSync, readFileSync, readdirSync, rmSync, statSync } from "node:fs"; +import { tmpdir } from "node:os"; +import path from "node:path"; +import process from "node:process"; + +const source = process.argv[2] ?? "latest"; +const outDir = path.join(import.meta.dirname, "..", "skills"); +const work = mkdtempSync(path.join(tmpdir(), "hostinger-skills-")); + +function resolveSkillsRoot() { + if (existsSync(source) && statSync(source).isDirectory()) { + const root = path.resolve(source); + return { root, label: root }; + } + + const spec = `hostinger-api-mcp@${source}`; + const packed = execFileSync("npm", ["pack", spec, "--silent", "--pack-destination", work], { + encoding: "utf8", + }) + .trim() + .split("\n") + .pop(); + execFileSync("tar", ["xzf", path.join(work, packed), "-C", work]); + + const pkgRoot = path.join(work, "package"); + const { version } = JSON.parse(readFileSync(path.join(pkgRoot, "package.json"), "utf8")); + return { root: path.join(pkgRoot, "skills"), label: `hostinger-api-mcp@${version}` }; +} + +function skillName(skillFile) { + const text = readFileSync(skillFile, "utf8").replace(/\r\n/g, "\n"); + const end = text.indexOf("\n---\n", 4); + const frontmatter = text.startsWith("---\n") && end !== -1 ? text.slice(4, end) : ""; + const match = frontmatter.match(/^name:\s*["']?([a-z0-9]+(?:-[a-z0-9]+)*)["']?\s*$/m); + if (!match) { + throw new Error(`No valid name in the frontmatter of ${skillFile}`); + } + return match[1]; +} + +try { + const { root, label } = resolveSkillsRoot(); + const folders = readdirSync(root) + .filter((folder) => existsSync(path.join(root, folder, "SKILL.md"))) + .sort(); + if (folders.length === 0) { + throw new Error(`No skills found in ${root}`); + } + + rmSync(outDir, { recursive: true, force: true }); + + const names = []; + for (const folder of folders) { + const from = path.join(root, folder); + const name = skillName(path.join(from, "SKILL.md")); + cpSync(from, path.join(outDir, name), { + recursive: true, + filter: (src) => path.relative(from, src).split(path.sep)[0] !== "entry", + }); + names.push(name); + } + + console.log(`Synced ${names.length} skills from ${label}: ${names.join(", ")}`); +} finally { + rmSync(work, { recursive: true, force: true }); +} diff --git a/skills/audit-hosting/SKILL.md b/skills/audit-hosting/SKILL.md new file mode 100644 index 0000000..fa71453 --- /dev/null +++ b/skills/audit-hosting/SKILL.md @@ -0,0 +1,83 @@ +--- +name: audit-hosting +description: "Audit a Hostinger web hosting account (Shared, Cloud and Agency plans) and report what needs attention: every plan and website, SSL problems, broken or vulnerable WordPress installs, failed Node.js builds and vulnerable npm packages, databases and Agency Plan orders near their limits, and hosting plans about to lapse. Read-only; produces a prioritised to-do list and names the skill that fixes each item. Triggers: what do I have on Hostinger, audit my hosting, check all my websites, what needs attention, health check my sites, are any of my sites broken, which of my sites are insecure." +--- + +# Audit hosting + +Read-only from start to finish. The output is a report; every fix is a separate, confirmed step through another skill. + +## Calling the operations + +- Read the `inputSchema` that `search` returns before the first call of each operation. If a name below is rejected as unknown, `search` for what the step does (e.g. "ssl status"). +- Batch reads with `multi-execute`, up to 20 steps a batch. Batches chain operations from one server only — the Hostinger Connector runs `hosting`, `wordpress`, `agency-hosting` and `billing` as separate servers. When a product group is switched off, skip its checks and say which were skipped. +- Page every list until `meta.total` is covered. +- Leave `hosting_php_get` out — it is large and says nothing about account health. + +## 1. Inventory + +One batch per server: + +- `hosting_orders_list` with `statuses: ["active", "suspended"]`. `cloud_*` plan names are Cloud; the rest (`hostinger_premium_*`, `hostinger_business_*`, …) are Shared. +- `hosting_websites_list` — every Shared and Cloud website with `website_type`, `username`, `order_id` and `is_enabled` (false means suspended). +- `wordpress_installations_list` with `ownership: "all"` — `id`, `is_valid`, `validation_error`. +- `agency-hosting_orders_list` and `agency-hosting_websites_list-plan` — Agency orders and sites (`details.uid`, `details.type`, `details.domains`, `details.state`). + +Tell the user the size (plans, websites, WordPress installs) before the checks. For large accounts offer to scope the audit to one order or domain. + +## 2. Checks + +Per Shared and Cloud website (skip `builder` and `horizons`): + +- `hosting_ssl_status` — flag `failed` (with `last_error`), `expired`, `not_installed` on a custom domain, `is_https_redirect_enabled: false`, and `expires_at` within 30 days when `is_lifetime` is false. Lifetime certificates renew on their own. +- Node.js sites: `hosting_nodejs_list-builds` with `per_page: 1` (latest build failed?) and `hosting_nodejs_list-vulnerabilities` with `severities: ["critical", "high"]`. + +Per hosting account (`username`): `hosting_databases_list` — flag `disk_usage_mb` above 80% of `max_size_mb`. + +Per WordPress install: `wordpress_installations_show-core-version` (core vulnerabilities) and `wordpress_plugins_list-installed` (vulnerable plugins and pending updates). + +Per Agency order: `agency-hosting_metrics_list-order-resource-usage` with `time_frame_hours: 168` and `agency-hosting_metrics_list-plan-order-disk-usage` with `time_frame_days: 7` — flag usage above 80% of the plan quota and name the heaviest websites. + +Per Agency site: `agency-hosting_ssl_website-status` for each custom domain in `details.domains`; flag `details.state` other than `active`. + +Plans about to lapse (billing product): `billing_subscriptions_list`, joined to hosting orders on `subscription_id` — flag `not_renewing`, or `is_auto_renewed: false` with `expires_at` within 30 days. Every site on that plan goes down with it. + +From outside, for each custom domain: + +```bash +curl -sS -o /dev/null -w "%{http_code}\n" --max-time 15 https://DOMAIN/ +curl -sSI --max-time 15 https://DOMAIN/ | grep -i "^platform" +``` + +No `platform: hostinger` header means the domain does not reach this hosting. + +## 3. Report + +``` +# Hosting audit +3 plans (2 Shared, 1 Cloud) + 1 Agency · 14 websites (6 WordPress, 3 Node.js, 5 other) · 4 Agency sites + +## Fix now +- shop.example.com — latest Node.js build failed 2 days ago → troubleshoot-website +- blog.example.com — contact-form-x 5.2 has a known vulnerability, fixed in 5.3 → maintain-wordpress + +## Soon +- example.org — Business plan expires in 12 days, auto-renew off → renew in hPanel +- Agency order 1000000001 — 86% of disk quota, mostly client-a.com + +## Worth knowing +- 4 WordPress installs have pending plugin updates → maintain-wordpress + +## Healthy +docs.example.com, portfolio.example.com, … +``` + +- **Fix now:** site down or not reaching Hostinger, failed latest build, invalid WordPress install, SSL `failed` or `expired`, critical vulnerabilities, suspended website. +- **Soon:** high vulnerabilities, a plan lapsing within 30 days, usage above 80% of a quota, HTTPS redirect off, non-lifetime certificates expiring. +- **Worth knowing:** pending updates, inactive plugins with vulnerabilities. + +Each item names the evidence and the skill that fixes it: `troubleshoot-website`, `maintain-wordpress`, `connect-domain` or `deploy-to-hosting`. Patchable npm vulnerabilities on GitHub-deployed sites can be fixed with `hosting_nodejs_patch-vulnerabilities`, which opens a pull request — offer it, do not run it as part of the audit. + +## Not available through the API + +CPU and memory usage for Shared and Cloud plans, website backup status, and PHP error logs. Mention them once at the end so the user knows what the audit could not see. diff --git a/skills/connect-domain/SKILL.md b/skills/connect-domain/SKILL.md new file mode 100644 index 0000000..1f4d5eb --- /dev/null +++ b/skills/connect-domain/SKILL.md @@ -0,0 +1,69 @@ +--- +name: connect-domain +description: "Connect a custom domain to a website on Hostinger web hosting (Shared, Cloud or Agency plans) end to end: attach the domain to the site, point DNS at Hostinger without breaking existing email or verification records, install SSL, turn on the HTTPS redirect, and verify it resolves. Handles domains registered at Hostinger or elsewhere, moving a site off its free *.hostingersite.com subdomain, subdomains and aliases. Triggers: connect my domain, point my domain to my site, use my own domain, move off the free subdomain, add a subdomain, park a domain, add a domain alias, my domain is not working." +--- + +# Connect a domain + +Get a domain serving a Hostinger website over HTTPS. The one rule that matters most: records that already work — email (`MX`), SPF, DKIM and verification `TXT` records — must survive every DNS change. + +## Calling the operations + +- Read the `inputSchema` that `search` returns before the first call of each operation. If a name below is rejected as unknown, `search` for what the step does (e.g. "dns records"). +- Batch reads with `multi-execute`; values pass between steps as `$steps..`. Batches chain operations from one server only — the Hostinger Connector runs `hosting`, `agency-hosting`, `domains` and `dns` as separate servers. When `search` cannot find an operation, ask the user to enable that product group in the Connector. +- Every write here changes what the public sees. State the change and get a yes before each one. + +## 1. Gather the facts (read-only) + +- **Website.** `hosting_websites_list` with `domain` (Shared and Cloud; substring match, so take the exact entry) and `agency-hosting_websites_list-plan` with `domain` (Agency). Keep `username` and `order_id`, or `details.uid` and `details.ipv4`. When the domain has no website yet, find the site the user means — often a `*.hostingersite.com` one — or the order to create it on (`hosting_orders_list`, `agency-hosting_orders_list`). +- **Registration.** `domains_portfolio_get` succeeds only for domains registered at Hostinger; its `name_servers` show who runs DNS. Hostinger's own nameservers end in `dns-parking.com`. +- **Live DNS**, to know what must be kept: + +```bash +dig +short NS DOMAIN; dig +short A DOMAIN; dig +short CNAME www.DOMAIN +dig +short MX DOMAIN; dig +short TXT DOMAIN; dig +short CAA DOMAIN +``` + +## 2. Attach the domain to hosting + +Shared and Cloud: + +- **New website on the domain.** For a domain outside this account run `hosting_domains_verify-ownership` first. When `is_accessible` is false, give the user the `TXT` record it returns to add next to the existing ones, then verify again (propagation takes up to ~10 minutes). Create with `hosting_websites_create` (`domain` without `www.`, `order_id`), then poll `hosting_websites_list-setups` with `domain` every 10–15 s until `status: completed`. +- **Moving a site off its free subdomain.** Shared and Cloud websites cannot be renamed through the API. Either create a website on the real domain and redeploy the project there (the `deploy-to-hosting` skill) — required for WordPress, which keeps its URL in the database — or, for static sites, park the domain on the existing site with `hosting_domains_create-website-parked`. +- **Alias** (a second domain showing the same site): `hosting_domains_create-website-parked`. +- **Subdomain** (`shop.example.com`): `hosting_domains_create-website-subdomain`. `www` belongs to the main domain — do not create it as a subdomain. + +Agency Plan: + +- `agency-hosting_domains_link-to-website` adds a domain to the site. +- `agency-hosting_domains_change-website` replaces the primary domain, for example the free subdomain. The old name stops serving at once — confirm first. + +## 3. Point DNS at Hostinger + +**DNS already at Hostinger** (nameservers end in `dns-parking.com`): creating the website normally writes the records. Read the zone with `dns_records_list`; when `@` and `www` already point at the site, change nothing. Before any edit, note the newest `dns_snapshots_list` entry — `dns_snapshots_restore` rolls back to it. + +`dns_records_update` defaults to `overwrite: true`, which deletes every existing record with the same name and type. Adding a `TXT` at `@` that way wipes SPF and verification records. Send `overwrite: false` when adding; use `true` only to replace one record set on purpose, and run `dns_records_validate` on the same payload first. + +**DNS elsewhere** (registered at Hostinger with other nameservers, or registered elsewhere). Two options — let the user choose: + +- **Switch nameservers to Hostinger.** Hostinger then manages every record. First copy each record the current DNS serves beyond the website — `MX`, `TXT`, service `CNAME`s — into the Hostinger zone with `dns_records_update` (`overwrite: false`); otherwise email breaks when the nameservers flip. Hostinger's nameserver pairs differ between domains (`ns1`/`ns2.dns-parking.com`, `solar`/`lunar.dns-parking.com` and others), so use the pair hPanel shows for this domain, never a guessed one. For Hostinger-registered domains apply it with `domains_portfolio_update-nameservers`; otherwise the user changes it at their registrar. +- **Keep the current DNS provider** and change only the web records: `A` for `@` and `CNAME` (or `A`) for `www`. Agency sites point at `details.ipv4`. Shared and Cloud sites point at the targets in the Hostinger zone for the domain (`dns_records_list`) or, when that zone does not exist, the IP hPanel shows for the website. The user makes this change at their DNS provider. + +## 4. SSL and HTTPS + +Start only once `dig` shows the domain resolving to Hostinger — the certificate authority checks the domain against the server. + +- Shared and Cloud: `hosting_ssl_status`. When it is not `active`, call `hosting_ssl_install` and poll the status until `active` (or `failed` with `last_error`), then `hosting_ssl_toggle-https-redirect` with `is_enabled: true` (it returns 422 until a certificate exists). +- Agency: `agency-hosting_ssl_install-website` for each domain, then poll `agency-hosting_ssl_website-status`. A domain allows three setups per seven days and one request a minute — install once DNS is right and never loop. +- A `CAA` record at the apex that does not allow `letsencrypt.org` blocks issuance; the user adds `0 issue "letsencrypt.org"` beside the existing ones. + +## 5. Verify + +```bash +dig +short A DOMAIN; dig +short A www.DOMAIN +curl -sSI https://DOMAIN/ | grep -i -E "^(HTTP|platform|location)" +curl -sSI http://DOMAIN/ | grep -i -E "^(HTTP|location)" +dig +short MX DOMAIN +``` + +Done when both names resolve to Hostinger, HTTPS answers with `platform: hostinger`, HTTP redirects to HTTPS, and `MX` still matches step 1. Nameserver changes can take up to 24–48 hours to reach every resolver; when records are still old, report what is pending and the command to re-check instead of polling for hours. diff --git a/skills/deploy-nodejs-app/SKILL.md b/skills/deploy-nodejs-app/SKILL.md deleted file mode 100644 index f0412ef..0000000 --- a/skills/deploy-nodejs-app/SKILL.md +++ /dev/null @@ -1,51 +0,0 @@ ---- -name: deploy-nodejs-app -description: Guide the agent through deploying a Node.js or static project to Hostinger. Use when the user asks to deploy, publish, or push an app to Hostinger. -when-to-use: User mentions deploying a Node.js / React / Vue / Vite / Express / Nest / Fastify / SvelteKit / Next.js app to Hostinger, or asks how to put a project live on a Hostinger domain. ---- - -# Deploy an app to Hostinger - -## When to use - -- User says "deploy this to Hostinger", "ship this app", or "put this live on ". -- User wants to redeploy a project that already lives on Hostinger. - -## Inputs to gather first - -1. Target domain. Call `hosting_listWebsitesV1` if it's ambiguous — filter with the `domain` parameter, or page through with `page` / `per_page`. -2. Whether the project needs a build. A `package.json` with a `build` script means yes; a folder of finished HTML/CSS/JS means no. -3. Project root, if it isn't the repo root (monorepos). -4. Any build overrides the user already knows they need — Node version, package manager, entry file, output directory. - -See `rules/nodejs-deployments.mdc` for the full list of accepted override values. Do not invent `app_type` values. - -## Steps - -1. Confirm the target domain and that deploying will overwrite what is currently live. Wait for explicit approval. -2. Build the archive: source only, excluding `node_modules/`, build output, `.git/`, and `.env`. Keep it under 50 MB. -3. Deploy: - - **Default** — `hosting_deployJsApplication` with `domain` and `archivePath`. Hostinger auto-detects build settings and resolves the username. - - **Needs overrides** — `hosting_createNodeJSBuildFromArchiveV1`. This one also needs `username`, from `hosting_listWebsitesV1`. - - **No build step** — `hosting_deployStaticWebsite`, with the archive named `_YYYYMMDD_HHMMSS.zip`. -4. Track the build: `hosting_listJsDeployments` (or `hosting_listNodeJSBuildsV1`) for state and the build `uuid`. -5. Stream logs while the state is `running` — `hosting_showJsDeploymentLogs` or `hosting_getNodeJSBuildLogsV1`, passing the last line count back as `fromLine` / `from_line`. -6. On success, report the live URL and the build duration. On failure, hand off to `diagnose-build-failure`. - -## Optional follow-ups - -- `hosting_clearWebsiteCacheV1` if the user is seeing stale content. -- `hosting_restartNode_jsApplicationV1` if the process needs a bounce after a config change. -- `hosting_listNode_jsVulnerabilitiesV1` to audit dependencies post-deploy. -- `DNS_getDNSRecordsV1` to confirm the domain actually points at Hostinger before promising the user a working URL. - -## Failure handling - -- Surface Hostinger API errors verbatim — do not fabricate causes. -- If authentication fails, the MCP server opens a browser sign-in on the next tool call. Only if the user needs a non-interactive setup should they generate an API token in hPanel (**Profile & settings → API Tokens**) and export `HOSTINGER_API_TOKEN` before launching Cursor. - -## Do not - -- Do not include `.env`, credentials, or tokens in the archive. -- Do not deploy to a production domain without explicit confirmation. -- Do not promise to set application environment variables — the API cannot. Direct the user to hPanel. diff --git a/skills/deploy-to-hosting/SKILL.md b/skills/deploy-to-hosting/SKILL.md new file mode 100644 index 0000000..eb13470 --- /dev/null +++ b/skills/deploy-to-hosting/SKILL.md @@ -0,0 +1,90 @@ +--- +name: deploy-to-hosting +description: "Deploy an existing project to a website on Hostinger web hosting (Shared, Cloud or Agency plans) and keep it deployed: picks the right deploy for static sites, Node.js apps (Next.js, Nuxt, Express, Vite and similar), PHP apps and WordPress plugins or themes, sets up auto-deploy from GitHub or GitLab, manages Node.js environment variables and MySQL databases, and verifies the live site. Triggers: deploy this project, deploy to Hostinger, push my app live, redeploy, set up auto-deploy, connect my GitHub repo, add environment variables, my app needs a database, deploy my WordPress plugin or theme." +--- + +# Deploy to hosting + +Ship a project that already exists — on disk or in a Git repository — to a Hostinger website. Building a new site from a prompt (design, store, blog) is the `hostinger-headless` skill; this one takes code as it is. + +## Calling the operations + +- Read the `inputSchema` that `search` returns before the first call of each operation. If a name below is rejected as unknown, `search` for what the step does (e.g. "deploy static"). +- Batch reads with `multi-execute`; values pass between steps as `$steps..`. Batches chain operations from one server only — the Hostinger Connector runs `hosting`, `wordpress` and `agency-hosting` as separate servers. +- Every deploy overwrites the website's current contents. Confirm the target domain with the user before the first deploy to a site that already has content. + +## 1. Pick the target website + +- `.hostinger/site.json` in the project: reuse its `domain` and `username` (or `website_uid`). +- Otherwise look the domain up with `hosting_websites_list` (Shared and Cloud; substring match — take the exact entry) and `agency-hosting_websites_list-plan` (Agency; keep `details.uid`). +- No website yet: + - Shared and Cloud: take a domain (the user's own, or `hosting_domains_generate-free-subdomain`) and an `order_id` from `hosting_orders_list`, then `hosting_websites_create`. `datacenter_code` is needed only for the first website on a new plan (first entry of `hosting_datacenters_list`). Poll `hosting_websites_list-setups` with `domain` every 10–15 s until `status: completed` — uploads and deploys return 404 or 409 before that. + - Agency: `agency-hosting_website-setups_create` with `flavor: "php-fpm"` (plus `type: "node-static"` for built frontends), `settings.php.version` from `agency-hosting_php_list-versions-for-order` and `datacenter_code` from `agency-hosting_datacenters_list`. Poll `agency-hosting_website-setups_status` until `completed`; it returns the `website_uid`. +- Node.js apps run on Business and Cloud plans (`hostinger_business_*`, `cloud_*` in `hosting_orders_list`). On other plans, build locally and deploy the output as a static site. + +## 2. Choose the deploy + +Wrong method is the most common failure. Decide from the project on disk: + +| Project | Shared / Cloud | +| --- | --- | +| Plain HTML/CSS/JS, or a framework's static build output | `hosting_deploy-static-website` | +| `package.json` with a build or a server (Next.js, Nuxt, Express, Vite…) | `hosting_deploy-js-application` | +| PHP code, no build step | `hosting_deploy-static-website` (extracts the archive as-is) | +| WordPress plugin or theme folder | `hosting_deploy-wordpress-plugin` / `hosting_deploy-wordpress-theme` (`activate` optional) | +| A whole WordPress site from elsewhere | the `migrate-to-hosting` skill | + +On Agency the website's type decides (`agency-hosting_websites_get`): sites created as `node-static` take `agency-hosting_deploy-node-static-website`, which runs the build when the project has one; every other site takes `agency-hosting_deploy-php-application`, which extracts the archive as-is. WordPress plugins and themes on Agency sites are installed from wp-admin. + +Archive rules (name archives `name_YYYYMMDD_HHMMSS.zip`): + +- **Static:** build locally first. `index.html` must be at the archive root, not inside a folder: `cd dist && zip -r ../site_20260101_120000.zip .` +- **Node.js source:** no `node_modules/`, no build output (`dist/`, `.next/`, `build/`), no `.env*`, nothing matched by `.gitignore`; 50 MB at most. `git archive --format=zip -o app_20260101_120000.zip HEAD` produces exactly the committed files — mention that uncommitted changes are left out. +- Agency deploys are synchronous: the site is live when the call returns. + +## 3. Node.js builds + +`hosting_deploy-js-application` uploads the archive to the document root, detects settings from `package.json` and starts a build. Track it with `hosting_nodejs_list-builds` and `hosting_nodejs_build` (by `uuid`), polling every 10–20 s — builds take minutes. + +- Failed build: `hosting_nodejs_analyse-failed-build` once per build (5 calls a minute), then `hosting_nodejs_build-logs` if the analysis is empty. +- Wrong detection (framework, `output_directory`, or a missing `entry_file` for express, fastify, nest, nuxt and hono): run `hosting_nodejs_start-build` with explicit values, `source_type: "archive"` and `source_options.archive_path` set to the archive's file name in the document root (check it is there with `hosting_files_list-website-and-directories`). Store the same values with `hosting_nodejs_update-build-settings`. +- To review detected settings before any build: upload the archive with `hosting_files_generate-upload-url` (the TUS steps are in its description), then `hosting_nodejs_build-settings-from-archive`. + +## 4. Auto-deploy from Git + +1. `hosting_git_list-installations`. With no `active` installation (check `status: "pending"` and `"suspended"` too), the user connects GitHub or GitLab once in hPanel under Websites → Manage → Advanced → Git; there is no API for that step. +2. `hosting_git_list-installation-repositories` with the installation `uuid` gives `owner`, `name` and `default_branch` (10 calls a minute). +3. `hosting_git_update-auto-deployment-settings` with `installation_uuid`, `owner`, `repository`, `branch` and optional `directory`. + - PHP and static sites deploy the branch immediately and again on every push. + - Node.js sites clone nothing on save: start the first build with `hosting_nodejs_start-build`, `source_type: "git"` and the same `source_options`. Later pushes build with the stored settings, so keep `hosting_nodejs_update-build-settings` correct. +4. Confirm with `hosting_git_auto-deployment-settings`. + +Agency Plan sites have no Git operations; deploy archives. + +## 5. Environment variables (Node.js) + +`hosting_nodejs_replace-environment-variables` replaces the whole set — anything not sent is deleted. + +1. `hosting_nodejs_list-environment-variables` for the current keys. Values come back as `********`; never send those back. +2. Build the full set: every current key plus the new ones, with real values from the project's `.env` or the user. When the real value of an existing key is unknown, ask — do not drop it. +3. Send it once; the app restarts. Values baked in at build time (Next.js `NEXT_PUBLIC_*`, Vite `VITE_*`) need a new `hosting_nodejs_start-build`. + +Keys use uppercase letters, digits and underscores. Never commit `.env` files or repeat secret values back to the user. + +## 6. Database + +- **Node.js:** `hosting_databases_setup-website` creates a MySQL database and writes `DB_HOST`, `DB_PORT`, `DB_NAME`, `DB_USER`, `DB_PASSWORD` and `DATABASE_URL` into the environment, then restarts the app. The password is generated and never returned. It fails with 422 when any of those keys exists — remove them first with the step 5 replace. +- **PHP:** `hosting_databases_create` with `name`, `user`, a strong generated `password` and `website_domain`; read the prefixed full name back from `hosting_databases_list`. The app reads credentials from a config file on the server that stays out of Git, and connects to `localhost` or `127.0.0.1`. +- **Agency:** `agency-hosting_databases_create-website`; the app connects to `localhost`. +- The `srvNNNN.hstgr.io` host from `hosting_databases_list` is only for connections from outside Hostinger — never put it in the deployed app. + +## 7. Verify and record + +```bash +curl -s -o /dev/null -w "%{http_code}\n" https://DOMAIN/ +curl -s https://DOMAIN/ | grep -o "TEXT THE PROJECT RENDERS" +``` + +A `200` alone can be a placeholder page; match real copy. A new site may serve a default page briefly — clear it with `hosting_cache_clear-website` and retry before calling the deploy failed. + +Write `.hostinger/site.json` in the project so later runs reuse the target: `{ "domain", "username" or "website_uid", "type": "static" | "nodejs" | "php" }`, keeping any fields already there. diff --git a/skills/diagnose-build-failure/SKILL.md b/skills/diagnose-build-failure/SKILL.md deleted file mode 100644 index c2d2e45..0000000 --- a/skills/diagnose-build-failure/SKILL.md +++ /dev/null @@ -1,55 +0,0 @@ ---- -name: diagnose-build-failure -description: Pull build logs from a failed Hostinger deployment and identify the failure mode (missing dependency, Node version mismatch, wrong output directory, dependency resolution conflict). -when-to-use: A Hostinger deployment failed, the build is stuck, or the user reports "my site won't deploy" / "the build broke". ---- - -# Diagnose a Hostinger build failure - -## When to use - -- A deploy returned state `failed`. -- The user reports the live site showing a build or runtime error. -- The user asks "why did my deploy fail?". - -## Inputs to gather first - -1. Domain. -2. The build `uuid`. If the user doesn't have it, call `hosting_listJsDeployments` (or `hosting_listNodeJSBuildsV1`) for the domain and filter `states` to `failed`. -3. For `hosting_getNodeJSBuildLogsV1` you also need `username` — get it from `hosting_listWebsitesV1`. - -## Steps - -1. Fetch the logs: - - `hosting_showJsDeploymentLogs` with `domain` + `buildUuid` for `hosting_deployJsApplication` builds. - - `hosting_getNodeJSBuildLogsV1` with `username` + `domain` + `uuid` for archive builds. - - Both accept a start line (`fromLine` / `from_line`) — page through rather than requesting one enormous response. -2. Log content may contain ANSI escape sequences. Strip them before quoting. -3. Match against the signatures below, then report the most likely cause and a concrete fix. - -## Common failure modes - -| Signature in logs | Likely cause | Fix | -|---|---|---| -| `Error: Cannot find module 'X'` | Dependency missing from `package.json`, or it was only in `devDependencies` | Add it to `dependencies`, rebuild | -| `npm ERR! ERESOLVE`, `peer dep` | Dependency resolution conflict | Pin versions, or commit a lockfile so the server installs deterministically | -| `engine "node" is incompatible`, syntax errors in dependency code | Node version mismatch | Set `node_version` to `18`, `20`, `22`, or `24` on `hosting_createNodeJSBuildFromArchiveV1`, or fix `engines.node` | -| `sh: vite: not found`, `build script not found` | Wrong `build_script`, or the build tool is in `devDependencies` and wasn't installed | Override `build_script`; verify the script name in `package.json` | -| `no such file or directory` for `package.json` | Wrong project root in a monorepo | Set `root_directory` relative to `public_html` | -| Build succeeds but the site 404s | Wrong `output_directory` | Point `output_directory` at the real build output, relative to the root directory | -| `JavaScript heap out of memory` | Build exceeded the memory cap | Reduce the build footprint, or upgrade the plan | -| Wrong package manager resolving deps | Lockfile/manager mismatch | Set `package_manager` to `npm`, `yarn`, or `pnpm` | -| App builds and starts but gets no traffic | Port hardcoded | Bind to `process.env.PORT` | -| `undefined` config values at runtime | Missing environment variables | Environment variables are not settable via the API — the user must add them in hPanel | - -## Report format - -1. **Cause** — one sentence, quoting the exact log line. -2. **Fix** — the concrete code change or the tool call with the specific override. -3. **Next action** — offer to apply it, and wait for confirmation. - -## Do not - -- Do not guess a cause without quoting a log line. -- Do not redeploy automatically — a redeploy overwrites what is live. Propose and wait. -- Do not blame a "framework preset" — Hostinger has no preset parameter. The overrides in `rules/nodejs-deployments.mdc` are the real knobs. diff --git a/skills/hostinger-headless/SKILL.md b/skills/hostinger-headless/SKILL.md new file mode 100644 index 0000000..d709179 --- /dev/null +++ b/skills/hostinger-headless/SKILL.md @@ -0,0 +1,56 @@ +--- +name: hostinger-headless +description: "Build, connect, or iterate on a website hosted on Hostinger — provision hosting and a domain, optionally seed an ecommerce store with a real hosted checkout or a WordPress content backend (headless CMS/blog), build the frontend, deploy, and verify. Requires an authenticated Hostinger MCP session (see entry/skill.md). Triggers: build me a site on Hostinger, deploy this to Hostinger, connect this project to Hostinger, add a store to my Hostinger site, add a blog to my Hostinger site, update my Hostinger site." +--- + +# Hostinger Headless + +This skill turns a prompt into a live website on Hostinger. Its job is to own the full run: check the account, provision hosting and a domain, seed the backend (an ecommerce store when the intent calls for selling), build the frontend, deploy it, and verify the result. + +The frontend is built ad-hoc to the user's intent — there is no template library. The backend pieces are real Hostinger products: web hosting (static or Node.js), domains and DNS, Hostinger Ecommerce with its public Storefront API and hosted checkout, and WordPress as a headless CMS/blog backend read through the public WP REST API. + +## Preconditions + +1. An authenticated Hostinger MCP session — the entry skill's bootstrap handled this. If MCP tools fail with auth errors, send the user back through `entry/skill.md`. +2. The Hostinger MCP operations for **hosting** (plus **ecommerce** when the run involves a store, and **wordpress** when it involves a content backend). Operation availability varies per user — product groups can be toggled in the Hostinger Connector. When search cannot find a needed operation, ask the user to enable that product group (or configure the scoped binary, e.g. `hostinger-hosting-mcp`) rather than improvising around it. +3. An active hosting plan (checked in Setup §1). Hostinger hosting is a paid product: if the account has no usable plan, inform the user plainly that a subscription is required, link them to https://www.hostinger.com/web-hosting, and pause until they confirm the purchase. Do not treat this as an error — it is a normal step for new accounts. Everything that doesn't need hosting (planning, building the frontend locally) can proceed while they decide. + +## Resolving the operation + +Resolve by intent first, disk second — never let an empty directory override what the user is asking for. Check `iterate` first (it's decided by an unambiguous on-disk signal): + +- `iterate` — a `.hostinger/site.json` is present in the project: the site is already deployed through this skill and the user wants changes. Reuse the recorded domain/site details; apply only the delta the new intent needs (edit frontend → redeploy; add a store → run `references/STORE.md` then redeploy). Never re-provision an existing site. +- `connect` — a frontend project already on disk (or brought in as a zip/export/URL) that is not yet on Hostinger, with language like "deploy this / host this on Hostinger / connect this project". Emptiness of the CWD at trigger time is not a create signal when a design is brought in from elsewhere. +- `create` — a new site from a prompt with nothing brought in: "build me a store / portfolio / site…". + +If signals conflict, ask the user — don't guess. + +## The run + +1. **Discovery** — from the prompt (and the project on disk for connect/iterate), infer: does this run need a store (any buy/sell/product intent → yes)? Does it need owner-managed content — a blog, news, or anything the owner edits without a redeploy (→ WordPress backend; static copy that rarely changes does not qualify)? What brand, copy, and structure does the site need? What stack fits — plain static HTML/CSS/JS for simple sites (default), a static-output framework build, or a Node.js app when SSR or server code is genuinely required? Prefer static: it deploys fastest and has no build-failure surface. Ask the user only what you cannot infer. +2. **Setup** (`references/SETUP.md`) — plan check, then provision the website on a free subdomain (or the user's own domain) and wait until it's ready. +3. **Store** (`references/STORE.md`, only when commerce is needed) — resolve or create the store and its `custom` sales channel, seed products/shipping/payment, and note the `sales_channel_id` for the frontend. +4. **Content backend** (`references/WORDPRESS.md`, only when owner-managed content is needed) — install WordPress on a dedicated subdomain, wait until it's ready, and hand the owner a wp-admin login link. The frontend reads it through the public WP REST API. +5. **Build** — create or wire the frontend. For store runs, follow the frontend contract in `references/STORE.md` (catalog fetched at runtime from the public Storefront API, cart in `localStorage`, client-side checkout — never embed an API token in the site). For content runs, follow the frontend contract in `references/WORDPRESS.md` (posts fetched at runtime from the WP REST API, rendered HTML bodies, graceful empty states). When the app needs MySQL, follow `references/DATABASE.md` (which host each runtime connects to, credentials via env vars or a server-side config file, never in the frontend). +6. **Deploy** (`references/DEPLOYMENT.md`) — archive and deploy via the matching hosting operation, polling build logs for Node.js runs. +7. **Verify** — curl the live URL for a 200 and a piece of real page copy; for store runs, confirm a checkout POST returns a redirect URL; for content runs, confirm the WP REST API returns 200 and the frontend renders posts. Show the user the live URL and where to manage things (hPanel: https://hpanel.hostinger.com, the store dashboard for commerce, wp-admin for content). +8. **Record** — write `.hostinger/site.json` into the project: `{ "domain", "username", "type": "static"|"nodejs", "sales_channel_id"?, "store_id"?, "cms_domain"? }`. This is what makes future runs resolve as `iterate`. + +Run non-interactively wherever possible. The exceptions that must involve the user: the paid-plan confirmation, anything that costs money (a domain purchase, a new subscription), and picking between genuinely equal options the prompt doesn't decide. + +## Paths + +| What | Path | +| --- | --- | +| Plan check + website/domain provisioning | `references/SETUP.md` | +| Store: seed the backend + the frontend API contract | `references/STORE.md` | +| WordPress: headless CMS/blog backend + the frontend read contract | `references/WORDPRESS.md` | +| Deploy: static vs Node.js, archive rules, logs, verify | `references/DEPLOYMENT.md` | +| Database + runtime: MySQL host per runtime, env vars, what to poll, runtime logs | `references/DATABASE.md` | + +## Where the how comes from + +The inputSchema returned by search is authoritative for request shapes — read it before calling. Two live sources supersede anything written here when they disagree: + +- The ecommerce operation `ecommerce_miscellaneous_custom-storefront-setup-instructions` returns the current storefront integration guide from the server — execute it at the start of any store run. +- The Hostinger API reference at https://developers.hostinger.com describes every endpoint behind the operations. diff --git a/skills/hostinger-headless/references/DATABASE.md b/skills/hostinger-headless/references/DATABASE.md new file mode 100644 index 0000000..e66d085 --- /dev/null +++ b/skills/hostinger-headless/references/DATABASE.md @@ -0,0 +1,49 @@ +# Database and runtime — MySQL hosts, env vars, and what to poll + +Use this when the app needs MySQL, or when a deploy "succeeded" but the app cannot reach its database. Everything here uses the **hosting** MCP operations; Agency Plan differences are at the end. + +## 1. Create the database + +1. `hosting_databases_create` on the site's `username` with a database name, a user and a password you generate — name and user are prefixed with the account username automatically. +2. Read the full, prefixed `name` and `user` back from `hosting_databases_list`; every other database operation wants the full name. +3. Do not print the password to the user once it is stored, and never commit it. + +## 2. Which host the app connects to + +| Where the code runs | Host | Port | Why | +| --- | --- | --- | --- | +| PHP on the website | `localhost` or `127.0.0.1` | 3306 | `localhost` uses the MySQL socket; both are granted | +| Node.js on the website | `127.0.0.1` | 3306 | `localhost` may resolve to IPv6 `::1`, and not every database user has that grant | +| Outside Hostinger (a laptop, another server) | the `host` from `hosting_databases_list` (`srvNNNN.hstgr.io`) | 3306 | needs `hosting_databases_create-remote-connection` for that client's IP first — never `%`, it opens the database to every address on the internet | + +Do not put the remote `host` into the deployed app — it has no grant for connections that come from the server itself. + +## 3. Hand credentials to the app + +- **Node.js:** environment variables via `hosting_nodejs_replace-environment-variables`. Conventional keys: `DB_HOST=127.0.0.1`, `DB_PORT=3306`, `DB_NAME`, `DB_USER`, `DB_PASSWORD`, plus `DATABASE_URL=mysql://USER:PASSWORD@127.0.0.1:3306/NAME` for ORMs that want one string — URL-encode `USER` and `PASSWORD` there (`encodeURIComponent`), or a password containing `@`, `:`, `/` or `#` breaks the URL. The call is a **full replace**: list the current keys with `hosting_nodejs_list-environment-variables` (values come back masked as `********` — never copy those), then send the complete set with real values. Saving restarts the process; frameworks that bake variables into the build (Next.js, `NEXT_PUBLIC_*`) need a new `hosting_nodejs_start-build` afterwards. +- **PHP:** a config file on the server that the app reads (its own `config.php`, WordPress's `wp-config.php`). Ship it with the deploy, keep it out of git. +- **Never** in client-side JavaScript, in a committed `.env`, or in chat replies. + +## 4. What is asynchronous, and what to poll + +| You called | Poll this until | Typical wait | +| --- | --- | --- | +| `hosting_websites_create` | `hosting_websites_list` lists the domain | up to a few minutes | +| `hosting_deploy-js-application` | `hosting_list-js-deployments` shows the build finished | minutes | +| `hosting_nodejs_start-build` | `hosting_nodejs_build` state is `completed` or `failed` | minutes | +| `wordpress_installations_install`, plugin/theme/core jobs | `wordpress_installations_list` lists the install | 1–2 minutes | +| `hosting_databases_repair`, `hosting_websites_delete` | nothing to poll — runs in the background | minutes | +| `agency-hosting_website-setups_create` | `agency-hosting_website-setups_status` with `setup_uuid` is `completed` | minutes | + +Poll with backoff (a few seconds, then tens of seconds). A queued response is not a failure, and re-sending the write does not speed it up. + +## 5. When the app is up but broken + +- Build failed: `hosting_nodejs_build-logs` for the raw log, `hosting_nodejs_analyse-failed-build` for a diagnosis. +- Build passed, app errors: `hosting_nodejs_runtime-logs` with a `period` such as `1h`; entries before `last_deployed_at` belong to the previous deploy. +- `Access denied for user 'x'@'::1'`: the app used `localhost` from Node.js — set `DB_HOST` to `127.0.0.1`. +- PHP limits (memory, upload size): `hosting_php_get`, then `hosting_php_update-options`. + +## Agency Plan websites (`agency-hosting_*`) + +`agency-hosting_databases_create-website` creates the database and its single user; the user is granted on `localhost`, so the PHP app connects to `localhost`. Deploys are synchronous there — `agency-hosting_deploy-php-application` returns when the site is live. Other jobs (SSL, backups) show up in `agency-hosting_websites_list-processes`. diff --git a/skills/hostinger-headless/references/DEPLOYMENT.md b/skills/hostinger-headless/references/DEPLOYMENT.md new file mode 100644 index 0000000..31a8882 --- /dev/null +++ b/skills/hostinger-headless/references/DEPLOYMENT.md @@ -0,0 +1,34 @@ +# Deployment — getting the build live + +Match the deploy method to what the project actually is — this is the single most common failure point. + +## Static site → `hosting_deploy-static-website` + +For pre-built files only: plain HTML/CSS/JS, or the **build output** of a framework (run the build locally first). + +- The archive (zip/tar) must have `index.html` at its **root** — not nested inside a folder. +- Name it `name_YYYYMMDD_HHMMSS.zip`; pass `removeArchive: true` to clean up after upload. +- Deployment is effectively immediate — verify right after. + +## Node.js app → `hosting_deploy-js-application` + +For anything that needs a server or a server-side build (Express, Next.js, NestJS, SSR frameworks). + +- Archive the **source**, not the output: exclude `node_modules/`, `dist/`, `.next/`, `build/`, and everything matched by `.gitignore`. The install and build run on Hostinger. +- Hard cap: **50 MB** archive. +- The upload starts a build. Track it with `hosting_list-js-deployments`; on failure pull `hosting_show-js-deployment-logs`, fix, and redeploy. Poll with backoff — builds take minutes, not seconds. + +## WordPress → `hosting_import-wordpress-website` / plugin & theme operations + +Only when the user explicitly wants a WordPress *site* migrated or themed. Site imports take an archive plus a `.sql` dump and can run for several minutes. (WordPress as a headless content backend behind an agent-built frontend is a different flow — that's `WORDPRESS.md`, installed fresh via the API, not imported here.) + +## Verify (every deploy) + +```bash +curl -s -o /dev/null -w "%{http_code}\n" https://YOURDOMAIN/ +curl -s https://YOURDOMAIN/ | grep -o "SOME_REAL_HEADLINE_TEXT" +``` + +Replace the grep target with copy you actually rendered — a 200 alone can be a placeholder page. A freshly created site may serve a default page for a short while; if content doesn't match, clear the cache (`hosting_cache_clear-website`) and retry before assuming the deploy failed. For store runs, also run the checkout verification in `STORE.md`. + +When the site is live, report the URL and remind the user the site is managed from hPanel (https://hpanel.hostinger.com). diff --git a/skills/hostinger-headless/references/SETUP.md b/skills/hostinger-headless/references/SETUP.md new file mode 100644 index 0000000..0d0d0aa --- /dev/null +++ b/skills/hostinger-headless/references/SETUP.md @@ -0,0 +1,34 @@ +# Setup — plan check and website provisioning + +Everything here uses the **hosting** MCP operations. If they're missing, ask the user to enable the Websites product group in the Hostinger Connector (or use the `hostinger-hosting-mcp` scoped binary). + +## 1. Plan check (gate — run this first) + +A website can only be created on an active hosting plan. + +1. `hosting_websites_list` — if it returns websites, the account has a working plan; note any existing `order_id` and `username` for step 2. +2. Otherwise `hosting_orders_list` — look for an order in a usable state. A fresh order that has never hosted a website still works; note its `order_id`. +3. If there is no usable order: **stop and tell the user** that an active Hostinger hosting plan is required to deploy, link https://www.hostinger.com/web-hosting, and offer to continue building the site locally in the meantime. When they confirm the purchase, re-run this check. + +Never purchase a plan, domain, or any paid item without the user explicitly approving that specific purchase. + +## 2. Choose the domain + +- **Default: free subdomain.** `hosting_domains_generate-free-subdomain` returns a `*.hostingersite.com` domain. No verification needed. Tell the user a custom domain can be connected later. +- **User-owned domain:** `hosting_domains_verify-ownership` first. If not accessible, relay the TXT record it returns, remind them propagation can take ~10 minutes, and re-verify before continuing. +- **New domain purchase:** only on explicit request — check availability with `domains_availability_check`, state the price, and get an explicit yes before `domains_portfolio_purchase` (needs the domains product group). + +## 3. Create the website and wait for it + +Generating a subdomain does **not** create a website — deploying straight to it fails with `No website found for domain`. The working sequence: + +1. `hosting_websites_create { domain, order_id }` — `datacenter_code` is required only for the first website on a brand-new plan (pick the first entry from `hosting_datacenters_list`). +2. **Poll** `hosting_websites_list` filtered by the domain until the site appears. Creation takes up to a few minutes — poll with backoff, don't fail fast. +3. Note the site's `username` — deployment and database operations are keyed on it. + +If the domain already has a website (an `iterate` run, or the user pointed at an existing site), skip creation entirely. + +## 4. Optional extras (only when the run needs them) + +- **Database:** when the app needs MySQL, follow `DATABASE.md` — creating it, which host each runtime connects to (Node.js must use `127.0.0.1`), and handing the credentials to the app without hard-coding them. +- **DNS records:** the DNS operations (`dns_records_update` etc.) for custom-domain records; take a snapshot (`dns_snapshots_list` context) before destructive changes. diff --git a/skills/hostinger-headless/references/STORE.md b/skills/hostinger-headless/references/STORE.md new file mode 100644 index 0000000..d370006 --- /dev/null +++ b/skills/hostinger-headless/references/STORE.md @@ -0,0 +1,52 @@ +# Store — Hostinger Ecommerce with a custom storefront + +**Primary source:** execute `ecommerce_miscellaneous_custom-storefront-setup-instructions` at the start of every store run — it returns the current, server-maintained integration guide, and it supersedes this file wherever they disagree. This file carries the essentials so the run can be planned before that call, and a fallback when the ecommerce operations aren't enabled. + +## Mental model + +Two API surfaces — don't mix them: + +| Surface | Base URL | Auth | Use for | +| --- | --- | --- | --- | +| Storefront Core V2 | `https://api-ecommerce.hostinger.com/v2` | none (public, open CORS) | read products/variants, checkout — called from the browser | +| Management (MCP tools) | Hostinger API | authenticated | create stores/products/shipping/payments | + +- A **store** (`store_*`) has **sales channels** (`scha_*`). The Storefront API is keyed on the **`sales_channel_id`** of a `custom`-type channel — not the store id. Managed channels (e.g. `quick-link`) cannot be used here, and only `custom` channels are API-creatable. +- **Prices are integers in minor units** (`2900` = €29.00 → divide by `10^decimal_digits`) and live on **variants**, not products. +- **Security boundary:** storefront reads and checkout are public and run client-side — **never embed a Hostinger API token in the storefront**. Only the management/hosting side (MCP tools) is authenticated. + +## Backend setup (management tools) + +1. Resolve the `custom` sales channel: `ecommerce_stores_list` → `ecommerce_sales-channels_list`. If a `custom` channel already exists, confirm with the user that it's the one to use; otherwise add one to the existing store with `ecommerce_sales-channels_create`, or create a store with `ecommerce_stores_create` (with a `custom` sales channel) when no suitable store exists. Set the channel `url` to the storefront's public domain at creation when it's already known. +2. For checkout to work the store needs all three: ≥ 1 product, ≥ 1 payment method, ≥ 1 shipping zone. Payment, shipping, and currency are store-level — check `ecommerce_stores_metadata` (`has_payment_methods` and `has_shipping` must be `true`; the same response carries `default_currency` with `code`, `decimal_digits`, `template` — read it here rather than guessing). Products are **per sales channel** — verify by listing products for the resolved channel on the Storefront API and confirming the list is non-empty. Seed what's missing with `ecommerce_products_create-physical` / `ecommerce_products_create-digital`, `ecommerce_payments_enable-manual-method`, and `ecommerce_shipping_set-store` (price `0` = free shipping) — confirming with the user before writing to a store that already has real data. +3. After deploy, point the channel at the live site: `ecommerce_sales-channels_update { store_id, sales_channel_id, url }`. + +If the ecommerce operations aren't available, ask the user to enable the Ecommerce product group in the Hostinger Connector — or to provide the `sales_channel_id` directly, after which the public endpoints suffice for the frontend work. + +## Frontend contract + +The OpenAPI schema for the Storefront API is at `https://api-ecommerce.hostinger.com/v2/docs.json` — fetch it for exact endpoint shapes. Non-obvious rules the schema won't spell out: + +- **Fetch the catalog at runtime, not build time** — client-side fetch on page load (CORS is open). Baking products into a static build makes the site stale on the first catalog edit. (SSR / Node.js hosting may fetch it server-side per request.) +- Each variant has a **`prices[]` array** (one entry per currency) — read `prices[0].amount` / `prices[0].sale_amount` (minor units) and `prices[0].currency` (which carries `decimal_digits` and the `template` whose `$1` placeholder you replace when formatting). There is **no** top-level `amount` on a variant. +- Product detail resolves by **product id** only; a slug returns 404 (map slug→id from the list endpoint). `limit` max is 100 on list endpoints (else 400). +- Filter variants with `product_ids[]=...`; the unfiltered variant list is eventually-consistent right after catalog edits. +- Cart lives in the site (e.g. `localStorage`): persist only `variant_id` + `quantity`, resolve display data from the live catalog — never a saved copy — then send everything in one checkout call: + +``` +POST /channels/{sales_channel_id}/checkout +{ "items":[{ "variant_id":"variant_01...", "quantity":1 }], + "success_url":"https://YOURDOMAIN/checkout/success/", + "cancel_url":"https://YOURDOMAIN/checkout/cancel/", + "locale":"en" } +``` + +`success_url`/`cancel_url` are required — build them from `window.location.origin` and make sure both pages exist. The response is `{ url, cart_token }`; redirect the browser to `url` (Hostinger's hosted checkout). + +A multi-step cart alternative exists (`POST /channels/{sales_channel_id}/carts`, `PATCH /carts/{cart_id}`, `POST /carts/{cart_id}/line-items`, `PUT /carts/{cart_id}/shipping-method`, `POST /carts/{cart_id}/complete`) — the single checkout call suffices for a simple buy flow. + +## Verify + +- A `POST` to the checkout endpoint returns a `url` — test it directly with curl before calling the run done. +- The `success_url`/`cancel_url` pages return 200 (`curl -s -o /dev/null -w "%{http_code}" https://YOURDOMAIN/checkout/success/`). +- Open the live site, add to cart, and confirm the redirect to `https://checkout.hostinger.com`. diff --git a/skills/hostinger-headless/references/WORDPRESS.md b/skills/hostinger-headless/references/WORDPRESS.md new file mode 100644 index 0000000..9e1a3c0 --- /dev/null +++ b/skills/hostinger-headless/references/WORDPRESS.md @@ -0,0 +1,51 @@ +# WordPress — headless CMS and blog backend + +Use WordPress when the site has **owner-managed content**: a blog, news, articles, or any content the owner must edit without a developer or a redeploy. The frontend stays agent-built and is deployed like any other run; WordPress runs separately as the content backend, and the owner writes in wp-admin. + +**When NOT to use this:** static copy that rarely changes (an about page, a services list, a brochure site) — bake that into the frontend. Installing WordPress for content the owner will never edit adds a moving part for nothing. Any buy/sell intent is `STORE.md`, not this file. + +Everything in Setup uses the **hosting** and **wordpress** MCP operation groups; ask the user to enable them in the Hostinger Connector if operations are missing. + +## Mental model + +Two surfaces, like the store recipe: + +| Surface | Base URL | Auth | Use for | +| --- | --- | --- | --- | +| WP REST API | `https://CMS_DOMAIN/wp-json/wp/v2` | none for published content (open CORS) | frontend reads: posts, pages, media, categories | +| Management (MCP tools + wp-admin) | Hostinger API / wp-admin | authenticated | install WP, plugins, cache; the owner writes content | + +- Published content is publicly readable and the REST API allows cross-origin browser reads — a static frontend on another domain fetches it directly at runtime. +- Drafts, previews, and **writes need authentication** (WP application passwords). Never put credentials of any kind in the frontend; a public site only needs the anonymous read path. + +## Setup (management tools) + +1. **Check for an existing installation first**: `wordpress_installations_list` filtered by the site's username/domain. Reuse a valid install when the user agrees — never overwrite one silently. +2. **Choose where WordPress lives.** Default: a dedicated subdomain of the site, e.g. `cms.` — create it with `hosting_domains_create-website-subdomain` so the main domain stays free for the frontend. A separate free-subdomain website (per `SETUP.md`) also works when the plan allows another website. +3. **Install**: `wordpress_installations_install` on that domain. The call only queues the job — **poll** `wordpress_installations_list` until the installation appears (typically 1–2 minutes). Don't proceed on the queued response alone. +4. **Hand the owner their editor**: mint a one-click wp-admin link with `wordpress_login_create-links` and show it to the user. Content authoring happens in wp-admin — the skill does not seed posts (there is no anonymous write path, by design). The fresh install ships with a sample post, which is enough to build and verify the frontend against. +5. **Caching** (recommended before finishing): enable the object cache with `wordpress_object-cache_toggle-memcached`; after config changes, purge with `wordpress_litespeed-cache_purge-lite-speed`. + +## Frontend contract + +All paths relative to `https://CMS_DOMAIN/wp-json/wp/v2`. Non-obvious rules: + +- **Fetch at runtime, not build time** — content changes whenever the owner publishes; baking it into a static build defeats the purpose. Client-side fetch on page load is fine (CORS is open). +- List posts with `/posts?per_page=10&_embed` — `_embed` inlines featured images (`_embedded['wp:featuredmedia'][0].source_url`), authors, and terms; without it you get IDs that need extra requests. +- Post bodies are **rendered HTML**: use `title.rendered` / `content.rendered` / `excerpt.rendered` and render them as HTML — do not treat them as plain text and do not try to restyle their inner markup beyond CSS. +- Single post by slug: `/posts?slug=my-post` — returns an **array** (take the first element); there is no direct slug path. +- Pagination comes from the `X-WP-Total` / `X-WP-TotalPages` response headers, not the body. +- Only **published** content is returned anonymously. An empty list is a normal state — the frontend must render it gracefully, not error. +- SEO is the frontend's job: page titles, meta tags, and the sitemap come from the frontend build, not from WordPress plugins. + +## Verify + +```bash +curl -s -o /dev/null -w "%{http_code}\n" "https://CMS_DOMAIN/wp-json/wp/v2/posts?per_page=1" +``` + +Expect 200 with a JSON array. Then confirm the deployed frontend renders the sample post (or a clean empty state), and show the user two links: the live site and the wp-admin login for writing content. + +## Record + +Add `"cms_domain"` to `.hostinger/site.json` (see `SKILL.md` §Record) so iterate runs know a WordPress backend exists and reuse it instead of installing again. diff --git a/skills/maintain-wordpress/SKILL.md b/skills/maintain-wordpress/SKILL.md new file mode 100644 index 0000000..cfa18f3 --- /dev/null +++ b/skills/maintain-wordpress/SKILL.md @@ -0,0 +1,74 @@ +--- +name: maintain-wordpress +description: "Keep WordPress sites on Hostinger web hosting updated and secure: checks core, plugin and theme versions, known vulnerabilities and install health on one site or every site in the account, reports what needs doing, applies updates in a safe order, and confirms each site still loads. Also covers cache purges, the Memcached object cache, maintenance mode and one-click wp-admin login links. Triggers: update my WordPress, update plugins, are my WordPress sites secure, WordPress vulnerabilities, outdated plugins or themes, WordPress maintenance, speed up WordPress, log me into wp-admin." +--- + +# Maintain WordPress + +Check first, report, then update only what the user approves — one site at a time, proving each still loads before moving to the next. + +## Calling the operations + +- Read the `inputSchema` that `search` returns before the first call of each operation. If a name below is rejected as unknown, `search` for what the step does (e.g. "wordpress plugins update"). +- Batch reads with `multi-execute` (up to 20 steps a batch). Batches chain operations from one server only — the Hostinger Connector runs `wordpress`, `hosting` and `agency-hosting` as separate servers. +- Updates, activations, uninstalls and core changes are queued jobs: a success response means "queued". Poll the matching read operation every 10–20 s until the change shows; never re-send the write. + +## 1. Find the installs + +- One site: `wordpress_installations_list` with `domain` (substring match — take the exact entry). Every site: `wordpress_installations_list` without filters, adding `ownership: "all"` to include sites the user manages for others. +- Keep `id` (the `software` parameter of every `wordpress_*` call), `username`, `domain`, `directory`, `is_valid` and `validation_error`. +- Agency Plan WordPress sites come from `agency-hosting_websites_list-plan` with `website_types: ["wordpress"]`. When `wordpress_installations_list` does not include them, only `agency-hosting_wordpress_settings` and core version changes (`agency-hosting_wordpress_list-versions`, `agency-hosting_wordpress_change-version`) are available; plugins and themes are updated from wp-admin. + +## 2. Check (read-only) + +Per install: + +- `wordpress_installations_show-core-version` — core version and the known vulnerabilities that affect it. +- `wordpress_installations_list-core-updates` — available core versions. +- `wordpress_plugins_list-installed` — `status`, `update` (the newer version, when there is one) and `vulnerabilities[]` with `fixed_in`. +- `wordpress_themes_list-installed` — the same for themes. + +Four steps per install fit five installs in one batch. For an install with `is_valid: false`, run `wordpress_installations_check-if-are-valid` with `force: true` for a fresh reason and leave it out of updates — a broken install is the `troubleshoot-website` skill's job. + +## 3. Report before changing anything + +``` +## example.com (WordPress 6.8.1) +Vulnerable: contact-form-x 5.2 → fixed in 5.3 (update available) +Vulnerable, inactive: old-slider 1.0 — no fix; uninstall recommended +Updates: core 6.8.1 → 6.8.3 (minor), 4 plugins, 1 theme +``` + +Vulnerable items come first. A vulnerable plugin without a fix, or an inactive one, is better removed with `wordpress_plugins_uninstall` than left installed — inactive code on disk can still be reached. Ask which updates to apply. + +## 4. Back up first + +The API has no backup operation. Before updating, ask the user to create a backup in hPanel or confirm the latest automatic one is recent enough. Say it plainly; the user may choose to go ahead without one. + +## 5. Update, one site at a time + +1. Plugins that fix a vulnerability — `wordpress_plugins_update` with their slugs. +2. The remaining approved plugins, then themes with `wordpress_themes_update`. +3. Core with `wordpress_installations_update-core`: `minor: true` for patch releases; a major `version` only when the user asks for it. + +For a busy site the user may want `wordpress_maintenance_toggle` with `enabled: true` during the run — and it is always turned off again afterwards, even when something failed. + +After each site: + +1. `wordpress_litespeed-cache_purge-lite-speed`. +2. `curl -s -o /dev/null -w "%{http_code}\n" https://DOMAIN/` and the same for `https://DOMAIN/wp-login.php` — both should be `200` with no PHP error in the body. +3. `wordpress_installations_check-if-are-valid` with `force: true`. + +When a site breaks: stop the run, deactivate the plugin updated last with `wordpress_plugins_deactivate`, check again, and report which update caused it before touching any other site. + +## 6. Performance and access + +- Page cache: `wordpress_litespeed-cache_show-lite-speed-status`; purge after design or content changes. +- Object cache: `wordpress_object-cache_show-memcached-status`, then `wordpress_object-cache_toggle-memcached` with `enabled: true` — the usual quick speed-up. +- PHP version: `hosting_php_get` is large (around 25 KB), so read it only when the user asks about PHP. PHP 8.x is markedly faster than 7.x; confirm plugin compatibility before `hosting_php_update-version`. +- wp-admin: `wordpress_login_create-links` returns temporary one-click login links. Give them to the user only; never store or reuse them. +- Hostinger's own plugins update with `wordpress_plugins_update-hostinger` (`slug`). + +## Many sites + +Tell the user how many installs there are before starting. Check in batches, keep one running report, and update site by site so a failure stops the run with the rest untouched. diff --git a/skills/manage-dns-records/SKILL.md b/skills/manage-dns-records/SKILL.md deleted file mode 100644 index ffa6f8b..0000000 --- a/skills/manage-dns-records/SKILL.md +++ /dev/null @@ -1,74 +0,0 @@ ---- -name: manage-dns-records -description: Read, update, and delete DNS zone records on a Hostinger-managed domain via the Hostinger DNS MCP server. Use for any DNS change, lookup, or troubleshooting. -when-to-use: User wants to add/update/delete an A, AAAA, CNAME, ALIAS, MX, TXT, NS, SRV, or CAA record on a Hostinger domain; or asks "what are my DNS records for X?". ---- - -# Manage Hostinger DNS records - -## When to use - -- "Point example.com at this server / IP." -- "Add a TXT record for SPF / DKIM / domain verification." -- "Remove the old MX records and set up Google Workspace." -- "Why isn't my DNS resolving?" - -## The zone model matters - -Hostinger's DNS API is **zone-oriented, not record-oriented**. There is no `create record` or `update record` tool. `DNS_updateDNSRecordsV1` takes a `zone` array where each entry groups all records sharing one name and type: - -```json -{ - "domain": "example.com", - "overwrite": true, - "zone": [ - { "name": "@", "type": "A", "ttl": 300, "records": [{ "content": "192.0.2.10" }] }, - { "name": "www", "type": "CNAME", "ttl": 300, "records": [{ "content": "example.com" }] } - ] -} -``` - -`name`, `type`, and `records` are required per entry; `ttl` is optional. Use `@` for the apex. - -The `overwrite` flag is the critical decision: - -- **`overwrite: true`** — every existing record matching that name+type is deleted and replaced by exactly what you send. Use this when you want the listed records to be the complete set (e.g. replacing all MX records). -- **`overwrite: false`** (or omitted) — TTLs are updated and your records are *appended* alongside the existing ones. Use this when adding a record without touching siblings (e.g. adding one more TXT verification record). - -Getting this backwards either silently duplicates records or silently deletes the ones you meant to keep. State which mode you're using, and why, in your confirmation message. - -Valid `type` values: `A`, `AAAA`, `CNAME`, `ALIAS`, `MX`, `TXT`, `NS`, `SOA`, `SRV`, `CAA`. - -## Steps - -1. **Read current state** — `DNS_getDNSRecordsV1` for the domain. -2. **Note the rollback point** — `DNS_getDNSSnapshotListV1` and record the most recent snapshot ID. Hostinger creates snapshots automatically; there is no tool to create one on demand, so capture the existing ID rather than promising a fresh backup. -3. **Dry run** — `DNS_validateDNSRecordsV1` with the exact `zone` and `overwrite` values you intend to send. It returns `200` when valid and `422` with details when not. Always do this before mutating. -4. **Show the diff** — list what will be created, changed, and removed, and name the `overwrite` mode. -5. **Wait for explicit confirmation.** -6. **Apply** — `DNS_updateDNSRecordsV1`. -7. **Verify** — re-read with `DNS_getDNSRecordsV1` and confirm the resulting zone. Optionally corroborate with `dig` against public DNS, noting that propagation lags the API. - -## Deleting - -`DNS_deleteDNSRecordsV1` filters by name and type, and removes *all* records matching each filter. To drop only some of several records sharing a name and type, use `DNS_updateDNSRecordsV1` with `overwrite: true` and the records you want to keep. - -`DNS_resetDNSRecordsV1` returns the entire zone to Hostinger defaults. It is not a targeted delete — treat it as a last resort and confirm explicitly. - -## Rolling back - -`DNS_restoreDNSSnapshotV1` with the domain and a snapshot ID from `DNS_getDNSSnapshotListV1`. Inspect a snapshot's contents first with `DNS_getDNSSnapshotV1` so the user knows what state they're reverting to. - -## Validation before calling - -- `A` → IPv4 only; `AAAA` → IPv6 only. -- `CNAME` cannot coexist with other record types on the same name, and cannot be used on the apex — use `ALIAS` there. -- `MX` must point at a hostname, never an IP. -- `TXT` → escape inner double quotes. -- Changing apex `A`/`AAAA`, `NS`, or `MX` breaks live traffic or mail. Call these out prominently. - -## Do not - -- Do not call `DNS_updateDNSRecordsV1` without a preceding `DNS_validateDNSRecordsV1`. -- Do not claim a backup was created — snapshots are automatic, so cite an existing snapshot ID instead. -- Do not use `dig` as the primary read path; it reflects cached data, not the zone. diff --git a/skills/migrate-to-hosting/SKILL.md b/skills/migrate-to-hosting/SKILL.md new file mode 100644 index 0000000..12a76e5 --- /dev/null +++ b/skills/migrate-to-hosting/SKILL.md @@ -0,0 +1,78 @@ +--- +name: migrate-to-hosting +description: "Move an existing website from another host to Hostinger web hosting (Shared, Cloud or Agency plans) without downtime: WordPress sites from a files archive and SQL dump, static and PHP sites from an archive, Node.js apps from source. Creates the website on the real domain, imports files and database, tests the copy before any DNS change, then switches the domain and SSL. Triggers: migrate my site to Hostinger, move my website from another host, transfer my WordPress site, import my website, switch hosting to Hostinger." +--- + +# Migrate to hosting + +The old host keeps serving the site until the copy on Hostinger is proven to work. DNS moves last. + +## Calling the operations + +- Read the `inputSchema` that `search` returns before the first call of each operation. If a name below is rejected as unknown, `search` for what the step does (e.g. "import wordpress"). +- Batch reads with `multi-execute`. Batches chain operations from one server only — the Hostinger Connector runs `hosting`, `wordpress`, `agency-hosting`, `domains` and `dns` as separate servers. +- Imports and deploys overwrite the target website's contents; confirm the target before each one. + +## 1. What is being moved + +Establish, asking only for what cannot be inferred: + +- **Site type:** WordPress, static, PHP (with or without MySQL), or Node.js. +- **The export:** + - WordPress — an archive of the whole WordPress root (including `wp-content` and `wp-config.php`) and a `.sql` dump. + - PHP — a files archive, plus a `.sql` dump when the app has a database. + - Static — the files. Node.js — the source or its Git repository. + - Without one, the user exports it from the old host's panel, or over SSH: `tar -czf site.tar.gz -C /path/to/site .` and `mysqldump --single-transaction -u USER -p DBNAME > db.sql`. +- **The exact home URL** (`https`, with or without `www`) — WordPress keeps it in the database, so the new site must answer on the same one. +- **Current DNS**, to preserve what already works: + +```bash +dig +short NS DOMAIN; dig +short A DOMAIN; dig +short MX DOMAIN; dig +short TXT DOMAIN +``` + +- **Cron jobs** on the old host — they do not move with the files. + +## 2. Create the website on the real domain + +Create the Hostinger website on the domain being migrated, not on a free subdomain: step 4 tests it by pointing only this machine at Hostinger, so nothing changes for visitors yet. + +- Shared and Cloud: `hosting_domains_verify-ownership` first. When `is_accessible` is false, the user adds the returned `TXT` record at their current DNS provider, next to the existing ones, and verification is repeated. Then `hosting_websites_create` with `domain` and `order_id` (from `hosting_orders_list`), and poll `hosting_websites_list-setups` with `domain` every 10–15 s until `status: completed`. +- Agency: `agency-hosting_website-setups_create` with the `domain`, `flavor: "php-fpm"`, a `settings.php.version` matching the old host (options in `agency-hosting_php_list-versions-for-order`) and `datacenter_code` from `agency-hosting_datacenters_list`. Poll `agency-hosting_website-setups_status` until `completed` for the `website_uid`. + +## 3. Import + +**WordPress, Shared and Cloud:** `hosting_import-wordpress-website` with `domain`, `archivePath` and `databaseDump` uploads both, extracts the files and imports the database; large sites take several minutes. Then `wordpress_installations_detect` with `username` and poll `wordpress_installations_list` with `domain` until the install appears with `is_valid: true`. + +**Static, or PHP without a database:** `hosting_deploy-static-website` (Agency: `agency-hosting_deploy-php-application`). + +**PHP with a database, Shared and Cloud:** + +1. `hosting_databases_create` with a strong generated password; read the full name, user and `host` back from `hosting_databases_list`. +2. Import the dump from this machine: allow only its public IP (`curl -s https://api.ipify.org`) with `hosting_databases_create-remote-connection`, run `mysql -h HOST -u USER -p NAME < db.sql`, then remove the rule with `hosting_databases_delete-remote-connection`. Without a MySQL client, use `hosting_databases_phpmyadmin-link` and import there. +3. Put the new name, user and password into the app's config file, with host `localhost`, before archiving; then deploy as above. Never ship the `srvNNNN.hstgr.io` host inside the app. + +**Node.js:** deploy with the `deploy-to-hosting` skill. When a dump must be imported, create the database with `hosting_databases_create` rather than `hosting_databases_setup-website` — the latter never reveals the password needed for the import — and pass the credentials through environment variables. + +**Agency:** files with `agency-hosting_deploy-php-application`; the database with `agency-hosting_databases_create-website`, imported through phpMyAdmin in hPanel (no import operation exists). For WordPress, set the new credentials in `wp-config.php` before archiving. + +## 4. Test before DNS moves + +Find the server address: `details.ipv4` for Agency; for Shared and Cloud, the `A` record for `@` in the Hostinger zone (`dns_records_list` on the domain) or the IP hPanel shows for the website. Then point only this machine at it: + +```bash +curl -sSk --resolve DOMAIN:443:IP -o /dev/null -w "%{http_code}\n" https://DOMAIN/ +curl -sSk --resolve DOMAIN:443:IP https://DOMAIN/ | grep -o "TEXT FROM THE LIVE SITE" +curl -sSk --resolve DOMAIN:443:IP -o /dev/null -w "%{http_code}\n" https://DOMAIN/wp-login.php +``` + +`-k` is expected here: the certificate for the domain can only be issued after DNS moves. Check the home page, an inner page and, for WordPress, the login page. For a look in the browser, the user adds `IP DOMAIN www.DOMAIN` to their hosts file and removes it afterwards. Fix anything broken now, while visitors still see the old site. + +## 5. Switch DNS and SSL + +Follow the `connect-domain` skill from its DNS step: keep every `MX` and `TXT` record from step 1, then install SSL once the domain resolves to Hostinger. When the user controls the old DNS, lowering the TTL of the `A` records to 300 a day ahead shortens the switch. + +## 6. After the switch + +- Verify without `--resolve`: `curl -sSI https://DOMAIN/` shows `platform: hostinger`. +- Recreate the old host's cron jobs with `hosting_cron-jobs_create` (Agency: `agency-hosting_cron-jobs_create-website`). +- Keep the old hosting running for a few days — some resolvers still cache the old address, and email hosted there must be moved separately. diff --git a/skills/query-deployment-logs/SKILL.md b/skills/query-deployment-logs/SKILL.md deleted file mode 100644 index 64d5f66..0000000 --- a/skills/query-deployment-logs/SKILL.md +++ /dev/null @@ -1,53 +0,0 @@ ---- -name: query-deployment-logs -description: Pull and summarize the logs Hostinger's API exposes — Node.js build logs, JS deployment logs, and cron job output — for a domain. Use to investigate a failed or slow build, or a cron job that isn't doing what the user expects. -when-to-use: User asks to "see the logs", investigates why a build or deployment behaved oddly, or wants to know whether a scheduled job ran. ---- - -# Query Hostinger logs - -## What is actually available - -Hostinger's API exposes three kinds of log output: - -| Log | Tool | Needs | -|---|---|---| -| JS deployment logs | `hosting_showJsDeploymentLogs` | `domain`, `buildUuid`, optional `fromLine` | -| Node.js build logs | `hosting_getNodeJSBuildLogsV1` | `username`, `domain`, `uuid`, optional `from_line` | -| Cron job output | `hosting_getCronJobOutputV1` | cron job identifiers from `hosting_listAccountCronJobsV1` | - -**Raw HTTP access logs and PHP error logs are not exposed by the API.** If the user asks for 4xx/5xx breakdowns, traffic spikes, per-request timings, or PHP fatals, tell them directly that these live in hPanel and cannot be fetched here. Do not substitute a plausible-sounding tool name, and do not present build logs as if they were access logs. - -## Inputs to gather first - -1. Domain. -2. Which log the user actually needs — a build/deployment, or a cron job. -3. For builds: the build `uuid`. Get it from `hosting_listJsDeployments` or `hosting_listNodeJSBuildsV1`, filtering `states` when the user only cares about failures. -4. For `hosting_getNodeJSBuildLogsV1`: the `username`, from `hosting_listWebsitesV1`. - -## Steps - -1. Resolve the build or cron job identifier first — every log tool requires one. -2. Fetch the log, starting at line `0`. -3. To follow a running build, poll while the state is `running`, passing the previously returned line count as `fromLine` / `from_line` so each call returns only new output. -4. Strip ANSI escape sequences before quoting — build output is colorized. -5. Summarize rather than dumping. Report: - - The final state and, for a failure, the first error and the last 20 lines verbatim. - - Which step failed (install, build, start). - - Total duration, if the timestamps allow it. -6. Offer drill-down: "Want the full log?", "Want me to compare this against the last successful build?". - -## Handing off - -- A failed build with an identified error signature → `diagnose-build-failure`. -- A cron job producing no output → check the schedule and command with `hosting_listAccountCronJobsV1` before assuming the script is broken. - -## Privacy - -- Build logs can echo environment values and tokens. Redact anything that looks like a credential before quoting, and never paste a full environment dump into chat. - -## Do not - -- Do not dump thousands of log lines into the conversation — page through and summarize. -- Do not infer a cause from a single line; corroborate with the surrounding context. -- Do not claim access-log or error-log capability the API doesn't have. diff --git a/skills/troubleshoot-website/SKILL.md b/skills/troubleshoot-website/SKILL.md new file mode 100644 index 0000000..2357f0e --- /dev/null +++ b/skills/troubleshoot-website/SKILL.md @@ -0,0 +1,104 @@ +--- +name: troubleshoot-website +description: "Diagnose and fix a website on Hostinger web hosting (Shared, Cloud or Agency plans) that is down, slow, erroring, insecure or failing to build. Checks the site from outside, reads builds, runtime logs, WordPress health, SSL, cache and PHP settings through the Hostinger MCP, names the cause with evidence, and applies the fix once the user agrees. Triggers: my site is down, site not loading, 500 / 502 / 503 error, white screen, error establishing a database connection, site is slow, SSL or certificate warning, not secure, changes not showing, Node.js build failed, app keeps crashing." +--- + +# Troubleshoot a website + +One website and one symptom in; a named cause, the evidence for it, and a fix out. Stay read-only until the cause is stated and the user agrees to the fix. + +## Calling the operations + +- Read the `inputSchema` that `search` returns before the first call of each operation. If a name below is rejected as unknown, `search` for what the step does (e.g. "ssl status"). +- Batch reads with `multi-execute` and pass values between steps with `$steps..`, e.g. `"$steps.0.data.0.username"`. A batch stops at its first failure. +- The Hostinger Connector runs each product as its own MCP server (`hosting`, `wordpress`, `agency-hosting`, `domains`, `dns`), so a batch can only chain operations from one server. When `search` cannot find an operation, that product group is switched off in the Connector — ask the user to enable it. + +## 1. Find the website + +Run both lookups, and keep the result for every later call: + +- `hosting_websites_list` with `domain` — Shared and Cloud plans. The filter is a substring match, so take the entry whose `domain` is exactly the one asked about. Keep `username`, `order_id` and `website_type`; every `hosting_*` and `wordpress_*` call is keyed on `username` + `domain`. +- `agency-hosting_websites_list-plan` with `domain` — Agency Plan. Keep `details.uid` (the `website_uid` of every `agency-hosting_*` call), `details.type`, `details.ipv4` and `details.domains`. + +Neither finds it: the domain is not a website on this account — say so and show where it points (step 2). + +## 2. Look at it from outside + +Run these alongside step 1: + +```bash +curl -sS -o /dev/null -w "%{http_code} ttfb=%{time_starttransfer}s %{redirect_url}\n" https://DOMAIN/ +curl -sSI https://DOMAIN/ +dig +short NS DOMAIN; dig +short A DOMAIN; dig +short CAA DOMAIN +``` + +- Hostinger responses carry `platform: hostinger` and `panel: hpanel`. Without them, or with DNS pointing elsewhere, traffic is not reaching this hosting — hand over to the `connect-domain` skill. +- Certificate or TLS errors → [SSL](#ssl). +- `5xx`, a blank page or an error page → [By website type](#3-by-website-type). +- `200` with old content → cache: `hosting_cache_clear-website` (also purges the Hostinger CDN; pass `directory` for WordPress in a subfolder), plus `wordpress_litespeed-cache_purge-lite-speed` for WordPress; Agency: `agency-hosting_cache_clear-website`. +- Slow first byte → [Slow site](#slow-site). Behind the Hostinger CDN, `x-hcdn-upstream-rt` is the origin's own response time in seconds; a small value there means the server is not the bottleneck. + +## 3. By website type + +`website_type` (Shared/Cloud) or `details.type` (Agency) picks the branch. `builder` and `horizons` sites are not managed by these operations — send the user to hPanel. + +### Node.js (`nodejs`) + +Batch `hosting_nodejs_list-builds` (`per_page: 3`), `hosting_nodejs_runtime-logs` (`period: "1h"`, `levels: ["ERROR", "WARN"]`) and `hosting_nodejs_list-environment-variables` (keys only; values always come back masked). + +- Latest build `failed`: call `hosting_nodejs_analyse-failed-build` once for that build — every call re-runs the analysis and the limit is 5 a minute. When it returns nulls, read `hosting_nodejs_build-logs`. A wrongly detected framework, output directory or missing `entry_file` is the usual cause: compare `hosting_nodejs_build-settings` with the project, rebuild with corrected values through `hosting_nodejs_start-build`, and store the same values with `hosting_nodejs_update-build-settings` so Git pushes build correctly too. +- Build `completed` but the app errors: read the runtime logs; entries older than `last_deployed_at` belong to the previous deploy. A variable the code reads but the key list lacks is fixed with the `deploy-to-hosting` skill's environment step. `Access denied for user '…'@'::1'` means the app connects to `localhost` — `DB_HOST` must be `127.0.0.1`. +- Hung process: `hosting_nodejs_restart-application` (restarts without rebuilding). + +### WordPress (`wordpress`) + +`wordpress_installations_list` with `username` and `domain` gives the install `id` — the `software` parameter of every `wordpress_*` call. Batch `wordpress_installations_check-if-are-valid` (`software_ids: [id]`, `force: true`), `wordpress_plugins_list-installed` and `wordpress_maintenance_show-status`. + +- Invalid install: `validation_error` names the problem. +- Broken after a plugin or theme change: deactivate suspects one at a time with `wordpress_plugins_deactivate` (queued — allow a few seconds), re-run the step 2 check after each, and reactivate with `wordpress_plugins_activate` any that were not the cause. +- Maintenance page stuck: `wordpress_maintenance_toggle` with `enabled: false`. +- "Error establishing a database connection": `hosting_databases_list` with `domain`; corrupted tables are fixed by `hosting_databases_repair` (runs in the background). `wp-config.php` cannot be read through the API because files with secrets are refused — for credential checks hand the user `hosting_databases_phpmyadmin-link`. +- Memory or upload-size errors → [PHP](#php). + +### Static or PHP (`other`) + +- `403` or `404` at `/`: `hosting_files_list-website-and-directories` with `max_depth: 1`. `index.html` or `index.php` must sit at the document root; finding it one folder down means the archive was packed a level too deep — redeploy it flat with the `deploy-to-hosting` skill. +- Read `.htaccess` or other config with `hosting_files_website-content`. +- PHP errors → [PHP](#php). + +### PHP + +`hosting_php_get` returns every extension and option (around 25 KB), so call it only for PHP problems. `hosting_php_update-version` switches versions. `hosting_php_update-options` silently caps values at the plan maximum (`max` in `hosting_php_get`), so read the applied value back. Agency: `agency-hosting_php_list-options-for-website`, then `agency-hosting_php_replace-website-options` — a full replace, so send every custom option that should stay. + +### Agency Plan sites + +`agency-hosting_websites_get` shows state and quotas, `agency-hosting_websites_list-processes` shows failed jobs (SSL setup, backups), and `agency-hosting_wordpress_settings` shows core version, LiteSpeed, object cache and maintenance mode. When `wordpress_installations_list` does not return the site, plugins are managed from wp-admin. + +## SSL + +Shared and Cloud, `hosting_ssl_status`: + +- `not_installed` or `failed` (`last_error` says why): the domain must resolve to Hostinger, and any CAA record must allow `letsencrypt.org`, before `hosting_ssl_install` can succeed. Poll the status until `active` or `failed`. +- `installing` or `waiting_for_retry`: wait — another install request returns 422. +- Free `*.hostingersite.com` subdomains use a platform certificate; installing returns 422 by design. +- Browser still warns while the certificate is `active`: turn on the redirect with `hosting_ssl_toggle-https-redirect` when `is_https_redirect_enabled` is false, otherwise the page loads `http://` assets (mixed content) that the app has to change. + +Agency, `agency-hosting_ssl_website-status` for each domain: every domain allows three certificate setups per seven days and one request a minute. Check `agency-hosting_websites_list-processes` before `agency-hosting_ssl_install-website` or `agency-hosting_ssl_reinstall-website`, and never retry in a loop. + +## Slow site + +- WordPress: `wordpress_litespeed-cache_show-lite-speed-status` and `wordpress_object-cache_show-memcached-status`. Turning on Memcached with `wordpress_object-cache_toggle-memcached` is the usual quick win. +- Other sites: server caching may be off or development mode left on, and neither has a read operation. `hosting_cache_toggle-website` with `enabled: true` does nothing when caching is already on; `hosting_cache_toggle-cacheless` with `enabled: false` turns development mode off. +- PHP 7.x runs markedly slower than 8.x; propose a supported 8.x version from `hosting_php_get` when the app allows it. +- Agency: `agency-hosting_metrics_list-order-resource-usage` with `time_frame_hours: 24` shows CPU, memory and processes per website against the plan quota. +- Shared and Cloud have no CPU or memory metrics in the API. When the evidence points at resource limits, send the user to hPanel's resource usage page. + +## Apply the fix + +State the cause, the evidence and the single change proposed, and make it only after the user agrees. Then re-run the step 2 check, polling any queued job first, to show the symptom is gone. When the fix needs something the API cannot do, say so and name the hPanel page. + +## Not available through the API + +- Website backups and restores — hPanel's Backups page. +- CPU, memory and process usage for Shared and Cloud plans. +- PHP error and access logs. diff --git a/skills/troubleshoot-wordpress/SKILL.md b/skills/troubleshoot-wordpress/SKILL.md deleted file mode 100644 index 5ccc440..0000000 --- a/skills/troubleshoot-wordpress/SKILL.md +++ /dev/null @@ -1,69 +0,0 @@ ---- -name: troubleshoot-wordpress -description: Diagnose common Hostinger WordPress issues — PHP version and extension mismatches, plugin/theme conflicts, white screen of death, slow admin, stale cache, failed core updates. -when-to-use: User reports their Hostinger-hosted WordPress site is broken, slow, throwing errors, or behaving unexpectedly after a change. ---- - -# Troubleshoot a Hostinger WordPress site - -## When to use - -- "My WordPress site is down / blank / showing a 500." -- "Admin is really slow." -- "I updated a plugin and now nothing works." -- "My changes aren't showing up." - -## What the API can and can't see - -The WordPress and hosting MCP servers expose installations, plugins, themes, core version, caches, maintenance mode, and PHP configuration. They do **not** expose PHP error logs or shared-hosting backups. When the diagnosis genuinely needs an error log or a restore, say so and point the user to hPanel rather than calling a tool that doesn't exist. - -## Inputs to gather first - -1. Domain. -2. Symptom: blank front-end, 500, 502/504, slow admin, login loop, stale content, failed update. -3. What changed recently — plugin install or update, theme switch, PHP version bump, core update. - -## Steps - -1. Establish the baseline: - - `hosting_listWordPressInstallationsV1` → which installations exist on the account. - - `hosting_checkIfWordPressInstallationsAreValidV1` → whether Hostinger considers the install healthy. Run `hosting_detectWordPressInstallationsV1` first if the site isn't listed. - - `hosting_showWordPressCoreVersionV1` and `hosting_listAvailableWordPressCoreUpdatesV1` → core version and pending updates. - - `hosting_listInstalledWordPressPluginsV1` and `hosting_listInstalledWordPressThemesV1` → versions and active state. - - `hosting_getPHPDetailsV1` (and `hosting_getPHPInfoV1` for the full dump) → PHP version, extensions, limits. -2. Map the symptom using the table below. -3. Propose **one** fix at a time, confirm, then apply. - -## Common issues - -| Symptom | Likely cause | First check / fix | -|---|---|---| -| Blank front-end ("white screen of death") | Fatal PHP error, usually from a plugin or theme update | Compare `hosting_listInstalledWordPressPluginsV1` against what the user just changed, then `hosting_deactivateWordPressPluginV1` on the suspect. The fatal itself is only visible in the hPanel error log. | -| 500 site-wide | PHP fatal, or a PHP version/extension mismatch | `hosting_getPHPDetailsV1` to confirm the version and loaded extensions; `hosting_updatePHPExtensionsV1` if something required is missing | -| Admin extremely slow | Heavy plugin, or a low PHP memory limit / execution time | `hosting_updatePHPOptionsV1` to raise the limits; deactivate non-essential plugins one at a time | -| "Requires PHP x.y" warning | Plugin needs a newer PHP than the site runs | Confirm theme and plugin compatibility, then `hosting_updatePHPVersionV1` | -| Changes not appearing | LiteSpeed or website cache serving stale content | `hosting_showLiteSpeedCacheStatusV1`, then `hosting_purgeLiteSpeedCacheV1`; also `hosting_clearWebsiteCacheV1`, and `hosting_toggleCachelessModeV1` while actively debugging | -| Object cache errors after a plugin change | Memcached object cache out of sync | `hosting_showMemcachedObjectCacheStatusV1`, then `hosting_toggleMemcachedObjectCacheV1` | -| Site stuck showing "briefly unavailable" | Maintenance mode left on | `hosting_showMaintenanceStatusV1`, then `hosting_toggleMaintenanceModeV1` | -| Core auto-update failed | Version conflict or a blocking plugin | `hosting_listAvailableWordPressCoreUpdatesV1`, then `hosting_updateWordPressCoreV1` | -| Can't reach wp-admin to verify a fix | Lost or broken admin session | `hosting_createLoginLinksV1` for a one-time admin login link | -| Broken checkout on a shop | WooCommerce missing or inactive | `hosting_checkIfWooCommerceIsInstalledV1` | -| Hostinger plugin features misbehaving | Outdated Hostinger plugin | `hosting_updateHostingerWordPressPluginV1` | - -## Isolating a plugin conflict - -Deactivate one plugin at a time with `hosting_deactivateWordPressPluginV1`, re-test, and reactivate with `hosting_activateWordPressPluginV1` before moving to the next. Say which plugin you're about to disable and what user-facing feature might break. Do not bulk-deactivate a production site without explicit approval. - -## Confirm before - -- `hosting_deleteWordPressInstallationV1` — destroys the site. -- `hosting_uninstallWordPressPluginsV1`, `hosting_uninstallWordPressThemesV1` — may drop plugin data. -- `hosting_updateWordPressCoreV1`, `hosting_updateWordPressPluginsV1`, `hosting_updateWordPressThemesV1` — can introduce new breakage. -- `hosting_updatePHPVersionV1` — can break themes and plugins that don't support the new version. -- `hosting_toggleMaintenanceModeV1` on production — takes the site offline for visitors. - -## Do not - -- Do not claim to have read an error log. Direct the user to hPanel for PHP error logs. -- Do not offer to restore a backup — shared-hosting backups aren't exposed by the API. -- Do not edit `wp-config.php` or `.htaccess` directly when a Hostinger MCP tool covers the same change.