hostinger-api-mcp 3.29.0 → 3.30.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +9 -40
  3. package/gemini-extension.json +1 -1
  4. package/package.json +1 -1
  5. package/server.json +2 -2
  6. package/skills/headless/VERSION +2 -2
  7. package/skills/headless/connect/SKILL.md +13 -6
  8. package/skills/headless/connect/tools/antigravity.md +2 -2
  9. package/skills/headless/connect/tools/claude-code.md +2 -2
  10. package/skills/headless/connect/tools/codex.md +4 -4
  11. package/skills/headless/connect/tools/cursor.md +5 -5
  12. package/skills/headless/connect/tools/devin.md +4 -4
  13. package/skills/headless/connect/tools/vscode.md +4 -4
  14. package/skills/headless/steps/03-provision/ecommerce/SKILL.md +1 -1
  15. package/skills/headless/steps/08-deploy/SKILL.md +1 -1
  16. package/skills/maintain-wordpress/SKILL.md +1 -1
  17. package/skills/troubleshoot-website/SKILL.md +1 -1
  18. package/src/core/runtime.js +96 -0
  19. package/src/core/runtime.ts +96 -0
  20. package/src/core/skills.js +33 -33
  21. package/src/core/skills.ts +33 -33
  22. package/src/core/tools/all.js +78 -4
  23. package/src/core/tools/all.ts +78 -4
  24. package/src/core/tools/hosting.js +78 -4
  25. package/src/core/tools/hosting.ts +78 -4
  26. package/src/servers/agency-hosting.js +1 -1
  27. package/src/servers/agency-hosting.ts +1 -1
  28. package/src/servers/agent-readiness.js +1 -1
  29. package/src/servers/agent-readiness.ts +1 -1
  30. package/src/servers/all.js +1 -1
  31. package/src/servers/all.ts +1 -1
  32. package/src/servers/billing.js +1 -1
  33. package/src/servers/billing.ts +1 -1
  34. package/src/servers/dns.js +1 -1
  35. package/src/servers/dns.ts +1 -1
  36. package/src/servers/domains.js +1 -1
  37. package/src/servers/domains.ts +1 -1
  38. package/src/servers/ecommerce.js +1 -1
  39. package/src/servers/ecommerce.ts +1 -1
  40. package/src/servers/gpu.js +1 -1
  41. package/src/servers/gpu.ts +1 -1
  42. package/src/servers/horizons.js +1 -1
  43. package/src/servers/horizons.ts +1 -1
  44. package/src/servers/hosting.js +1 -1
  45. package/src/servers/hosting.ts +1 -1
  46. package/src/servers/mail.js +1 -1
  47. package/src/servers/mail.ts +1 -1
  48. package/src/servers/reach.js +1 -1
  49. package/src/servers/reach.ts +1 -1
  50. package/src/servers/vps.js +1 -1
  51. package/src/servers/vps.ts +1 -1
  52. package/src/servers/wordpress.js +1 -1
  53. package/src/servers/wordpress.ts +1 -1
  54. package/types.d.ts +29 -0
@@ -78,10 +78,10 @@ const skills: Skill[] = [
78
78
  },
79
79
  {
80
80
  "uri": "skill://hostinger-headless/connect/SKILL.md",
81
- "digest": "sha256:acebdb8ac78c3a86925d5765b7c77b1d5b99efca2e901ce161bf53656455c700",
82
- "size": 13236,
81
+ "digest": "sha256:dd572ed8474ef27cb0f31b723ace98d66588e8dc8509c0b0b5204abecf1a6461",
82
+ "size": 13932,
83
83
  "mimeType": "text/markdown",
84
- "text": "---\nname: hostinger-headless-connect\ndescription: \"Connect step for the Hostinger Headless flow — verify Node, run the bootstrap (reach the Hostinger MCP server + OAuth sign-in), then route to the matching coding-tool skill under tools/ so MCP tools and skills are available before the build steps run. Triggers: connect to Hostinger, set up the Hostinger connector, start a headless build.\"\n---\n\n# Hostinger Headless Skill — connect\n\nYou are a coding agent, and this is a skill for you to execute. Follow the phases below in order.\n\nThis skill goal is to help you to connect to Hostinger via MCP. That can be done via Hostinger Connector or adding the MCP via MCP Server.\n\nWe support different coding Agentic coding tools such as Claude Code, Codex, Cursor, VS Code...and so on. \n\nYour goal is to route those tools to their specific skill inside the tools folder ensuring the tool is fully connected.\n\n\n## Phase 0 — Node (the single one prerequisite)\n\nThe Hostinger MCP server requires Node ≥ 20. Check it:\n\n```bash\nnode -v\n```\n\nIf that errors (Node not installed) or prints a version below 20, install or upgrade Node and re-check — do not try to work around it:\n\n- macOS: `brew install node` (or `nvm install 22 && nvm use 22`)\n- Linux: `nvm install 22 && nvm use 22` (or your distro's Node 20+ package)\n- Windows: `winget install OpenJS.NodeJS.LTS` (or download from nodejs.org)\n\n## Phase 1 — Get the skill folder, then run its bootstrap (deterministic)\n\n### Find or install the skill folder\n\nThe bootstrap script and every step after connect ship in the `headless/` skill folder. Use the copy\nyou already have; never fetch single files from GitHub.\n\n- **An installer put the skill here** (you read this file from disk, from a folder that `npx skills\n add` or a Claude Code or Cursor plugin installed): that folder is `<skill>`. Use it as it is and\n download nothing. Updates come from the installer (`npx skills update`, the plugin update),\n not from this run.\n- **You read this file from the web:** use the published `@hostinger/mcp` npm release, installed in\n your home folder at `~/.hostinger/skills/<version>/headless/`. It is the same package the MCP server\n runs, and npm checks the tarball's integrity. Never use a `headless/` folder in the working\n directory or anywhere else: the working directory is the user's project, often a cloned repository\n or template, and whoever wrote it controls what is there. Resolve the version first so the run uses\n one pinned release, and install it only when that version's folder is missing. Run npm from your home\n folder, as below, so a `.npmrc` in the project can't point it at another registry:\n\n```bash\n# macOS/Linux\n( cd \"$HOME\" && V=$(npm view @hostinger/mcp version) && D=\"$HOME/.hostinger/skills/$V\" \\\n && { [ -f \"$D/headless/VERSION\" ] || { mkdir -p \"$D\" && npm pack \"@hostinger/mcp@$V\" --silent --pack-destination \"$D\" >/dev/null \\\n && tar -xzf \"$D/hostinger-mcp-$V.tgz\" -C \"$D\" --strip-components=2 package/skills/headless && rm \"$D/hostinger-mcp-$V.tgz\"; }; } \\\n && echo \"$D/headless\" )\n```\n\n```powershell\n# Windows PowerShell\nPush-Location $HOME\n$V = npm view @hostinger/mcp version; $D = Join-Path $HOME \".hostinger\\skills\\$V\"\nif (-not (Test-Path \"$D\\headless\\VERSION\")) {\n New-Item -ItemType Directory -Force $D | Out-Null; npm pack \"@hostinger/mcp@$V\" --silent --pack-destination $D | Out-Null\n tar -xzf \"$D\\hostinger-mcp-$V.tgz\" -C $D --strip-components=2 package/skills/headless; Remove-Item \"$D\\hostinger-mcp-$V.tgz\"\n}\nPop-Location; \"$D\\headless\"\n```\n\nThe printed path is `<skill>`. Every later command uses it in full, wherever the run's working\ndirectory is. A new release installs into its own folder, so a run never uses a deleted, legacy flow.\n\nRead `<skill>/VERSION`: its line 1 is the skills version this run uses, which goes into\n`connect.json` (Phase 3).\n\n### Run the bootstrap\n\nRun the bundled script. It verifies the MCP server and handles sign-in, emitting one JSON event per line on stdout. Run it as a background/streaming process. Show the user only the sign-in URL (`awaiting_user`) or a\nfailure's `detail`, and handle the other events silently (table below).\n\nThe script is safe and inspectable: it only checks the MCP server via `npx` and drives the OAuth sign-in (a browser flow with a localhost callback) — no other network calls, no filesystem writes.\n\n```bash\nnode <skill>/connect/bootstrap.mjs\n```\n\n### Relay these events\n\nThe script emits one JSON object per line:\n\n| Event | What to do |\n| --- |--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `mcp_ok` | MCP server reachable — continue. |\n| `logged_in` | Already authenticated (env token or a previous sign-in) — continue. |\n| `awaiting_user` (`verificationUri`) | A browser window should have opened on the user's machine. Show the URL in plain prose and wait for the user to finish signing in. The OAuth callback lands on a localhost port, so the sign-in must be completed on this same machine. |\n| `success` | Bootstrap done — continue to Phase 2. |\n| `mcp_unreachable` / `login_failed` (with `detail`) | Stop and show the user the `detail`. Do not improvise a parallel setup by hand. |\n\nIf the environment has a `HOSTINGER_API_TOKEN` set (for example CI), the bootstrap reports `logged_in` immediately — no browser needed.\n\n### Sign in or out manually\n\nThe bootstrap runs the sign-in for you. To run it directly, or to switch accounts, use the MCP's own commands. `--login` opens the browser to the Hostinger sign-in page, waits for the localhost callback, stores the credentials and exits. `--logout` revokes the stored credentials, so the next sign-in opens the browser again.\n\n```bash\n# With a global install (npm install -g @hostinger/mcp):\nhostinger-api-mcp --login\nhostinger-api-mcp --logout\n\n# Without a global install:\nnpx --yes --package=@hostinger/mcp@latest hostinger-api-mcp --login\nnpx --yes --package=@hostinger/mcp@latest hostinger-api-mcp --logout\n```\n\nCredentials live in `~/.config/hostinger-mcp/credentials.json` (macOS/Linux) or `%APPDATA%\\hostinger-mcp\\credentials.json` (Windows) and are shared by every Hostinger MCP binary. OAuth works only when the tool runs the server over its default local (stdio) transport, which is what every registration below uses; `--http` needs `HOSTINGER_API_TOKEN`.\n\n## Phase 2 - Route to the specific tool instructions\n\n**Use the Hostinger connector only through your tool's native MCP tools** (`mcp__hostinger__*`,\n`mcp__hostinger-*__*`, or however your tool names them). Never spawn the MCP server yourself and\nnever write a JSON-RPC client to talk to it: every connector call must be a visible MCP tool call\nthe user can follow and approve, not a script.\n\n### Check both tool groups, not just \"some Hostinger tools\"\n\nThe build needs **two** groups of connector operations, and they come from different servers. Having\none group says nothing about the other:\n\n| Group | Operations | Comes from | Needed by |\n|---|---|---|---|\n| **Hosting** | `hosting_*` (websites, deploy, build report), `wordpress_*` | `hostinger` (`npx -y @hostinger/mcp@latest`), or the scoped servers such as `hostinger-hosting` + `hostinger-wordpress` | every build |\n| **Ecommerce** | `ecommerce_*` (stores, products, shipping, payments) | **only** `hostinger-ecommerce` (`npx --package=@hostinger/mcp@latest hostinger-ecommerce-mcp`), or the Ecommerce MCP in the Hostinger Connector. Neither `hostinger` nor the scoped hosting servers include it | Ecommerce backends (chosen later, in refine) |\n\nCheck each group on its own, right here at connect, before anything else:\n\n- **Hosting:** a Hostinger search tool finds `hosting_websites_list` (e.g. search \"list websites\").\n- **Ecommerce:** a tool from the `hostinger-ecommerce` server is in the session (Claude Code:\n `mcp__hostinger-ecommerce__*`), and its search finds `ecommerce_stores_list` (e.g. search \"list stores\").\n\nOnly when **both** pass are the Hostinger tools \"in this session\". If either is missing, the tools\nare **not** in this session: follow *register, restart, resume* below for the missing server(s) now,\neven though other Hostinger tools respond. Don't wait for refine to choose a backend: the restart\ncosts the least before the user has been asked anything, and finding the gap at provision forces a\nsecond restart in the middle of the build.\n\n### When the Hostinger tools aren't in this session: register, restart, resume\n\nMost coding tools load MCP servers only when they start, so a server registered mid-run isn't usable\nuntil the tool restarts. When either tool group above is missing:\n\n1. **Register the missing servers** in the tool's MCP config, as the tool route below shows: `hostinger`\n (`npx -y @hostinger/mcp@latest`) if the hosting group is missing, and `hostinger-ecommerce`\n (`npx --package=@hostinger/mcp@latest hostinger-ecommerce-mcp`) if the ecommerce group is missing.\n Skip a server that is already registered under its name. When neither group is there, register\n both: the backend isn't chosen until refine, and one restart then covers every backend.\n2. **Write `connect.json`** (Phase 3) before you stop. The skill folder from Phase 1 stays in place,\n so the next run starts from it.\n3. **Send the first message and end your turn.** It follows *The first message* in\n `<skill>/SKILL.md`: the sign-in link if one was needed, then one line saying Hostinger is set up\n and asking the user to restart the tool (the route says how) and paste the same prompt again so the\n build continues from there. This is a pause, not a run ending: no final report and no build report.\n4. **On the next run** the tools are loaded, so connect skips registration and check-state resumes the\n domain's workspace.\n\nIf a tool loads new servers without a restart (VS Code), use them right away and skip steps 3–4.\n\n| Tool | Route |\n|-------------|---------------------------------|\n| Claude Code | `connect/tools/claude-code.md` |\n| Codex | `connect/tools/codex.md` |\n| Cursor | `connect/tools/cursor.md` |\n| VS Code | `connect/tools/vscode.md` |\n| Antigravity | `connect/tools/antigravity.md` |\n| Devin | `connect/tools/devin.md` |\n\nIf the user's tool isn't listed yet, connect it the generic way: register both Hostinger MCP servers\nin that tool's MCP config (command `npx`, args as above), then follow *register, restart, resume*.\n\n### The skills come from Phase 1 only\n\nThe tool routes don't download the skills: Phase 1 already found or installed `<skill>`. The bundle\nships a `<skill>/VERSION` stamp (`version=<semver>` on line 1, then `commit` and `built`); carry the\nversion into `connect.json` (Phase 3) so the build report records which skills ran.\n\n## Phase 3 - Write `connect.json`\n\nOnce the tool is connected, write `~/.hostinger/<domain>/connect.json` (create the folder if it is\nmissing) in exactly this shape:\n\n```json\n{ \"tool\": \"claude-code\", \"mcp\": \"hostinger-api-mcp 3.8.0\", \"skills\": \"3.9.0\", \"auth\": \"oauth_cached\",\n \"order_id\": null, \"plan\": null, \"connected_at\": \"2026-10-28T10:46:12Z\" }\n```\n\n- `tool`: the `<tool>` from the Phase 2 route (`claude-code`, `codex`, `cursor`, `vscode`,\n `antigravity`, `devin`, or a short lowercase name for an unlisted tool), the same value later\n passed to `run-start --tool`.\n- `mcp`: one string, `hostinger-api-mcp <version>`, with the `version` from the `mcp_ok` event.\n Never an object.\n- `skills`: the `version=` value from the `<skill>/VERSION` stamp you just pulled — the skills bundle\n version this run used. `\"unknown\"` if the stamp is absent (an old bundle predating the stamp).\n- `auth`: the `method` from the `logged_in` event (`api_token`, `oauth_cached` or `oauth`).\n- `order_id`, `plan`: the active hosting plan's order id and name when you looked them up, else `null`.\n- `connected_at`: the current time in UTC, ISO 8601 to the second.\n\nExtra keys are allowed, but keep these seven as above: the build report reads `tool`, `mcp` and `skills`.\n\n## Phase 4 - Continue to Headless Hosting steps\n\nOnce everything is verified and you have the Hostinger MCP Server and the skills, tell the user in\none line that you're connected to Hostinger, as *The first message* in `<skill>/SKILL.md` describes.\nThen go back to `<skill>/SKILL.md` and continue with check-state without waiting for a reply.\n\n\n## Reference Sources\n\n- https://mcpservers.org/servers/hostinger/api-mcp-server\n- https://www.hostinger.com/connector\n"
84
+ "text": "---\nname: hostinger-headless-connect\ndescription: \"Connect step for the Hostinger Headless flow — verify Node, run the bootstrap (reach the Hostinger MCP server + OAuth sign-in), then route to the matching coding-tool skill under tools/ so MCP tools and skills are available before the build steps run. Triggers: connect to Hostinger, set up the Hostinger connector, start a headless build.\"\n---\n\n# Hostinger Headless Skill — connect\n\nYou are a coding agent, and this is a skill for you to execute. Follow the phases below in order.\n\nThis skill goal is to help you to connect to Hostinger via MCP. That can be done via Hostinger Connector or adding the MCP via MCP Server.\n\nWe support different coding Agentic coding tools such as Claude Code, Codex, Cursor, VS Code...and so on. \n\nYour goal is to route those tools to their specific skill inside the tools folder ensuring the tool is fully connected.\n\n\n## Phase 0 — Node (the single one prerequisite)\n\nThe Hostinger MCP server requires Node ≥ 20. Check it:\n\n```bash\nnode -v\n```\n\nIf that errors (Node not installed) or prints a version below 20, install or upgrade Node and re-check — do not try to work around it:\n\n- macOS: `brew install node` (or `nvm install 22 && nvm use 22`)\n- Linux: `nvm install 22 && nvm use 22` (or your distro's Node 20+ package)\n- Windows: `winget install OpenJS.NodeJS.LTS` (or download from nodejs.org)\n\n## Phase 1 — Get the skill folder, then run its bootstrap (deterministic)\n\n### Find or install the skill folder\n\nThe bootstrap script and every step after connect ship in the `headless/` skill folder. Use the copy\nyou already have; never fetch single files from GitHub.\n\n- **An installer put the skill here** (you read this file from disk, from a folder that `npx skills\n add` or a Claude Code or Cursor plugin installed): that folder is `<skill>`. Use it as it is and\n download nothing. Updates come from the installer (`npx skills update`, the plugin update),\n not from this run.\n- **You read this file from the web:** use the published `@hostinger/mcp` npm release, installed in\n your home folder at `~/.hostinger/skills/<version>/headless/`. It is the same package the MCP server\n runs, and npm checks the tarball's integrity. Never use a `headless/` folder in the working\n directory or anywhere else: the working directory is the user's project, often a cloned repository\n or template, and whoever wrote it controls what is there. Resolve the version first so the run uses\n one pinned release, and install it only when that version's folder is missing. Run npm from your home\n folder, as below, so a `.npmrc` in the project can't point it at another registry:\n\n```bash\n# macOS/Linux\n( cd \"$HOME\" && V=$(npm view @hostinger/mcp version) && D=\"$HOME/.hostinger/skills/$V\" \\\n && { [ -f \"$D/headless/VERSION\" ] || { mkdir -p \"$D\" && npm pack \"@hostinger/mcp@$V\" --silent --pack-destination \"$D\" >/dev/null \\\n && tar -xzf \"$D/hostinger-mcp-$V.tgz\" -C \"$D\" --strip-components=2 package/skills/headless && rm \"$D/hostinger-mcp-$V.tgz\"; }; } \\\n && echo \"$D/headless\" )\n```\n\n```powershell\n# Windows PowerShell\nPush-Location $HOME\n$V = npm view @hostinger/mcp version; $D = Join-Path $HOME \".hostinger\\skills\\$V\"\nif (-not (Test-Path \"$D\\headless\\VERSION\")) {\n New-Item -ItemType Directory -Force $D | Out-Null; npm pack \"@hostinger/mcp@$V\" --silent --pack-destination $D | Out-Null\n tar -xzf \"$D\\hostinger-mcp-$V.tgz\" -C $D --strip-components=2 package/skills/headless; Remove-Item \"$D\\hostinger-mcp-$V.tgz\"\n}\nPop-Location; \"$D\\headless\"\n```\n\nThe printed path is `<skill>`. Every later command uses it in full, wherever the run's working\ndirectory is. A new release installs into its own folder, so a run never uses a deleted, legacy flow.\n\nRead `<skill>/VERSION`: its line 1 is the skills version this run uses, which goes into\n`connect.json` (Phase 3).\n\n### Run the bootstrap\n\nRun the bundled script. It verifies the MCP server and handles sign-in, emitting one JSON event per line on stdout. Run it as a background/streaming process. Show the user only the sign-in URL (`awaiting_user`) or a\nfailure's `detail`, and handle the other events silently (table below).\n\nThe script is safe and inspectable: it only checks the MCP server via `npx` and drives the OAuth sign-in (a browser flow with a localhost callback) — no other network calls, no filesystem writes.\n\n```bash\nnode <skill>/connect/bootstrap.mjs\n```\n\n### Relay these events\n\nThe script emits one JSON object per line:\n\n| Event | What to do |\n| --- |--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `mcp_ok` | MCP server reachable — continue. |\n| `logged_in` | Already authenticated (env token or a previous sign-in) — continue. |\n| `awaiting_user` (`verificationUri`) | A browser window should have opened on the user's machine. Show the URL in plain prose and wait for the user to finish signing in. The OAuth callback lands on a localhost port, so the sign-in must be completed on this same machine. |\n| `success` | Bootstrap done — continue to Phase 2. |\n| `mcp_unreachable` / `login_failed` (with `detail`) | Stop and show the user the `detail`. Do not improvise a parallel setup by hand. |\n\nIf the environment has a `HOSTINGER_API_TOKEN` set (for example CI), the bootstrap reports `logged_in` immediately — no browser needed.\n\n### Sign in or out manually\n\nThe bootstrap runs the sign-in for you. To run it directly, or to switch accounts, use the MCP's own commands. `--login` opens the browser to the Hostinger sign-in page, waits for the localhost callback, stores the credentials and exits. `--logout` revokes the stored credentials, so the next sign-in opens the browser again.\n\n```bash\n# With a global install (npm install -g @hostinger/mcp):\nhostinger-api-mcp --login\nhostinger-api-mcp --logout\n\n# Without a global install:\nnpx --yes --prefix=\"$HOME\" --package=@hostinger/mcp@latest hostinger-api-mcp --login\nnpx --yes --prefix=\"$HOME\" --package=@hostinger/mcp@latest hostinger-api-mcp --logout\n```\n\nCredentials live in `~/.config/hostinger-mcp/credentials.json` (macOS/Linux) or `%APPDATA%\\hostinger-mcp\\credentials.json` (Windows) and are shared by every Hostinger MCP binary. OAuth works only when the tool runs the server over its default local (stdio) transport, which is what every registration below uses; `--http` needs `HOSTINGER_API_TOKEN`.\n\n## Phase 2 - Route to the specific tool instructions\n\n**Use the Hostinger connector only through your tool's native MCP tools** (`mcp__hostinger__*`,\n`mcp__hostinger-*__*`, or however your tool names them). Never spawn the MCP server yourself and\nnever write a JSON-RPC client to talk to it: every connector call must be a visible MCP tool call\nthe user can follow and approve, not a script.\n\n### Check both tool groups, not just \"some Hostinger tools\"\n\nThe build needs **two** groups of connector operations, and they come from different servers. Having\none group says nothing about the other:\n\n| Group | Operations | Comes from | Needed by |\n|---|---|---|---|\n| **Hosting** | `hosting_*` (websites, deploy, build report), `wordpress_*` | `hostinger` (`npx -y --prefix=\"$HOME\" @hostinger/mcp@latest`), or the scoped servers such as `hostinger-hosting` + `hostinger-wordpress` | every build |\n| **Ecommerce** | `ecommerce_*` (stores, products, shipping, payments) | **only** `hostinger-ecommerce` (`npx --prefix=\"$HOME\" --package=@hostinger/mcp@latest hostinger-ecommerce-mcp`), or the Ecommerce MCP in the Hostinger Connector. Neither `hostinger` nor the scoped hosting servers include it | Ecommerce backends (chosen later, in refine) |\n\nCheck each group on its own, right here at connect, before anything else:\n\n- **Hosting:** a Hostinger search tool finds `hosting_websites_list` (e.g. search \"list websites\").\n- **Ecommerce:** a tool from the `hostinger-ecommerce` server is in the session (Claude Code:\n `mcp__hostinger-ecommerce__*`), and its search finds `ecommerce_stores_list` (e.g. search \"list stores\").\n\nOnly when **both** pass are the Hostinger tools \"in this session\". If either is missing, the tools\nare **not** in this session: follow *register, restart, resume* below for the missing server(s) now,\neven though other Hostinger tools respond. Don't wait for refine to choose a backend: the restart\ncosts the least before the user has been asked anything, and finding the gap at provision forces a\nsecond restart in the middle of the build.\n\n### When the Hostinger tools aren't in this session: register, restart, resume\n\nMost coding tools load MCP servers only when they start, so a server registered mid-run isn't usable\nuntil the tool restarts. When either tool group above is missing:\n\n1. **Register the missing servers** in the tool's MCP config, as the tool route below shows: `hostinger`\n (`npx -y --prefix=\"$HOME\" @hostinger/mcp@latest`) if the hosting group is missing, and `hostinger-ecommerce`\n (`npx --prefix=\"$HOME\" --package=@hostinger/mcp@latest hostinger-ecommerce-mcp`) if the ecommerce group is missing.\n Skip a server that is already registered under its name. When neither group is there, register\n both: the backend isn't chosen until refine, and one restart then covers every backend.\n2. **Write `connect.json`** (Phase 3) before you stop. The skill folder from Phase 1 stays in place,\n so the next run starts from it.\n3. **Send the first message and end your turn.** It follows *The first message* in\n `<skill>/SKILL.md`: the sign-in link if one was needed, then one line saying Hostinger is set up\n and asking the user to restart the tool (the route says how) and paste the same prompt again so the\n build continues from there. This is a pause, not a run ending: no final report and no build report.\n4. **On the next run** the tools are loaded, so connect skips registration and check-state resumes the\n domain's workspace.\n\nIf a tool loads new servers without a restart (VS Code), use them right away and skip steps 3–4.\n\n**Every `npx` command and registration keeps `--prefix` set to the user's home folder.** The tool\nstarts the server, and you run commands, in the user's project, often a cloned repository or\ntemplate. Without `--prefix`, npm reads the `.npmrc` there, and whoever wrote it can point npx at\nanother registry or run their own code. In shell commands write `--prefix=\"$HOME\"` (the shell expands\nit when you register). In JSON or TOML configs replace `<home>` with the home folder's absolute path\n(`echo $HOME`, e.g. `/Users/me` or `C:\\\\Users\\\\me`). Never drop the flag or leave `<home>` in place.\n\n| Tool | Route |\n|-------------|---------------------------------|\n| Claude Code | `connect/tools/claude-code.md` |\n| Codex | `connect/tools/codex.md` |\n| Cursor | `connect/tools/cursor.md` |\n| VS Code | `connect/tools/vscode.md` |\n| Antigravity | `connect/tools/antigravity.md` |\n| Devin | `connect/tools/devin.md` |\n\nIf the user's tool isn't listed yet, connect it the generic way: register both Hostinger MCP servers\nin that tool's MCP config (command `npx`, args as above), then follow *register, restart, resume*.\n\n### The skills come from Phase 1 only\n\nThe tool routes don't download the skills: Phase 1 already found or installed `<skill>`. The bundle\nships a `<skill>/VERSION` stamp (`version=<semver>` on line 1, then `commit` and `built`); carry the\nversion into `connect.json` (Phase 3) so the build report records which skills ran.\n\n## Phase 3 - Write `connect.json`\n\nOnce the tool is connected, write `~/.hostinger/<domain>/connect.json` (create the folder if it is\nmissing) in exactly this shape:\n\n```json\n{ \"tool\": \"claude-code\", \"mcp\": \"hostinger-api-mcp 3.8.0\", \"skills\": \"3.9.0\", \"auth\": \"oauth_cached\",\n \"order_id\": null, \"plan\": null, \"connected_at\": \"2026-10-28T10:46:12Z\" }\n```\n\n- `tool`: the `<tool>` from the Phase 2 route (`claude-code`, `codex`, `cursor`, `vscode`,\n `antigravity`, `devin`, or a short lowercase name for an unlisted tool), the same value later\n passed to `run-start --tool`.\n- `mcp`: one string, `hostinger-api-mcp <version>`, with the `version` from the `mcp_ok` event.\n Never an object.\n- `skills`: the `version=` value from the `<skill>/VERSION` stamp you just pulled — the skills bundle\n version this run used. `\"unknown\"` if the stamp is absent (an old bundle predating the stamp).\n- `auth`: the `method` from the `logged_in` event (`api_token`, `oauth_cached` or `oauth`).\n- `order_id`, `plan`: the active hosting plan's order id and name when you looked them up, else `null`.\n- `connected_at`: the current time in UTC, ISO 8601 to the second.\n\nExtra keys are allowed, but keep these seven as above: the build report reads `tool`, `mcp` and `skills`.\n\n## Phase 4 - Continue to Headless Hosting steps\n\nOnce everything is verified and you have the Hostinger MCP Server and the skills, tell the user in\none line that you're connected to Hostinger, as *The first message* in `<skill>/SKILL.md` describes.\nThen go back to `<skill>/SKILL.md` and continue with check-state without waiting for a reply.\n\n\n## Reference Sources\n\n- https://mcpservers.org/servers/hostinger/api-mcp-server\n- https://www.hostinger.com/connector\n"
85
85
  },
