hostinger-api-mcp 3.27.2 → 3.27.3

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 CHANGED
@@ -4,6 +4,13 @@ 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.27.3 — 2026-11-03
8
+
9
+ ### Also in this release
10
+
11
+ - Bundled agent skills: `skills/headless/VERSION`
12
+ - Request handling and authentication: `src/core/runtime.js`, `src/core/runtime.ts`
13
+
7
14
  ## v3.27.2 — 2026-11-03
8
15
 
9
16
  ### Also in this release
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hostinger-api-mcp",
3
- "version": "3.27.2",
3
+ "version": "3.27.3",
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.27.2",
3
+ "version": "3.27.3",
4
4
  "mcpName": "io.github.hostinger/hostinger-api-mcp",
5
5
  "description": "MCP server for Hostinger API",
6
6
  "repository": {
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.27.2",
9
+ "version": "3.27.3",
10
10
  "packages": [
11
11
  {
12
12
  "registryType": "npm",
13
13
  "identifier": "@hostinger/mcp",
14
- "version": "3.27.2",
14
+ "version": "3.27.3",
15
15
  "transport": {
16
16
  "type": "stdio"
17
17
  },
@@ -1,3 +1,3 @@
1
- version=3.27.2
2
- commit=728f90a8ae7c
1
+ version=3.27.3
2
+ commit=fddd58626392
3
3
  built=2026-11-03
@@ -357,6 +357,8 @@ class MCPServer {
357
357
 
358
358
  // System directories and the roots of the home directories. /var as a whole stays deployable: sites live in /var/www, and macOS keeps its temp dir there.
359
359
  systemDirectories = ['/etc', '/root', '/usr', '/bin', '/sbin', '/boot', '/proc', '/sys', '/dev', '/var/lib', '/var/log', '/var/db', '/var/root', '/private/etc', '/private/var/lib', '/private/var/log', '/private/var/db', '/private/var/root', '/System', '/Library',
360
+ // Homebrew on Apple silicon and MacPorts keep service data (databases) and configs under their prefix.
361
+ '/opt/homebrew', '/opt/local',
360
362
  // Container secrets (Docker's /run/secrets, the Kubernetes service-account token under /var/run/secrets) and runtime state.
361
363
  '/run', '/var/run', '/private/var/run',
362
364
  // Every user's home directory, whatever the current home is (root's is /root, a container's can be /).
@@ -402,10 +404,9 @@ class MCPServer {
402
404
  // A profile reached outside the home roots (WSL's /mnt/c/Users/me, a home on a mounted disk): the top-level data
403
405
  // of a directory under a Users or home directory, and a profile, or a directory holding profiles (/mnt/c/Users,
404
406
  // /mnt/c), that has the data of one; a project's own users/ or home/ deploys.
405
- const looksLikeProfile = (dir) => ['AppData', 'Library', '.ssh', '.aws', '.gnupg', '.kube'].some((name) => fs.existsSync(path.join(dir, name)));
406
407
  const holdsProfiles = (dir) => {
407
408
  try {
408
- return fs.readdirSync(dir, { withFileTypes: true }).some((entry) => entry.isDirectory() && looksLikeProfile(path.join(dir, entry.name)));
409
+ return fs.readdirSync(dir, { withFileTypes: true }).some((entry) => entry.isDirectory() && this.looksLikeProfile(path.join(dir, entry.name)));
409
410
  } catch (error) {
410
411
  return false;
411
412
  }
@@ -417,11 +418,11 @@ class MCPServer {
417
418
  }
418
419
  const top = segments[i + 2];
419
420
  if (top === undefined) {
420
- return i + 1 < segments.length ? looksLikeProfile(real) : holdsProfiles(real);
421
+ return i + 1 < segments.length ? this.looksLikeProfile(real) : holdsProfiles(real);
421
422
  }
422
423
  return top !== '.claude-worktrees' && (top.startsWith('.') || ['library', 'appdata', 'snap'].includes(top.toLowerCase()));
423
424
  }));
424
- const inSystem = !inHome && this.systemDirectories.some((dir) => inside(dir, real));
425
+ const inSystem = !inHome && this.systemDirectories.some((dir) => inside(dir, real) || inside(real, dir));
425
426
  // Windows drops trailing dots and spaces from a name: .ssh. is .ssh.
426
427
  const inCredentials = real.split(path.sep).some((segment) => this.credentialDirectories.includes((process.platform === 'win32' ? segment.replace(/[. ]+$/, '') : segment).toLowerCase()));
427
428
  if (inside(real, home) || otherHome || inHomeData || inProfile || inSystem || inCredentials || (fs.statSync(real).isFile() && this.isSecretFile(path.basename(real)))) {
@@ -429,6 +430,12 @@ class MCPServer {
429
430
  }
430
431
  }
431
432
 
433
+ // A user's profile, told by its app data or credentials: Library/Application Support rather than Library, which
434
+ // on a case-insensitive file system also matches a project's own library/.
435
+ looksLikeProfile(dir) {
436
+ return ['AppData', path.join('Library', 'Application Support'), '.ssh', '.aws', '.gnupg', '.kube'].some((name) => fs.existsSync(path.join(dir, name)));
437
+ }
438
+
432
439
  // Env files (examples such as .env.example are kept), credentials and private keys: a deploy would serve them
433
440
  // publicly. Matches the CLI's isSecretFile, plus .npmrc and the Hostinger credential files.
434
441
  isSecretFile(name) {
@@ -453,6 +460,11 @@ class MCPServer {
453
460
  listDeployFiles(dir, exclude) {
454
461
  const files = [];
455
462
  const skipped = [];
463
+ // A profile copy deployed as is (a backup disk's me/), told by its app data alone: a project's own .aws/ or .kube/
464
+ // at the top is skipped below, as before.
465
+ if (['AppData', path.join('Library', 'Application Support')].some((name) => fs.existsSync(path.join(dir, name)))) {
466
+ throw new Error('Refusing to deploy ' + dir + ': it is a home directory. Pass the project directory or archive.');
467
+ }
456
468
  const walk = (relativeDir) => {
457
469
  for (const entry of fs.readdirSync(path.join(dir, relativeDir), { withFileTypes: true })) {
458
470
  if (entry.isSymbolicLink()) {
@@ -464,6 +476,11 @@ class MCPServer {
464
476
  if (entry.name !== '.config' && this.credentialDirectories.includes(entry.name.toLowerCase())) {
465
477
  skipped.push(relativePath);
466
478
  } else if (entry.name !== 'node_modules' && entry.name !== '.git') {
479
+ // assertDeployablePath judges only the top of the path: a profile copy nested deeper (a backup disk's
480
+ // Users/<name>) would ship its app data and keys.
481
+ if (this.looksLikeProfile(path.join(dir, relativePath))) {
482
+ throw new Error('Refusing to deploy ' + dir + ': ' + relativePath + ' is a home directory. Pass the project directory or archive.');
483
+ }
467
484
  walk(relativePath);
468
485
  }
469
486
  } else if (entry.isFile() && entry.name !== '.DS_Store' && path.resolve(dir, relativePath) !== exclude) {
@@ -389,6 +389,8 @@ class MCPServer {
389
389
 
390
390
  // System directories and the roots of the home directories. /var as a whole stays deployable: sites live in /var/www, and macOS keeps its temp dir there.
391
391
  private systemDirectories = ['/etc', '/root', '/usr', '/bin', '/sbin', '/boot', '/proc', '/sys', '/dev', '/var/lib', '/var/log', '/var/db', '/var/root', '/private/etc', '/private/var/lib', '/private/var/log', '/private/var/db', '/private/var/root', '/System', '/Library',
392
+ // Homebrew on Apple silicon and MacPorts keep service data (databases) and configs under their prefix.
393
+ '/opt/homebrew', '/opt/local',
392
394
  // Container secrets (Docker's /run/secrets, the Kubernetes service-account token under /var/run/secrets) and runtime state.
393
395
  '/run', '/var/run', '/private/var/run',
394
396
  // Every user's home directory, whatever the current home is (root's is /root, a container's can be /).
@@ -434,10 +436,9 @@ class MCPServer {
434
436
  // A profile reached outside the home roots (WSL's /mnt/c/Users/me, a home on a mounted disk): the top-level data
435
437
  // of a directory under a Users or home directory, and a profile, or a directory holding profiles (/mnt/c/Users,
436
438
  // /mnt/c), that has the data of one; a project's own users/ or home/ deploys.
437
- const looksLikeProfile = (dir: string): boolean => ['AppData', 'Library', '.ssh', '.aws', '.gnupg', '.kube'].some((name: string) => fs.existsSync(path.join(dir, name)));
438
439
  const holdsProfiles = (dir: string): boolean => {
439
440
  try {
440
- return fs.readdirSync(dir, { withFileTypes: true }).some((entry) => entry.isDirectory() && looksLikeProfile(path.join(dir, entry.name)));
441
+ return fs.readdirSync(dir, { withFileTypes: true }).some((entry) => entry.isDirectory() && this.looksLikeProfile(path.join(dir, entry.name)));
441
442
  } catch (error) {
442
443
  return false;
443
444
  }
@@ -449,11 +450,11 @@ class MCPServer {
449
450
  }
450
451
  const top = segments[i + 2];
451
452
  if (top === undefined) {
452
- return i + 1 < segments.length ? looksLikeProfile(real) : holdsProfiles(real);
453
+ return i + 1 < segments.length ? this.looksLikeProfile(real) : holdsProfiles(real);
453
454
  }
454
455
  return top !== '.claude-worktrees' && (top.startsWith('.') || ['library', 'appdata', 'snap'].includes(top.toLowerCase()));
455
456
  }));
456
- const inSystem = !inHome && this.systemDirectories.some((dir: string) => inside(dir, real));
457
+ const inSystem = !inHome && this.systemDirectories.some((dir: string) => inside(dir, real) || inside(real, dir));
457
458
  // Windows drops trailing dots and spaces from a name: .ssh. is .ssh.
458
459
  const inCredentials = real.split(path.sep).some((segment: string) => this.credentialDirectories.includes((process.platform === 'win32' ? segment.replace(/[. ]+$/, '') : segment).toLowerCase()));
459
460
  if (inside(real, home) || otherHome || inHomeData || inProfile || inSystem || inCredentials || (fs.statSync(real).isFile() && this.isSecretFile(path.basename(real)))) {
@@ -461,6 +462,12 @@ class MCPServer {
461
462
  }
462
463
  }
463
464
 
465
+ // A user's profile, told by its app data or credentials: Library/Application Support rather than Library, which
466
+ // on a case-insensitive file system also matches a project's own library/.
467
+ private looksLikeProfile(dir: string): boolean {
468
+ return ['AppData', path.join('Library', 'Application Support'), '.ssh', '.aws', '.gnupg', '.kube'].some((name: string) => fs.existsSync(path.join(dir, name)));
469
+ }
470
+
464
471
  // Env files (examples such as .env.example are kept), credentials and private keys: a deploy would serve them
465
472
  // publicly. Matches the CLI's isSecretFile, plus .npmrc and the Hostinger credential files.
466
473
  private isSecretFile(name: string): boolean {
@@ -485,6 +492,11 @@ class MCPServer {
485
492
  private listDeployFiles(dir: string, exclude?: string): { files: string[]; skipped: string[] } {
486
493
  const files: string[] = [];
487
494
  const skipped: string[] = [];
495
+ // A profile copy deployed as is (a backup disk's me/), told by its app data alone: a project's own .aws/ or .kube/
496
+ // at the top is skipped below, as before.
497
+ if (['AppData', path.join('Library', 'Application Support')].some((name: string) => fs.existsSync(path.join(dir, name)))) {
498
+ throw new Error('Refusing to deploy ' + dir + ': it is a home directory. Pass the project directory or archive.');
499
+ }
488
500
  const walk = (relativeDir: string): void => {
489
501
  for (const entry of fs.readdirSync(path.join(dir, relativeDir), { withFileTypes: true })) {
490
502
  if (entry.isSymbolicLink()) {
@@ -496,6 +508,11 @@ class MCPServer {
496
508
  if (entry.name !== '.config' && this.credentialDirectories.includes(entry.name.toLowerCase())) {
497
509
  skipped.push(relativePath);
498
510
  } else if (entry.name !== 'node_modules' && entry.name !== '.git') {
511
+ // assertDeployablePath judges only the top of the path: a profile copy nested deeper (a backup disk's
512
+ // Users/<name>) would ship its app data and keys.
513
+ if (this.looksLikeProfile(path.join(dir, relativePath))) {
514
+ throw new Error('Refusing to deploy ' + dir + ': ' + relativePath + ' is a home directory. Pass the project directory or archive.');
515
+ }
499
516
  walk(relativePath);
500
517
  }
501
518
  } else if (entry.isFile() && entry.name !== '.DS_Store' && path.resolve(dir, relativePath) !== exclude) {
@@ -4,4 +4,4 @@ import { startServer } from '../core/runtime.js';
4
4
  import tools from '../core/tools/agency-hosting.js';
5
5
  import instructions from '../core/instructions.agency-hosting.js';
6
6
 
7
- startServer({ name: 'hostinger-agency-hosting-mcp', version: '3.27.2', tools, instructions });
7
+ startServer({ name: 'hostinger-agency-hosting-mcp', version: '3.27.3', tools, instructions });
@@ -4,4 +4,4 @@ import { startServer } from '../core/runtime.js';
4
4
  import tools from '../core/tools/agency-hosting.js';
5
5
  import instructions from '../core/instructions.agency-hosting.js';
6
6
 
7
- startServer({ name: 'hostinger-agency-hosting-mcp', version: '3.27.2', tools, instructions });
7
+ startServer({ name: 'hostinger-agency-hosting-mcp', version: '3.27.3', tools, instructions });
@@ -3,4 +3,4 @@
3
3
  import { startServer } from '../core/runtime.js';
4
4
  import tools from '../core/tools/agent-readiness.js';
5
5
 
6
- startServer({ name: 'hostinger-agent-readiness-mcp', version: '3.27.2', tools });
6
+ startServer({ name: 'hostinger-agent-readiness-mcp', version: '3.27.3', tools });
@@ -3,4 +3,4 @@
3
3
  import { startServer } from '../core/runtime.js';
4
4
  import tools from '../core/tools/agent-readiness.js';
5
5
 
6
- startServer({ name: 'hostinger-agent-readiness-mcp', version: '3.27.2', tools });
6
+ startServer({ name: 'hostinger-agent-readiness-mcp', version: '3.27.3', tools });
@@ -5,4 +5,4 @@ import tools from '../core/tools/all.js';
5
5
  import instructions from '../core/instructions.js';
6
6
  import skills from '../core/skills.js';
7
7
 
8
- startServer({ name: 'hostinger-api-mcp', version: '3.27.2', tools, instructions, skills });
8
+ startServer({ name: 'hostinger-api-mcp', version: '3.27.3', tools, instructions, skills });
@@ -5,4 +5,4 @@ import tools from '../core/tools/all.js';
5
5
  import instructions from '../core/instructions.js';
6
6
  import skills from '../core/skills.js';
7
7
 
8
- startServer({ name: 'hostinger-api-mcp', version: '3.27.2', tools, instructions, skills });
8
+ startServer({ name: 'hostinger-api-mcp', version: '3.27.3', tools, instructions, skills });
@@ -3,4 +3,4 @@
3
3
  import { startServer } from '../core/runtime.js';
4
4
  import tools from '../core/tools/billing.js';
5
5
 
6
- startServer({ name: 'hostinger-billing-mcp', version: '3.27.2', tools });
6
+ startServer({ name: 'hostinger-billing-mcp', version: '3.27.3', tools });
@@ -3,4 +3,4 @@
3
3
  import { startServer } from '../core/runtime.js';
4
4
  import tools from '../core/tools/billing.js';
5
5
 
6
- startServer({ name: 'hostinger-billing-mcp', version: '3.27.2', tools });
6
+ startServer({ name: 'hostinger-billing-mcp', version: '3.27.3', tools });
@@ -3,4 +3,4 @@
3
3
  import { startServer } from '../core/runtime.js';
4
4
  import tools from '../core/tools/dns.js';
5
5
 
6
- startServer({ name: 'hostinger-dns-mcp', version: '3.27.2', tools });
6
+ startServer({ name: 'hostinger-dns-mcp', version: '3.27.3', tools });
@@ -3,4 +3,4 @@
3
3
  import { startServer } from '../core/runtime.js';
4
4
  import tools from '../core/tools/dns.js';
5
5
 
6
- startServer({ name: 'hostinger-dns-mcp', version: '3.27.2', tools });
6
+ startServer({ name: 'hostinger-dns-mcp', version: '3.27.3', tools });
@@ -3,4 +3,4 @@
3
3
  import { startServer } from '../core/runtime.js';
4
4
  import tools from '../core/tools/domains.js';
5
5
 
6
- startServer({ name: 'hostinger-domains-mcp', version: '3.27.2', tools });
6
+ startServer({ name: 'hostinger-domains-mcp', version: '3.27.3', tools });
@@ -3,4 +3,4 @@
3
3
  import { startServer } from '../core/runtime.js';
4
4
  import tools from '../core/tools/domains.js';
5
5
 
6
- startServer({ name: 'hostinger-domains-mcp', version: '3.27.2', tools });
6
+ startServer({ name: 'hostinger-domains-mcp', version: '3.27.3', tools });
@@ -3,4 +3,4 @@
3
3
  import { startServer } from '../core/runtime.js';
4
4
  import tools from '../core/tools/ecommerce.js';
5
5
 
6
- startServer({ name: 'hostinger-ecommerce-mcp', version: '3.27.2', tools });
6
+ startServer({ name: 'hostinger-ecommerce-mcp', version: '3.27.3', tools });
@@ -3,4 +3,4 @@
3
3
  import { startServer } from '../core/runtime.js';
4
4
  import tools from '../core/tools/ecommerce.js';
5
5
 
6
- startServer({ name: 'hostinger-ecommerce-mcp', version: '3.27.2', tools });
6
+ startServer({ name: 'hostinger-ecommerce-mcp', version: '3.27.3', tools });
@@ -3,4 +3,4 @@
3
3
  import { startServer } from '../core/runtime.js';
4
4
  import tools from '../core/tools/gpu.js';
5
5
 
6
- startServer({ name: 'hostinger-gpu-mcp', version: '3.27.2', tools });
6
+ startServer({ name: 'hostinger-gpu-mcp', version: '3.27.3', tools });
@@ -3,4 +3,4 @@
3
3
  import { startServer } from '../core/runtime.js';
4
4
  import tools from '../core/tools/gpu.js';
5
5
 
6
- startServer({ name: 'hostinger-gpu-mcp', version: '3.27.2', tools });
6
+ startServer({ name: 'hostinger-gpu-mcp', version: '3.27.3', tools });
@@ -3,4 +3,4 @@
3
3
  import { startServer } from '../core/runtime.js';
4
4
  import tools from '../core/tools/horizons.js';
5
5
 
6
- startServer({ name: 'hostinger-horizons-mcp', version: '3.27.2', tools });
6
+ startServer({ name: 'hostinger-horizons-mcp', version: '3.27.3', tools });
@@ -3,4 +3,4 @@
3
3
  import { startServer } from '../core/runtime.js';
4
4
  import tools from '../core/tools/horizons.js';
5
5
 
6
- startServer({ name: 'hostinger-horizons-mcp', version: '3.27.2', tools });
6
+ startServer({ name: 'hostinger-horizons-mcp', version: '3.27.3', tools });
@@ -4,4 +4,4 @@ import { startServer } from '../core/runtime.js';
4
4
  import tools from '../core/tools/hosting.js';
5
5
  import instructions from '../core/instructions.js';
6
6
 
7
- startServer({ name: 'hostinger-hosting-mcp', version: '3.27.2', tools, instructions });
7
+ startServer({ name: 'hostinger-hosting-mcp', version: '3.27.3', tools, instructions });
@@ -4,4 +4,4 @@ import { startServer } from '../core/runtime.js';
4
4
  import tools from '../core/tools/hosting.js';
5
5
  import instructions from '../core/instructions.js';
6
6
 
7
- startServer({ name: 'hostinger-hosting-mcp', version: '3.27.2', tools, instructions });
7
+ startServer({ name: 'hostinger-hosting-mcp', version: '3.27.3', tools, instructions });
@@ -3,4 +3,4 @@
3
3
  import { startServer } from '../core/runtime.js';
4
4
  import tools from '../core/tools/mail.js';
5
5
 
6
- startServer({ name: 'hostinger-mail-mcp', version: '3.27.2', tools });
6
+ startServer({ name: 'hostinger-mail-mcp', version: '3.27.3', tools });
@@ -3,4 +3,4 @@
3
3
  import { startServer } from '../core/runtime.js';
4
4
  import tools from '../core/tools/mail.js';
5
5
 
6
- startServer({ name: 'hostinger-mail-mcp', version: '3.27.2', tools });
6
+ startServer({ name: 'hostinger-mail-mcp', version: '3.27.3', tools });
@@ -3,4 +3,4 @@
3
3
  import { startServer } from '../core/runtime.js';
4
4
  import tools from '../core/tools/reach.js';
5
5
 
6
- startServer({ name: 'hostinger-reach-mcp', version: '3.27.2', tools });
6
+ startServer({ name: 'hostinger-reach-mcp', version: '3.27.3', tools });
@@ -3,4 +3,4 @@
3
3
  import { startServer } from '../core/runtime.js';
4
4
  import tools from '../core/tools/reach.js';
5
5
 
6
- startServer({ name: 'hostinger-reach-mcp', version: '3.27.2', tools });
6
+ startServer({ name: 'hostinger-reach-mcp', version: '3.27.3', tools });
@@ -3,4 +3,4 @@
3
3
  import { startServer } from '../core/runtime.js';
4
4
  import tools from '../core/tools/vps.js';
5
5
 
6
- startServer({ name: 'hostinger-vps-mcp', version: '3.27.2', tools });
6
+ startServer({ name: 'hostinger-vps-mcp', version: '3.27.3', tools });
@@ -3,4 +3,4 @@
3
3
  import { startServer } from '../core/runtime.js';
4
4
  import tools from '../core/tools/vps.js';
5
5
 
6
- startServer({ name: 'hostinger-vps-mcp', version: '3.27.2', tools });
6
+ startServer({ name: 'hostinger-vps-mcp', version: '3.27.3', tools });
@@ -3,4 +3,4 @@
3
3
  import { startServer } from '../core/runtime.js';
4
4
  import tools from '../core/tools/wordpress.js';
5
5
 
6
- startServer({ name: 'hostinger-wordpress-mcp', version: '3.27.2', tools });
6
+ startServer({ name: 'hostinger-wordpress-mcp', version: '3.27.3', tools });
@@ -3,4 +3,4 @@
3
3
  import { startServer } from '../core/runtime.js';
4
4
  import tools from '../core/tools/wordpress.js';
5
5
 
6
- startServer({ name: 'hostinger-wordpress-mcp', version: '3.27.2', tools });
6
+ startServer({ name: 'hostinger-wordpress-mcp', version: '3.27.3', tools });