hostinger-api-mcp 3.29.0 → 3.30.0

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 (45) hide show
  1. package/CHANGELOG.md +15 -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/maintain-wordpress/SKILL.md +1 -1
  8. package/skills/troubleshoot-website/SKILL.md +1 -1
  9. package/src/core/runtime.js +96 -0
  10. package/src/core/runtime.ts +96 -0
  11. package/src/core/skills.js +6 -6
  12. package/src/core/skills.ts +6 -6
  13. package/src/core/tools/all.js +78 -4
  14. package/src/core/tools/all.ts +78 -4
  15. package/src/core/tools/hosting.js +78 -4
  16. package/src/core/tools/hosting.ts +78 -4
  17. package/src/servers/agency-hosting.js +1 -1
  18. package/src/servers/agency-hosting.ts +1 -1
  19. package/src/servers/agent-readiness.js +1 -1
  20. package/src/servers/agent-readiness.ts +1 -1
  21. package/src/servers/all.js +1 -1
  22. package/src/servers/all.ts +1 -1
  23. package/src/servers/billing.js +1 -1
  24. package/src/servers/billing.ts +1 -1
  25. package/src/servers/dns.js +1 -1
  26. package/src/servers/dns.ts +1 -1
  27. package/src/servers/domains.js +1 -1
  28. package/src/servers/domains.ts +1 -1
  29. package/src/servers/ecommerce.js +1 -1
  30. package/src/servers/ecommerce.ts +1 -1
  31. package/src/servers/gpu.js +1 -1
  32. package/src/servers/gpu.ts +1 -1
  33. package/src/servers/horizons.js +1 -1
  34. package/src/servers/horizons.ts +1 -1
  35. package/src/servers/hosting.js +1 -1
  36. package/src/servers/hosting.ts +1 -1
  37. package/src/servers/mail.js +1 -1
  38. package/src/servers/mail.ts +1 -1
  39. package/src/servers/reach.js +1 -1
  40. package/src/servers/reach.ts +1 -1
  41. package/src/servers/vps.js +1 -1
  42. package/src/servers/vps.ts +1 -1
  43. package/src/servers/wordpress.js +1 -1
  44. package/src/servers/wordpress.ts +1 -1
  45. package/types.d.ts +29 -0
package/CHANGELOG.md CHANGED
@@ -4,6 +4,21 @@ Generated at release time from the diff between the previous and current
4
4
  build. Tool changes are listed individually; everything else is summarised
5
5
  by area.
6
6
 
7
+ ## v3.30.0 — 2026-11-05
8
+
9
+ ### Operations
10
+
11
+ - Added `hosting_php_update` — Update website PHP version, extensions and options
12
+ - Hid 4 operations from search (they still run through `execute`): `hosting_php_reset-extensions`, `hosting_php_update-extensions`, `hosting_php_update-options`, `hosting_php_update-version`
13
+
14
+ ### Also in this release
15
+
16
+ - Documentation: `README.md`
17
+ - Bundled agent skills: `skills/headless/VERSION`, `skills/maintain-wordpress/SKILL.md`, `skills/troubleshoot-website/SKILL.md`
18
+ - Request handling and authentication: `src/core/runtime.js`, `src/core/runtime.ts`
19
+ - Other files: `src/core/skills.js`, `src/core/skills.ts`
20
+ - Build and packaging: `types.d.ts`
21
+
7
22
  ## v3.29.0 — 2026-11-05
8
23
 
9
24
  ### Also in this release
package/README.md CHANGED
@@ -69,7 +69,7 @@ pnpm update -g @hostinger/mcp
69
69
 
70
70
  This package installs the following MCP server commands:
71
71
 
72
- - `hostinger-api-mcp` — unified server over every operation (412 total)
72
+ - `hostinger-api-mcp` — unified server over every operation (409 total)
73
73
  - `hostinger-agency-hosting-mcp` — 43 operations for agency-hosting