86
86
  {
87
87
  "uri": "skill://hostinger-headless/connect/bootstrap.mjs",
@@ -92,45 +92,45 @@ const skills: Skill[] = [
92
92
  },
93
93
  {
94
94
  "uri": "skill://hostinger-headless/connect/tools/antigravity.md",
95
- "digest": "sha256:5fffcf33a1f45bcb18a80a449decbec1988edc3fde18e49e2a70c6d507222985",
96
- "size": 2254,
95
+ "digest": "sha256:a3fcaf2d98a5ab92953dc76e50f91635528529d98c3a7aad4254ef54ea8ae780",
96
+ "size": 2292,
97
97
  "mimeType": "text/markdown",
98
- "text": "---\nname: headless-connect-antigravity\ndescription: \"Specific Antigravity Hostinger MCP Server connection instructions for the Hostinger Headless flow.\"\n---\n\n## Goals\n\n- Make Hostinger MCP tools and Skills available in Google Antigravity.\n- Don't provision hosting, create stores, install WordPress, or deploy by hand here — the skill does all of that. This entry stops at signed in and checking that Hostinger MCP and skills are reachable.\n\n## Step 1 — Make the MCP tools available\n\nThe later stages drive the Hostinger connector through Antigravity's native MCP tools. Check **both** tool groups (hosting and ecommerce), as *Check both tool groups* in `connect/SKILL.md`\nsays: other Hostinger tools responding doesn't mean the `hostinger-ecommerce` ones are there. Both\nrespond → use them and move on.\n\nIf either is missing, add the missing server(s) to `~/.gemini/antigravity/mcp_config.json` (Windows:\n`C:\\Users\\<USERNAME>\\.gemini\\antigravity\\mcp_config.json`), which you can reach from the Agent panel's\n\"...\" → MCP Servers → Manage MCP Servers → View raw config. Merge them into any existing\n`mcpServers` rather than replacing the file. The bootstrap already signed you in, so they reuse the\ncached sign-in (`~/.config/hostinger-mcp/credentials.json`, or `HOSTINGER_API_TOKEN`) and there are\nno tokens to handle:\n\n```json\n{\n \"mcpServers\": {\n \"hostinger\": {\n \"command\": \"npx\",\n \"args\": [\"-y\", \"@hostinger/mcp@latest\"]\n },\n \"hostinger-ecommerce\": {\n \"command\": \"npx\",\n \"args\": [\"--package=@hostinger/mcp@latest\", \"hostinger-ecommerce-mcp\"]\n }\n }\n}\n```\n\nAntigravity loads MCP servers only when the editor starts, so follow *register, restart, resume* in\n`connect/SKILL.md`: write `connect.json`, then ask the user to fully quit and reopen\nAntigravity and paste the same prompt. Never spawn the server yourself or write a JSON-RPC client to\nreach it.\n\n## Step 2 — Hand off\n\nThe skills are already in place: Phase 1 of `connect/SKILL.md` found the installed `<skill>`\nfolder or installed it from the `@hostinger/mcp` release. Don't download them again here. Read the\n`<skill>/VERSION` stamp (record it in `connect.json`), open `<skill>/SKILL.md` and follow its\nsteps. That skill owns the rest of the run.\n"
98
+ "text": "---\nname: headless-connect-antigravity\ndescription: \"Specific Antigravity Hostinger MCP Server connection instructions for the Hostinger Headless flow.\"\n---\n\n## Goals\n\n- Make Hostinger MCP tools and Skills available in Google Antigravity.\n- Don't provision hosting, create stores, install WordPress, or deploy by hand here — the skill does all of that. This entry stops at signed in and checking that Hostinger MCP and skills are reachable.\n\n## Step 1 — Make the MCP tools available\n\nThe later stages drive the Hostinger connector through Antigravity's native MCP tools. Check **both** tool groups (hosting and ecommerce), as *Check both tool groups* in `connect/SKILL.md`\nsays: other Hostinger tools responding doesn't mean the `hostinger-ecommerce` ones are there. Both\nrespond → use them and move on.\n\nIf either is missing, add the missing server(s) to `~/.gemini/antigravity/mcp_config.json` (Windows:\n`C:\\Users\\<USERNAME>\\.gemini\\antigravity\\mcp_config.json`), which you can reach from the Agent panel's\n\"...\" → MCP Servers → Manage MCP Servers → View raw config. Merge them into any existing\n`mcpServers` rather than replacing the file. The bootstrap already signed you in, so they reuse the\ncached sign-in (`~/.config/hostinger-mcp/credentials.json`, or `HOSTINGER_API_TOKEN`) and there are\nno tokens to handle:\n\n```json\n{\n \"mcpServers\": {\n \"hostinger\": {\n \"command\": \"npx\",\n \"args\": [\"-y\", \"--prefix=<home>\", \"@hostinger/mcp@latest\"]\n },\n \"hostinger-ecommerce\": {\n \"command\": \"npx\",\n \"args\": [\"--prefix=<home>\", \"--package=@hostinger/mcp@latest\", \"hostinger-ecommerce-mcp\"]\n }\n }\n}\n```\n\nAntigravity loads MCP servers only when the editor starts, so follow *register, restart, resume* in\n`connect/SKILL.md`: write `connect.json`, then ask the user to fully quit and reopen\nAntigravity and paste the same prompt. Never spawn the server yourself or write a JSON-RPC client to\nreach it.\n\n## Step 2 — Hand off\n\nThe skills are already in place: Phase 1 of `connect/SKILL.md` found the installed `<skill>`\nfolder or installed it from the `@hostinger/mcp` release. Don't download them again here. Read the\n`<skill>/VERSION` stamp (record it in `connect.json`), open `<skill>/SKILL.md` and follow its\nsteps. That skill owns the rest of the run.\n"
99
99
  },
100
100
  {
101
101
  "uri": "skill://hostinger-headless/connect/tools/claude-code.md",
102
- "digest": "sha256:d7f07f341f4b9cef21d97e16d5fe8b9d8cb58a0c075296934250d2b926c623e2",
103
- "size": 2329,
102
+ "digest": "sha256:6b4d177e1803809fc08dbb0b8d9fe382862a11db700bde2d4424e9ed4baa9b48",
103
+ "size": 2363,
104
104
  "mimeType": "text/markdown",
105
- "text": "---\nname: headless-connect-claude-code\ndescription: \"Specific Claude Code Hostinger MCP Server connection instructions for the Hostinger Headless flow.\"\n---\n\n## Goals\n\n- Your goal is to make Hostinger MCP tools and Skills available in Claude Code. \n- Don't provision hosting, create stores, install WordPress, or deploy by hand here — the skill does all of that. This entry stops at signed in and checking that Hostinger MCP and skills are available.\n\n\n## Step 1 — Make the MCP tools available\n\nStages 2–10 drive the Hostinger connector through Claude Code's native MCP tools. Check **both** tool\ngroups as *Check both tool groups* in `connect/SKILL.md` says:\n\n- **Hosting:** `mcp__hostinger__*`, or the scoped `mcp__hostinger-hosting__*`.\n- **Ecommerce:** `mcp__hostinger-ecommerce__*`. The scoped servers (`hostinger-hosting`,\n `hostinger-wordpress`, `hostinger-domains`, …) never include it, so a session full of\n `mcp__hostinger-*__*` tools can still be missing it.\n\nBoth present → use them and move on. Either missing → register the missing server(s) at user scope\nnow (`claude mcp list` shows what's already registered; skip those). The bootstrap already signed you\nin, so they reuse the cached sign-in (`~/.config/hostinger-mcp/credentials.json`, or\n`HOSTINGER_API_TOKEN`) and there are no tokens to handle:\n\n```bash\nclaude mcp add hostinger --scope user -- npx -y @hostinger/mcp@latest # hosting group missing\nclaude mcp add hostinger-ecommerce --scope user -- npx --package=@hostinger/mcp@latest hostinger-ecommerce-mcp # ecommerce group missing\n```\n\nClaude Code loads MCP servers only at startup, so follow *register, restart, resume* in\n`connect/SKILL.md`: write `connect.json`, then ask the user to exit Claude Code, start it\nagain in the same folder (`claude`, or `claude --continue` to keep this conversation) and paste the\nsame prompt. Never spawn the server yourself or write a JSON-RPC client to reach it.\n\n## Step 2 — Hand off\n\nThe skills are already in place: Phase 1 of `connect/SKILL.md` found the installed `<skill>`\nfolder or installed it from the `@hostinger/mcp` release. Don't download them again here. Read the\n`<skill>/VERSION` stamp (record it in `connect.json`), open `<skill>/SKILL.md` and follow its\nsteps. That skill owns the rest of the run.\n"
105
+ "text": "---\nname: headless-connect-claude-code\ndescription: \"Specific Claude Code Hostinger MCP Server connection instructions for the Hostinger Headless flow.\"\n---\n\n## Goals\n\n- Your goal is to make Hostinger MCP tools and Skills available in Claude Code. \n- Don't provision hosting, create stores, install WordPress, or deploy by hand here — the skill does all of that. This entry stops at signed in and checking that Hostinger MCP and skills are available.\n\n\n## Step 1 — Make the MCP tools available\n\nStages 2–10 drive the Hostinger connector through Claude Code's native MCP tools. Check **both** tool\ngroups as *Check both tool groups* in `connect/SKILL.md` says:\n\n- **Hosting:** `mcp__hostinger__*`, or the scoped `mcp__hostinger-hosting__*`.\n- **Ecommerce:** `mcp__hostinger-ecommerce__*`. The scoped servers (`hostinger-hosting`,\n `hostinger-wordpress`, `hostinger-domains`, …) never include it, so a session full of\n `mcp__hostinger-*__*` tools can still be missing it.\n\nBoth present → use them and move on. Either missing → register the missing server(s) at user scope\nnow (`claude mcp list` shows what's already registered; skip those). The bootstrap already signed you\nin, so they reuse the cached sign-in (`~/.config/hostinger-mcp/credentials.json`, or\n`HOSTINGER_API_TOKEN`) and there are no tokens to handle:\n\n```bash\nclaude mcp add hostinger --scope user -- npx -y --prefix=\"$HOME\" @hostinger/mcp@latest # hosting group missing\nclaude mcp add hostinger-ecommerce --scope user -- npx --prefix=\"$HOME\" --package=@hostinger/mcp@latest hostinger-ecommerce-mcp # ecommerce group missing\n```\n\nClaude Code loads MCP servers only at startup, so follow *register, restart, resume* in\n`connect/SKILL.md`: write `connect.json`, then ask the user to exit Claude Code, start it\nagain in the same folder (`claude`, or `claude --continue` to keep this conversation) and paste the\nsame prompt. Never spawn the server yourself or write a JSON-RPC client to reach it.\n\n## Step 2 — Hand off\n\nThe skills are already in place: Phase 1 of `connect/SKILL.md` found the installed `<skill>`\nfolder or installed it from the `@hostinger/mcp` release. Don't download them again here. Read the\n`<skill>/VERSION` stamp (record it in `connect.json`), open `<skill>/SKILL.md` and follow its\nsteps. That skill owns the rest of the run.\n"
106
106
  },
107
107
  {
108
108
  "uri": "skill://hostinger-headless/connect/tools/codex.md",
109
- "digest": "sha256:0c994eef605dea3b7321ae12fb30079faeeff944ebe9637477ecf46e4686e309",
110
- "size": 2076,
109
+ "digest": "sha256:b948ee392b92daec4d4fb58e5c7e06d81a2ed0b6a203795d124478601966c991",
110
+ "size": 2148,
111
111
  "mimeType": "text/markdown",
112
- "text": "---\nname: headless-connect-codex\ndescription: \"Specific Codex Hostinger MCP Server connection instructions for the Hostinger Headless flow.\"\n---\n\n## Goals\n\n- Make Hostinger MCP tools and Skills available in Codex.\n- Don't provision hosting, create stores, install WordPress, or deploy by hand here — the skill does all of that. This entry stops at signed in and checking that Hostinger MCP and skills are reachable.\n\n## Step 1 — Make the MCP tools available\n\nThe later stages drive the Hostinger connector through Codex's native MCP tools. Check **both** tool groups (hosting and ecommerce), as *Check both tool groups* in `connect/SKILL.md`\nsays: other Hostinger tools responding doesn't mean the `hostinger-ecommerce` ones are there. Both\nrespond → use them and move on.\n\nIf either is missing, register the missing server(s); skip one already registered. The bootstrap already signed you in, so they reuse the cached\nsign-in (`~/.config/hostinger-mcp/credentials.json`, or `HOSTINGER_API_TOKEN`) and there are no tokens\nto handle:\n\n```bash\ncodex mcp add hostinger -- npx -y @hostinger/mcp@latest\ncodex mcp add hostinger-ecommerce -- npx --package=@hostinger/mcp@latest hostinger-ecommerce-mcp\n```\n\nor add them to `~/.codex/config.toml`:\n\n```toml\n[mcp_servers.hostinger]\ncommand = \"npx\"\nargs = [\"-y\", \"@hostinger/mcp@latest\"]\n\n[mcp_servers.hostinger-ecommerce]\ncommand = \"npx\"\nargs = [\"--package=@hostinger/mcp@latest\", \"hostinger-ecommerce-mcp\"]\n```\n\nCodex loads MCP servers only when it starts, so follow *register, restart, resume* in\n`connect/SKILL.md`: write `connect.json`, then ask the user to restart Codex and paste the\nsame prompt. Never spawn the server yourself or write a JSON-RPC client to reach it.\n\n## Step 2 — Hand off\n\nThe skills are already in place: Phase 1 of `connect/SKILL.md` found the installed `<skill>`\nfolder or installed it from the `@hostinger/mcp` release. Don't download them again here. Read the\n`<skill>/VERSION` stamp (record it in `connect.json`), open `<skill>/SKILL.md` and follow its\nsteps. That skill owns the rest of the run.\n"
112
+ "text": "---\nname: headless-connect-codex\ndescription: \"Specific Codex Hostinger MCP Server connection instructions for the Hostinger Headless flow.\"\n---\n\n## Goals\n\n- Make Hostinger MCP tools and Skills available in Codex.\n- Don't provision hosting, create stores, install WordPress, or deploy by hand here — the skill does all of that. This entry stops at signed in and checking that Hostinger MCP and skills are reachable.\n\n## Step 1 — Make the MCP tools available\n\nThe later stages drive the Hostinger connector through Codex's native MCP tools. Check **both** tool groups (hosting and ecommerce), as *Check both tool groups* in `connect/SKILL.md`\nsays: other Hostinger tools responding doesn't mean the `hostinger-ecommerce` ones are there. Both\nrespond → use them and move on.\n\nIf either is missing, register the missing server(s); skip one already registered. The bootstrap already signed you in, so they reuse the cached\nsign-in (`~/.config/hostinger-mcp/credentials.json`, or `HOSTINGER_API_TOKEN`) and there are no tokens\nto handle:\n\n```bash\ncodex mcp add hostinger -- npx -y --prefix=\"$HOME\" @hostinger/mcp@latest\ncodex mcp add hostinger-ecommerce -- npx --prefix=\"$HOME\" --package=@hostinger/mcp@latest hostinger-ecommerce-mcp\n```\n\nor add them to `~/.codex/config.toml`:\n\n```toml\n[mcp_servers.hostinger]\ncommand = \"npx\"\nargs = [\"-y\", \"--prefix=<home>\", \"@hostinger/mcp@latest\"]\n\n[mcp_servers.hostinger-ecommerce]\ncommand = \"npx\"\nargs = [\"--prefix=<home>\", \"--package=@hostinger/mcp@latest\", \"hostinger-ecommerce-mcp\"]\n```\n\nCodex loads MCP servers only when it starts, so follow *register, restart, resume* in\n`connect/SKILL.md`: write `connect.json`, then ask the user to restart Codex and paste the\nsame prompt. Never spawn the server yourself or write a JSON-RPC client to reach it.\n\n## Step 2 — Hand off\n\nThe skills are already in place: Phase 1 of `connect/SKILL.md` found the installed `<skill>`\nfolder or installed it from the `@hostinger/mcp` release. Don't download them again here. Read the\n`<skill>/VERSION` stamp (record it in `connect.json`), open `<skill>/SKILL.md` and follow its\nsteps. That skill owns the rest of the run.\n"
113
113
  },
114
114
  {
115
115
  "uri": "skill://hostinger-headless/connect/tools/cursor.md",
116
- "digest": "sha256:4851b75665d3f1f6a2d06d609cd13e7dc47caf99302ccc7b8bf27ceab4045e54",
117
- "size": 4953,
116
+ "digest": "sha256:470dd966f74cd4e22665b4a8b0fa11829e7ff0c7122f9269cf5e9d1413528b3a",
117
+ "size": 5042,
118
118
  "mimeType": "text/markdown",
119
- "text": "---\nname: headless-connect-cursor\ndescription: \"Specific Cursor Hostinger MCP Server connection instructions for the Hostinger Headless flow.\"\n---\n\n## Goals\n\n- Install (or reach) the Hostinger MCP in Cursor.\n- Sign the user in with OAuth 2.0 PKCE — the MCP opens a browser so they only need to log in.\n- Don't provision hosting, create stores, install WordPress, or deploy by hand here — the skill does all of that. This entry stops at signed in and checking that Hostinger MCP and skills are reachable.\n\n## Step 1 — Install / reach Hostinger MCP\n\n**Prerequisite:** Node.js 20 or newer on `PATH` (`node -v`). The published package engines require `>=20`; use Node 24 if you follow the package README.\n\nThe Hostinger MCP is the npm package `@hostinger/mcp`. You do **not** need a global install — Cursor (and this session) can run it through `npx`. Prefer the unified binary `hostinger-api-mcp` (every operation). Scoped binaries such as `hostinger-hosting-mcp` are optional.\n\n### Option A — no global install (recommended)\n\n```bash\n# Verify the package/binary is reachable\nnpx --yes --package=@hostinger/mcp@latest hostinger-api-mcp --version\n```\n\n### Option B — global install\n\n```bash\nnpm install -g @hostinger/mcp\n# or: yarn global add @hostinger/mcp\n# or: pnpm add -g @hostinger/mcp\n\nhostinger-api-mcp --version\n```\n\n### Register the servers in Cursor\n\nCheck **both** tool groups, as *Check both tool groups* in `connect/SKILL.md` says: hosting\n(`mcp__hostinger__*` or `mcp__hostinger-hosting__*`) **and** ecommerce (`mcp__hostinger-ecommerce__*`).\nThe scoped `mcp__hostinger-*__*` servers never include ecommerce, so they alone don't count. Both\npresent → skip registration: go to Step 2 only if you still need sign-in, otherwise go to Step 3.\n\nIf either is missing, register the missing server(s) in `~/.cursor/mcp.json` (global, so every project and every\nlater run has them). Merge them into any existing `mcpServers` rather than replacing the file.\n**Do not** put `HOSTINGER_API_TOKEN` in this file when you want OAuth: leave the token unset so\nOAuth can run:\n\n```json\n{\n \"mcpServers\": {\n \"hostinger\": {\n \"command\": \"npx\",\n \"args\": [\"--yes\", \"--package=@hostinger/mcp@latest\", \"hostinger-api-mcp\"]\n },\n \"hostinger-ecommerce\": {\n \"command\": \"npx\",\n \"args\": [\"--yes\", \"--package=@hostinger/mcp@latest\", \"hostinger-ecommerce-mcp\"]\n }\n }\n}\n```\n\nCursor loads MCP servers only when the editor starts, so after Step 2 write `connect.json` and follow\n*register, restart, resume* in `connect/SKILL.md`: ask the user to fully quit and reopen Cursor\n(and, if Cursor asks, enable the two Hostinger servers under Settings → MCP), then paste the same\nprompt. Never spawn the server yourself or write a JSON-RPC client to reach it: every connector call\nmust be a native MCP tool call.\n\n## Step 2 — Authenticate with OAuth (browser opens)\n\nPrefer OAuth for interactive Cursor use. OAuth works only in **stdio** mode (Cursor's default for this local server). Skip this step if `HOSTINGER_API_TOKEN` is already set in the environment (that bypasses OAuth entirely).\n\nIf the connect bootstrap already emitted `logged_in` / `success`, credentials are already on disk — continue to Step 3.\n\nOtherwise run the login command. It registers a dynamic OAuth client (once per machine), **opens the user's browser** to Hostinger's authorization page, listens on a localhost callback, stores tokens, and exits:\n\n```bash\nnpx --yes --package=@hostinger/mcp@latest hostinger-api-mcp --login\n```\n\n(With a global install: `hostinger-api-mcp --login`.)\n\nWhat to tell the user:\n\n1. A browser window should open automatically for Hostinger sign-in.\n2. If it does not, the process prints `[OAuth] Opening browser for sign-in:` plus the URL on stderr — show that URL and ask them to open it on **this same machine** (the callback is `http://127.0.0.1:<ephemeral-port>/oauth/callback`).\n3. Wait until the command prints that sign-in succeeded and exits 0.\n\nCredentials are stored and reused automatically:\n\n- macOS / Linux: `~/.config/hostinger-mcp/credentials.json` (mode 0600)\n- Windows: `%APPDATA%\\hostinger-mcp\\credentials.json`\n\nThey are shared across every Hostinger MCP binary. Expired access tokens refresh on their own; a revoked refresh token re-triggers the browser flow.\n\nTo sign out later:\n\n```bash\nnpx --yes --package=@hostinger/mcp@latest hostinger-api-mcp --logout\n```\n\n**API token (optional, CI / scripts):** set `HOSTINGER_API_TOKEN` in the environment before launching Cursor. When present it always wins and no OAuth code runs. Do not commit the token.\n\n## Step 3 — Hand off\n\nThe skills are already in place: Phase 1 of `connect/SKILL.md` found the installed `<skill>`\nfolder or installed it from the `@hostinger/mcp` release. Don't download them again here. Read the\n`<skill>/VERSION` stamp (record it in `connect.json`), open `<skill>/SKILL.md` and follow its\nsteps. That skill owns the rest of the run.\n"
119
+ "text": "---\nname: headless-connect-cursor\ndescription: \"Specific Cursor Hostinger MCP Server connection instructions for the Hostinger Headless flow.\"\n---\n\n## Goals\n\n- Install (or reach) the Hostinger MCP in Cursor.\n- Sign the user in with OAuth 2.0 PKCE — the MCP opens a browser so they only need to log in.\n- Don't provision hosting, create stores, install WordPress, or deploy by hand here — the skill does all of that. This entry stops at signed in and checking that Hostinger MCP and skills are reachable.\n\n## Step 1 — Install / reach Hostinger MCP\n\n**Prerequisite:** Node.js 20 or newer on `PATH` (`node -v`). The published package engines require `>=20`; use Node 24 if you follow the package README.\n\nThe Hostinger MCP is the npm package `@hostinger/mcp`. You do **not** need a global install — Cursor (and this session) can run it through `npx`. Prefer the unified binary `hostinger-api-mcp` (every operation). Scoped binaries such as `hostinger-hosting-mcp` are optional.\n\n### Option A — no global install (recommended)\n\n```bash\n# Verify the package/binary is reachable\nnpx --yes --prefix=\"$HOME\" --package=@hostinger/mcp@latest hostinger-api-mcp --version\n```\n\n### Option B — global install\n\n```bash\nnpm install -g @hostinger/mcp\n# or: yarn global add @hostinger/mcp\n# or: pnpm add -g @hostinger/mcp\n\nhostinger-api-mcp --version\n```\n\n### Register the servers in Cursor\n\nCheck **both** tool groups, as *Check both tool groups* in `connect/SKILL.md` says: hosting\n(`mcp__hostinger__*` or `mcp__hostinger-hosting__*`) **and** ecommerce (`mcp__hostinger-ecommerce__*`).\nThe scoped `mcp__hostinger-*__*` servers never include ecommerce, so they alone don't count. Both\npresent → skip registration: go to Step 2 only if you still need sign-in, otherwise go to Step 3.\n\nIf either is missing, register the missing server(s) in `~/.cursor/mcp.json` (global, so every project and every\nlater run has them). Merge them into any existing `mcpServers` rather than replacing the file.\n**Do not** put `HOSTINGER_API_TOKEN` in this file when you want OAuth: leave the token unset so\nOAuth can run:\n\n```json\n{\n \"mcpServers\": {\n \"hostinger\": {\n \"command\": \"npx\",\n \"args\": [\"--yes\", \"--prefix=<home>\", \"--package=@hostinger/mcp@latest\", \"hostinger-api-mcp\"]\n },\n \"hostinger-ecommerce\": {\n \"command\": \"npx\",\n \"args\": [\"--yes\", \"--prefix=<home>\", \"--package=@hostinger/mcp@latest\", \"hostinger-ecommerce-mcp\"]\n }\n }\n}\n```\n\nCursor loads MCP servers only when the editor starts, so after Step 2 write `connect.json` and follow\n*register, restart, resume* in `connect/SKILL.md`: ask the user to fully quit and reopen Cursor\n(and, if Cursor asks, enable the two Hostinger servers under Settings → MCP), then paste the same\nprompt. Never spawn the server yourself or write a JSON-RPC client to reach it: every connector call\nmust be a native MCP tool call.\n\n## Step 2 — Authenticate with OAuth (browser opens)\n\nPrefer OAuth for interactive Cursor use. OAuth works only in **stdio** mode (Cursor's default for this local server). Skip this step if `HOSTINGER_API_TOKEN` is already set in the environment (that bypasses OAuth entirely).\n\nIf the connect bootstrap already emitted `logged_in` / `success`, credentials are already on disk — continue to Step 3.\n\nOtherwise run the login command. It registers a dynamic OAuth client (once per machine), **opens the user's browser** to Hostinger's authorization page, listens on a localhost callback, stores tokens, and exits:\n\n```bash\nnpx --yes --prefix=\"$HOME\" --package=@hostinger/mcp@latest hostinger-api-mcp --login\n```\n\n(With a global install: `hostinger-api-mcp --login`.)\n\nWhat to tell the user:\n\n1. A browser window should open automatically for Hostinger sign-in.\n2. If it does not, the process prints `[OAuth] Opening browser for sign-in:` plus the URL on stderr — show that URL and ask them to open it on **this same machine** (the callback is `http://127.0.0.1:<ephemeral-port>/oauth/callback`).\n3. Wait until the command prints that sign-in succeeded and exits 0.\n\nCredentials are stored and reused automatically:\n\n- macOS / Linux: `~/.config/hostinger-mcp/credentials.json` (mode 0600)\n- Windows: `%APPDATA%\\hostinger-mcp\\credentials.json`\n\nThey are shared across every Hostinger MCP binary. Expired access tokens refresh on their own; a revoked refresh token re-triggers the browser flow.\n\nTo sign out later:\n\n```bash\nnpx --yes --prefix=\"$HOME\" --package=@hostinger/mcp@latest hostinger-api-mcp --logout\n```\n\n**API token (optional, CI / scripts):** set `HOSTINGER_API_TOKEN` in the environment before launching Cursor. When present it always wins and no OAuth code runs. Do not commit the token.\n\n## Step 3 — Hand off\n\nThe skills are already in place: Phase 1 of `connect/SKILL.md` found the installed `<skill>`\nfolder or installed it from the `@hostinger/mcp` release. Don't download them again here. Read the\n`<skill>/VERSION` stamp (record it in `connect.json`), open `<skill>/SKILL.md` and follow its\nsteps. That skill owns the rest of the run.\n"
120
120
  },
121
121
  {
122
122
  "uri": "skill://hostinger-headless/connect/tools/devin.md",
123
- "digest": "sha256:d7bdd2e2da9c1b06aa5bcb607bef72fa0282d548b9a4b81a02dd5836b80880a7",
124
- "size": 2216,
123
+ "digest": "sha256:bccd88ea73737adeae7f53f1f9cb8eb416431c537a7a5cc765d60475405e1bce",
124
+ "size": 2288,
125
125
  "mimeType": "text/markdown",
126
- "text": "---\nname: headless-connect-devin\ndescription: \"Specific Devin Hostinger MCP Server connection instructions for the Hostinger Headless flow.\"\n---\n\n## Goals\n\n- Make Hostinger MCP tools and Skills available in Devin.\n- Don't provision hosting, create stores, install WordPress, or deploy by hand here — the skill does all of that. This entry stops at signed in and checking that Hostinger MCP and skills are reachable.\n\n## Step 1 — Make the MCP tools available\n\nThe later stages drive the Hostinger connector through Devin's native MCP tools. Check **both** tool groups (hosting and ecommerce), as *Check both tool groups* in `connect/SKILL.md`\nsays: other Hostinger tools responding doesn't mean the `hostinger-ecommerce` ones are there. Both\nrespond → use them and move on.\n\nIf either is missing, register the missing server(s) for Devin; skip one already registered. The bootstrap already signed you in, so they reuse the\ncached sign-in (`~/.config/hostinger-mcp/credentials.json`, or `HOSTINGER_API_TOKEN`) and there are no\ntokens to handle.\n\n- **Devin CLI:**\n\n ```bash\n devin mcp add hostinger -- npx -y @hostinger/mcp@latest\n devin mcp add hostinger-ecommerce -- npx --package=@hostinger/mcp@latest hostinger-ecommerce-mcp\n ```\n\n- **Devin app:** Settings → MCP Marketplace → **Add Your Own**, transport **STDIO**, once per server:\n\n ```json\n { \"command\": \"npx\", \"args\": [\"-y\", \"@hostinger/mcp@latest\"] }\n ```\n\n ```json\n { \"command\": \"npx\", \"args\": [\"--package=@hostinger/mcp@latest\", \"hostinger-ecommerce-mcp\"] }\n ```\n\nIf the tools still aren't in this session, follow *register, restart, resume* in `connect/SKILL.md`:\nwrite `connect.json`, then ask the user to restart the Devin session (on Devin Desktop,\nfully quit and reopen the app) and paste the same prompt. Never spawn the server yourself or write a\nJSON-RPC client to reach it.\n\n## Step 2 — Hand off\n\nThe skills are already in place: Phase 1 of `connect/SKILL.md` found the installed `<skill>`\nfolder or installed it from the `@hostinger/mcp` release. Don't download them again here. Read the\n`<skill>/VERSION` stamp (record it in `connect.json`), open `<skill>/SKILL.md` and follow its\nsteps. That skill owns the rest of the run.\n"
126
+ "text": "---\nname: headless-connect-devin\ndescription: \"Specific Devin Hostinger MCP Server connection instructions for the Hostinger Headless flow.\"\n---\n\n## Goals\n\n- Make Hostinger MCP tools and Skills available in Devin.\n- Don't provision hosting, create stores, install WordPress, or deploy by hand here — the skill does all of that. This entry stops at signed in and checking that Hostinger MCP and skills are reachable.\n\n## Step 1 — Make the MCP tools available\n\nThe later stages drive the Hostinger connector through Devin's native MCP tools. Check **both** tool groups (hosting and ecommerce), as *Check both tool groups* in `connect/SKILL.md`\nsays: other Hostinger tools responding doesn't mean the `hostinger-ecommerce` ones are there. Both\nrespond → use them and move on.\n\nIf either is missing, register the missing server(s) for Devin; skip one already registered. The bootstrap already signed you in, so they reuse the\ncached sign-in (`~/.config/hostinger-mcp/credentials.json`, or `HOSTINGER_API_TOKEN`) and there are no\ntokens to handle.\n\n- **Devin CLI:**\n\n ```bash\n devin mcp add hostinger -- npx -y --prefix=\"$HOME\" @hostinger/mcp@latest\n devin mcp add hostinger-ecommerce -- npx --prefix=\"$HOME\" --package=@hostinger/mcp@latest hostinger-ecommerce-mcp\n ```\n\n- **Devin app:** Settings → MCP Marketplace → **Add Your Own**, transport **STDIO**, once per server:\n\n ```json\n { \"command\": \"npx\", \"args\": [\"-y\", \"--prefix=<home>\", \"@hostinger/mcp@latest\"] }\n ```\n\n ```json\n { \"command\": \"npx\", \"args\": [\"--prefix=<home>\", \"--package=@hostinger/mcp@latest\", \"hostinger-ecommerce-mcp\"] }\n ```\n\nIf the tools still aren't in this session, follow *register, restart, resume* in `connect/SKILL.md`:\nwrite `connect.json`, then ask the user to restart the Devin session (on Devin Desktop,\nfully quit and reopen the app) and paste the same prompt. Never spawn the server yourself or write a\nJSON-RPC client to reach it.\n\n## Step 2 — Hand off\n\nThe skills are already in place: Phase 1 of `connect/SKILL.md` found the installed `<skill>`\nfolder or installed it from the `@hostinger/mcp` release. Don't download them again here. Read the\n`<skill>/VERSION` stamp (record it in `connect.json`), open `<skill>/SKILL.md` and follow its\nsteps. That skill owns the rest of the run.\n"
127
127
  },
128
128
  {
129
129
  "uri": "skill://hostinger-headless/connect/tools/vscode.md",
130
- "digest": "sha256:149bd7f16df386f7d7cee2301416b6bf120114a1f0d445aa9d0a04bc94d963a4",
131
- "size": 2650,
130
+ "digest": "sha256:f39d2b2441aead1d2454d613d653a6858f5f613d2cb7def5065fd987e01f5202",
131
+ "size": 2724,
132
132
  "mimeType": "text/markdown",
133
- "text": "---\nname: headless-connect-vscode\ndescription: \"Specific VS Code Hostinger MCP Server connection instructions for the Hostinger Headless flow.\"\n---\n\n## Goals\n\n- Make Hostinger MCP tools and Skills available in VS Code (GitHub Copilot / agent mode).\n- Don't provision hosting, create stores, install WordPress, or deploy by hand here — the skill does all of that. This entry stops at signed in and checking that Hostinger MCP and skills are reachable.\n\n## Step 1 — Make the MCP tools available\n\nThe later stages drive the Hostinger connector through VS Code's native MCP tools. Check **both** tool groups (hosting and ecommerce), as *Check both tool groups* in `connect/SKILL.md`\nsays: other Hostinger tools responding doesn't mean the `hostinger-ecommerce` ones are there. Both\nrespond → use them and move on.\n\nIf either is missing, register the missing server(s) for VS Code; skip one already registered. The bootstrap already signed you in, so they reuse\nthe cached sign-in (`~/.config/hostinger-mcp/credentials.json`, or `HOSTINGER_API_TOKEN`) and there are\nno tokens to handle. Add them to `.vscode/mcp.json` (workspace) or your user `settings.json` under\n`mcp`:\n\n```json\n// .vscode/mcp.json\n{\n \"servers\": {\n \"hostinger\": {\n \"type\": \"stdio\",\n \"command\": \"npx\",\n \"args\": [\"-y\", \"@hostinger/mcp@latest\"]\n },\n \"hostinger-ecommerce\": {\n \"type\": \"stdio\",\n \"command\": \"npx\",\n \"args\": [\"--package=@hostinger/mcp@latest\", \"hostinger-ecommerce-mcp\"]\n }\n }\n}\n```\n\nOr add them in two commands:\n\n```bash\ncode --add-mcp '{\"name\":\"hostinger\",\"command\":\"npx\",\"args\":[\"-y\",\"@hostinger/mcp@latest\"]}'\ncode --add-mcp '{\"name\":\"hostinger-ecommerce\",\"command\":\"npx\",\"args\":[\"--package=@hostinger/mcp@latest\",\"hostinger-ecommerce-mcp\"]}'\n```\n\nVS Code picks up new servers and their tools become available right away, with no editor restart.\n(Alternatively, the official Hostinger Connector extension installs the same server:\n`code --install-extension hostinger-official.hostinger-connector`.)\n\nIf the tools still don't show up, follow *register, restart, resume* in `connect/SKILL.md`: write\n`connect.json`, then ask the user to run **Developer: Reload Window** and paste the same\nprompt. Never spawn the server yourself or write a JSON-RPC client to reach it.\n\n## Step 2 — Hand off\n\nThe skills are already in place: Phase 1 of `connect/SKILL.md` found the installed `<skill>`\nfolder or installed it from the `@hostinger/mcp` release. Don't download them again here. Read the\n`<skill>/VERSION` stamp (record it in `connect.json`), open `<skill>/SKILL.md` and follow its\nsteps. That skill owns the rest of the run.\n"
133
+ "text": "---\nname: headless-connect-vscode\ndescription: \"Specific VS Code Hostinger MCP Server connection instructions for the Hostinger Headless flow.\"\n---\n\n## Goals\n\n- Make Hostinger MCP tools and Skills available in VS Code (GitHub Copilot / agent mode).\n- Don't provision hosting, create stores, install WordPress, or deploy by hand here — the skill does all of that. This entry stops at signed in and checking that Hostinger MCP and skills are reachable.\n\n## Step 1 — Make the MCP tools available\n\nThe later stages drive the Hostinger connector through VS Code's native MCP tools. Check **both** tool groups (hosting and ecommerce), as *Check both tool groups* in `connect/SKILL.md`\nsays: other Hostinger tools responding doesn't mean the `hostinger-ecommerce` ones are there. Both\nrespond → use them and move on.\n\nIf either is missing, register the missing server(s) for VS Code; skip one already registered. The bootstrap already signed you in, so they reuse\nthe cached sign-in (`~/.config/hostinger-mcp/credentials.json`, or `HOSTINGER_API_TOKEN`) and there are\nno tokens to handle. Add them to `.vscode/mcp.json` (workspace) or your user `settings.json` under\n`mcp`:\n\n```json\n// .vscode/mcp.json\n{\n \"servers\": {\n \"hostinger\": {\n \"type\": \"stdio\",\n \"command\": \"npx\",\n \"args\": [\"-y\", \"--prefix=<home>\", \"@hostinger/mcp@latest\"]\n },\n \"hostinger-ecommerce\": {\n \"type\": \"stdio\",\n \"command\": \"npx\",\n \"args\": [\"--prefix=<home>\", \"--package=@hostinger/mcp@latest\", \"hostinger-ecommerce-mcp\"]\n }\n }\n}\n```\n\nOr add them in two commands:\n\n```bash\ncode --add-mcp '{\"name\":\"hostinger\",\"command\":\"npx\",\"args\":[\"-y\",\"--prefix=<home>\",\"@hostinger/mcp@latest\"]}'\ncode --add-mcp '{\"name\":\"hostinger-ecommerce\",\"command\":\"npx\",\"args\":[\"--prefix=<home>\",\"--package=@hostinger/mcp@latest\",\"hostinger-ecommerce-mcp\"]}'\n```\n\nVS Code picks up new servers and their tools become available right away, with no editor restart.\n(Alternatively, the official Hostinger Connector extension installs the same server:\n`code --install-extension hostinger-official.hostinger-connector`.)\n\nIf the tools still don't show up, follow *register, restart, resume* in `connect/SKILL.md`: write\n`connect.json`, then ask the user to run **Developer: Reload Window** and paste the same\nprompt. Never spawn the server yourself or write a JSON-RPC client to reach it.\n\n## Step 2 — Hand off\n\nThe skills are already in place: Phase 1 of `connect/SKILL.md` found the installed `<skill>`\nfolder or installed it from the `@hostinger/mcp` release. Don't download them again here. Read the\n`<skill>/VERSION` stamp (record it in `connect.json`), open `<skill>/SKILL.md` and follow its\nsteps. That skill owns the rest of the run.\n"
134
134
  },
135
135
  {
136
136
  "uri": "skill://hostinger-headless/hostinger-mcp.mjs",
@@ -183,10 +183,10 @@ const skills: Skill[] = [
183
183
  },
184
184
  {
185
185
  "uri": "skill://hostinger-headless/steps/03-provision/ecommerce/SKILL.md",
186
- "digest": "sha256:99dfefa5cb3ba9a07124bb2956c403768686d25a86a4f9cbe1e02c71db988311",
187
- "size": 8093,
186
+ "digest": "sha256:be6d1b92e4c0d692b96016d58bb8b7312893a97493e2f6b2b91418bf6076ff0f",
187
+ "size": 8110,
188
188
  "mimeType": "text/markdown",
189
- "text": "---\nname: headless-step-provision-ecommerce\ndescription: \"Provision a Hostinger Ecommerce backend for the Hostinger Headless flow: ensure the hostinger-ecommerce MCP is connected, create the headless website (frontend host) and a Hostinger Ecommerce STORE with a custom (headless) sales channel, set shipping + payment, and record it all in site.json.\"\n---\n\n# headless-step-provision-ecommerce\n\nProvision a **Hostinger Ecommerce** backend for `<domain>` and prove it is ready. Two distinct things:\n\n1. A **headless website** (`type: \"headless_ecommerce\"`) — this is only the **hosting slot for the\n storefront you'll build**. It installs **no store engine** (no `/cms`, no API on the domain).\n2. A **Hostinger Ecommerce store** — a separate object created through the **Ecommerce API**, with a\n **custom (headless) sales channel**. The storefront reads this store's **public Storefront Core V2\n API**, keyed on the sales channel. This is the real backend.\n\n**Out:** `site.json` (store id, custom sales channel id, URLs).\n\n> ## ⚠️ Prerequisite: the `hostinger-ecommerce` MCP server must be connected\n> Store management is **not** in the `hosting`/`wordpress` MCP groups — it's the separate\n> **`hostinger-ecommerce`** group (`ecommerce_*` ops, OAuth-backed, no API token needed). Connect\n> registers it alongside `hostinger`, so its tools are normally already in the session. If they\n> aren't (for example, a tool connected before connect registered both servers), register it now. The\n> command for each tool is in its `connect/tools/<tool>.md`; for Claude Code:\n> ```bash\n> claude mcp add hostinger-ecommerce --scope user -- npx --package=@hostinger/mcp@latest hostinger-ecommerce-mcp\n> ```\n> (Or enable the **Ecommerce** MCP in the Hostinger Connector.) A newly added MCP server only loads\n> after a restart, so pause the run with *register, restart, resume* from `connect/SKILL.md`: save\n> state, ask the user to restart the tool and paste the same prompt, and end your turn. Check-state then\n> resumes at provision. Never spawn the server yourself or write a JSON-RPC client to reach it.\n>\n> Verify the tools work in-session with a search — e.g. \"list stores\" →\n> `ecommerce_stores_list` — before proceeding.\n> The authoritative, always-current build guide is `ecommerce_miscellaneous_custom-storefront-setup-instructions` — read it once per build.\n\nAll website-lifecycle calls go through the `hosting` connector (`execute`); all store calls through the\n`hostinger-ecommerce` MCP.\n\n## Step 1 — The headless website (frontend host)\n\nSame lifecycle as `../wordpress/SKILL.md` Steps 1–2, but `type: \"headless_ecommerce\"`:\n\n1. Detect: `hosting_websites_list` / `hosting_websites_list-setups` (exact-match the domain). `completed`\n → adopt. `running` → record `provision.in_progress` and wait at the join. `failed` → stop, point at hPanel.\n If `website_type` is not `headless`\n (plain WordPress, Node.js, …), stop and tell the user — don't overwrite another kind of site.\n2. Create (only if **no** site exists): `hosting_websites_create { order_id, type: \"headless_ecommerce\", domain }`\n — **omit `domain`** for a temporary subdomain: the setup generates one itself and returns it (don't\n call a separate subdomain op first — there's no need in this flow). The response carries the\n generated domain; if it differs from the workspace dir, move\n `~/.hostinger/<old>/` → `<new>/` (if `<new>/` already exists, stop and ask instead of moving\n into it) and update `brief.md`. Don't wait here. At the join, `node <skill>/wait.mjs setup <domain>`\n returns once the setup is `completed` (see *Waiting on Hostinger* in the top-level `SKILL.md`).\n `website_type` will be `headless`.\n\n> **Onboarding handoff — an empty headless shell needs no new website.** If the domain already has an\n> empty headless shell (`website_type: headless`, setup `type: headless_empty`, nothing installed —\n> typically created in onboarding), **adopt it as the storefront host and skip the create.** A\n> `headless_ecommerce` website installs no store engine either, so an empty headless shell *is* the host\n> you need — there is no backend to \"swap\" on the website side. Record its `username`/`order_id` and go\n> straight to Step 2 (store + custom sales channel). Never call `hosting_websites_create` on a domain that already\n> has a site.\n\n> Don't expect `/cms` or any API on this domain — it serves the storefront you build (Step 6) and\n> deploy (Step 8).\n\n## Step 2 — Resolve or create the store + custom sales channel\n\n`ecommerce_stores_list` → reuse an existing store if this build already made one; otherwise create:\n\n```json\n{ \"operation\": \"ecommerce_stores_create\",\n \"params\": { \"name\": \"<store name>\", \"country_code\": \"US\", \"company_email\": \"<email>\",\n \"company_name\": \"<store name>\", \"language\": \"en\",\n \"sales_channel\": { \"type\": \"custom\", \"external_id\": \"<slug>-headless\" } } }\n```\n\nThis returns `store.id` (`store_*`) **and** a custom `sales_channel.id` (`scha_*`) in one call — the\ncustom channel is what the headless storefront is keyed on. (You can also add one later with\n`ecommerce_sales-channels_create { store_id, type:\"custom\", name }`.) Currency comes from\n`country_code` (`default_currency_code`); all prices are **minor units** (cents).\n\n## Step 3 — Store readiness: shipping + payment\n\nA store needs ≥1 shipping zone and ≥1 payment method to check out.\n\n- **Shipping:** `ecommerce_shipping_set-store { store_id, price }` (minor units; 0 = free).\n- **Payment:** `ecommerce_payments_enable-manual-method { store_id, title }`.\n\n> **Known gate — free accounts have no payment method, and therefore no checkout.** On a **free\n> account** (and any account/store without a paid Hostinger Ecommerce plan), `enable-manual-method`\n> returns **HTTP 402**, and checkout later fails with `400 payment_provider_not_configured`. This is\n> **expected, not an error** — payment, and therefore checkout, is simply unavailable on the plan. The\n> catalog and the storefront still work fully. **Never block, stop, loop or retry the build on this:**\n> - Record `payment_enabled: false` in `site.json` (set `shipping_enabled` as usual).\n> - Tell the user in one line that checkout needs a paid Ecommerce plan, and carry straight on.\n> - Treat the 402 as a normal outcome — don't surface it as a failure or re-attempt the call.\n> - Never ask the user for payment credentials.\n>\n> Every downstream stage reads `site.json → ecommerce.payment_enabled` and, when it is `false`, skips\n> all checkout wiring and verification (see content-model Step 5, frontend, deploy). A store with\n> `payment_enabled: false` is a **complete, valid build** — a browsable catalog without checkout.\n\n## Step 4 — Verify\n\n- `ecommerce_stores_metadata { store_id }` → `has_shipping` true; `has_payment_methods` (may be false,\n see the gate); read `default_currency` (`decimal_digits`, `template`) here, don't guess.\n- Public Storefront V2 answers for the channel (no auth):\n `GET https://api-ecommerce.hostinger.com/v2/channels/<scha_*>/products?limit=100` → 200 (empty until\n content-model seeds products — fine here).\n- `GET https://<domain>/` returns the Hostinger default page until the storefront is deployed — expected.\n\n## Step 5 — Record `site.json`\n\n```json\n{\n \"backend\": \"ecommerce\",\n \"website_type\": \"headless\",\n \"order_id\": 1007223428,\n \"username\": \"u…\",\n \"ecommerce\": {\n \"store_id\": \"store_…\",\n \"sales_channel\": { \"id\": \"scha_…\", \"type\": \"custom\", \"external_id\": \"…-headless\" },\n \"currency\": \"usd\",\n \"payment_enabled\": false,\n \"shipping_enabled\": true\n },\n \"urls\": {\n \"site\": \"https://<domain>\",\n \"storefront_api\": \"https://api-ecommerce.hostinger.com/v2/channels/<scha_*>\",\n \"store_admin\": \"https://ecommerce.hostinger.com/store/<store_*>\"\n },\n \"provisioned_at\": \"…\",\n \"verified_at\": \"…\"\n}\n```\n\nNo secrets in `site.json`. After deploy, link the channel to the live domain with\n`ecommerce_sales-channels_update { store_id, sales_channel_id, url }` (done in the deploy stage).\n"
189
+ "text": "---\nname: headless-step-provision-ecommerce\ndescription: \"Provision a Hostinger Ecommerce backend for the Hostinger Headless flow: ensure the hostinger-ecommerce MCP is connected, create the headless website (frontend host) and a Hostinger Ecommerce STORE with a custom (headless) sales channel, set shipping + payment, and record it all in site.json.\"\n---\n\n# headless-step-provision-ecommerce\n\nProvision a **Hostinger Ecommerce** backend for `<domain>` and prove it is ready. Two distinct things:\n\n1. A **headless website** (`type: \"headless_ecommerce\"`) — this is only the **hosting slot for the\n storefront you'll build**. It installs **no store engine** (no `/cms`, no API on the domain).\n2. A **Hostinger Ecommerce store** — a separate object created through the **Ecommerce API**, with a\n **custom (headless) sales channel**. The storefront reads this store's **public Storefront Core V2\n API**, keyed on the sales channel. This is the real backend.\n\n**Out:** `site.json` (store id, custom sales channel id, URLs).\n\n> ## ⚠️ Prerequisite: the `hostinger-ecommerce` MCP server must be connected\n> Store management is **not** in the `hosting`/`wordpress` MCP groups — it's the separate\n> **`hostinger-ecommerce`** group (`ecommerce_*` ops, OAuth-backed, no API token needed). Connect\n> registers it alongside `hostinger`, so its tools are normally already in the session. If they\n> aren't (for example, a tool connected before connect registered both servers), register it now. The\n> command for each tool is in its `connect/tools/<tool>.md`; for Claude Code:\n> ```bash\n> claude mcp add hostinger-ecommerce --scope user -- npx --prefix=\"$HOME\" --package=@hostinger/mcp@latest hostinger-ecommerce-mcp\n> ```\n> (Or enable the **Ecommerce** MCP in the Hostinger Connector.) A newly added MCP server only loads\n> after a restart, so pause the run with *register, restart, resume* from `connect/SKILL.md`: save\n> state, ask the user to restart the tool and paste the same prompt, and end your turn. Check-state then\n> resumes at provision. Never spawn the server yourself or write a JSON-RPC client to reach it.\n>\n> Verify the tools work in-session with a search — e.g. \"list stores\" →\n> `ecommerce_stores_list` — before proceeding.\n> The authoritative, always-current build guide is `ecommerce_miscellaneous_custom-storefront-setup-instructions` — read it once per build.\n\nAll website-lifecycle calls go through the `hosting` connector (`execute`); all store calls through the\n`hostinger-ecommerce` MCP.\n\n## Step 1 — The headless website (frontend host)\n\nSame lifecycle as `../wordpress/SKILL.md` Steps 1–2, but `type: \"headless_ecommerce\"`:\n\n1. Detect: `hosting_websites_list` / `hosting_websites_list-setups` (exact-match the domain). `completed`\n → adopt. `running` → record `provision.in_progress` and wait at the join. `failed` → stop, point at hPanel.\n If `website_type` is not `headless`\n (plain WordPress, Node.js, …), stop and tell the user — don't overwrite another kind of site.\n2. Create (only if **no** site exists): `hosting_websites_create { order_id, type: \"headless_ecommerce\", domain }`\n — **omit `domain`** for a temporary subdomain: the setup generates one itself and returns it (don't\n call a separate subdomain op first — there's no need in this flow). The response carries the\n generated domain; if it differs from the workspace dir, move\n `~/.hostinger/<old>/` → `<new>/` (if `<new>/` already exists, stop and ask instead of moving\n into it) and update `brief.md`. Don't wait here. At the join, `node <skill>/wait.mjs setup <domain>`\n returns once the setup is `completed` (see *Waiting on Hostinger* in the top-level `SKILL.md`).\n `website_type` will be `headless`.\n\n> **Onboarding handoff — an empty headless shell needs no new website.** If the domain already has an\n> empty headless shell (`website_type: headless`, setup `type: headless_empty`, nothing installed —\n> typically created in onboarding), **adopt it as the storefront host and skip the create.** A\n> `headless_ecommerce` website installs no store engine either, so an empty headless shell *is* the host\n> you need — there is no backend to \"swap\" on the website side. Record its `username`/`order_id` and go\n> straight to Step 2 (store + custom sales channel). Never call `hosting_websites_create` on a domain that already\n> has a site.\n\n> Don't expect `/cms` or any API on this domain — it serves the storefront you build (Step 6) and\n> deploy (Step 8).\n\n## Step 2 — Resolve or create the store + custom sales channel\n\n`ecommerce_stores_list` → reuse an existing store if this build already made one; otherwise create:\n\n```json\n{ \"operation\": \"ecommerce_stores_create\",\n \"params\": { \"name\": \"<store name>\", \"country_code\": \"US\", \"company_email\": \"<email>\",\n \"company_name\": \"<store name>\", \"language\": \"en\",\n \"sales_channel\": { \"type\": \"custom\", \"external_id\": \"<slug>-headless\" } } }\n```\n\nThis returns `store.id` (`store_*`) **and** a custom `sales_channel.id` (`scha_*`) in one call — the\ncustom channel is what the headless storefront is keyed on. (You can also add one later with\n`ecommerce_sales-channels_create { store_id, type:\"custom\", name }`.) Currency comes from\n`country_code` (`default_currency_code`); all prices are **minor units** (cents).\n\n## Step 3 — Store readiness: shipping + payment\n\nA store needs ≥1 shipping zone and ≥1 payment method to check out.\n\n- **Shipping:** `ecommerce_shipping_set-store { store_id, price }` (minor units; 0 = free).\n- **Payment:** `ecommerce_payments_enable-manual-method { store_id, title }`.\n\n> **Known gate — free accounts have no payment method, and therefore no checkout.** On a **free\n> account** (and any account/store without a paid Hostinger Ecommerce plan), `enable-manual-method`\n> returns **HTTP 402**, and checkout later fails with `400 payment_provider_not_configured`. This is\n> **expected, not an error** — payment, and therefore checkout, is simply unavailable on the plan. The\n> catalog and the storefront still work fully. **Never block, stop, loop or retry the build on this:**\n> - Record `payment_enabled: false` in `site.json` (set `shipping_enabled` as usual).\n> - Tell the user in one line that checkout needs a paid Ecommerce plan, and carry straight on.\n> - Treat the 402 as a normal outcome — don't surface it as a failure or re-attempt the call.\n> - Never ask the user for payment credentials.\n>\n> Every downstream stage reads `site.json → ecommerce.payment_enabled` and, when it is `false`, skips\n> all checkout wiring and verification (see content-model Step 5, frontend, deploy). A store with\n> `payment_enabled: false` is a **complete, valid build** — a browsable catalog without checkout.\n\n## Step 4 — Verify\n\n- `ecommerce_stores_metadata { store_id }` → `has_shipping` true; `has_payment_methods` (may be false,\n see the gate); read `default_currency` (`decimal_digits`, `template`) here, don't guess.\n- Public Storefront V2 answers for the channel (no auth):\n `GET https://api-ecommerce.hostinger.com/v2/channels/<scha_*>/products?limit=100` → 200 (empty until\n content-model seeds products — fine here).\n- `GET https://<domain>/` returns the Hostinger default page until the storefront is deployed — expected.\n\n## Step 5 — Record `site.json`\n\n```json\n{\n \"backend\": \"ecommerce\",\n \"website_type\": \"headless\",\n \"order_id\": 1007223428,\n \"username\": \"u…\",\n \"ecommerce\": {\n \"store_id\": \"store_…\",\n \"sales_channel\": { \"id\": \"scha_…\", \"type\": \"custom\", \"external_id\": \"…-headless\" },\n \"currency\": \"usd\",\n \"payment_enabled\": false,\n \"shipping_enabled\": true\n },\n \"urls\": {\n \"site\": \"https://<domain>\",\n \"storefront_api\": \"https://api-ecommerce.hostinger.com/v2/channels/<scha_*>\",\n \"store_admin\": \"https://ecommerce.hostinger.com/store/<store_*>\"\n },\n \"provisioned_at\": \"…\",\n \"verified_at\": \"…\"\n}\n```\n\nNo secrets in `site.json`. After deploy, link the channel to the live domain with\n`ecommerce_sales-channels_update { store_id, sales_channel_id, url }` (done in the deploy stage).\n"
190
190
  },
191
191
  {
192
192
  "uri": "skill://hostinger-headless/steps/03-provision/webapp/SKILL.md",
@@ -274,10 +274,10 @@ const skills: Skill[] = [
274
274
  },
275
275
  {
276
276
  "uri": "skill://hostinger-headless/steps/08-deploy/SKILL.md",
277
- "digest": "sha256:cbefe6ae459a1cbbac3c18fc1ce0d1c72fd77f598d1accac62357f538d1b3b4c",
278
- "size": 15961,
277
+ "digest": "sha256:5379169ce5f9c490cd9e31123a5fcc88c73b4964dd32ed9aa150504fb86cbb6e",
278
+ "size": 15978,
279
279
  "mimeType": "text/markdown",
280
- "text": "---\nname: headless-step-deploy\ndescription: \"Step 8 (deploy) of the Hostinger Headless flow. Archives the frontend source, then deploys through the connector's one-shot hosting_deploy (type nodejs — it uploads the source, runs npm install and the build on the server) with the chosen framework's build settings (appType + output dir) passed explicitly, polls to completion with hosting_get-deployment, verifies the live site and that the backend survived, then writes deploy.json.\"\n---\n\n# headless-step-deploy\n\nShip the frontend to the live domain through the Hostinger connector. You hand the connector a zip of\nthe project **source** and it uploads it, runs `npm install` + the build script on the server, and\nserves the result. **Out:** `deploy.json`.\n\nThis is an **instance-barrier** step: run it only after `provision.status == \"completed\"` and validate\n(if run) has passed. All operations go through the connector (`execute`).\n\n> **Connected to the hosted server (`mcp.hostinger.com`)?** Its `hosting_deploy` deploys Git\n> repositories only and cannot read a local `path`. Run this step through the local\n> `hostinger-api-mcp` server instead (see the connect step:\n> `npx --yes --package=@hostinger/mcp@latest hostinger-api-mcp`).\n\n> **With a shell and the Hostinger CLI installed**, the same deploy is one command, with the build\n> settings passed exactly as the table below says. Run it from the project, since it links the current directory:\n> `cd ~/.hostinger/<domain>/src && hostinger up --domain <domain> --type nodejs --app-type <appType> --output-directory <dir> --build-script build --node-version 22 --package-manager npm --yes --format json`.\n> It uploads, builds, waits and returns `data.buildUuid` and `data.buildState`; add `--no-wait` to\n> return at once and wait with `node <skill>/wait.mjs build <domain> <uuid>`. Then continue at\n> \"Verify\" below. Recent CLI versions see that the site is headless (`data.headless: true`) and\n> deploy only its Node.js frontend, leaving `/cms` alone; still pass the settings above.\n\n**In:** the project in `~/.hostinger/<domain>/src/`, `site.json` (`username`, `urls`), `state.json`\n(`frontend.framework`, `frontend.rendering`).\n\n> ## ⚠️ The one rule that makes this work: pass build settings explicitly\n> If you let `hosting_deploy` **auto-detect** build settings, for a headless site that detection returns\n> **null** (`appType:null`, `outputDirectory:null`, Node 20) — the build \"completes\" but the **root\n> serves 403** (no `appType`/output → the vhost serves a directory with no entry). This is reproducible.\n> **Always pass `appType`, `outputDirectory`, `nodeVersion`, `buildScript`, `packageManager` and\n> `rootDirectory` on the `hosting_deploy` call** with the framework's values; the deploy then resolves\n> them and serves correctly, and `/cms` is preserved.\n\n## Build configurations\n\nMap `frontend.framework` (from `state.json`) to its `appType` + build output directory, and pass those\non the `hosting_deploy` call. `appType` is the framework's value, e.g. `next`, `nuxt`, `astro`,\n`svelte-kit`, `nest`, `express`, `fastify`, `react`, `create-react-app`, `gatsby`, `vite`, `vue`,\n`angular`, `parcel`, `svelte`, `other`.\n\nCommon cases (all `nodeVersion: 22`, `buildScript: \"build\"`, `packageManager: \"npm\"`, `rootDirectory: \".\"`):\n\n| Framework / mode | Local config | `appType` | `outputDirectory` |\n|---|---|---|---|\n| **Next.js — SSR** *(default)* | `output: 'standalone'` | `next` | `.next` |\n| **Nuxt — SSR** | default | `nuxt` | `.output` |\n| **SvelteKit — SSR** | node adapter | `svelte-kit` | `build` |\n| **Astro — static** | `output: 'static'` | `astro` | `dist` |\n| **Vite / React / Vue — static (SPA)** | — | `vite` (or `react`/`vue`) | `dist` |\n| **Express / NestJS / Fastify — server** | — | `express`/`nest`/`fastify` | build output dir |\n\nSSR/server frameworks run a persistent Node process (root sends `x-powered-by` / `cache-control: no-store`);\nstatic frameworks serve their output dir as files. If unsure of a framework's output dir, check the\nframework's own build output, but **always pass `appType`/`outputDirectory` on the deploy call with the\nframework's correct values** — don't rely on auto-detection (it returns null → 403).\n\n> ## ⚠️ Next.js 16+: build with webpack, not Turbopack (recurring deploy failure)\n> Next.js 16 makes **Turbopack** the default bundler for `next build`. Turbopack's native (Rust)\n> compiler needs a newer **glibc** than the Hostinger build server ships, so the server build aborts\n> with a \"glibc too old\" error — and Turbopack **does not fall back** to webpack on its own, so the\n> deploy fails every time. **Fix: build with webpack.** On **Next.js 16+** the project's `build` script\n> in `package.json` must be `next build --webpack` (not plain `next build`); `buildScript` on the deploy\n> call stays `\"build\"` — that's what runs it. The `--webpack` flag exists only on Next.js 16+; on\n> **Next.js 15.x** leave the build script as plain `next build` (it already defaults to webpack).\n> **Confirm this in `package.json` before deploying** (the frontend step scaffolds it this way — see\n> `../06-frontend`). Alternative, only if you must avoid the `--webpack` flag: pin Next.js to `15.x`\n> (keeping plain `next build`), whose `next build` still defaults to webpack. Next.js only — other\n> frameworks are unaffected.\n\n## Step 1 — Pre-flight\n\n1. **Instance live (the deploy gate).** `provision.status == \"completed\"`, re-verified with\n `hosting_websites_list-setups { \"domain\": \"<domain>\" }`: deploy overwrites the site root, so it never\n trusts `state.json` alone.\n2. **Build output exists locally** (the framework's output dir — e.g. `.next/`, `dist/`, `.output/`) and\n the project builds clean (frontend/validate already did this). If not, go back.\n - **Next.js 16+:** `package.json`'s `build` script is `next build --webpack`, not plain `next build` —\n the server's glibc can't run Turbopack's native compiler (see the box above). On Next.js 15.x leave it\n as plain `next build`. Fix it here if it isn't.\n3. **Env vars (only if used).** Build-time vars (e.g. `NEXT_PUBLIC_*`) must be set **before** the build\n with `hosting_nodejs_replace-environment-variables` (full replace). Never put secrets in the repo or\n the archive. **Webapp with a database:** that full replace wipes the `DB_*`/`DATABASE_URL` that\n `hosting_databases_setup-website` injected (the generated password reads back masked), so set all app\n vars in the same replace and, if you can't, first reset the password with\n `hosting_databases_change-password` and resend every `DB_*`/`DATABASE_URL` — see\n `../03-provision/webapp/SKILL.md` Step 3.\n\n## Step 2 — Archive the source\n\nZip the project **source** with `package.json` at the archive root. **Include** the app/source,\n`public/`, the framework config (`next.config.mjs` / `astro.config.mjs`), `tsconfig.json`,\n`package.json`, `package-lock.json`, `.nvmrc`. **Exclude** `node_modules/`, the build output\n(`.next/`, `dist/`, `out/`), `.git/`, and any `.htaccess` — the server installs and builds from source.\n\n> ## ⚠️ Write the archive outside `~/.hostinger/` (the deploy refuses it there)\n> `hosting_deploy` refuses any `path` under a hidden folder at the top of the home directory\n> (`~/.hostinger`, `~/.config`, …) with *\"it is, or contains, a home, system or credentials location\"*:\n> a deploy publishes what it reads, so the connector protects dotfolders that can hold tokens. That\n> covers both `~/.hostinger/<domain>/app.zip` **and** the `~/.hostinger/<domain>/src/` directory\n> itself, so neither can be passed as `path`. Keep the source in the workspace as usual, but write the\n> archive to a fresh temp directory and pass that. Don't copy the archive somewhere else after a\n> refusal, and don't move `src/` out of the workspace.\n\n```bash\n# macOS/Linux — the OS temp dir is outside the home directory\ncd ~/.hostinger/<domain>/src\nDEPLOY_DIR=$(mktemp -d)\nzip -r -X \"$DEPLOY_DIR/app.zip\" . -x 'node_modules/*' '.next/*' 'dist/*' 'out/*' '.git/*' '**/.htaccess'\necho \"$DEPLOY_DIR/app.zip\" # the absolute path to pass as `path` in Step 3\n```\n\n```powershell\n# Windows PowerShell — %TEMP% sits under AppData, which the deploy also refuses; use a plain folder in your\n# profile, which only you can write to (never a folder under C:\\, which every local account can write into)\ncd $HOME\\.hostinger\\<domain>\\src\n$DeployDir = Join-Path $HOME \"hostinger-deploy\\<domain>\"; New-Item -ItemType Directory -Force $DeployDir | Out-Null\ntar -a -cf \"$DeployDir\\app.zip\" --exclude=node_modules --exclude=.next --exclude=dist --exclude=out --exclude=.git --exclude=.htaccess .\n\"$DeployDir\\app.zip\" # the absolute path to pass as `path` in Step 3\n```\n\nDelete the temp directory once the build in Step 4 is `completed`.\n\n## Step 3 — Deploy (one shot: settings + upload + build)\n\nPass the framework's build settings **inline** on the deploy call — there is no separate settings step.\n`path` is the absolute path of the archive from Step 2, in its temp directory. Never pass a path under\n`~/.hostinger/`, the `src/` directory included: the deploy refuses it (see Step 2).\n\n```json\n{ \"operation\": \"hosting_deploy\",\n \"params\": {\n \"domain\": \"<domain>\",\n \"path\": \"<absolute path printed in Step 2, e.g. /var/folders/…/tmp.XXXX/app.zip>\",\n \"type\": \"nodejs\",\n \"appType\": \"<framework appType, e.g. next>\",\n \"outputDirectory\": \"<framework output dir, e.g. .next>\",\n \"nodeVersion\": 22,\n \"buildScript\": \"build\",\n \"packageManager\": \"npm\",\n \"rootDirectory\": \".\" } }\n```\n\n`hosting_deploy` uploads the source and triggers `npm install` + the build **end-to-end** — do not call\nany upload or build operation yourself. It returns a build `buildUuid` at once. This **overwrites the\nwebsite root** (expected) and keeps `/cms` intact. `type` is always `\"nodejs\"` for a headless site —\n**never `\"static\"`** (see the gate below). Passing `appType`/`outputDirectory` explicitly is what\nkeeps the root from serving 403 (see the rule above).\n\n## Step 4 — Wait for the build and the live root\n\nWait for it in one command (see *Waiting on Hostinger* in the top-level `SKILL.md`):\n\n```bash\nnode <skill>/wait.mjs build <domain> <buildUuid>\n```\n\nIt waits for `hosting_get-deployment` to report the build `completed`, then for `https://<domain>/` to\nserve 200. What its result means here:\n\n- `ok: true`: the build is done and the root serves 200. Go on to Step 5.\n- `ok: false` with `build.state: failed`: read `log_tail` (the end of the build log) and `analysis`, fix\n locally, re-archive and redeploy (pass `fromLine` to `hosting_get-deployment` to read only new log\n output).\n- `ok: false` with `build settings missing`: the settings didn't take (the root would 403 for good). Fix\n the deploy call and redeploy.\n- `ok: false` with `timed_out`: `last_status` says whether the build was still running or the root\n wasn't serving 200 yet (see Step 5's note on a 403). For an SSR app that built but hangs,\n `hosting_nodejs_restart-application { username, domain }` recovers the server without a rebuild.\n\n## Step 5 — Verify the live site (all must pass)\n\n| Check | How | Expect |\n|---|---|---|\n| Root serves | `GET https://<domain>/` | **200** (not 403 / 404) |\n| SSR is live *(server frameworks)* | response headers | `x-powered-by` present, `cache-control: no-store` |\n| Static serves *(static frameworks)* | `GET https://<domain>/` | 200 HTML with real home content |\n| Key routes | a couple of planned routes + one deep route | 200, real content |\n| **Backend survived** *(WordPress / WooCommerce)* | `GET https://<domain>/cms/wp-json/wp/v2/posts?per_page=1` | 200 |\n\n> **Only WordPress (including WordPress + WooCommerce) has a `/cms` on the domain**, so the *Backend\n> survived* check is WordPress-only.\n> - **WordPress + WooCommerce:** also confirm the store survived the deploy —\n> `GET https://<domain>/cms/wp-json/wc/store/v1/products?per_page=1` → 200. There is nothing external to\n> link (no sales-channel step — that's Ecommerce only). **Checkout** stays owner-completed: when\n> `site.json → woocommerce.payment_enabled` is `false`, don't test `/checkout`; a catalog that serves 200\n> is a passing, complete deploy.\n> - **Ecommerce:** the backend is the **Hostinger Ecommerce store**, read over the external Storefront API\n> (`api-ecommerce.hostinger.com`) — nothing a deploy touches lives on this domain. There is no `/cms`;\n> confirm the storefront still fetches products as a frontend check if you like. **Only check that\n> checkout can be reached when `site.json → ecommerce.payment_enabled` is `true`** — on a free account\n> (`payment_enabled: false`) there is no checkout, so **skip that check entirely**; a catalog that serves\n> 200 is a passing, complete deploy. Never fail or hold the deploy because checkout is unavailable.\n> - **Webapp:** no content backend at all — the whole root is the app. Skip the check; `/cms` won't resolve.\n\nThe root check is already done when `wait.mjs build` returned `ok: true`; check the rest in one shell\ncommand (`curl` each URL). A `completed` build whose root 403s almost always means the build settings\nweren't passed to `hosting_deploy` (null settings) — pass them and redeploy. Caching can briefly serve a\nstale root; re-check after ~30 s.\n\n## Step 6 — Record `deploy.json`\n\n```json\n{\n \"build_uuid\": \"…\",\n \"stack\": \"nextjs\",\n \"rendering\": \"ssr\",\n \"app_type\": \"next\",\n \"output_directory\": \".next\",\n \"node_version\": 22,\n \"method\": \"hosting_deploy (type: nodejs, settings passed inline)\",\n \"deployed_at\": \"…\",\n \"verified_at\": \"…\",\n \"urls\": { \"live\": \"https://<domain>\", \"cms\": \"https://<domain>/cms\" },\n \"checks\": { \"root\": 200, \"ssr_headers\": true, \"backend_preserved\": true }\n}\n```\n\nNo secrets in `deploy.json`. The `urls.cms` field and the `backend_preserved` check above are\n**WordPress-only** — adjust the record to the backend:\n\n- **WordPress:** keep `urls.cms` and `checks.backend_preserved` as shown.\n- **WordPress + WooCommerce:** keep `urls.cms` + `checks.backend_preserved` (it *is* WordPress), and add\n `urls.store_api` from `site.json`; no external link step.\n- **Ecommerce:** omit `urls.cms`/`backend_preserved`; record the store URLs instead (`urls.storefront_api`,\n `urls.store_admin` from `site.json`) and link the custom sales channel to the live domain —\n `ecommerce_sales-channels_update { store_id, sales_channel_id, url }` (see the ecommerce provision step).\n- **Webapp:** omit `urls.cms` and `backend_preserved` entirely — `urls` is just `{ \"live\": \"https://<domain>\" }`.\n\nUpdate `state.json` (`stage` → `done`), then continue to Step 9 (done).\n\n---\n\n## Gate — a headless deploy is always `type: \"nodejs\"`, never `type: \"static\"`\n\n**Hard rule, check it before every deploy:** a headless site is deployed **only** with\n`type: \"nodejs\"` plus the explicit `appType`/`outputDirectory`/`nodeVersion`/`buildScript`/\n`packageManager`/`rootDirectory` from the table. **Never pass `type: \"static\"` on a headless site** — it\nserves the build as plain files over the document root and **overwrites `/cms`**, destroying the\nWordPress backend (this is the \"accidentally cleared cms\" failure). A static-built SPA (Vite/React/Vue)\nstill deploys as `type: \"nodejs\"` with its `appType` — the framework's static output is served correctly\nthat way, and `/cms` is preserved. If a flow ever tells you to deploy a headless site as `type:\n\"static\"`, you are on a stale/legacy skill copy — stop and update the skill (`npx skills update` or the plugin update for an installed skill, or connect's Phase 1 npm install for a web-installed `headless/` folder).\n\n`hosting_deploy` on the local server is the only path: give its `path` a local **archive** (`zip`, `tar`,\n`tar.gz`, `tgz`, `7z`) written outside `~/.hostinger/` as in Step 2. There is no separate upload or\nbuild step to fall back to.\n"
280
+ "text": "---\nname: headless-step-deploy\ndescription: \"Step 8 (deploy) of the Hostinger Headless flow. Archives the frontend source, then deploys through the connector's one-shot hosting_deploy (type nodejs — it uploads the source, runs npm install and the build on the server) with the chosen framework's build settings (appType + output dir) passed explicitly, polls to completion with hosting_get-deployment, verifies the live site and that the backend survived, then writes deploy.json.\"\n---\n\n# headless-step-deploy\n\nShip the frontend to the live domain through the Hostinger connector. You hand the connector a zip of\nthe project **source** and it uploads it, runs `npm install` + the build script on the server, and\nserves the result. **Out:** `deploy.json`.\n\nThis is an **instance-barrier** step: run it only after `provision.status == \"completed\"` and validate\n(if run) has passed. All operations go through the connector (`execute`).\n\n> **Connected to the hosted server (`mcp.hostinger.com`)?** Its `hosting_deploy` deploys Git\n> repositories only and cannot read a local `path`. Run this step through the local\n> `hostinger-api-mcp` server instead (see the connect step:\n> `npx --yes --prefix=\"$HOME\" --package=@hostinger/mcp@latest hostinger-api-mcp`).\n\n> **With a shell and the Hostinger CLI installed**, the same deploy is one command, with the build\n> settings passed exactly as the table below says. Run it from the project, since it links the current directory:\n> `cd ~/.hostinger/<domain>/src && hostinger up --domain <domain> --type nodejs --app-type <appType> --output-directory <dir> --build-script build --node-version 22 --package-manager npm --yes --format json`.\n> It uploads, builds, waits and returns `data.buildUuid` and `data.buildState`; add `--no-wait` to\n> return at once and wait with `node <skill>/wait.mjs build <domain> <uuid>`. Then continue at\n> \"Verify\" below. Recent CLI versions see that the site is headless (`data.headless: true`) and\n> deploy only its Node.js frontend, leaving `/cms` alone; still pass the settings above.\n\n**In:** the project in `~/.hostinger/<domain>/src/`, `site.json` (`username`, `urls`), `state.json`\n(`frontend.framework`, `frontend.rendering`).\n\n> ## ⚠️ The one rule that makes this work: pass build settings explicitly\n> If you let `hosting_deploy` **auto-detect** build settings, for a headless site that detection returns\n> **null** (`appType:null`, `outputDirectory:null`, Node 20) — the build \"completes\" but the **root\n> serves 403** (no `appType`/output → the vhost serves a directory with no entry). This is reproducible.\n> **Always pass `appType`, `outputDirectory`, `nodeVersion`, `buildScript`, `packageManager` and\n> `rootDirectory` on the `hosting_deploy` call** with the framework's values; the deploy then resolves\n> them and serves correctly, and `/cms` is preserved.\n\n## Build configurations\n\nMap `frontend.framework` (from `state.json`) to its `appType` + build output directory, and pass those\non the `hosting_deploy` call. `appType` is the framework's value, e.g. `next`, `nuxt`, `astro`,\n`svelte-kit`, `nest`, `express`, `fastify`, `react`, `create-react-app`, `gatsby`, `vite`, `vue`,\n`angular`, `parcel`, `svelte`, `other`.\n\nCommon cases (all `nodeVersion: 22`, `buildScript: \"build\"`, `packageManager: \"npm\"`, `rootDirectory: \".\"`):\n\n| Framework / mode | Local config | `appType` | `outputDirectory` |\n|---|---|---|---|\n| **Next.js — SSR** *(default)* | `output: 'standalone'` | `next` | `.next` |\n| **Nuxt — SSR** | default | `nuxt` | `.output` |\n| **SvelteKit — SSR** | node adapter | `svelte-kit` | `build` |\n| **Astro — static** | `output: 'static'` | `astro` | `dist` |\n| **Vite / React / Vue — static (SPA)** | — | `vite` (or `react`/`vue`) | `dist` |\n| **Express / NestJS / Fastify — server** | — | `express`/`nest`/`fastify` | build output dir |\n\nSSR/server frameworks run a persistent Node process (root sends `x-powered-by` / `cache-control: no-store`);\nstatic frameworks serve their output dir as files. If unsure of a framework's output dir, check the\nframework's own build output, but **always pass `appType`/`outputDirectory` on the deploy call with the\nframework's correct values** — don't rely on auto-detection (it returns null → 403).\n\n> ## ⚠️ Next.js 16+: build with webpack, not Turbopack (recurring deploy failure)\n> Next.js 16 makes **Turbopack** the default bundler for `next build`. Turbopack's native (Rust)\n> compiler needs a newer **glibc** than the Hostinger build server ships, so the server build aborts\n> with a \"glibc too old\" error — and Turbopack **does not fall back** to webpack on its own, so the\n> deploy fails every time. **Fix: build with webpack.** On **Next.js 16+** the project's `build` script\n> in `package.json` must be `next build --webpack` (not plain `next build`); `buildScript` on the deploy\n> call stays `\"build\"` — that's what runs it. The `--webpack` flag exists only on Next.js 16+; on\n> **Next.js 15.x** leave the build script as plain `next build` (it already defaults to webpack).\n> **Confirm this in `package.json` before deploying** (the frontend step scaffolds it this way — see\n> `../06-frontend`). Alternative, only if you must avoid the `--webpack` flag: pin Next.js to `15.x`\n> (keeping plain `next build`), whose `next build` still defaults to webpack. Next.js only — other\n> frameworks are unaffected.\n\n## Step 1 — Pre-flight\n\n1. **Instance live (the deploy gate).** `provision.status == \"completed\"`, re-verified with\n `hosting_websites_list-setups { \"domain\": \"<domain>\" }`: deploy overwrites the site root, so it never\n trusts `state.json` alone.\n2. **Build output exists locally** (the framework's output dir — e.g. `.next/`, `dist/`, `.output/`) and\n the project builds clean (frontend/validate already did this). If not, go back.\n - **Next.js 16+:** `package.json`'s `build` script is `next build --webpack`, not plain `next build` —\n the server's glibc can't run Turbopack's native compiler (see the box above). On Next.js 15.x leave it\n as plain `next build`. Fix it here if it isn't.\n3. **Env vars (only if used).** Build-time vars (e.g. `NEXT_PUBLIC_*`) must be set **before** the build\n with `hosting_nodejs_replace-environment-variables` (full replace). Never put secrets in the repo or\n the archive. **Webapp with a database:** that full replace wipes the `DB_*`/`DATABASE_URL` that\n `hosting_databases_setup-website` injected (the generated password reads back masked), so set all app\n vars in the same replace and, if you can't, first reset the password with\n `hosting_databases_change-password` and resend every `DB_*`/`DATABASE_URL` — see\n `../03-provision/webapp/SKILL.md` Step 3.\n\n## Step 2 — Archive the source\n\nZip the project **source** with `package.json` at the archive root. **Include** the app/source,\n`public/`, the framework config (`next.config.mjs` / `astro.config.mjs`), `tsconfig.json`,\n`package.json`, `package-lock.json`, `.nvmrc`. **Exclude** `node_modules/`, the build output\n(`.next/`, `dist/`, `out/`), `.git/`, and any `.htaccess` — the server installs and builds from source.\n\n> ## ⚠️ Write the archive outside `~/.hostinger/` (the deploy refuses it there)\n> `hosting_deploy` refuses any `path` under a hidden folder at the top of the home directory\n> (`~/.hostinger`, `~/.config`, …) with *\"it is, or contains, a home, system or credentials location\"*:\n> a deploy publishes what it reads, so the connector protects dotfolders that can hold tokens. That\n> covers both `~/.hostinger/<domain>/app.zip` **and** the `~/.hostinger/<domain>/src/` directory\n> itself, so neither can be passed as `path`. Keep the source in the workspace as usual, but write the\n> archive to a fresh temp directory and pass that. Don't copy the archive somewhere else after a\n> refusal, and don't move `src/` out of the workspace.\n\n```bash\n# macOS/Linux — the OS temp dir is outside the home directory\ncd ~/.hostinger/<domain>/src\nDEPLOY_DIR=$(mktemp -d)\nzip -r -X \"$DEPLOY_DIR/app.zip\" . -x 'node_modules/*' '.next/*' 'dist/*' 'out/*' '.git/*' '**/.htaccess'\necho \"$DEPLOY_DIR/app.zip\" # the absolute path to pass as `path` in Step 3\n```\n\n```powershell\n# Windows PowerShell — %TEMP% sits under AppData, which the deploy also refuses; use a plain folder in your\n# profile, which only you can write to (never a folder under C:\\, which every local account can write into)\ncd $HOME\\.hostinger\\<domain>\\src\n$DeployDir = Join-Path $HOME \"hostinger-deploy\\<domain>\"; New-Item -ItemType Directory -Force $DeployDir | Out-Null\ntar -a -cf \"$DeployDir\\app.zip\" --exclude=node_modules --exclude=.next --exclude=dist --exclude=out --exclude=.git --exclude=.htaccess .\n\"$DeployDir\\app.zip\" # the absolute path to pass as `path` in Step 3\n```\n\nDelete the temp directory once the build in Step 4 is `completed`.\n\n## Step 3 — Deploy (one shot: settings + upload + build)\n\nPass the framework's build settings **inline** on the deploy call — there is no separate settings step.\n`path` is the absolute path of the archive from Step 2, in its temp directory. Never pass a path under\n`~/.hostinger/`, the `src/` directory included: the deploy refuses it (see Step 2).\n\n```json\n{ \"operation\": \"hosting_deploy\",\n \"params\": {\n \"domain\": \"<domain>\",\n \"path\": \"<absolute path printed in Step 2, e.g. /var/folders/…/tmp.XXXX/app.zip>\",\n \"type\": \"nodejs\",\n \"appType\": \"<framework appType, e.g. next>\",\n \"outputDirectory\": \"<framework output dir, e.g. .next>\",\n \"nodeVersion\": 22,\n \"buildScript\": \"build\",\n \"packageManager\": \"npm\",\n \"rootDirectory\": \".\" } }\n```\n\n`hosting_deploy` uploads the source and triggers `npm install` + the build **end-to-end** — do not call\nany upload or build operation yourself. It returns a build `buildUuid` at once. This **overwrites the\nwebsite root** (expected) and keeps `/cms` intact. `type` is always `\"nodejs\"` for a headless site —\n**never `\"static\"`** (see the gate below). Passing `appType`/`outputDirectory` explicitly is what\nkeeps the root from serving 403 (see the rule above).\n\n## Step 4 — Wait for the build and the live root\n\nWait for it in one command (see *Waiting on Hostinger* in the top-level `SKILL.md`):\n\n```bash\nnode <skill>/wait.mjs build <domain> <buildUuid>\n```\n\nIt waits for `hosting_get-deployment` to report the build `completed`, then for `https://<domain>/` to\nserve 200. What its result means here:\n\n- `ok: true`: the build is done and the root serves 200. Go on to Step 5.\n- `ok: false` with `build.state: failed`: read `log_tail` (the end of the build log) and `analysis`, fix\n locally, re-archive and redeploy (pass `fromLine` to `hosting_get-deployment` to read only new log\n output).\n- `ok: false` with `build settings missing`: the settings didn't take (the root would 403 for good). Fix\n the deploy call and redeploy.\n- `ok: false` with `timed_out`: `last_status` says whether the build was still running or the root\n wasn't serving 200 yet (see Step 5's note on a 403). For an SSR app that built but hangs,\n `hosting_nodejs_restart-application { username, domain }` recovers the server without a rebuild.\n\n## Step 5 — Verify the live site (all must pass)\n\n| Check | How | Expect |\n|---|---|---|\n| Root serves | `GET https://<domain>/` | **200** (not 403 / 404) |\n| SSR is live *(server frameworks)* | response headers | `x-powered-by` present, `cache-control: no-store` |\n| Static serves *(static frameworks)* | `GET https://<domain>/` | 200 HTML with real home content |\n| Key routes | a couple of planned routes + one deep route | 200, real content |\n| **Backend survived** *(WordPress / WooCommerce)* | `GET https://<domain>/cms/wp-json/wp/v2/posts?per_page=1` | 200 |\n\n> **Only WordPress (including WordPress + WooCommerce) has a `/cms` on the domain**, so the *Backend\n> survived* check is WordPress-only.\n> - **WordPress + WooCommerce:** also confirm the store survived the deploy —\n> `GET https://<domain>/cms/wp-json/wc/store/v1/products?per_page=1` → 200. There is nothing external to\n> link (no sales-channel step — that's Ecommerce only). **Checkout** stays owner-completed: when\n> `site.json → woocommerce.payment_enabled` is `false`, don't test `/checkout`; a catalog that serves 200\n> is a passing, complete deploy.\n> - **Ecommerce:** the backend is the **Hostinger Ecommerce store**, read over the external Storefront API\n> (`api-ecommerce.hostinger.com`) — nothing a deploy touches lives on this domain. There is no `/cms`;\n> confirm the storefront still fetches products as a frontend check if you like. **Only check that\n> checkout can be reached when `site.json → ecommerce.payment_enabled` is `true`** — on a free account\n> (`payment_enabled: false`) there is no checkout, so **skip that check entirely**; a catalog that serves\n> 200 is a passing, complete deploy. Never fail or hold the deploy because checkout is unavailable.\n> - **Webapp:** no content backend at all — the whole root is the app. Skip the check; `/cms` won't resolve.\n\nThe root check is already done when `wait.mjs build` returned `ok: true`; check the rest in one shell\ncommand (`curl` each URL). A `completed` build whose root 403s almost always means the build settings\nweren't passed to `hosting_deploy` (null settings) — pass them and redeploy. Caching can briefly serve a\nstale root; re-check after ~30 s.\n\n## Step 6 — Record `deploy.json`\n\n```json\n{\n \"build_uuid\": \"…\",\n \"stack\": \"nextjs\",\n \"rendering\": \"ssr\",\n \"app_type\": \"next\",\n \"output_directory\": \".next\",\n \"node_version\": 22,\n \"method\": \"hosting_deploy (type: nodejs, settings passed inline)\",\n \"deployed_at\": \"…\",\n \"verified_at\": \"…\",\n \"urls\": { \"live\": \"https://<domain>\", \"cms\": \"https://<domain>/cms\" },\n \"checks\": { \"root\": 200, \"ssr_headers\": true, \"backend_preserved\": true }\n}\n```\n\nNo secrets in `deploy.json`. The `urls.cms` field and the `backend_preserved` check above are\n**WordPress-only** — adjust the record to the backend:\n\n- **WordPress:** keep `urls.cms` and `checks.backend_preserved` as shown.\n- **WordPress + WooCommerce:** keep `urls.cms` + `checks.backend_preserved` (it *is* WordPress), and add\n `urls.store_api` from `site.json`; no external link step.\n- **Ecommerce:** omit `urls.cms`/`backend_preserved`; record the store URLs instead (`urls.storefront_api`,\n `urls.store_admin` from `site.json`) and link the custom sales channel to the live domain —\n `ecommerce_sales-channels_update { store_id, sales_channel_id, url }` (see the ecommerce provision step).\n- **Webapp:** omit `urls.cms` and `backend_preserved` entirely — `urls` is just `{ \"live\": \"https://<domain>\" }`.\n\nUpdate `state.json` (`stage` → `done`), then continue to Step 9 (done).\n\n---\n\n## Gate — a headless deploy is always `type: \"nodejs\"`, never `type: \"static\"`\n\n**Hard rule, check it before every deploy:** a headless site is deployed **only** with\n`type: \"nodejs\"` plus the explicit `appType`/`outputDirectory`/`nodeVersion`/`buildScript`/\n`packageManager`/`rootDirectory` from the table. **Never pass `type: \"static\"` on a headless site** — it\nserves the build as plain files over the document root and **overwrites `/cms`**, destroying the\nWordPress backend (this is the \"accidentally cleared cms\" failure). A static-built SPA (Vite/React/Vue)\nstill deploys as `type: \"nodejs\"` with its `appType` — the framework's static output is served correctly\nthat way, and `/cms` is preserved. If a flow ever tells you to deploy a headless site as `type:\n\"static\"`, you are on a stale/legacy skill copy — stop and update the skill (`npx skills update` or the plugin update for an installed skill, or connect's Phase 1 npm install for a web-installed `headless/` folder).\n\n`hosting_deploy` on the local server is the only path: give its `path` a local **archive** (`zip`, `tar`,\n`tar.gz`, `tgz`, `7z`) written outside `~/.hostinger/` as in Step 2. There is no separate upload or\nbuild step to fall back to.\n"
281
281
  },
282
282
  {
283
283
  "uri": "skill://hostinger-headless/steps/09-done/SKILL.md",
@@ -311,10 +311,10 @@ const skills: Skill[] = [
311
311
  "resources": [
312
312
  {
313
313
  "uri": "skill://maintain-wordpress/SKILL.md",
314
- "digest": "sha256:a2d9ba37087c2579b98500f754d70be233b85de18410af30d528363c2dc536b1",
315
- "size": 5573,
314
+ "digest": "sha256:ed3f65f4c1ff4253f5cb43c4d680357018f7dd8cc9e8508bc19e7f9cb7b3adcb",
315
+ "size": 5717,
316
316
  "mimeType": "text/markdown",
317
- "text": "---\nname: maintain-wordpress\ndescription: \"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.\"\n---\n\n# Maintain WordPress\n\nCheck first, report, then update only what the user approves — one site at a time, proving each still loads before moving to the next.\n\n## Calling the operations\n\n- 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\").\n- 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.\n- 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.\n\n## 1. Find the installs\n\n- 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.\n- Keep `id` (the `software` parameter of every `wordpress_*` call), `username`, `domain`, `directory`, `is_valid` and `validation_error`.\n- 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.\n\n## 2. Check (read-only)\n\nPer install:\n\n- `wordpress_installations_show-core-version` — core version and the known vulnerabilities that affect it.\n- `wordpress_installations_list-core-updates` — available core versions.\n- `wordpress_plugins_list-installed` — `status`, `update` (the newer version, when there is one) and `vulnerabilities[]` with `fixed_in`.\n- `wordpress_themes_list-installed` — the same for themes.\n\nFour 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.\n\n## 3. Report before changing anything\n\n```\n## example.com (WordPress 6.8.1)\nVulnerable: contact-form-x 5.2 → fixed in 5.3 (update available)\nVulnerable, inactive: old-slider 1.0 — no fix; uninstall recommended\nUpdates: core 6.8.1 → 6.8.3 (minor), 4 plugins, 1 theme\n```\n\nVulnerable 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.\n\n## 4. Back up first\n\nThe 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.\n\n## 5. Update, one site at a time\n\n1. Plugins that fix a vulnerability — `wordpress_plugins_update` with their slugs.\n2. The remaining approved plugins, then themes with `wordpress_themes_update`.\n3. Core with `wordpress_installations_update-core`: `minor: true` for patch releases; a major `version` only when the user asks for it.\n\nFor 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.\n\nAfter each site:\n\n1. `wordpress_litespeed-cache_purge-lite-speed`.\n2. `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.\n3. `wordpress_installations_check-if-are-valid` with `force: true`.\n\nWhen 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.\n\n## 6. Performance and access\n\n- Page cache: `wordpress_litespeed-cache_show-lite-speed-status`; purge after design or content changes.\n- Object cache: `wordpress_object-cache_show-memcached-status`, then `wordpress_object-cache_toggle-memcached` with `enabled: true` — the usual quick speed-up.\n- 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`.\n- wp-admin: `wordpress_login_create-links` returns temporary one-click login links. Give them to the user only; never store or reuse them.\n- Hostinger's own plugins update with `wordpress_plugins_update-hostinger` (`slug`).\n\n## Many sites\n\nTell 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.\n"
317
+ "text": "---\nname: maintain-wordpress\ndescription: \"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.\"\n---\n\n# Maintain WordPress\n\nCheck first, report, then update only what the user approves — one site at a time, proving each still loads before moving to the next.\n\n## Calling the operations\n\n- 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\").\n- 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.\n- 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.\n\n## 1. Find the installs\n\n- 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.\n- Keep `id` (the `software` parameter of every `wordpress_*` call), `username`, `domain`, `directory`, `is_valid` and `validation_error`.\n- 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.\n\n## 2. Check (read-only)\n\nPer install:\n\n- `wordpress_installations_show-core-version` — core version and the known vulnerabilities that affect it.\n- `wordpress_installations_list-core-updates` — available core versions.\n- `wordpress_plugins_list-installed` — `status`, `update` (the newer version, when there is one) and `vulnerabilities[]` with `fixed_in`.\n- `wordpress_themes_list-installed` — the same for themes.\n\nFour 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.\n\n## 3. Report before changing anything\n\n```\n## example.com (WordPress 6.8.1)\nVulnerable: contact-form-x 5.2 → fixed in 5.3 (update available)\nVulnerable, inactive: old-slider 1.0 — no fix; uninstall recommended\nUpdates: core 6.8.1 → 6.8.3 (minor), 4 plugins, 1 theme\n```\n\nVulnerable 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.\n\n## 4. Back up first\n\nThe 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.\n\n## 5. Update, one site at a time\n\n1. Plugins that fix a vulnerability — `wordpress_plugins_update` with their slugs.\n2. The remaining approved plugins, then themes with `wordpress_themes_update`.\n3. Core with `wordpress_installations_update-core`: `minor: true` for patch releases; a major `version` only when the user asks for it.\n\nFor 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.\n\nAfter each site:\n\n1. `wordpress_litespeed-cache_purge-lite-speed`.\n2. `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.\n3. `wordpress_installations_check-if-are-valid` with `force: true`.\n\nWhen 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.\n\n## 6. Performance and access\n\n- Page cache: `wordpress_litespeed-cache_show-lite-speed-status`; purge after design or content changes.\n- Object cache: `wordpress_object-cache_show-memcached-status`, then `wordpress_object-cache_toggle-memcached` with `enabled: true` — the usual quick speed-up.\n- 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 switching with `hosting_php_update` (`version`). The switch resets extensions and options to their defaults, so pass the custom ones from `hosting_php_get` in the same call.\n- wp-admin: `wordpress_login_create-links` returns temporary one-click login links. Give them to the user only; never store or reuse them.\n- Hostinger's own plugins update with `wordpress_plugins_update-hostinger` (`slug`).\n\n## Many sites\n\nTell 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.\n"
318
318
  }
319
319
  ]
320
320
  },
@@ -343,10 +343,10 @@ const skills: Skill[] = [
343
343
  "resources": [
344
344
  {
345
345
  "uri": "skill://troubleshoot-website/SKILL.md",
346
- "digest": "sha256:7b44bc7cb4f0d92c6837e6a3ea6051a5b2bbad1e70671b8368c2ef7d9ad1b05f",
347
- "size": 9723,
346
+ "digest": "sha256:12e103ace83cf7d5e32968138eef34e33621256d7cefc9ab9fd6eda556adc18d",
347
+ "size": 9855,
348
348
  "mimeType": "text/markdown",
349
- "text": "---\nname: troubleshoot-website\ndescription: \"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.\"\n---\n\n# Troubleshoot a website\n\nOne 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.\n\n## Calling the operations\n\n- 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\").\n- Batch reads with `multi-execute` and pass values between steps with `$steps.<i>.<path>`, e.g. `\"$steps.0.data.0.username\"`. A batch stops at its first failure.\n- 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.\n\n## 1. Find the website\n\nRun both lookups, and keep the result for every later call:\n\n- `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`.\n- `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`.\n\nNeither finds it: the domain is not a website on this account — say so and show where it points (step 2).\n\n## 2. Look at it from outside\n\nRun these alongside step 1:\n\n```bash\ncurl -sS -o /dev/null -w \"%{http_code} ttfb=%{time_starttransfer}s %{redirect_url}\\n\" https://DOMAIN/\ncurl -sSI https://DOMAIN/\ndig +short NS DOMAIN; dig +short A DOMAIN; dig +short CAA DOMAIN\n```\n\n- 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.\n- Certificate or TLS errors → [SSL](#ssl).\n- `5xx`, a blank page or an error page → [By website type](#3-by-website-type).\n- `200` with old content → cache: `hosting_cache_clear` (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`.\n- 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.\n\n## 3. By website type\n\n`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.\n\n### Node.js (`nodejs`)\n\nBatch `hosting_get-deployment` (the latest build with its log, failure analysis and stored settings), `hosting_nodejs_runtime-logs` (`period: \"1h\"`, `levels: [\"ERROR\", \"WARN\"]`) and `hosting_nodejs_list-environment-variables` (keys only; values always come back masked).\n\n- Latest build `failed`: `hosting_get-deployment` already holds the `analysis` (fetched once per build — the API re-runs it on every call and allows 5 a minute). A wrongly detected framework, output directory or missing `entry_file` is the usual cause: compare the returned `buildSettings` with the project and deploy again with corrected `appType`, `outputDirectory`, `buildScript` or `entryFile`; a Git deploy stores them, so pushes build correctly too. A site deployed on the hosted server from uploaded files has no `hosting_deploy` to repeat: rebuild through `hosting_nodejs_start-build` with the corrected values and store them with `hosting_nodejs_update-build-settings`.\n- 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`.\n- Hung process: `hosting_nodejs_restart-application` (restarts without rebuilding).\n\n### WordPress (`wordpress`)\n\n`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`.\n\n- Invalid install: `validation_error` names the problem.\n- 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.\n- Maintenance page stuck: `wordpress_maintenance_toggle` with `enabled: false`.\n- \"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`.\n- Memory or upload-size errors → [PHP](#php).\n\n### Static or PHP (`other`)\n\n- `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.\n- Read `.htaccess` or other config with `hosting_files_website-content`.\n- PHP errors → [PHP](#php).\n\n### PHP\n\n`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.\n\n### Agency Plan sites\n\n`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.\n\n## SSL\n\nShared and Cloud, `ssl` in `hosting_websites_get-settings`:\n\n- `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 `hosting_websites_get-settings` until `ssl.status` is `active` or `failed`.\n- `installing` or `waiting_for_retry`: wait — another install request returns 422.\n- Free `*.hostingersite.com` subdomains use a platform certificate; installing returns 422 by design.\n- Browser still warns while the certificate is `active`: turn on the redirect with `hosting_websites_update-settings` (`httpsRedirect: true`) when `is_https_redirect_enabled` is false, otherwise the page loads `http://` assets (mixed content) that the app has to change.\n\nAgency, `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.\n\n## Slow site\n\n- 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.\n- Other sites: server caching may be off or development mode left on. `hosting_websites_get-settings` shows both in `cache` (`is_enabled`, `is_cacheless_mode_enabled`); fix them with `hosting_websites_update-settings` (`cache: true`, `cachelessMode: false`). Development mode takes a few minutes to turn off.\n- PHP 7.x runs markedly slower than 8.x; propose a supported 8.x version from `hosting_php_get` when the app allows it.\n- Agency: `agency-hosting_metrics_list-order-resource-usage` with `time_frame_hours: 24` shows CPU, memory and processes per website against the plan quota.\n- 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.\n\n## Apply the fix\n\nState 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.\n\n## Not available through the API\n\n- Website backups and restores — hPanel's Backups page.\n- CPU, memory and process usage for Shared and Cloud plans.\n- PHP error and access logs.\n"
349
+ "text": "---\nname: troubleshoot-website\ndescription: \"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.\"\n---\n\n# Troubleshoot a website\n\nOne 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.\n\n## Calling the operations\n\n- 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\").\n- Batch reads with `multi-execute` and pass values between steps with `$steps.<i>.<path>`, e.g. `\"$steps.0.data.0.username\"`. A batch stops at its first failure.\n- 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.\n\n## 1. Find the website\n\nRun both lookups, and keep the result for every later call:\n\n- `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`.\n- `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`.\n\nNeither finds it: the domain is not a website on this account — say so and show where it points (step 2).\n\n## 2. Look at it from outside\n\nRun these alongside step 1:\n\n```bash\ncurl -sS -o /dev/null -w \"%{http_code} ttfb=%{time_starttransfer}s %{redirect_url}\\n\" https://DOMAIN/\ncurl -sSI https://DOMAIN/\ndig +short NS DOMAIN; dig +short A DOMAIN; dig +short CAA DOMAIN\n```\n\n- 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.\n- Certificate or TLS errors → [SSL](#ssl).\n- `5xx`, a blank page or an error page → [By website type](#3-by-website-type).\n- `200` with old content → cache: `hosting_cache_clear` (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`.\n- 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.\n\n## 3. By website type\n\n`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.\n\n### Node.js (`nodejs`)\n\nBatch `hosting_get-deployment` (the latest build with its log, failure analysis and stored settings), `hosting_nodejs_runtime-logs` (`period: \"1h\"`, `levels: [\"ERROR\", \"WARN\"]`) and `hosting_nodejs_list-environment-variables` (keys only; values always come back masked).\n\n- Latest build `failed`: `hosting_get-deployment` already holds the `analysis` (fetched once per build — the API re-runs it on every call and allows 5 a minute). A wrongly detected framework, output directory or missing `entry_file` is the usual cause: compare the returned `buildSettings` with the project and deploy again with corrected `appType`, `outputDirectory`, `buildScript` or `entryFile`; a Git deploy stores them, so pushes build correctly too. A site deployed on the hosted server from uploaded files has no `hosting_deploy` to repeat: rebuild through `hosting_nodejs_start-build` with the corrected values and store them with `hosting_nodejs_update-build-settings`.\n- 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`.\n- Hung process: `hosting_nodejs_restart-application` (restarts without rebuilding).\n\n### WordPress (`wordpress`)\n\n`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`.\n\n- Invalid install: `validation_error` names the problem.\n- 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.\n- Maintenance page stuck: `wordpress_maintenance_toggle` with `enabled: false`.\n- \"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`.\n- Memory or upload-size errors → [PHP](#php).\n\n### Static or PHP (`other`)\n\n- `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.\n- Read `.htaccess` or other config with `hosting_files_website-content`.\n- PHP errors → [PHP](#php).\n\n### PHP\n\n`hosting_php_get` returns every extension and option (around 25 KB), so call it only for PHP problems. `hosting_php_update` changes the version, extensions and options in one call. Switching the version resets the extensions and options to their defaults, so pass the ones to keep in the same call. Option values are silently capped 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.\n\n### Agency Plan sites\n\n`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.\n\n## SSL\n\nShared and Cloud, `ssl` in `hosting_websites_get-settings`:\n\n- `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 `hosting_websites_get-settings` until `ssl.status` is `active` or `failed`.\n- `installing` or `waiting_for_retry`: wait — another install request returns 422.\n- Free `*.hostingersite.com` subdomains use a platform certificate; installing returns 422 by design.\n- Browser still warns while the certificate is `active`: turn on the redirect with `hosting_websites_update-settings` (`httpsRedirect: true`) when `is_https_redirect_enabled` is false, otherwise the page loads `http://` assets (mixed content) that the app has to change.\n\nAgency, `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.\n\n## Slow site\n\n- 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.\n- Other sites: server caching may be off or development mode left on. `hosting_websites_get-settings` shows both in `cache` (`is_enabled`, `is_cacheless_mode_enabled`); fix them with `hosting_websites_update-settings` (`cache: true`, `cachelessMode: false`). Development mode takes a few minutes to turn off.\n- PHP 7.x runs markedly slower than 8.x; propose a supported 8.x version from `hosting_php_get` when the app allows it.\n- Agency: `agency-hosting_metrics_list-order-resource-usage` with `time_frame_hours: 24` shows CPU, memory and processes per website against the plan quota.\n- 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.\n\n## Apply the fix\n\nState 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.\n\n## Not available through the API\n\n- Website backups and restores — hPanel's Backups page.\n- CPU, memory and process usage for Shared and Cloud plans.\n- PHP error and access logs.\n"
350
350
  }
351
351
  ]
352
352
  }
@@ -349,6 +349,76 @@ export default [
349
349
  "handlerMethod": "handleHostingCacheClear",
350
350
  "group": "hosting"
351
351
  },
352
+ {
353
+ "name": "hosting_php_update",
354
+ "title": "Update website PHP version, extensions and options",
355
+ "annotations": {
356
+ "title": "Update website PHP version, extensions and options",
357
+ "readOnlyHint": false,
358
+ "destructiveHint": true
359
+ },
360
+ "topic": "hosting",
361
+ "description": "Change a website's PHP version, extensions and php.ini options in one call. Pass only what to change, at least one of version, resetExtensions, extensions and options. Steps run in the order version, resetExtensions, extensions, options, and the call stops at the first failure. applied lists the steps that ran. When a step fails, the call returns an error whose text is the same JSON: failed names the step and why, and skipped lists the ones not attempted; a multi-execute batch stops there too. WARNING: changing the version resets the website's extensions (PHP 8.0 and later) and its options to their defaults, so pass the extensions and options to keep in the same call; they are applied after the version. Option values above the plan limit are capped silently: read the applied values back with hosting_php_get, which also lists the available versions, extensions and options. Works for Shared and Cloud hosting websites.",
362
+ "method": "",
363
+ "path": "",
364
+ "inputSchema": {
365
+ "type": "object",
366
+ "properties": {
367
+ "domain": {
368
+ "type": "string",
369
+ "description": "Domain name of the website, exactly as listed on the account and without www. (e.g., example.com)"
370
+ },
371
+ "version": {
372
+ "type": "string",
373
+ "description": "PHP version to switch to, as major.minor (e.g. 8.3). Must be one of the supported versions in hosting_php_get."
374
+ },
375
+ "resetExtensions": {
376
+ "type": "boolean",
377
+ "description": "true resets every PHP extension to its default state; runs after version and before extensions."
378
+ },
379
+ "extensions": {
380
+ "type": "object",
381
+ "description": "Extensions to enable and disable, by name as hosting_php_get lists them. Pass at least one of enable and disable.",
382
+ "properties": {
383
+ "enable": {
384
+ "type": "array",
385
+ "items": {
386
+ "type": "string"
387
+ },
388
+ "description": "Extensions to enable."
389
+ },
390
+ "disable": {
391
+ "type": "array",
392
+ "items": {
393
+ "type": "string"
394
+ },
395
+ "description": "Extensions to disable."
396
+ }
397
+ }
398
+ },
399
+ "options": {
400
+ "type": "object",
401
+ "description": "php.ini options to change, keyed by name as hosting_php_get lists them (e.g. {\"memory_limit\": \"512M\", \"max_execution_time\": 300}). Only include options to change.",
402
+ "additionalProperties": {
403
+ "type": [
404
+ "string",
405
+ "integer"
406
+ ]
407
+ }
408
+ }
409
+ },
410
+ "required": [
411
+ "domain"
412
+ ]
413
+ },
414
+ "security": [],
415
+ "custom": true,
416
+ "replacesHidden": true,
417
+ "templateFile": "update-php.template.js",
418
+ "templateFileTS": "update-php.template.ts",
419
+ "handlerMethod": "handleHostingPhpUpdate",
420
+ "group": "hosting"
421
+ },
352
422
  {
353
423
  "name": "hosting_websites_list-domains",
354
424
  "title": "List website parked (alias) domains and subdomains",
@@ -9757,7 +9827,8 @@ export default [
9757
9827
  "apiToken": []
9758
9828
  }
9759
9829
  ],
9760
- "group": "hosting"
9830
+ "group": "hosting",
9831
+ "hidden": true
9761
9832
  },
9762
9833
  {
9763
9834
  "name": "hosting_php_get",
@@ -9879,7 +9950,8 @@ export default [
9879
9950
  "apiToken": []
9880
9951
  }
9881
9952
  ],
9882
- "group": "hosting"
9953
+ "group": "hosting",
9954
+ "hidden": true
9883
9955
  },
9884
9956
  {
9885
9957
  "name": "hosting_php_update-options",
@@ -9921,7 +9993,8 @@ export default [
9921
9993
  "apiToken": []
9922
9994
  }
9923
9995
  ],
9924
- "group": "hosting"
9996
+ "group": "hosting",
9997
+ "hidden": true
9925
9998
  },
9926
9999
  {
9927
10000
  "name": "hosting_php_update-version",
@@ -9962,7 +10035,8 @@ export default [
9962
10035
  "apiToken": []
9963
10036
  }
9964
10037
  ],
9965
- "group": "hosting"
10038
+ "group": "hosting",
10039
+ "hidden": true
9966
10040
  },
9967
10041
  {
9968
10042
  "name": "hosting_redirects_list-website",