hostinger-api-mcp 3.22.0 → 3.22.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,12 @@ 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.22.1 — 2026-10-29
8
+
9
+ ### Also in this release
10
+
11
+ - OAuth sign-in flow: `src/core/oauth.js`, `src/core/oauth.ts`
12
+
7
13
  ## v3.22.0 — 2026-10-29
8
14
 
9
15
  ### Also in this release
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hostinger-api-mcp",
3
- "version": "3.22.0",
3
+ "version": "3.22.1",
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.22.0",
3
+ "version": "3.22.1",
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.22.0",
9
+ "version": "3.22.1",
10
10
  "packages": [
11
11
  {
12
12
  "registryType": "npm",
13
13
  "identifier": "@hostinger/mcp",
14
- "version": "3.22.0",
14
+ "version": "3.22.1",
15
15
  "transport": {
16
16
  "type": "stdio"
17
17
  },
package/src/core/oauth.js CHANGED
@@ -12,16 +12,6 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
12
12
  const SUCCESS_HTML = readFileSync(path.join(__dirname, "oauth-success.html"), "utf8");
13
13
  const ERROR_HTML_TEMPLATE = readFileSync(path.join(__dirname, "oauth-error.html"), "utf8");
14
14
 
15
- function escapeHtml(s) {
16
- return String(s).replace(/[&<>"']/g, (ch) => ({
17
- "&": "&amp;",
18
- "<": "&lt;",
19
- ">": "&gt;",
20
- '"': "&quot;",
21
- "'": "&#39;",
22
- })[ch]);
23
- }
24
-
25
15
  const DEFAULT_ISSUER = "https://auth.hostinger.com";
26
16
  const REGISTER_PATH = "/api/external/v1/oauth-server/register";
27
17
  const AUTHORIZE_PATH = "/api/external/v1/oauth-server/authorize";
@@ -29,6 +19,16 @@ const TOKEN_PATH = "/api/external/v1/oauth-server/token";
29
19
  const REVOKE_PATH = "/api/external/v1/oauth-server/token/revoke";
30
20
  const CLIENT_NAME = "hostinger-mcp";
31
21
  const CALLBACK_PATH = "/oauth/callback";
22
+ // RFC 6749 §4.1.2.1 — anything else in ?error= is reported as unknown_error
23
+ const OAUTH_ERROR_CODES = new Set([
24
+ "invalid_request",
25
+ "unauthorized_client",
26
+ "access_denied",
27
+ "unsupported_response_type",
28
+ "invalid_scope",
29
+ "server_error",
30
+ "temporarily_unavailable",
31
+ ]);
32
32
  const CREDENTIALS_DIR_NAME = "hostinger-mcp";
33
33
  const CREDENTIALS_FILE_NAME = "credentials.json";
34
34
  const EXPIRY_BUFFER_SECONDS = 60;
@@ -349,22 +349,27 @@ export class OAuthProvider {
349
349
  const state = url.searchParams.get("state");
350
350
  const error = url.searchParams.get("error");
351
351
 
352
- const body = error
353
- ? ERROR_HTML_TEMPLATE.replace("{{error}}", escapeHtml(error))
352
+ if (state !== expectedState) {
353
+ res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
354
+ res.end(ERROR_HTML_TEMPLATE.replace("{{error}}", "invalid_state"));
355
+ return;
356
+ }
357
+
358
+ const oauthError =
359
+ error && (OAUTH_ERROR_CODES.has(error) ? error : "unknown_error");
360
+ const body = oauthError
361
+ ? ERROR_HTML_TEMPLATE.replace("{{error}}", oauthError)
354
362
  : SUCCESS_HTML;
355
363
  res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
356
364
  res.end(body);
357
365
 
358
366
  setImmediate(() => server.close());
359
367
 
360
- if (error) {
368
+ if (oauthError) {
361
369
  return reject(
362
- new Error(`OAuth error from authorization server: ${error}`),
370
+ new Error(`OAuth error from authorization server: ${oauthError}`),
363
371
  );
364
372
  }
365
- if (!state || state !== expectedState) {
366
- return reject(new Error("OAuth state mismatch"));
367
- }
368
373
  if (!code) {
369
374
  return reject(new Error("No authorization code received"));
370
375
  }
package/src/core/oauth.ts CHANGED
@@ -12,16 +12,6 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
12
12
  const SUCCESS_HTML: string = readFileSync(path.join(__dirname, 'oauth-success.html'), 'utf8');
13
13
  const ERROR_HTML_TEMPLATE: string = readFileSync(path.join(__dirname, 'oauth-error.html'), 'utf8');
14
14
 
15
- function escapeHtml(s: string): string {
16
- return String(s).replace(/[&<>"']/g, (ch) => ({
17
- '&': '&amp;',
18
- '<': '&lt;',
19
- '>': '&gt;',
20
- '"': '&quot;',
21
- "'": '&#39;',
22
- })[ch] as string);
23
- }
24
-
25
15
  const DEFAULT_ISSUER = 'https://auth.hostinger.com';
26
16
  const REGISTER_PATH = '/api/external/v1/oauth-server/register';
27
17
  const AUTHORIZE_PATH = '/api/external/v1/oauth-server/authorize';
@@ -29,6 +19,16 @@ const TOKEN_PATH = '/api/external/v1/oauth-server/token';
29
19
  const REVOKE_PATH = '/api/external/v1/oauth-server/token/revoke';
30
20
  const CLIENT_NAME = 'hostinger-mcp';
31
21
  const CALLBACK_PATH = '/oauth/callback';
22
+ // RFC 6749 §4.1.2.1 — anything else in ?error= is reported as unknown_error
23
+ const OAUTH_ERROR_CODES = new Set([
24
+ 'invalid_request',
25
+ 'unauthorized_client',
26
+ 'access_denied',
27
+ 'unsupported_response_type',
28
+ 'invalid_scope',
29
+ 'server_error',
30
+ 'temporarily_unavailable',
31
+ ]);
32
32
  const CREDENTIALS_DIR_NAME = 'hostinger-mcp';
33
33
  const CREDENTIALS_FILE_NAME = 'credentials.json';
34
34
  const EXPIRY_BUFFER_SECONDS = 60;
@@ -321,19 +321,23 @@ export class OAuthProvider {
321
321
  const state = url.searchParams.get('state');
322
322
  const error = url.searchParams.get('error');
323
323
 
324
- const body = error
325
- ? ERROR_HTML_TEMPLATE.replace('{{error}}', escapeHtml(error))
324
+ if (state !== expectedState) {
325
+ res.writeHead(400, { 'Content-Type': 'text/html; charset=utf-8' });
326
+ res.end(ERROR_HTML_TEMPLATE.replace('{{error}}', 'invalid_state'));
327
+ return;
328
+ }
329
+
330
+ const oauthError = error && (OAUTH_ERROR_CODES.has(error) ? error : 'unknown_error');
331
+ const body = oauthError
332
+ ? ERROR_HTML_TEMPLATE.replace('{{error}}', oauthError)
326
333
  : SUCCESS_HTML;
327
334
  res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
328
335
  res.end(body);
329
336
 
330
337
  setImmediate(() => server.close());
331
338
 
332
- if (error) {
333
- return reject(new Error(`OAuth error from authorization server: ${error}`));
334
- }
335
- if (!state || state !== expectedState) {
336
- return reject(new Error('OAuth state mismatch'));
339
+ if (oauthError) {
340
+ return reject(new Error(`OAuth error from authorization server: ${oauthError}`));
337
341
  }
338
342
  if (!code) {
339
343
  return reject(new Error('No authorization code received'));
@@ -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.22.0', tools, instructions });
7
+ startServer({ name: 'hostinger-agency-hosting-mcp', version: '3.22.1', 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.22.0', tools, instructions });
7
+ startServer({ name: 'hostinger-agency-hosting-mcp', version: '3.22.1', 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.22.0', tools });
6
+ startServer({ name: 'hostinger-agent-readiness-mcp', version: '3.22.1', 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.22.0', tools });
6
+ startServer({ name: 'hostinger-agent-readiness-mcp', version: '3.22.1', 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.22.0', tools, instructions, skills });
8
+ startServer({ name: 'hostinger-api-mcp', version: '3.22.1', 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.22.0', tools, instructions, skills });
8
+ startServer({ name: 'hostinger-api-mcp', version: '3.22.1', 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.22.0', tools });
6
+ startServer({ name: 'hostinger-billing-mcp', version: '3.22.1', 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.22.0', tools });
6
+ startServer({ name: 'hostinger-billing-mcp', version: '3.22.1', 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.22.0', tools });
6
+ startServer({ name: 'hostinger-dns-mcp', version: '3.22.1', 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.22.0', tools });
6
+ startServer({ name: 'hostinger-dns-mcp', version: '3.22.1', 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.22.0', tools });
6
+ startServer({ name: 'hostinger-domains-mcp', version: '3.22.1', 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.22.0', tools });
6
+ startServer({ name: 'hostinger-domains-mcp', version: '3.22.1', 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.22.0', tools });
6
+ startServer({ name: 'hostinger-ecommerce-mcp', version: '3.22.1', 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.22.0', tools });
6
+ startServer({ name: 'hostinger-ecommerce-mcp', version: '3.22.1', 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.22.0', tools });
6
+ startServer({ name: 'hostinger-horizons-mcp', version: '3.22.1', 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.22.0', tools });
6
+ startServer({ name: 'hostinger-horizons-mcp', version: '3.22.1', 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.22.0', tools, instructions });
7
+ startServer({ name: 'hostinger-hosting-mcp', version: '3.22.1', 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.22.0', tools, instructions });
7
+ startServer({ name: 'hostinger-hosting-mcp', version: '3.22.1', 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.22.0', tools });
6
+ startServer({ name: 'hostinger-mail-mcp', version: '3.22.1', 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.22.0', tools });
6
+ startServer({ name: 'hostinger-mail-mcp', version: '3.22.1', 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.22.0', tools });
6
+ startServer({ name: 'hostinger-reach-mcp', version: '3.22.1', 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.22.0', tools });
6
+ startServer({ name: 'hostinger-reach-mcp', version: '3.22.1', 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.22.0', tools });
6
+ startServer({ name: 'hostinger-vps-mcp', version: '3.22.1', 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.22.0', tools });
6
+ startServer({ name: 'hostinger-vps-mcp', version: '3.22.1', 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.22.0', tools });
6
+ startServer({ name: 'hostinger-wordpress-mcp', version: '3.22.1', 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.22.0', tools });
6
+ startServer({ name: 'hostinger-wordpress-mcp', version: '3.22.1', tools });