74
74
  - `hostinger-agent-readiness-mcp` — 4 operations for agent-readiness
75
75
  - `hostinger-billing-mcp` — 10 operations for billing
@@ -78,7 +78,7 @@ This package installs the following MCP server commands:
78
78
  - `hostinger-ecommerce-mcp` — 30 operations for ecommerce
79
79
  - `hostinger-gpu-mcp` — 5 operations for gpu
80
80
  - `hostinger-horizons-mcp` — 7 operations for horizons
81
- - `hostinger-hosting-mcp` — 57 operations for hosting
81
+ - `hostinger-hosting-mcp` — 54 operations for hosting
82
82
  - `hostinger-mail-mcp` — 39 operations for mail
83
83
  - `hostinger-reach-mcp` — 71 operations for reach
84
84
  - `hostinger-vps-mcp` — 68 operations for vps
@@ -1960,6 +1960,13 @@ Clear a website's server cache (the hosting server's page cache) and Hostinger C
1960
1960
  - **Method**: `custom`
1961
1961
  - **Path**: `custom`
1962
1962
 
1963
+ #### hosting_php_update
1964
+
1965
+ 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.
1966
+
1967
+ - **Method**: `custom`
1968
+ - **Path**: `custom`
1969
+
1963
1970
  #### hosting_websites_list-domains
1964
1971
 
1965
1972
  List the extra domains of a website: its parked (alias) domains, which show the same site under another domain name, and its subdomains (e.g. blog.example.com), with each one's root directory. parked items have domain (a domain name or an IP address) and type (domain or ip); subdomains items have domain (the full name), subdomain (the prefix) and root_directory. A list that cannot be read comes back as { error } and the other is still returned. To add or remove one use hosting_websites_add-domain or hosting_websites_remove-domain. Works for Shared and Cloud hosting websites.
@@ -2441,15 +2448,6 @@ or specific order IDs for more targeted results.
2441
2448
  - **Method**: `GET`
2442
2449
  - **Path**: `/api/hosting/v1/orders`
2443
2450
 
2444
- #### hosting_php_reset-extensions
2445
-
2446
- Resets all PHP extensions of the website to their default state.
2447
-
2448
- Use it to recover from extension conflicts or restore the original configuration.
2449
-
2450
- - **Method**: `PATCH`
2451
- - **Path**: `/api/hosting/v1/accounts/{username}/websites/{domain}/php/extensions/reset`
2452
-
2453
2451
  #### hosting_php_get
2454
2452
 
2455
2453
  Returns the full PHP configuration for the website: current version, available versions
@@ -2470,35 +2468,6 @@ Use it to debug PHP issues or inspect the complete PHP environment of the websit
2470
2468
  - **Method**: `GET`
2471
2469
  - **Path**: `/api/hosting/v1/accounts/{username}/websites/{domain}/php/php-info`
2472
2470
 
2473
- #### hosting_php_update-extensions
2474
-
2475
- Enables or disables PHP extensions (modules) for the website.
2476
-
2477
- Use the Get PHP details endpoint to check the current extension states before changing them.
2478
-
2479
- - **Method**: `PATCH`
2480
- - **Path**: `/api/hosting/v1/accounts/{username}/websites/{domain}/php/extensions`
2481
-
2482
- #### hosting_php_update-options
2483
-
2484
- Updates PHP options for the website (e.g. `memory_limit`, `max_execution_time`, `upload_max_filesize`).
2485
- Only provide the options you want to change, inside the `options` object.
2486
-
2487
- Values above the account plan limit are silently capped to that limit, so the request can succeed
2488
- with a smaller applied value. Call the Get PHP details endpoint afterwards to read the applied value.
2489
-
2490
- - **Method**: `PATCH`
2491
- - **Path**: `/api/hosting/v1/accounts/{username}/websites/{domain}/php/options`
2492
-
2493
- #### hosting_php_update-version
2494
-
2495
- Changes the PHP version of the website.
2496
-
2497
- Use the Get PHP details endpoint to see the versions available for the website.
2498
-
2499
- - **Method**: `PATCH`
2500
- - **Path**: `/api/hosting/v1/accounts/{username}/websites/{domain}/php/version`
2501
-
2502
2471
  #### hosting_redirects_list-website
