hostinger-api-mcp 3.19.0 → 3.19.2
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/CHANGELOG.md +14 -0
- package/gemini-extension.json +1 -1
- package/package.json +1 -1
- package/server.json +2 -2
- package/skills/headless/SKILL.md +1 -1
- package/skills/headless/connect/SKILL.md +22 -1
- package/skills/headless/run-log.mjs +24 -9
- package/skills/headless/steps/09-done/SKILL.md +35 -30
- package/src/core/skills.js +12 -12
- package/src/core/skills.ts +12 -12
- package/src/servers/agency-hosting.js +1 -1
- package/src/servers/agency-hosting.ts +1 -1
- package/src/servers/agent-readiness.js +1 -1
- package/src/servers/agent-readiness.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/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,20 @@ 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.19.2 — 2026-10-28
|
|
8
|
+
|
|
9
|
+
### Also in this release
|
|
10
|
+
|
|
11
|
+
- Bundled agent skills: `skills/headless/steps/09-done/SKILL.md`
|
|
12
|
+
- Other files: `src/core/skills.js`, `src/core/skills.ts`
|
|
13
|
+
|
|
14
|
+
## v3.19.1 — 2026-10-28
|
|
15
|
+
|
|
16
|
+
### Also in this release
|
|
17
|
+
|
|
18
|
+
- Bundled agent skills: `skills/headless/SKILL.md`, `skills/headless/connect/SKILL.md`, `skills/headless/run-log.mjs`
|
|
19
|
+
- Other files: `src/core/skills.js`, `src/core/skills.ts`
|
|
20
|
+
|
|
7
21
|
## v3.19.0 — 2026-10-28
|
|
8
22
|
|
|
9
23
|
No changes to the published artifact.
|
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": "3.19.
|
|
9
|
+
"version": "3.19.2",
|
|
10
10
|
"packages": [
|
|
11
11
|
{
|
|
12
12
|
"registryType": "npm",
|
|
13
13
|
"identifier": "@hostinger/mcp",
|
|
14
|
-
"version": "3.19.
|
|
14
|
+
"version": "3.19.2",
|
|
15
15
|
"transport": {
|
|
16
16
|
"type": "stdio"
|
|
17
17
|
},
|
package/skills/headless/SKILL.md
CHANGED
|
@@ -170,7 +170,7 @@ took from it.
|
|
|
170
170
|
| File (under `~/.hostinger/<domain>/`) | Written by | Contains |
|
|
171
171
|
|---|---|---|
|
|
172
172
|
| `state.json` | every stage | tool, next stage, Hostinger connector version, timestamps |
|
|
173
|
-
| `connect.json` | connect | tool,
|
|
173
|
+
| `connect.json` | connect | tool, MCP version, sign-in method, plan; shape in `connect/SKILL.md` |
|
|
174
174
|
| `brief.md` | refine | refined brief, audience, tone, pages, must-haves |
|
|
175
175
|
| `site.json` | provision | chosen backend, username, install id, all URLs |
|
|
176
176
|
| `content-model.md` | content model | entities, fields, pages, navigation |
|
|
@@ -87,7 +87,28 @@ Credentials live in `~/.config/hostinger-mcp/credentials.json` (macOS/Linux) or
|
|
|
87
87
|
If the user's tool isn't listed yet, connect it the generic way (register the Hostinger MCP server
|
|
88
88
|
for that tool, or drive `npx --yes --package=@hostinger/mcp@latest hostinger-api-mcp` over stdio) and continue.
|
|
89
89
|
|
|
90
|
-
## Phase 3 -
|
|
90
|
+
## Phase 3 - Write `connect.json`
|
|
91
|
+
|
|
92
|
+
Once the tool is connected, write `~/.hostinger/<domain>/connect.json` (create the folder if it is
|
|
93
|
+
missing) in exactly this shape:
|
|
94
|
+
|
|
95
|
+
```json
|
|
96
|
+
{ "tool": "claude-code", "mcp": "hostinger-api-mcp 3.8.0", "auth": "oauth_cached",
|
|
97
|
+
"order_id": null, "plan": null, "connected_at": "2026-10-28T10:46:12Z" }
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
- `tool`: the `<tool>` from the Phase 2 route (`claude-code`, `codex`, `cursor`, `vscode`,
|
|
101
|
+
`antigravity`, `devin`, or a short lowercase name for an unlisted tool), the same value later
|
|
102
|
+
passed to `run-start --tool`.
|
|
103
|
+
- `mcp`: one string, `hostinger-api-mcp <version>`, with the `version` from the `mcp_ok` event.
|
|
104
|
+
Never an object.
|
|
105
|
+
- `auth`: the `method` from the `logged_in` event (`api_token`, `oauth_cached` or `oauth`).
|
|
106
|
+
- `order_id`, `plan`: the active hosting plan's order id and name when you looked them up, else `null`.
|
|
107
|
+
- `connected_at`: the current time in UTC, ISO 8601 to the second.
|
|
108
|
+
|
|
109
|
+
Extra keys are allowed, but keep these six as above: the build report reads `tool` and `mcp`.
|
|
110
|
+
|
|
111
|
+
## Phase 4 - Continue to Headless Hosting steps
|
|
91
112
|
|
|
92
113
|
After evrything is verified and you have access to the Hostinger MCP Server and the skills then you can go back to `headless/SKILL.md` and follow the steps section to start the process.
|
|
93
114
|
|
|
@@ -100,6 +100,15 @@ function readEvents(ws) {
|
|
|
100
100
|
.map((line) => JSON.parse(line));
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
+
// connect.json from older runs may hold mcp as an object or only mcp_version; the report needs strings.
|
|
104
|
+
function readConnect(ws) {
|
|
105
|
+
const connect = readJson(join(ws.dir, 'connect.json'), {});
|
|
106
|
+
const mcp = connect.mcp ?? connect.mcp_version ?? null;
|
|
107
|
+
let version = mcp;
|
|
108
|
+
if (mcp !== null && typeof mcp !== 'string') version = mcp.name || mcp.version ? [mcp.name, mcp.version].filter(Boolean).join(' ') : JSON.stringify(mcp);
|
|
109
|
+
return { tool: typeof connect.tool === 'string' ? connect.tool : null, mcp: version };
|
|
110
|
+
}
|
|
111
|
+
|
|
103
112
|
function runStart(ws, flags) {
|
|
104
113
|
const state = loadState(ws);
|
|
105
114
|
// A previous run that never reached its report (closed window, crash) is closed out first, so
|
|
@@ -111,7 +120,7 @@ function runStart(ws, flags) {
|
|
|
111
120
|
const fresh = loadState(ws);
|
|
112
121
|
const previous = fresh.run ?? {};
|
|
113
122
|
const startedAt = now();
|
|
114
|
-
const connect =
|
|
123
|
+
const connect = readConnect(ws);
|
|
115
124
|
// Run ids are the start time; a second run in the same second gets a suffix.
|
|
116
125
|
const taken = new Set([
|
|
117
126
|
...(fresh.history ?? []).map((h) => h.run),
|
|
@@ -268,7 +277,7 @@ function buildReport(ws, { outcome, tokens } = {}) {
|
|
|
268
277
|
const run = currentRun(ws, state);
|
|
269
278
|
const site = readJson(join(ws.dir, 'site.json'), {});
|
|
270
279
|
const deploy = readJson(join(ws.dir, 'deploy.json'), {});
|
|
271
|
-
const connect =
|
|
280
|
+
const connect = readConnect(ws);
|
|
272
281
|
const history = (state.history ?? []).filter((h) => h.run === run.id);
|
|
273
282
|
// Lines logged before events were checked on write may not match the schema.
|
|
274
283
|
const dropped = [];
|
|
@@ -291,7 +300,7 @@ function buildReport(ws, { outcome, tokens } = {}) {
|
|
|
291
300
|
const stillOpen = resolvedOutcome !== 'abandoned' && history.some((h) => !h.ended_at);
|
|
292
301
|
const endedAt = stillOpen ? now() : lastActivity;
|
|
293
302
|
const stages = history.map((h) => ({ ...h, minutes: minutesBetween(h.started_at, h.ended_at ?? endedAt) }));
|
|
294
|
-
const gaps = [...loggingGaps(stages, run, resolvedOutcome), ...dropped];
|
|
303
|
+
const gaps = [...loggingGaps(stages, run, resolvedOutcome, site.backend), ...dropped];
|
|
295
304
|
const strip = ({ ts, run: _run, type: _type, ...rest }) => ({ ...rest, logged_at: ts });
|
|
296
305
|
const liveStage = stages.find((s) => s.stage === 'deploy' && s.result?.startsWith('ok'));
|
|
297
306
|
if (!OUTCOMES.includes(resolvedOutcome)) fail(`unknown outcome "${resolvedOutcome}"; one of ${OUTCOMES.join(', ')}`);
|
|
@@ -316,7 +325,7 @@ function buildReport(ws, { outcome, tokens } = {}) {
|
|
|
316
325
|
model: run.model ?? null,
|
|
317
326
|
os: process.platform,
|
|
318
327
|
node: process.version,
|
|
319
|
-
mcp: connect.mcp
|
|
328
|
+
mcp: connect.mcp,
|
|
320
329
|
backend: site.backend ?? null,
|
|
321
330
|
frontend: frontendStack(ws.dir),
|
|
322
331
|
outcome: resolvedOutcome,
|
|
@@ -400,15 +409,21 @@ function reportRejected(ws, [runId]) {
|
|
|
400
409
|
|
|
401
410
|
// Gaps are printed as warnings by `report`, so guesses never pass as measurements.
|
|
402
411
|
const EXPECTED_FOR_BUILD = ['check_state', 'refine', 'provision', 'content_model', 'design', 'frontend', 'validate', 'deploy'];
|
|
403
|
-
|
|
412
|
+
// Stages a backend skips on purpose: webapp has no CMS, so no content model.
|
|
413
|
+
const SKIPPED_BY_BACKEND = { webapp: ['content_model'] };
|
|
414
|
+
function loggingGaps(stages, run, outcome, backend) {
|
|
404
415
|
const gaps = [];
|
|
405
416
|
for (const s of stages) {
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
417
|
+
// A skipped stage did no work, so it has no duration to measure.
|
|
418
|
+
const problem = s.result === 'skipped' ? null
|
|
419
|
+
: !s.ended_at ? 'started but never ended'
|
|
420
|
+
: s.start_estimated ? 'start time estimated, not measured'
|
|
421
|
+
: Date.parse(s.ended_at) === Date.parse(s.started_at) ? 'logged with no duration; probably started and ended in one go after the work'
|
|
422
|
+
: null;
|
|
423
|
+
if (problem) gaps.push({ stage: s.stage, phase: s.phase, problem });
|
|
409
424
|
}
|
|
410
425
|
if (run.kind === 'build' && outcome === 'live') {
|
|
411
|
-
for (const stage of EXPECTED_FOR_BUILD) {
|
|
426
|
+
for (const stage of EXPECTED_FOR_BUILD.filter((name) => !SKIPPED_BY_BACKEND[backend]?.includes(name))) {
|
|
412
427
|
if (!stages.some((s) => s.stage === stage)) gaps.push({ stage, phase: null, problem: 'never logged, though the site went live' });
|
|
413
428
|
}
|
|
414
429
|
}
|
|
@@ -9,29 +9,11 @@ Verify the live site, then hand over the URLs (success page, main site, WordPres
|
|
|
9
9
|
summary of what was built, and what the user can do next. **On a successful run, open the success page
|
|
10
10
|
for the user** (see *Open the success page*).
|
|
11
11
|
|
|
12
|
-
## Verify the live site (brief)
|
|
13
|
-
|
|
14
|
-
On a run that deployed this session, re-confirm what you're about to hand over actually works (deploy
|
|
15
|
-
already checked this — this is a quick re-confirm, not a full re-run):
|
|
16
|
-
|
|
17
|
-
- **Main site:** `GET https://<domain>/` → 200 (Next.js SSR: `x-powered-by: Next.js`; Astro SSG: static
|
|
18
|
-
HTML with real content).
|
|
19
|
-
- **WordPress admin:** `GET {urls.wp_admin}` → reachable (a login page, 200/302), not 404.
|
|
20
|
-
- **Backend API:** `GET {urls.rest_api}/posts?per_page=1` → 200 (WordPress); for a **WooCommerce** store
|
|
21
|
-
(`site.json → woocommerce.enabled`) also `GET {urls.store_api}/products?per_page=1` → 200; or the
|
|
22
|
-
storefront API for the sales channel → 200 (Ecommerce).
|
|
23
|
-
- **Contact form** (`site.json → contact_form.enabled`): validate already sent a test submission, so
|
|
24
|
-
don't send another here. In the handover, tell the owner the contact page is live and enquiries email
|
|
25
|
-
the **WordPress admin address** (via `wp_mail`) — and that for the most reliable delivery that address
|
|
26
|
-
should be a Hostinger mailbox on the domain. If validate reported `mail_failed`, say so plainly instead
|
|
27
|
-
of claiming the form sends.
|
|
28
|
-
|
|
29
|
-
If a check fails, say so in the outcome paragraph and don't claim the site is live.
|
|
30
|
-
|
|
31
12
|
## Final report
|
|
32
13
|
|
|
33
14
|
Every run ends with this report, whether it finishes here or stops at an earlier stage (a dummy or
|
|
34
|
-
missing step, a failed provision, the user pausing).
|
|
15
|
+
missing step, a failed provision, the user pausing). On a run that deployed this session, run the
|
|
16
|
+
checks in *Verify the live site* below first. Build the report from `state.json`, `site.json`,
|
|
35
17
|
`deploy.json` and what you actually did and checked in this run. Report only what you verified. Don't
|
|
36
18
|
claim a stage passed if you didn't check it.
|
|
37
19
|
|
|
@@ -41,19 +23,23 @@ Write it in this order:
|
|
|
41
23
|
early, name the stage it stopped at, why, and what the user will see right now (e.g. "the site
|
|
42
24
|
root returns 403 until the frontend is deployed").
|
|
43
25
|
|
|
44
|
-
2. **Steps completed
|
|
26
|
+
2. **Steps completed.** This table is required on every run, including early stops: never replace
|
|
27
|
+
it with prose. Give one row per stage that ran, using the stage numbers and names from the
|
|
28
|
+
orchestrator's flow table. Leave out stages that didn't run and name them in the outcome
|
|
29
|
+
paragraph instead. A finished WordPress blog build looks like this:
|
|
45
30
|
|
|
46
31
|
| Stage | Result |
|
|
47
32
|
|---|---|
|
|
48
|
-
| connect |
|
|
49
|
-
| 1 check state | New build
|
|
50
|
-
| 2 refine |
|
|
51
|
-
| 3 provision |
|
|
52
|
-
| 4 content model |
|
|
53
|
-
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
33
|
+
| connect | Hostinger connector reached over MCP (`@hostinger/mcp` 1.9.2); signed in through the browser login. |
|
|
34
|
+
| 1 check state | New build for `harbourlight.blog`; no earlier workspace. |
|
|
35
|
+
| 2 refine | Personal travel blog about coastal towns in the UK; backend: WordPress. |
|
|
36
|
+
| 3 provision | Business plan, order 41875230; setup took 6 min; domain, SSL and WordPress checks passed. |
|
|
37
|
+
| 4 content model | 5 pages, 8 posts, 6 categories, 1 menu, 14 images (Hostinger AI), SEO titles on every page; counts re-read from the REST API. |
|
|
38
|
+
| 5 design | "Salt & Ink" direction: serif headings, sea-blue accents; `design.md` and logo written, logo check passed. |
|
|
39
|
+
| 6 frontend | Next.js SSR with home, blog, post, category and contact pages wired to the REST API; local build passed. |
|
|
40
|
+
| 7 validate | Instance setup completed; REST API returned the 8 pushed posts; no preview (nobody signing off). |
|
|
41
|
+
| 8 deploy | Source uploaded, build finished in 3 min; site root returns 200. |
|
|
42
|
+
| 9 done | Main site, wp-admin and REST API re-checked live: all returned 200. |
|
|
57
43
|
|
|
58
44
|
3. **Links** — every URL that exists at this point. Build the hPanel URLs from `site.json`
|
|
59
45
|
(`order_id`, `username`, `domain`) and the onboarding slug; take the rest from `site.json` /
|
|
@@ -92,6 +78,25 @@ Write it in this order:
|
|
|
92
78
|
Never include passwords, JWTs or other secrets in the report. For wp-admin access, give the URL and
|
|
93
79
|
offer a one-click login link (`wordpress_login_create-links`), not credentials.
|
|
94
80
|
|
|
81
|
+
## Verify the live site (brief)
|
|
82
|
+
|
|
83
|
+
On a run that deployed this session, re-confirm what you're about to hand over actually works (deploy
|
|
84
|
+
already checked this — this is a quick re-confirm, not a full re-run):
|
|
85
|
+
|
|
86
|
+
- **Main site:** `GET https://<domain>/` → 200 (Next.js SSR: `x-powered-by: Next.js`; Astro SSG: static
|
|
87
|
+
HTML with real content).
|
|
88
|
+
- **WordPress admin:** `GET {urls.wp_admin}` → reachable (a login page, 200/302), not 404.
|
|
89
|
+
- **Backend API:** `GET {urls.rest_api}/posts?per_page=1` → 200 (WordPress); for a **WooCommerce** store
|
|
90
|
+
(`site.json → woocommerce.enabled`) also `GET {urls.store_api}/products?per_page=1` → 200; or the
|
|
91
|
+
storefront API for the sales channel → 200 (Ecommerce).
|
|
92
|
+
- **Contact form** (`site.json → contact_form.enabled`): validate already sent a test submission, so
|
|
93
|
+
don't send another here. In the handover, tell the owner the contact page is live and enquiries email
|
|
94
|
+
the **WordPress admin address** (via `wp_mail`) — and that for the most reliable delivery that address
|
|
95
|
+
should be a Hostinger mailbox on the domain. If validate reported `mail_failed`, say so plainly instead
|
|
96
|
+
of claiming the form sends.
|
|
97
|
+
|
|
98
|
+
If a check fails, say so in the outcome paragraph and don't claim the site is live.
|
|
99
|
+
|
|
95
100
|
## Open the success page
|
|
96
101
|
|
|
97
102
|
On a **successful** run (the deploy succeeded and the live checks passed), open the success page in the
|