hostinger-api-mcp 1.17.0 → 1.18.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.
- package/gemini-extension.json +1 -1
- package/package.json +1 -1
- package/server.json +2 -2
- package/skills/headless/SKILL.md +53 -0
- package/skills/headless/entry/bootstrap.mjs +109 -0
- package/skills/headless/entry/skill.md +79 -0
- package/skills/headless/references/DEPLOYMENT.md +34 -0
- package/skills/headless/references/SETUP.md +34 -0
- package/skills/headless/references/STORE.md +52 -0
- package/src/servers/agency-hosting.js +1 -1
- package/src/servers/agency-hosting.ts +1 -1
- package/src/servers/all.js +1 -1
- package/src/servers/all.ts +1 -1
- package/src/servers/billing.js +1 -1
- package/src/servers/billing.ts +1 -1
- package/src/servers/dns.js +1 -1
- package/src/servers/dns.ts +1 -1
- package/src/servers/domains.js +1 -1
- package/src/servers/domains.ts +1 -1
- package/src/servers/ecommerce.js +1 -1
- package/src/servers/ecommerce.ts +1 -1
- package/src/servers/horizons.js +1 -1
- package/src/servers/horizons.ts +1 -1
- package/src/servers/hosting.js +1 -1
- package/src/servers/hosting.ts +1 -1
- package/src/servers/mail.js +1 -1
- package/src/servers/mail.ts +1 -1
- package/src/servers/reach.js +1 -1
- package/src/servers/reach.ts +1 -1
- package/src/servers/vps.js +1 -1
- package/src/servers/vps.ts +1 -1
- package/src/servers/wordpress.js +1 -1
- package/src/servers/wordpress.ts +1 -1
package/gemini-extension.json
CHANGED
package/package.json
CHANGED
package/server.json
CHANGED
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
"url": "https://github.com/hostinger/api-mcp-server",
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
|
-
"version": "1.
|
|
9
|
+
"version": "1.18.0",
|
|
10
10
|
"packages": [
|
|
11
11
|
{
|
|
12
12
|
"registryType": "npm",
|
|
13
13
|
"identifier": "hostinger-api-mcp",
|
|
14
|
-
"version": "1.
|
|
14
|
+
"version": "1.18.0",
|
|
15
15
|
"transport": {
|
|
16
16
|
"type": "stdio"
|
|
17
17
|
},
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: hostinger-headless
|
|
3
|
+
description: "Build, connect, or iterate on a website hosted on Hostinger — provision hosting and a domain, optionally seed an ecommerce store with a real hosted checkout, build the frontend, deploy, and verify. Requires an authenticated Hostinger MCP session (see entry/skill.md). Triggers: build me a site on Hostinger, deploy this to Hostinger, connect this project to Hostinger, add a store to my Hostinger site, update my Hostinger site."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Hostinger Headless
|
|
7
|
+
|
|
8
|
+
This skill turns a prompt into a live website on Hostinger. Its job is to own the full run: check the account, provision hosting and a domain, seed the backend (an ecommerce store when the intent calls for selling), build the frontend, deploy it, and verify the result.
|
|
9
|
+
|
|
10
|
+
The frontend is built ad-hoc to the user's intent — there is no template library. The backend pieces are real Hostinger products: web hosting (static or Node.js), domains and DNS, and Hostinger Ecommerce with its public Storefront API and hosted checkout.
|
|
11
|
+
|
|
12
|
+
## Preconditions
|
|
13
|
+
|
|
14
|
+
1. An authenticated Hostinger MCP session — the entry skill's bootstrap handled this. If MCP tools fail with auth errors, send the user back through `entry/skill.md`.
|
|
15
|
+
2. The Hostinger MCP tools for **hosting** (and **ecommerce**, when the run involves a store). Tool availability varies per user — product groups can be toggled in the Hostinger Connector, and some clients cap the number of exposed tools. When a needed tool is missing, ask the user to enable that product group (or configure the scoped binary, e.g. `hostinger-hosting-mcp`) rather than improvising around it.
|
|
16
|
+
3. An active hosting plan (checked in Setup §1). Hostinger hosting is a paid product: if the account has no usable plan, inform the user plainly that a subscription is required, link them to https://www.hostinger.com/web-hosting, and pause until they confirm the purchase. Do not treat this as an error — it is a normal step for new accounts. Everything that doesn't need hosting (planning, building the frontend locally) can proceed while they decide.
|
|
17
|
+
|
|
18
|
+
## Resolving the operation
|
|
19
|
+
|
|
20
|
+
Resolve by intent first, disk second — never let an empty directory override what the user is asking for. Check `iterate` first (it's decided by an unambiguous on-disk signal):
|
|
21
|
+
|
|
22
|
+
- `iterate` — a `.hostinger/site.json` is present in the project: the site is already deployed through this skill and the user wants changes. Reuse the recorded domain/site details; apply only the delta the new intent needs (edit frontend → redeploy; add a store → run `references/STORE.md` then redeploy). Never re-provision an existing site.
|
|
23
|
+
- `connect` — a frontend project already on disk (or brought in as a zip/export/URL) that is not yet on Hostinger, with language like "deploy this / host this on Hostinger / connect this project". Emptiness of the CWD at trigger time is not a create signal when a design is brought in from elsewhere.
|
|
24
|
+
- `create` — a new site from a prompt with nothing brought in: "build me a store / portfolio / site…".
|
|
25
|
+
|
|
26
|
+
If signals conflict, ask the user — don't guess.
|
|
27
|
+
|
|
28
|
+
## The run
|
|
29
|
+
|
|
30
|
+
1. **Discovery** — from the prompt (and the project on disk for connect/iterate), infer: does this run need a store (any buy/sell/product intent → yes)? What brand, copy, and structure does the site need? What stack fits — plain static HTML/CSS/JS for simple sites (default), a static-output framework build, or a Node.js app when SSR or server code is genuinely required? Prefer static: it deploys fastest and has no build-failure surface. Ask the user only what you cannot infer.
|
|
31
|
+
2. **Setup** (`references/SETUP.md`) — plan check, then provision the website on a free subdomain (or the user's own domain) and wait until it's ready.
|
|
32
|
+
3. **Store** (`references/STORE.md`, only when commerce is needed) — resolve or create the store and its `custom` sales channel, seed products/shipping/payment, and note the `sales_channel_id` for the frontend.
|
|
33
|
+
4. **Build** — create or wire the frontend. For store runs, follow the frontend contract in `references/STORE.md` (catalog fetched at runtime from the public Storefront API, cart in `localStorage`, client-side checkout — never embed an API token in the site).
|
|
34
|
+
5. **Deploy** (`references/DEPLOYMENT.md`) — archive and deploy via the matching hosting tool, polling build logs for Node.js runs.
|
|
35
|
+
6. **Verify** — curl the live URL for a 200 and a piece of real page copy; for store runs, confirm a checkout POST returns a redirect URL. Show the user the live URL and where to manage things (hPanel: https://hpanel.hostinger.com, and the store dashboard for commerce).
|
|
36
|
+
7. **Record** — write `.hostinger/site.json` into the project: `{ "domain", "username", "type": "static"|"nodejs", "sales_channel_id"?, "store_id"? }`. This is what makes future runs resolve as `iterate`.
|
|
37
|
+
|
|
38
|
+
Run non-interactively wherever possible. The exceptions that must involve the user: the paid-plan confirmation, anything that costs money (a domain purchase, a new subscription), and picking between genuinely equal options the prompt doesn't decide.
|
|
39
|
+
|
|
40
|
+
## Paths
|
|
41
|
+
|
|
42
|
+
| What | Path |
|
|
43
|
+
| --- | --- |
|
|
44
|
+
| Plan check + website/domain provisioning | `references/SETUP.md` |
|
|
45
|
+
| Store: seed the backend + the frontend API contract | `references/STORE.md` |
|
|
46
|
+
| Deploy: static vs Node.js, archive rules, logs, verify | `references/DEPLOYMENT.md` |
|
|
47
|
+
|
|
48
|
+
## Where the how comes from
|
|
49
|
+
|
|
50
|
+
The MCP tool descriptions are authoritative for request shapes — read them before calling. Two live sources supersede anything written here when they disagree:
|
|
51
|
+
|
|
52
|
+
- The ecommerce MCP tool `ecommerce_getCustomStorefrontSetupInstructionsV1` returns the current storefront integration guide from the server — call it at the start of any store run.
|
|
53
|
+
- The Hostinger API reference at https://developers.hostinger.com describes every endpoint behind the tools.
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Hostinger Headless — bootstrap.
|
|
3
|
+
// Verifies the Hostinger MCP server is reachable and drives the OAuth sign-in,
|
|
4
|
+
// emitting one JSON event per line on stdout so a coding agent can relay
|
|
5
|
+
// progress to the user. No filesystem writes besides what the MCP itself
|
|
6
|
+
// stores (OAuth credentials); no network calls of its own.
|
|
7
|
+
import { spawn, spawnSync } from 'node:child_process';
|
|
8
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
9
|
+
import path from 'node:path';
|
|
10
|
+
import os from 'node:os';
|
|
11
|
+
|
|
12
|
+
// ── tiny event protocol (one JSON object per line) ───────────────────────────
|
|
13
|
+
const emit = (event, extra = {}) =>
|
|
14
|
+
process.stdout.write(JSON.stringify({ event, ...extra }) + '\n');
|
|
15
|
+
const fail = (event, extra = {}) => {
|
|
16
|
+
emit(event, { ok: false, ...extra });
|
|
17
|
+
process.exit(1);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
// ── platform-safe binary names (npm/npx are .cmd on Windows) ─────────────────
|
|
21
|
+
const isWin = process.platform === 'win32';
|
|
22
|
+
const bin = (name) => (isWin ? `${name}.cmd` : name);
|
|
23
|
+
// Run the MCP via npx — no global install, always the latest published version.
|
|
24
|
+
const MCP = [bin('npx'), '-y', 'hostinger-api-mcp@latest'];
|
|
25
|
+
|
|
26
|
+
function capture(cmd, args) {
|
|
27
|
+
const r = spawnSync(cmd, args, { encoding: 'utf8', shell: false });
|
|
28
|
+
return { status: r.status ?? 1, out: `${r.stdout || ''}${r.stderr || ''}`, error: r.error };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// ── 1. MCP server reachable ──────────────────────────────────────────────────
|
|
32
|
+
function checkMcp() {
|
|
33
|
+
const r = capture(MCP[0], [...MCP.slice(1), '--version']);
|
|
34
|
+
if (r.status !== 0) fail('mcp_unreachable', { detail: r.out.trim().slice(0, 400) });
|
|
35
|
+
// npx interleaves "npm notice …" lines, so pick the first semver-looking token.
|
|
36
|
+
const version = (r.out.match(/\d+\.\d+\.\d+[^\s]*/) || [])[0] || r.out.trim().split('\n').pop();
|
|
37
|
+
emit('mcp_ok', { version });
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// ── 2. Existing auth? ────────────────────────────────────────────────────────
|
|
41
|
+
// An env token always wins (the MCP bypasses OAuth entirely when it is set).
|
|
42
|
+
// Otherwise stored OAuth credentials mean a previous sign-in can be reused —
|
|
43
|
+
// the MCP refreshes expired tokens automatically on the first tool call.
|
|
44
|
+
function existingAuth() {
|
|
45
|
+
if (process.env.HOSTINGER_API_TOKEN || process.env.API_TOKEN) {
|
|
46
|
+
return 'api_token';
|
|
47
|
+
}
|
|
48
|
+
const base = isWin ? (process.env.APPDATA || os.homedir()) : path.join(os.homedir(), '.config');
|
|
49
|
+
const credsPath = path.join(base, 'hostinger-mcp', 'credentials.json');
|
|
50
|
+
if (existsSync(credsPath)) {
|
|
51
|
+
try {
|
|
52
|
+
const creds = JSON.parse(readFileSync(credsPath, 'utf8'));
|
|
53
|
+
if (creds.refresh_token || (creds.access_token && creds.expires_at > Date.now())) {
|
|
54
|
+
return 'oauth_cached';
|
|
55
|
+
}
|
|
56
|
+
} catch {
|
|
57
|
+
/* unreadable credentials — fall through to a fresh login */
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// ── 3. Login (human-in-the-loop browser flow) ────────────────────────────────
|
|
64
|
+
// `hostinger-api-mcp --login` opens the Hostinger sign-in page and listens on
|
|
65
|
+
// an ephemeral localhost port for the OAuth callback, so the browser must run
|
|
66
|
+
// on this same machine. It prints the sign-in URL to stderr with a stable
|
|
67
|
+
// prefix — forward it so the agent can show it if the browser didn't open.
|
|
68
|
+
function login() {
|
|
69
|
+
return new Promise((resolve) => {
|
|
70
|
+
const child = spawn(MCP[0], [...MCP.slice(1), '--login'], { shell: false });
|
|
71
|
+
let diag = '';
|
|
72
|
+
let urlSent = false;
|
|
73
|
+
const collect = (chunk) => {
|
|
74
|
+
diag = (diag + chunk).slice(-2000);
|
|
75
|
+
if (!urlSent) {
|
|
76
|
+
const m = diag.match(/Opening browser for sign-in:\s*\n\s*(https?:\/\/\S+)/);
|
|
77
|
+
if (m) {
|
|
78
|
+
urlSent = true;
|
|
79
|
+
emit('awaiting_user', {
|
|
80
|
+
verificationUri: m[1],
|
|
81
|
+
note: 'A browser window should have opened. If not, open this URL manually on this machine and sign in.',
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
child.stderr?.on('data', collect);
|
|
87
|
+
child.stdout?.on('data', collect);
|
|
88
|
+
child.on('close', (code) => {
|
|
89
|
+
if (code === 0 && /Sign-in successful/i.test(diag)) {
|
|
90
|
+
emit('logged_in', { method: 'oauth' });
|
|
91
|
+
resolve();
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const detail = diag.trim().slice(-1500) || `--login exited (code ${code}) with no output.`;
|
|
95
|
+
fail('login_failed', { code, detail });
|
|
96
|
+
});
|
|
97
|
+
child.on('error', (e) => fail('login_failed', { detail: String(e) }));
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// ── main ─────────────────────────────────────────────────────────────────────
|
|
102
|
+
checkMcp();
|
|
103
|
+
const method = existingAuth();
|
|
104
|
+
if (method) {
|
|
105
|
+
emit('logged_in', { method });
|
|
106
|
+
} else {
|
|
107
|
+
await login();
|
|
108
|
+
}
|
|
109
|
+
emit('success');
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: hostinger-headless-entry
|
|
3
|
+
description: "Build a complete website on Hostinger from a single prompt — hosting, domain, and (optionally) a store with real checkout. The deterministic first steps — verifying the Hostinger MCP server and signing in — are handled by a single bootstrap script; the agent then installs the full skill and hands the run to it. Triggers: build me a site, create a website, make me a website, online store, I want to sell X, start a business online, launch a site, ecommerce, portfolio, business website, sell online, online shop, connect this project to Hostinger, host this on Hostinger, deploy this to Hostinger."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Hostinger Headless Skill — cold-start entry
|
|
7
|
+
|
|
8
|
+
You are a coding agent, and this is a skill for you to execute. Follow the phases below in order — do not summarize them. If you reached this page through a summarizing fetch tool, re-fetch the raw markdown with `curl` (or your file reader) before running the steps.
|
|
9
|
+
|
|
10
|
+
This entry gets a cold environment to the point where the real skill can run, then hands off:
|
|
11
|
+
|
|
12
|
+
1. Bootstrap (deterministic, scripted). A single script verifies the Hostinger MCP server is reachable and handles sign-in, so the run starts from a known-good, authenticated state. You just run it and relay its events.
|
|
13
|
+
2. Hand off (agentic). Install the skill, then open `headless/SKILL.md` and follow it — it resolves the operation (create / connect / iterate) and owns the whole build.
|
|
14
|
+
|
|
15
|
+
Three starting points come through here, all handled the same way — run the bootstrap, then hand off:
|
|
16
|
+
|
|
17
|
+
- new — a prompt with no project (empty CWD)
|
|
18
|
+
- connect — an existing frontend not yet on Hostinger
|
|
19
|
+
- iterate — a project already deployed to Hostinger (`.hostinger/site.json` present)
|
|
20
|
+
|
|
21
|
+
The bootstrap only verifies the MCP and signs you in, so it is safe to run in every case (an existing session just reports `logged_in`).
|
|
22
|
+
|
|
23
|
+
## Phase 0 — Node (the one manual prerequisite)
|
|
24
|
+
|
|
25
|
+
The Hostinger MCP server requires Node ≥ 20. Check it:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
node -v
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
If 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:
|
|
32
|
+
|
|
33
|
+
- macOS: `brew install node` (or `nvm install 22 && nvm use 22`)
|
|
34
|
+
- Linux: `nvm install 22 && nvm use 22` (or your distro's Node 20+ package)
|
|
35
|
+
- Windows: `winget install OpenJS.NodeJS.LTS` (or download from nodejs.org)
|
|
36
|
+
|
|
37
|
+
## Phase 1 — Run the bootstrap (deterministic)
|
|
38
|
+
|
|
39
|
+
Download the bootstrap script, then run it. It verifies the MCP server and handles sign-in, emitting one JSON event per line on stdout. Run it as a background/streaming process and relay its events to the user.
|
|
40
|
+
|
|
41
|
+
The 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. Read it first if your sandbox flags externally-downloaded code.
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# macOS/Linux:
|
|
45
|
+
curl -fsSL -O https://raw.githubusercontent.com/hostinger/api-mcp-server/main/skills/headless/entry/bootstrap.mjs
|
|
46
|
+
# Windows PowerShell:
|
|
47
|
+
iwr https://raw.githubusercontent.com/hostinger/api-mcp-server/main/skills/headless/entry/bootstrap.mjs -OutFile bootstrap.mjs
|
|
48
|
+
|
|
49
|
+
node bootstrap.mjs
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Relay these events
|
|
53
|
+
|
|
54
|
+
The script emits one JSON object per line:
|
|
55
|
+
|
|
56
|
+
| Event | What to do |
|
|
57
|
+
| --- | --- |
|
|
58
|
+
| `mcp_ok` | MCP server reachable — continue. |
|
|
59
|
+
| `logged_in` | Already authenticated (env token or a previous sign-in) — continue. |
|
|
60
|
+
| `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. |
|
|
61
|
+
| `success` | Bootstrap done — continue to Phase 2. |
|
|
62
|
+
| `mcp_unreachable` / `login_failed` (with `detail`) | Stop and show the user the `detail`. Do not improvise a parallel setup by hand. |
|
|
63
|
+
|
|
64
|
+
If the environment has a `HOSTINGER_API_TOKEN` set (for example CI), the bootstrap reports `logged_in` immediately — no browser needed.
|
|
65
|
+
|
|
66
|
+
## Phase 2 — Install the skill and hand off
|
|
67
|
+
|
|
68
|
+
Install the Hostinger skills locally so your agent reads files directly with no network calls:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
npx skills add hostinger/api-mcp-server --yes
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
If the installer is unavailable, fetch the files directly instead — the full set is listed in the skill folder at `https://github.com/hostinger/api-mcp-server/tree/main/skills/headless`.
|
|
75
|
+
|
|
76
|
+
Then open `headless/SKILL.md` and follow it. That skill owns the rest of the run — it resolves the operation (create / connect / iterate), checks the account has a usable hosting plan, provisions the website and domain, seeds the store when commerce is requested, builds, deploys, and verifies.
|
|
77
|
+
|
|
78
|
+
- Don't provision hosting, create stores, or deploy by hand here — the skill does all of that (`references/SETUP.md`, `references/DEPLOYMENT.md`, `references/STORE.md`). This entry stops at signed in.
|
|
79
|
+
- You're already authenticated from Phase 1, so every Hostinger MCP tool call will work without prompting again.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Deployment — getting the build live
|
|
2
|
+
|
|
3
|
+
Match the deploy method to what the project actually is — this is the single most common failure point.
|
|
4
|
+
|
|
5
|
+
## Static site → `hosting_deployStaticWebsite`
|
|
6
|
+
|
|
7
|
+
For pre-built files only: plain HTML/CSS/JS, or the **build output** of a framework (run the build locally first).
|
|
8
|
+
|
|
9
|
+
- The archive (zip/tar) must have `index.html` at its **root** — not nested inside a folder.
|
|
10
|
+
- Name it `name_YYYYMMDD_HHMMSS.zip`; pass `removeArchive: true` to clean up after upload.
|
|
11
|
+
- Deployment is effectively immediate — verify right after.
|
|
12
|
+
|
|
13
|
+
## Node.js app → `hosting_deployJsApplication`
|
|
14
|
+
|
|
15
|
+
For anything that needs a server or a server-side build (Express, Next.js, NestJS, SSR frameworks).
|
|
16
|
+
|
|
17
|
+
- Archive the **source**, not the output: exclude `node_modules/`, `dist/`, `.next/`, `build/`, and everything matched by `.gitignore`. The install and build run on Hostinger.
|
|
18
|
+
- Hard cap: **50 MB** archive.
|
|
19
|
+
- The upload starts a build. Track it with `hosting_listJsDeployments`; on failure pull `hosting_showJsDeploymentLogs`, fix, and redeploy. Poll with backoff — builds take minutes, not seconds.
|
|
20
|
+
|
|
21
|
+
## WordPress → `hosting_importWordpressWebsite` / plugin & theme tools
|
|
22
|
+
|
|
23
|
+
Only when the user explicitly wants WordPress. Site imports take an archive plus a `.sql` dump and can run for several minutes.
|
|
24
|
+
|
|
25
|
+
## Verify (every deploy)
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
curl -s -o /dev/null -w "%{http_code}\n" https://YOURDOMAIN/
|
|
29
|
+
curl -s https://YOURDOMAIN/ | grep -o "SOME_REAL_HEADLINE_TEXT"
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Replace the grep target with copy you actually rendered — a 200 alone can be a placeholder page. A freshly created site may serve a default page for a short while; if content doesn't match, clear the cache (`hosting_clearWebsiteCacheV1`) and retry before assuming the deploy failed. For store runs, also run the checkout verification in `STORE.md`.
|
|
33
|
+
|
|
34
|
+
When the site is live, report the URL and remind the user the site is managed from hPanel (https://hpanel.hostinger.com).
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Setup — plan check and website provisioning
|
|
2
|
+
|
|
3
|
+
Everything here uses the **hosting** MCP tools. If they're missing, ask the user to enable the Websites product group in the Hostinger Connector (or use the `hostinger-hosting-mcp` scoped binary).
|
|
4
|
+
|
|
5
|
+
## 1. Plan check (gate — run this first)
|
|
6
|
+
|
|
7
|
+
A website can only be created on an active hosting plan.
|
|
8
|
+
|
|
9
|
+
1. `hosting_listWebsitesV1` — if it returns websites, the account has a working plan; note any existing `order_id` and `username` for step 2.
|
|
10
|
+
2. Otherwise `hosting_listOrdersV1` — look for an order in a usable state. A fresh order that has never hosted a website still works; note its `order_id`.
|
|
11
|
+
3. If there is no usable order: **stop and tell the user** that an active Hostinger hosting plan is required to deploy, link https://www.hostinger.com/web-hosting, and offer to continue building the site locally in the meantime. When they confirm the purchase, re-run this check.
|
|
12
|
+
|
|
13
|
+
Never purchase a plan, domain, or any paid item without the user explicitly approving that specific purchase.
|
|
14
|
+
|
|
15
|
+
## 2. Choose the domain
|
|
16
|
+
|
|
17
|
+
- **Default: free subdomain.** `hosting_generateAFreeSubdomainV1` returns a `*.hostingersite.com` domain. No verification needed. Tell the user a custom domain can be connected later.
|
|
18
|
+
- **User-owned domain:** `hosting_verifyDomainOwnershipV1` first. If not accessible, relay the TXT record it returns, remind them propagation can take ~10 minutes, and re-verify before continuing.
|
|
19
|
+
- **New domain purchase:** only on explicit request — check availability with `domains_checkDomainAvailabilityV1`, state the price, and get an explicit yes before `domains_purchaseNewDomainV1` (needs the domains product group).
|
|
20
|
+
|
|
21
|
+
## 3. Create the website and wait for it
|
|
22
|
+
|
|
23
|
+
Generating a subdomain does **not** create a website — deploying straight to it fails with `No website found for domain`. The working sequence:
|
|
24
|
+
|
|
25
|
+
1. `hosting_createWebsiteV1 { domain, order_id }` — `datacenter_code` is required only for the first website on a brand-new plan (pick the first entry from `hosting_listAvailableDatacentersV1`).
|
|
26
|
+
2. **Poll** `hosting_listWebsitesV1` filtered by the domain until the site appears. Creation takes up to a few minutes — poll with backoff, don't fail fast.
|
|
27
|
+
3. Note the site's `username` — deployment and database tools are keyed on it.
|
|
28
|
+
|
|
29
|
+
If the domain already has a website (an `iterate` run, or the user pointed at an existing site), skip creation entirely.
|
|
30
|
+
|
|
31
|
+
## 4. Optional extras (only when the run needs them)
|
|
32
|
+
|
|
33
|
+
- **Database:** `hosting_createAccountDatabaseV1` for Node.js apps that need MySQL; pass the credentials to the app via its environment/config, never hard-coded into client-side code.
|
|
34
|
+
- **DNS records:** the DNS tools (`DNS_updateDNSRecordsV1` etc.) for custom-domain records; take a snapshot (`DNS_getDNSSnapshotListV1` context) before destructive changes.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Store — Hostinger Ecommerce with a custom storefront
|
|
2
|
+
|
|
3
|
+
**Primary source:** call `ecommerce_getCustomStorefrontSetupInstructionsV1` at the start of every store run — it returns the current, server-maintained integration guide, and it supersedes this file wherever they disagree. This file carries the essentials so the run can be planned before that call, and a fallback when the ecommerce tools aren't enabled.
|
|
4
|
+
|
|
5
|
+
## Mental model
|
|
6
|
+
|
|
7
|
+
Two API surfaces — don't mix them:
|
|
8
|
+
|
|
9
|
+
| Surface | Base URL | Auth | Use for |
|
|
10
|
+
| --- | --- | --- | --- |
|
|
11
|
+
| Storefront Core V2 | `https://api-ecommerce.hostinger.com/v2` | none (public, open CORS) | read products/variants, checkout — called from the browser |
|
|
12
|
+
| Management (MCP tools) | Hostinger API | authenticated | create stores/products/shipping/payments |
|
|
13
|
+
|
|
14
|
+
- A **store** (`store_*`) has **sales channels** (`scha_*`). The Storefront API is keyed on the **`sales_channel_id`** of a `custom`-type channel — not the store id. Managed channels (e.g. `quick-link`) cannot be used here, and only `custom` channels are API-creatable.
|
|
15
|
+
- **Prices are integers in minor units** (`2900` = €29.00 → divide by `10^decimal_digits`) and live on **variants**, not products.
|
|
16
|
+
- **Security boundary:** storefront reads and checkout are public and run client-side — **never embed a Hostinger API token in the storefront**. Only the management/hosting side (MCP tools) is authenticated.
|
|
17
|
+
|
|
18
|
+
## Backend setup (management tools)
|
|
19
|
+
|
|
20
|
+
1. Resolve the `custom` sales channel: `ecommerce_getStoresV1` → `ecommerce_listSalesChannelsV1`. If a `custom` channel already exists, confirm with the user that it's the one to use; otherwise add one to the existing store with `ecommerce_createCustomSalesChannelV1`, or create a store with `ecommerce_createStoreV1` (with a `custom` sales channel) when no suitable store exists. Set the channel `url` to the storefront's public domain at creation when it's already known.
|
|
21
|
+
2. For checkout to work the store needs all three: ≥ 1 product, ≥ 1 payment method, ≥ 1 shipping zone. Payment, shipping, and currency are store-level — check `ecommerce_getStoreMetadataV1` (`has_payment_methods` and `has_shipping` must be `true`; the same response carries `default_currency` with `code`, `decimal_digits`, `template` — read it here rather than guessing). Products are **per sales channel** — verify by listing products for the resolved channel on the Storefront API and confirming the list is non-empty. Seed what's missing with `ecommerce_createPhysicalProductV1` / `ecommerce_createDigitalProductV1`, `ecommerce_enableManualPaymentMethodV1`, and `ecommerce_setStoreShippingV1` (price `0` = free shipping) — confirming with the user before writing to a store that already has real data.
|
|
22
|
+
3. After deploy, point the channel at the live site: `ecommerce_updateSalesChannelV1 { store_id, sales_channel_id, url }`.
|
|
23
|
+
|
|
24
|
+
If the ecommerce tools aren't available, ask the user to enable the Ecommerce product group in the Hostinger Connector — or to provide the `sales_channel_id` directly, after which the public endpoints suffice for the frontend work.
|
|
25
|
+
|
|
26
|
+
## Frontend contract
|
|
27
|
+
|
|
28
|
+
The OpenAPI schema for the Storefront API is at `https://api-ecommerce.hostinger.com/v2/docs.json` — fetch it for exact endpoint shapes. Non-obvious rules the schema won't spell out:
|
|
29
|
+
|
|
30
|
+
- **Fetch the catalog at runtime, not build time** — client-side fetch on page load (CORS is open). Baking products into a static build makes the site stale on the first catalog edit. (SSR / Node.js hosting may fetch it server-side per request.)
|
|
31
|
+
- Each variant has a **`prices[]` array** (one entry per currency) — read `prices[0].amount` / `prices[0].sale_amount` (minor units) and `prices[0].currency` (which carries `decimal_digits` and the `template` whose `$1` placeholder you replace when formatting). There is **no** top-level `amount` on a variant.
|
|
32
|
+
- Product detail resolves by **product id** only; a slug returns 404 (map slug→id from the list endpoint). `limit` max is 100 on list endpoints (else 400).
|
|
33
|
+
- Filter variants with `product_ids[]=...`; the unfiltered variant list is eventually-consistent right after catalog edits.
|
|
34
|
+
- Cart lives in the site (e.g. `localStorage`): persist only `variant_id` + `quantity`, resolve display data from the live catalog — never a saved copy — then send everything in one checkout call:
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
POST /channels/{sales_channel_id}/checkout
|
|
38
|
+
{ "items":[{ "variant_id":"variant_01...", "quantity":1 }],
|
|
39
|
+
"success_url":"https://YOURDOMAIN/checkout/success/",
|
|
40
|
+
"cancel_url":"https://YOURDOMAIN/checkout/cancel/",
|
|
41
|
+
"locale":"en" }
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
`success_url`/`cancel_url` are required — build them from `window.location.origin` and make sure both pages exist. The response is `{ url, cart_token }`; redirect the browser to `url` (Hostinger's hosted checkout).
|
|
45
|
+
|
|
46
|
+
A multi-step cart alternative exists (`POST /channels/{sales_channel_id}/carts`, `PATCH /carts/{cart_id}`, `POST /carts/{cart_id}/line-items`, `PUT /carts/{cart_id}/shipping-method`, `POST /carts/{cart_id}/complete`) — the single checkout call suffices for a simple buy flow.
|
|
47
|
+
|
|
48
|
+
## Verify
|
|
49
|
+
|
|
50
|
+
- A `POST` to the checkout endpoint returns a `url` — test it directly with curl before calling the run done.
|
|
51
|
+
- The `success_url`/`cancel_url` pages return 200 (`curl -s -o /dev/null -w "%{http_code}" https://YOURDOMAIN/checkout/success/`).
|
|
52
|
+
- Open the live site, add to cart, and confirm the redirect to `https://checkout.hostinger.com`.
|
|
@@ -3,4 +3,4 @@
|
|
|
3
3
|
import { startServer } from '../core/runtime.js';
|
|
4
4
|
import tools from '../core/tools/agency-hosting.js';
|
|
5
5
|
|
|
6
|
-
startServer({ name: 'hostinger-agency-hosting-mcp', version: '1.
|
|
6
|
+
startServer({ name: 'hostinger-agency-hosting-mcp', version: '1.18.0', tools });
|
|
@@ -3,4 +3,4 @@
|
|
|
3
3
|
import { startServer } from '../core/runtime.js';
|
|
4
4
|
import tools from '../core/tools/agency-hosting.js';
|
|
5
5
|
|
|
6
|
-
startServer({ name: 'hostinger-agency-hosting-mcp', version: '1.
|
|
6
|
+
startServer({ name: 'hostinger-agency-hosting-mcp', version: '1.18.0', tools });
|
package/src/servers/all.js
CHANGED
package/src/servers/all.ts
CHANGED
package/src/servers/billing.js
CHANGED
package/src/servers/billing.ts
CHANGED
package/src/servers/dns.js
CHANGED
package/src/servers/dns.ts
CHANGED
package/src/servers/domains.js
CHANGED
package/src/servers/domains.ts
CHANGED
package/src/servers/ecommerce.js
CHANGED
package/src/servers/ecommerce.ts
CHANGED
package/src/servers/horizons.js
CHANGED
package/src/servers/horizons.ts
CHANGED
package/src/servers/hosting.js
CHANGED
package/src/servers/hosting.ts
CHANGED
package/src/servers/mail.js
CHANGED
package/src/servers/mail.ts
CHANGED
package/src/servers/reach.js
CHANGED
package/src/servers/reach.ts
CHANGED
package/src/servers/vps.js
CHANGED
package/src/servers/vps.ts
CHANGED
package/src/servers/wordpress.js
CHANGED
package/src/servers/wordpress.ts
CHANGED