2503
2472
 
2504
2473
  Returns a paginated list of redirects configured for the selected website.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hostinger-api-mcp",
3
- "version": "3.29.0",
3
+ "version": "3.30.0",
4
4
  "description": "Hostinger API — manage domains, DNS, hosting, VPS, billing and more from Gemini CLI.",
5
5
  "mcpServers": {
6
6
  "hostinger": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hostinger-api-mcp",
3
- "version": "3.29.0",
3
+ "version": "3.30.0",
4
4
  "mcpName": "io.github.hostinger/hostinger-api-mcp",
5
5
  "description": "MCP server for Hostinger API",
6
6
  "repository": {
package/server.json CHANGED
@@ -17,12 +17,12 @@
17
17
  "url": "https://github.com/hostinger/api-mcp-server",
18
18
  "source": "github"
19
19
  },
20
- "version": "3.29.0",
20
+ "version": "3.30.0",
21
21
  "packages": [
22
22
  {
23
23
  "registryType": "npm",
24
24
  "identifier": "@hostinger/mcp",
25
- "version": "3.29.0",
25
+ "version": "3.30.0",
26
26
  "transport": {
27
27
  "type": "stdio"
28
28
  },
@@ -1,3 +1,3 @@
1
- version=3.29.0
2
- commit=c4c3f98adbe2
1
+ version=3.30.0
2
+ commit=451f245ab30b
3
3
  built=2026-11-05
@@ -65,7 +65,7 @@ When a site breaks: stop the run, deactivate the plugin updated last with `wordp
65
65
 
66
66
  - Page cache: `wordpress_litespeed-cache_show-lite-speed-status`; purge after design or content changes.
67
67
  - Object cache: `wordpress_object-cache_show-memcached-status`, then `wordpress_object-cache_toggle-memcached` with `enabled: true` — the usual quick speed-up.
68
- - 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`.
68
+ - 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.
69
69
  - wp-admin: `wordpress_login_create-links` returns temporary one-click login links. Give them to the user only; never store or reuse them.
70
70
  - Hostinger's own plugins update with `wordpress_plugins_update-hostinger` (`slug`).
71
71
 
@@ -68,7 +68,7 @@ Batch `hosting_get-deployment` (the latest build with its log, failure analysis
68
68
 
69
69
  ### PHP
70
70
 
71
- `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.
71
+ `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.
72
72
 
73
73
  ### Agency Plan sites
74
74
 
@@ -304,6 +304,8 @@ class MCPServer {
304
304
  return await this.handleHostingWebsitesUpdateSettings(params);
305
305
  case 'hosting_cache_clear':
306
306
  return await this.handleHostingCacheClear(params);
307
+ case 'hosting_php_update':
308
+ return await this.handleHostingPhpUpdate(params);
307
309
  case 'hosting_websites_list-domains':
308
310
  return await this.handleHostingWebsitesListDomains(params);
309
311
  case 'hosting_websites_add-domain':
@@ -1605,6 +1607,100 @@ class MCPServer {
1605
1607
  return { domain, cleared: scope, directory, response };
1606
1608
  }
1607
1609
 
1610
+ // Application order, and how each step maps to its operation.
1611
+ hosting_phpUpdate_steps = [
1612
+ { setting: 'version', run: (target, value) => this.callOperation('hosting_php_update-version', { ...target, version: value }) },
1613
+ { setting: 'resetExtensions', run: (target) => this.callOperation('hosting_php_reset-extensions', target) },
1614
+ { setting: 'extensions', run: (target, value) => this.callOperation('hosting_php_update-extensions', { ...target, ...this.hosting_phpUpdate_extensionLists(value) }) },
1615
+ { setting: 'options', run: (target, value) => this.callOperation('hosting_php_update-options', { ...target, options: value }) },
1616
+ ];
1617
+
1618
+ // The enable and disable lists to send: only the non-empty ones.
1619
+ hosting_phpUpdate_extensionLists(extensions) {
1620
+ const lists = {};
1621
+ for (const key of ['enable', 'disable']) {
1622
+ if (Array.isArray(extensions[key]) && extensions[key].length > 0) {
1623
+ lists[key] = extensions[key];
1624
+ }
1625
+ }
1626
+ return lists;
1627
+ }
1628
+
1629
+ hosting_phpUpdate_validate(params) {
1630
+ // Models send null for fields they leave unset.
1631
+ const given = (name) => params[name] !== undefined && params[name] !== null;
1632
+
1633
+ if (given('version') && (typeof params.version !== 'string' || params.version.trim() === '')) {
1634
+ throw new Error('version must be a non-empty string when provided');
1635
+ }
1636
+ if (given('resetExtensions') && typeof params.resetExtensions !== 'boolean') {
1637
+ throw new Error('resetExtensions must be a boolean when provided');
1638
+ }
1639
+ if (given('extensions')) {
1640
+ const extensions = params.extensions;
1641
+ const list = (value) => value === undefined || value === null || (Array.isArray(value) && value.every((name) => typeof name === 'string' && name !== ''));
1642
+ const valid = typeof extensions === 'object' && !Array.isArray(extensions)
1643
+ && list(extensions.enable) && list(extensions.disable)
1644
+ && Object.keys(this.hosting_phpUpdate_extensionLists(extensions)).length > 0;
1645
+ if (!valid) {
1646
+ throw new Error('extensions must be an object with a non-empty enable or disable list of extension names');
1647
+ }
1648
+ }
1649
+ if (given('options')) {
1650
+ const options = params.options;
1651
+ const valid = typeof options === 'object' && !Array.isArray(options) && Object.keys(options).length > 0
1652
+ && Object.values(options).every(value => typeof value === 'string' || (typeof value === 'number' && Number.isInteger(value)));
1653
+ if (!valid) {
1654
+ throw new Error('options must be a non-empty object of option names to string or integer values');
1655
+ }
1656
+ }
1657
+ }
1658
+
1659
+ async handleHostingPhpUpdate(params) {
1660
+ const { domain: requestedDomain } = params;
1661
+ this.hosting_phpUpdate_validate(params);
1662
+
1663
+ // resetExtensions: false asks for nothing, like null.
1664
+ const requested = this.hosting_phpUpdate_steps.filter(step => step.setting === 'resetExtensions'
1665
+ ? params.resetExtensions === true
1666
+ : params[step.setting] !== undefined && params[step.setting] !== null);
1667
+ if (requested.length === 0) {
1668
+ throw new Error('Pass at least one of version, resetExtensions, extensions, options');
1669
+ }
1670
+ if (!requestedDomain || typeof requestedDomain !== 'string') {
1671
+ throw new Error('domain is required and must be a string');
1672
+ }
1673
+
1674
+ const { username, domain } = await this.findWebsite(requestedDomain);
1675
+ const target = { username, domain };
1676
+
1677
+ const applied = {};
1678
+ let failed = null;
1679
+ const skipped = [];
1680
+ for (const step of requested) {
1681
+ const value = params[step.setting];
1682
+ if (failed) {
1683
+ skipped.push(step.setting);
1684
+ continue;
1685
+ }
1686
+ try {
1687
+ await step.run(target, value);
1688
+ // What was sent: for extensions, only the non-empty lists.
1689
+ applied[step.setting] = step.setting === 'extensions' ? this.hosting_phpUpdate_extensionLists(value) : value;
1690
+ } catch (error) {
1691
+ failed = { setting: step.setting, error: this.formatToolError(error) };
1692
+ }
1693
+ }
1694
+
1695
+ const result = { domain, applied, failed, skipped };
1696
+ // A failed step is a tool error carrying the same JSON, so execute reports it and a
1697
+ // multi-execute batch stops at this step.
1698
+ if (failed) {
1699
+ throw new Error(JSON.stringify(result));
1700
+ }
1701
+ return result;
1702
+ }
1703
+
1608
1704
  // A 404 means the domain is not a website on the account, which fails the whole call; any other
1609
1705
  // failure only fails its own list.
1610
1706
  async hosting_websitesListDomains_section(request, pick) {
@@ -336,6 +336,8 @@ class MCPServer {
336
336
  return await this.handleHostingWebsitesUpdateSettings(params);
337
337
  case 'hosting_cache_clear':
338
338
  return await this.handleHostingCacheClear(params);
339
+ case 'hosting_php_update':
340
+ return await this.handleHostingPhpUpdate(params);
339
341
  case 'hosting_websites_list-domains':
340
342
  return await this.handleHostingWebsitesListDomains(params);
341
343
  case 'hosting_websites_add-domain':
@@ -1643,6 +1645,100 @@ class MCPServer {
1643
1645
  return { domain, cleared: scope, directory, response };
1644
1646
  }
1645
1647
 
1648
+ // Application order, and how each step maps to its operation.
1649
+ private hosting_phpUpdate_steps: Array<{ setting: string; run: (target: { username: string; domain: string }, value: any) => Promise<any> }> = [
1650
+ { setting: 'version', run: (target, value) => this.callOperation('hosting_php_update-version', { ...target, version: value }) },
1651
+ { setting: 'resetExtensions', run: (target) => this.callOperation('hosting_php_reset-extensions', target) },
1652
+ { setting: 'extensions', run: (target, value) => this.callOperation('hosting_php_update-extensions', { ...target, ...this.hosting_phpUpdate_extensionLists(value) }) },
1653
+ { setting: 'options', run: (target, value) => this.callOperation('hosting_php_update-options', { ...target, options: value }) },
1654
+ ];
1655
+
1656
+ // The enable and disable lists to send: only the non-empty ones.
1657
+ private hosting_phpUpdate_extensionLists(extensions: Record<string, any>): Record<string, string[]> {
1658
+ const lists: Record<string, string[]> = {};
1659
+ for (const key of ['enable', 'disable']) {
1660
+ if (Array.isArray(extensions[key]) && extensions[key].length > 0) {
1661
+ lists[key] = extensions[key];
1662
+ }
1663
+ }
1664
+ return lists;
1665
+ }
1666
+
1667
+ private hosting_phpUpdate_validate(params: Record<string, any>): void {
1668
+ // Models send null for fields they leave unset.
1669
+ const given = (name: string) => params[name] !== undefined && params[name] !== null;
1670
+
1671
+ if (given('version') && (typeof params.version !== 'string' || params.version.trim() === '')) {
1672
+ throw new Error('version must be a non-empty string when provided');
1673
+ }
1674
+ if (given('resetExtensions') && typeof params.resetExtensions !== 'boolean') {
1675
+ throw new Error('resetExtensions must be a boolean when provided');
1676
+ }
1677
+ if (given('extensions')) {
1678
+ const extensions = params.extensions;
1679
+ const list = (value: any) => value === undefined || value === null || (Array.isArray(value) && value.every((name: any) => typeof name === 'string' && name !== ''));
1680
+ const valid = typeof extensions === 'object' && !Array.isArray(extensions)
1681
+ && list(extensions.enable) && list(extensions.disable)
1682
+ && Object.keys(this.hosting_phpUpdate_extensionLists(extensions)).length > 0;
1683
+ if (!valid) {
1684
+ throw new Error('extensions must be an object with a non-empty enable or disable list of extension names');
1685
+ }
1686
+ }
1687
+ if (given('options')) {
1688
+ const options = params.options;
1689
+ const valid = typeof options === 'object' && !Array.isArray(options) && Object.keys(options).length > 0
1690
+ && Object.values(options).every(value => typeof value === 'string' || (typeof value === 'number' && Number.isInteger(value)));
1691
+ if (!valid) {
1692
+ throw new Error('options must be a non-empty object of option names to string or integer values');
1693
+ }
1694
+ }
1695
+ }
1696
+
1697
+ private async handleHostingPhpUpdate(params: Record<string, any>): Promise<any> {
1698
+ const { domain: requestedDomain } = params;
1699
+ this.hosting_phpUpdate_validate(params);
1700
+
1701
+ // resetExtensions: false asks for nothing, like null.
1702
+ const requested = this.hosting_phpUpdate_steps.filter(step => step.setting === 'resetExtensions'
1703
+ ? params.resetExtensions === true
1704
+ : params[step.setting] !== undefined && params[step.setting] !== null);
1705
+ if (requested.length === 0) {
1706
+ throw new Error('Pass at least one of version, resetExtensions, extensions, options');
1707
+ }
1708
+ if (!requestedDomain || typeof requestedDomain !== 'string') {
1709
+ throw new Error('domain is required and must be a string');
1710
+ }
1711
+
1712
+ const { username, domain } = await this.findWebsite(requestedDomain);
1713
+ const target = { username, domain };
1714
+
1715
+ const applied: Record<string, any> = {};
1716
+ let failed: { setting: string; error: string } | null = null;
1717
+ const skipped: string[] = [];
1718
+ for (const step of requested) {
1719
+ const value = params[step.setting];
1720
+ if (failed) {
1721
+ skipped.push(step.setting);
1722
+ continue;
1723
+ }
1724
+ try {
1725
+ await step.run(target, value);
1726
+ // What was sent: for extensions, only the non-empty lists.
1727
+ applied[step.setting] = step.setting === 'extensions' ? this.hosting_phpUpdate_extensionLists(value) : value;
1728
+ } catch (error) {
1729
+ failed = { setting: step.setting, error: this.formatToolError(error) };
1730
+ }
1731
+ }
1732
+
1733
+ const result = { domain, applied, failed, skipped };
1734
+ // A failed step is a tool error carrying the same JSON, so execute reports it and a
1735
+ // multi-execute batch stops at this step.
1736
+ if (failed) {
1737
+ throw new Error(JSON.stringify(result));
1738
+ }
1739
+ return result;
1740
+ }
1741
+
1646
1742
  // A 404 means the domain is not a website on the account, which fails the whole call; any other
1647
1743
  // failure only fails its own list.
1648
1744
  private async hosting_websitesListDomains_section(request: Promise<any>, pick: (item: any) => any): Promise<any> {
@@ -297,10 +297,10 @@ export default [
297
297
  "resources": [
298
298
  {
299
299
  "uri": "skill://maintain-wordpress/SKILL.md",
300
- "digest": "sha256:a2d9ba37087c2579b98500f754d70be233b85de18410af30d528363c2dc536b1",
301
- "size": 5573,
300
+ "digest": "sha256:ed3f65f4c1ff4253f5cb43c4d680357018f7dd8cc9e8508bc19e7f9cb7b3adcb",
301
+ "size": 5717,
302
302
  "mimeType": "text/markdown",
303
- "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"
303
+ "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"
304
304
  }
305
305
  ]
306
306
  },
@@ -329,10 +329,10 @@ export default [
329
329
  "resources": [
330
330
  {
331
331
  "uri": "skill://troubleshoot-website/SKILL.md",
332
- "digest": "sha256:7b44bc7cb4f0d92c6837e6a3ea6051a5b2bbad1e70671b8368c2ef7d9ad1b05f",
333
- "size": 9723,
332
+ "digest": "sha256:12e103ace83cf7d5e32968138eef34e33621256d7cefc9ab9fd6eda556adc18d",
333
+ "size": 9855,
334
334
  "mimeType": "text/markdown",
335
- "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"
335
+ "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"
336
336
  }
337
337
  ]
338
338
  }
@@ -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
  }