hostinger-api-mcp 0.0.30 → 0.0.32
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/README.md +2 -13
- package/package.json +11 -7
- package/server.js +84 -116
- package/server.ts +86 -121
- package/tsconfig.json +1 -1
- package/types.d.ts +4 -17
package/README.md
CHANGED
|
@@ -66,7 +66,7 @@ The following environment variables can be configured when running the server:
|
|
|
66
66
|
"command": "hostinger-api-mcp",
|
|
67
67
|
"env": {
|
|
68
68
|
"DEBUG": "false",
|
|
69
|
-
"
|
|
69
|
+
"API_TOKEN": "YOUR API TOKEN"
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
}
|
|
@@ -862,18 +862,6 @@ This endpoint retrieves a list of public keys attached to a specified virtual ma
|
|
|
862
862
|
- `virtualMachineId`: Virtual Machine ID (required)
|
|
863
863
|
- `page`: Page number
|
|
864
864
|
|
|
865
|
-
### VPS_deleteBackupV1
|
|
866
|
-
|
|
867
|
-
This endpoint deletes a specified backup for a virtual machine.
|
|
868
|
-
|
|
869
|
-
- **Method**: `DELETE`
|
|
870
|
-
- **Path**: `/api/vps/v1/virtual-machines/{virtualMachineId}/backups/{backupId}`
|
|
871
|
-
|
|
872
|
-
**Parameters**:
|
|
873
|
-
|
|
874
|
-
- `virtualMachineId`: Virtual Machine ID (required)
|
|
875
|
-
- `backupId`: Backup ID (required)
|
|
876
|
-
|
|
877
865
|
### VPS_getBackupListV1
|
|
878
866
|
|
|
879
867
|
This endpoint retrieves a list of backups for a specified virtual machine.
|
|
@@ -1038,6 +1026,7 @@ Be aware, that improper nameserver configuration can lead to the virtual machine
|
|
|
1038
1026
|
- `virtualMachineId`: Virtual Machine ID (required)
|
|
1039
1027
|
- `ns1`: ns1 parameter (required)
|
|
1040
1028
|
- `ns2`: ns2 parameter
|
|
1029
|
+
- `ns3`: ns3 parameter
|
|
1041
1030
|
|
|
1042
1031
|
### VPS_createPTRRecordV1
|
|
1043
1032
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hostinger-api-mcp",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.32",
|
|
4
4
|
"description": "MCP server for Hostinger API",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -25,17 +25,21 @@
|
|
|
25
25
|
"start:ts": "npx tsc && node dist/server.js"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@modelcontextprotocol/sdk": "^1.0.
|
|
28
|
+
"@modelcontextprotocol/sdk": "^1.0.6",
|
|
29
29
|
"minimist": "^1.2.8",
|
|
30
|
-
"express": "^
|
|
30
|
+
"express": "^4.21.2",
|
|
31
31
|
"axios": "^1.8.0",
|
|
32
|
-
"dotenv": "^16.
|
|
32
|
+
"dotenv": "^16.4.7",
|
|
33
|
+
"cors": "^2.8.5"
|
|
33
34
|
},
|
|
34
35
|
"devDependencies": {
|
|
35
|
-
"@types/node": "^
|
|
36
|
-
"
|
|
36
|
+
"@types/node": "^24.0.0",
|
|
37
|
+
"@types/express": "^5.0.0",
|
|
38
|
+
"@types/cors": "^2.8.17",
|
|
39
|
+
"@types/minimist": "^1.2.5",
|
|
40
|
+
"typescript": "^5.7.2"
|
|
37
41
|
},
|
|
38
42
|
"engines": {
|
|
39
|
-
"node": ">=
|
|
43
|
+
"node": ">=24.0.0"
|
|
40
44
|
}
|
|
41
45
|
}
|
package/server.js
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
4
4
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
|
-
import {
|
|
5
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
6
6
|
import minimist from 'minimist';
|
|
7
|
+
import cors from "cors";
|
|
7
8
|
import express from "express";
|
|
8
9
|
import axios from "axios";
|
|
9
10
|
import { config as dotenvConfig } from "dotenv";
|
|
@@ -1729,34 +1730,6 @@ const TOOLS = [
|
|
|
1729
1730
|
}
|
|
1730
1731
|
]
|
|
1731
1732
|
},
|
|
1732
|
-
{
|
|
1733
|
-
"name": "VPS_deleteBackupV1",
|
|
1734
|
-
"description": "This endpoint deletes a specified backup for a virtual machine.",
|
|
1735
|
-
"method": "DELETE",
|
|
1736
|
-
"path": "/api/vps/v1/virtual-machines/{virtualMachineId}/backups/{backupId}",
|
|
1737
|
-
"inputSchema": {
|
|
1738
|
-
"type": "object",
|
|
1739
|
-
"properties": {
|
|
1740
|
-
"virtualMachineId": {
|
|
1741
|
-
"type": "integer",
|
|
1742
|
-
"description": "Virtual Machine ID"
|
|
1743
|
-
},
|
|
1744
|
-
"backupId": {
|
|
1745
|
-
"type": "integer",
|
|
1746
|
-
"description": "Backup ID"
|
|
1747
|
-
}
|
|
1748
|
-
},
|
|
1749
|
-
"required": [
|
|
1750
|
-
"virtualMachineId",
|
|
1751
|
-
"backupId"
|
|
1752
|
-
]
|
|
1753
|
-
},
|
|
1754
|
-
"security": [
|
|
1755
|
-
{
|
|
1756
|
-
"apiToken": []
|
|
1757
|
-
}
|
|
1758
|
-
]
|
|
1759
|
-
},
|
|
1760
1733
|
{
|
|
1761
1734
|
"name": "VPS_getBackupListV1",
|
|
1762
1735
|
"description": "This endpoint retrieves a list of backups for a specified virtual machine.",
|
|
@@ -2063,6 +2036,10 @@ const TOOLS = [
|
|
|
2063
2036
|
"ns2": {
|
|
2064
2037
|
"type": "string",
|
|
2065
2038
|
"description": "ns2 parameter"
|
|
2039
|
+
},
|
|
2040
|
+
"ns3": {
|
|
2041
|
+
"type": "string",
|
|
2042
|
+
"description": "ns3 parameter"
|
|
2066
2043
|
}
|
|
2067
2044
|
},
|
|
2068
2045
|
"required": [
|
|
@@ -2512,7 +2489,7 @@ const SECURITY_SCHEMES = {
|
|
|
2512
2489
|
|
|
2513
2490
|
/**
|
|
2514
2491
|
* MCP Server for Hostinger API
|
|
2515
|
-
* Generated from OpenAPI spec version 0.0.
|
|
2492
|
+
* Generated from OpenAPI spec version 0.0.86
|
|
2516
2493
|
*/
|
|
2517
2494
|
class MCPServer {
|
|
2518
2495
|
constructor() {
|
|
@@ -2530,7 +2507,7 @@ class MCPServer {
|
|
|
2530
2507
|
this.server = new Server(
|
|
2531
2508
|
{
|
|
2532
2509
|
name: "hostinger-api-mcp",
|
|
2533
|
-
version: "0.0.
|
|
2510
|
+
version: "0.0.32",
|
|
2534
2511
|
},
|
|
2535
2512
|
{
|
|
2536
2513
|
capabilities: {
|
|
@@ -2555,7 +2532,7 @@ class MCPServer {
|
|
|
2555
2532
|
});
|
|
2556
2533
|
}
|
|
2557
2534
|
|
|
2558
|
-
headers['User-Agent'] = 'hostinger-mcp-server/0.0.
|
|
2535
|
+
headers['User-Agent'] = 'hostinger-mcp-server/0.0.32';
|
|
2559
2536
|
|
|
2560
2537
|
return headers;
|
|
2561
2538
|
}
|
|
@@ -2683,59 +2660,12 @@ class MCPServer {
|
|
|
2683
2660
|
return status < 500; // Resolve only if the status code is less than 500
|
|
2684
2661
|
}
|
|
2685
2662
|
};
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
if (
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
if (securityDefinition) {
|
|
2694
|
-
const authType = securityDefinition.type;
|
|
2695
|
-
|
|
2696
|
-
// Handle API key
|
|
2697
|
-
if (authType === 'apiKey') {
|
|
2698
|
-
const apiKeyName = securityDefinition.name;
|
|
2699
|
-
const envVarName = `${securitySchemeName.toUpperCase()}_${apiKeyName.toUpperCase()}`;
|
|
2700
|
-
const apiKeyValue = process.env[envVarName];
|
|
2701
|
-
|
|
2702
|
-
if (apiKeyValue) {
|
|
2703
|
-
if (securityDefinition.in === 'header') {
|
|
2704
|
-
config.headers[apiKeyName] = apiKeyValue;
|
|
2705
|
-
} else if (securityDefinition.in === 'query') {
|
|
2706
|
-
config.params = config.params || {};
|
|
2707
|
-
config.params[apiKeyName] = apiKeyValue;
|
|
2708
|
-
}
|
|
2709
|
-
} else {
|
|
2710
|
-
this.log('warning', `API Key environment variable not found: ${envVarName}`);
|
|
2711
|
-
}
|
|
2712
|
-
}
|
|
2713
|
-
// Handle bearer token
|
|
2714
|
-
else if (authType === 'http' && securityDefinition.scheme === 'bearer') {
|
|
2715
|
-
const envVarName = `${securitySchemeName.toUpperCase()}`;
|
|
2716
|
-
const bearerToken = process.env[envVarName];
|
|
2717
|
-
|
|
2718
|
-
if (bearerToken) {
|
|
2719
|
-
config.headers['Authorization'] = `Bearer ${bearerToken}`;
|
|
2720
|
-
} else {
|
|
2721
|
-
this.log('warning', `Bearer Token environment variable not found: ${envVarName}`);
|
|
2722
|
-
}
|
|
2723
|
-
}
|
|
2724
|
-
// Handle basic auth
|
|
2725
|
-
else if (authType === 'http' && securityDefinition.scheme === 'basic') {
|
|
2726
|
-
const username = process.env[`${securitySchemeName.toUpperCase()}_USERNAME`];
|
|
2727
|
-
const password = process.env[`${securitySchemeName.toUpperCase()}_PASSWORD`];
|
|
2728
|
-
|
|
2729
|
-
if (username && password) {
|
|
2730
|
-
const auth = Buffer.from(`${username}:${password}`).toString('base64');
|
|
2731
|
-
config.headers['Authorization'] = `Basic ${auth}`;
|
|
2732
|
-
} else {
|
|
2733
|
-
this.log('warning', `Basic auth credentials not found for ${securitySchemeName}`);
|
|
2734
|
-
}
|
|
2735
|
-
}
|
|
2736
|
-
}
|
|
2737
|
-
}
|
|
2738
|
-
}
|
|
2663
|
+
|
|
2664
|
+
const bearerToken = process.env['API_TOKEN'] || process.env['APITOKEN']; // APITOKEN for backwards compatibility
|
|
2665
|
+
if (bearerToken) {
|
|
2666
|
+
config.headers['Authorization'] = `Bearer ${bearerToken}`;
|
|
2667
|
+
} else {
|
|
2668
|
+
this.log('error', `Bearer Token environment variable not found: API_TOKEN`);
|
|
2739
2669
|
}
|
|
2740
2670
|
|
|
2741
2671
|
// Add parameters based on request method
|
|
@@ -2810,46 +2740,65 @@ class MCPServer {
|
|
|
2810
2740
|
}
|
|
2811
2741
|
|
|
2812
2742
|
/**
|
|
2813
|
-
*
|
|
2743
|
+
* Create and configure Express app with shared middleware
|
|
2814
2744
|
*/
|
|
2815
|
-
|
|
2745
|
+
createApp() {
|
|
2746
|
+
const app = express();
|
|
2747
|
+
app.use(express.json());
|
|
2748
|
+
app.use(cors());
|
|
2749
|
+
return app;
|
|
2750
|
+
}
|
|
2751
|
+
|
|
2752
|
+
/**
|
|
2753
|
+
* Start the server with HTTP streaming transport
|
|
2754
|
+
*/
|
|
2755
|
+
async startHttp(host, port) {
|
|
2816
2756
|
try {
|
|
2817
|
-
|
|
2818
|
-
const app = express();
|
|
2819
|
-
app.use(express.json());
|
|
2757
|
+
const app = this.createApp();
|
|
2820
2758
|
|
|
2821
|
-
|
|
2822
|
-
const
|
|
2759
|
+
// Create HTTP transport with session management
|
|
2760
|
+
const httpTransport = new StreamableHTTPServerTransport({
|
|
2761
|
+
sessionIdGenerator: () => {
|
|
2762
|
+
// Generate a simple session ID
|
|
2763
|
+
return `session-${Date.now()}-${Math.random().toString(36).substring(7)}`;
|
|
2764
|
+
},
|
|
2765
|
+
});
|
|
2823
2766
|
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
res.
|
|
2829
|
-
|
|
2830
|
-
});
|
|
2831
|
-
|
|
2832
|
-
this.server.connect(transport);
|
|
2767
|
+
// Set up CORS for all routes
|
|
2768
|
+
app.options("*", (req, res) => {
|
|
2769
|
+
res.header("Access-Control-Allow-Origin", "*");
|
|
2770
|
+
res.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
|
|
2771
|
+
res.header("Access-Control-Allow-Headers", "Content-Type, Authorization, x-session-id");
|
|
2772
|
+
res.sendStatus(200);
|
|
2833
2773
|
});
|
|
2834
2774
|
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
if (transport) {
|
|
2839
|
-
transport.handlePostMessage(req, res);
|
|
2840
|
-
} else {
|
|
2841
|
-
res.status(400).send('No transport found for sessionId');
|
|
2842
|
-
}
|
|
2775
|
+
// Health check endpoint
|
|
2776
|
+
app.get("/health", (req, res) => {
|
|
2777
|
+
res.status(200).json({ status: "ok", transport: "http" });
|
|
2843
2778
|
});
|
|
2844
2779
|
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2780
|
+
// Set up the HTTP transport endpoint
|
|
2781
|
+
app.post("/", async (req, res) => {
|
|
2782
|
+
res.header("Access-Control-Allow-Origin", "*");
|
|
2783
|
+
res.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
|
|
2784
|
+
res.header("Access-Control-Allow-Headers", "Content-Type, Authorization, x-session-id");
|
|
2785
|
+
await httpTransport.handleRequest(req, res, req.body);
|
|
2786
|
+
});
|
|
2787
|
+
|
|
2788
|
+
// Start the server
|
|
2789
|
+
const server = app.listen(port, host, () => {
|
|
2790
|
+
this.log('info', `MCP Server with HTTP streaming transport started successfully with ${this.tools.size} tools`);
|
|
2791
|
+
this.log('info', `Listening on http://${host}:${port}`);
|
|
2792
|
+
});
|
|
2793
|
+
|
|
2794
|
+
// Connect the MCP server to the transport
|
|
2795
|
+
await this.server.connect(httpTransport);
|
|
2796
|
+
|
|
2848
2797
|
} catch (error) {
|
|
2849
2798
|
console.error("Failed to start MCP server:", error);
|
|
2850
2799
|
process.exit(1);
|
|
2851
2800
|
}
|
|
2852
|
-
}
|
|
2801
|
+
}
|
|
2853
2802
|
|
|
2854
2803
|
/**
|
|
2855
2804
|
* Start the server
|
|
@@ -2879,16 +2828,35 @@ async function main() {
|
|
|
2879
2828
|
const argv = minimist(process.argv.slice(2), {
|
|
2880
2829
|
string: ['host'],
|
|
2881
2830
|
int: ['port'],
|
|
2882
|
-
boolean: ['
|
|
2831
|
+
boolean: ['stdio', 'http', 'help'],
|
|
2883
2832
|
default: {
|
|
2884
2833
|
host: '127.0.0.1',
|
|
2885
2834
|
port: 8100,
|
|
2835
|
+
stdio: true,
|
|
2886
2836
|
}
|
|
2887
2837
|
});
|
|
2838
|
+
|
|
2839
|
+
// Show help if requested
|
|
2840
|
+
if (argv.help) {
|
|
2841
|
+
console.log(`
|
|
2842
|
+
Hostinger API MCP Server
|
|
2843
|
+
Usage: hostinger-api-mcp [options]
|
|
2844
|
+
Options:
|
|
2845
|
+
--http Use HTTP streaming transport
|
|
2846
|
+
--stdio Use standard input/output transport (default)
|
|
2847
|
+
--host <host> Host to bind to (default: 127.0.0.1)
|
|
2848
|
+
--port <port> Port to bind to (default: 8100)
|
|
2849
|
+
--help Show this help message
|
|
2850
|
+
Environment Variables:
|
|
2851
|
+
API_TOKEN Your Hostinger API token (required)
|
|
2852
|
+
DEBUG Enable debug logging (true/false)
|
|
2853
|
+
`);
|
|
2854
|
+
process.exit(0);
|
|
2855
|
+
}
|
|
2888
2856
|
|
|
2889
2857
|
const server = new MCPServer();
|
|
2890
|
-
if (argv.
|
|
2891
|
-
await server.
|
|
2858
|
+
if (argv.http) {
|
|
2859
|
+
await server.startHttp(argv.host, argv.port);
|
|
2892
2860
|
} else {
|
|
2893
2861
|
await server.startStdio();
|
|
2894
2862
|
}
|
package/server.ts
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
4
4
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
|
-
import {
|
|
5
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
6
|
+
import minimist from 'minimist';
|
|
7
|
+
import cors from "cors";
|
|
6
8
|
import express from "express";
|
|
7
9
|
import {Request, Response} from "express";
|
|
8
10
|
import axios, { AxiosRequestConfig, AxiosError, AxiosResponse } from "axios";
|
|
@@ -1745,34 +1747,6 @@ const TOOLS: OpenApiTool[] = [
|
|
|
1745
1747
|
}
|
|
1746
1748
|
]
|
|
1747
1749
|
},
|
|
1748
|
-
{
|
|
1749
|
-
"name": "VPS_deleteBackupV1",
|
|
1750
|
-
"description": "This endpoint deletes a specified backup for a virtual machine.",
|
|
1751
|
-
"method": "DELETE",
|
|
1752
|
-
"path": "/api/vps/v1/virtual-machines/{virtualMachineId}/backups/{backupId}",
|
|
1753
|
-
"inputSchema": {
|
|
1754
|
-
"type": "object",
|
|
1755
|
-
"properties": {
|
|
1756
|
-
"virtualMachineId": {
|
|
1757
|
-
"type": "integer",
|
|
1758
|
-
"description": "Virtual Machine ID"
|
|
1759
|
-
},
|
|
1760
|
-
"backupId": {
|
|
1761
|
-
"type": "integer",
|
|
1762
|
-
"description": "Backup ID"
|
|
1763
|
-
}
|
|
1764
|
-
},
|
|
1765
|
-
"required": [
|
|
1766
|
-
"virtualMachineId",
|
|
1767
|
-
"backupId"
|
|
1768
|
-
]
|
|
1769
|
-
},
|
|
1770
|
-
"security": [
|
|
1771
|
-
{
|
|
1772
|
-
"apiToken": []
|
|
1773
|
-
}
|
|
1774
|
-
]
|
|
1775
|
-
},
|
|
1776
1750
|
{
|
|
1777
1751
|
"name": "VPS_getBackupListV1",
|
|
1778
1752
|
"description": "This endpoint retrieves a list of backups for a specified virtual machine.",
|
|
@@ -2079,6 +2053,10 @@ const TOOLS: OpenApiTool[] = [
|
|
|
2079
2053
|
"ns2": {
|
|
2080
2054
|
"type": "string",
|
|
2081
2055
|
"description": "ns2 parameter"
|
|
2056
|
+
},
|
|
2057
|
+
"ns3": {
|
|
2058
|
+
"type": "string",
|
|
2059
|
+
"description": "ns3 parameter"
|
|
2082
2060
|
}
|
|
2083
2061
|
},
|
|
2084
2062
|
"required": [
|
|
@@ -2528,7 +2506,7 @@ const SECURITY_SCHEMES: Record<string, SecurityScheme> = {
|
|
|
2528
2506
|
|
|
2529
2507
|
/**
|
|
2530
2508
|
* MCP Server for Hostinger API
|
|
2531
|
-
* Generated from OpenAPI spec version 0.0.
|
|
2509
|
+
* Generated from OpenAPI spec version 0.0.86
|
|
2532
2510
|
*/
|
|
2533
2511
|
class MCPServer {
|
|
2534
2512
|
private server: Server;
|
|
@@ -2550,7 +2528,7 @@ class MCPServer {
|
|
|
2550
2528
|
this.server = new Server(
|
|
2551
2529
|
{
|
|
2552
2530
|
name: "hostinger-api-mcp",
|
|
2553
|
-
version: "0.0.
|
|
2531
|
+
version: "0.0.32",
|
|
2554
2532
|
},
|
|
2555
2533
|
{
|
|
2556
2534
|
capabilities: {
|
|
@@ -2575,7 +2553,7 @@ class MCPServer {
|
|
|
2575
2553
|
});
|
|
2576
2554
|
}
|
|
2577
2555
|
|
|
2578
|
-
headers['User-Agent'] = 'hostinger-mcp-server/0.0.
|
|
2556
|
+
headers['User-Agent'] = 'hostinger-mcp-server/0.0.32';
|
|
2579
2557
|
|
|
2580
2558
|
return headers;
|
|
2581
2559
|
}
|
|
@@ -2702,62 +2680,12 @@ class MCPServer {
|
|
|
2702
2680
|
return status < 500; // Resolve only if the status code is less than 500
|
|
2703
2681
|
}
|
|
2704
2682
|
};
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
if (
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
if (securityDefinition) {
|
|
2713
|
-
const authType = securityDefinition.type;
|
|
2714
|
-
|
|
2715
|
-
// Handle API key
|
|
2716
|
-
if (authType === 'apiKey') {
|
|
2717
|
-
const apiKeyName = securityDefinition.name || '';
|
|
2718
|
-
const envVarName = `${securitySchemeName.toUpperCase()}_${apiKeyName.toUpperCase()}`;
|
|
2719
|
-
const apiKeyValue = process.env[envVarName];
|
|
2720
|
-
|
|
2721
|
-
if (apiKeyValue) {
|
|
2722
|
-
if (securityDefinition.in === 'header') {
|
|
2723
|
-
config.headers = config.headers || {};
|
|
2724
|
-
config.headers[apiKeyName] = apiKeyValue;
|
|
2725
|
-
} else if (securityDefinition.in === 'query') {
|
|
2726
|
-
config.params = config.params || {};
|
|
2727
|
-
config.params[apiKeyName] = apiKeyValue;
|
|
2728
|
-
}
|
|
2729
|
-
} else {
|
|
2730
|
-
this.log('warning', `API Key environment variable not found: ${envVarName}`);
|
|
2731
|
-
}
|
|
2732
|
-
}
|
|
2733
|
-
// Handle bearer token
|
|
2734
|
-
else if (authType === 'http' && securityDefinition.scheme === 'bearer') {
|
|
2735
|
-
const envVarName = `${securitySchemeName.toUpperCase()}`;
|
|
2736
|
-
const bearerToken = process.env[envVarName];
|
|
2737
|
-
|
|
2738
|
-
if (bearerToken) {
|
|
2739
|
-
config.headers = config.headers || {};
|
|
2740
|
-
config.headers['Authorization'] = `Bearer ${bearerToken}`;
|
|
2741
|
-
} else {
|
|
2742
|
-
this.log('warning', `Bearer Token environment variable not found: ${envVarName}`);
|
|
2743
|
-
}
|
|
2744
|
-
}
|
|
2745
|
-
// Handle basic auth
|
|
2746
|
-
else if (authType === 'http' && securityDefinition.scheme === 'basic') {
|
|
2747
|
-
const username = process.env[`${securitySchemeName.toUpperCase()}_USERNAME`];
|
|
2748
|
-
const password = process.env[`${securitySchemeName.toUpperCase()}_PASSWORD`];
|
|
2749
|
-
|
|
2750
|
-
if (username && password) {
|
|
2751
|
-
const auth = Buffer.from(`${username}:${password}`).toString('base64');
|
|
2752
|
-
config.headers = config.headers || {};
|
|
2753
|
-
config.headers['Authorization'] = `Basic ${auth}`;
|
|
2754
|
-
} else {
|
|
2755
|
-
this.log('warning', `Basic auth credentials not found for ${securitySchemeName}`);
|
|
2756
|
-
}
|
|
2757
|
-
}
|
|
2758
|
-
}
|
|
2759
|
-
}
|
|
2760
|
-
}
|
|
2683
|
+
|
|
2684
|
+
const bearerToken = process.env['API_TOKEN'] || process.env['APITOKEN']; // APITOKEN for backwards compatibility
|
|
2685
|
+
if (bearerToken) {
|
|
2686
|
+
config.headers['Authorization'] = `Bearer ${bearerToken}`;
|
|
2687
|
+
} else {
|
|
2688
|
+
this.log('error', `Bearer Token environment variable not found: API_TOKEN`);
|
|
2761
2689
|
}
|
|
2762
2690
|
|
|
2763
2691
|
// Add parameters based on request method
|
|
@@ -2833,48 +2761,67 @@ class MCPServer {
|
|
|
2833
2761
|
}
|
|
2834
2762
|
}
|
|
2835
2763
|
}
|
|
2836
|
-
|
|
2764
|
+
|
|
2765
|
+
/**
|
|
2766
|
+
* Create and configure Express app with shared middleware
|
|
2767
|
+
*/
|
|
2768
|
+
createApp() {
|
|
2769
|
+
const app = express();
|
|
2770
|
+
app.use(express.json());
|
|
2771
|
+
app.use(cors());
|
|
2772
|
+
return app;
|
|
2773
|
+
}
|
|
2774
|
+
|
|
2837
2775
|
/**
|
|
2838
|
-
* Start the
|
|
2776
|
+
* Start the server with HTTP streaming transport
|
|
2839
2777
|
*/
|
|
2840
|
-
async
|
|
2778
|
+
async startHttp(host: string, port: number): Promise<void> {
|
|
2841
2779
|
try {
|
|
2842
|
-
|
|
2843
|
-
const app = express();
|
|
2844
|
-
app.use(express.json());
|
|
2780
|
+
const app = this.createApp();
|
|
2845
2781
|
|
|
2846
|
-
|
|
2847
|
-
const
|
|
2782
|
+
// Create HTTP transport with session management
|
|
2783
|
+
const httpTransport = new StreamableHTTPServerTransport({
|
|
2784
|
+
sessionIdGenerator: () => {
|
|
2785
|
+
// Generate a simple session ID
|
|
2786
|
+
return `session-${Date.now()}-${Math.random().toString(36).substring(7)}`;
|
|
2787
|
+
},
|
|
2788
|
+
});
|
|
2848
2789
|
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
res.
|
|
2854
|
-
|
|
2855
|
-
});
|
|
2856
|
-
|
|
2857
|
-
this.server.connect(transport);
|
|
2790
|
+
// Set up CORS for all routes
|
|
2791
|
+
app.options("*", (req, res) => {
|
|
2792
|
+
res.header("Access-Control-Allow-Origin", "*");
|
|
2793
|
+
res.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
|
|
2794
|
+
res.header("Access-Control-Allow-Headers", "Content-Type, Authorization, x-session-id");
|
|
2795
|
+
res.sendStatus(200);
|
|
2858
2796
|
});
|
|
2859
2797
|
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2798
|
+
// Health check endpoint
|
|
2799
|
+
app.get("/health", (req, res) => {
|
|
2800
|
+
res.status(200).json({ status: "ok", transport: "http" });
|
|
2801
|
+
});
|
|
2802
|
+
|
|
2803
|
+
// Set up the HTTP transport endpoint
|
|
2804
|
+
app.post("/", async (req, res) => {
|
|
2805
|
+
res.header("Access-Control-Allow-Origin", "*");
|
|
2806
|
+
res.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
|
|
2807
|
+
res.header("Access-Control-Allow-Headers", "Content-Type, Authorization, x-session-id");
|
|
2808
|
+
await httpTransport.handleRequest(req, res, req.body);
|
|
2809
|
+
});
|
|
2810
|
+
|
|
2811
|
+
// Start the server
|
|
2812
|
+
const server = app.listen(port, host, () => {
|
|
2813
|
+
this.log('info', `MCP Server with HTTP streaming transport started successfully with ${this.tools.size} tools`);
|
|
2814
|
+
this.log('info', `Listening on http://${host}:${port}`);
|
|
2868
2815
|
});
|
|
2869
2816
|
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2817
|
+
// Connect the MCP server to the transport
|
|
2818
|
+
await this.server.connect(httpTransport);
|
|
2819
|
+
|
|
2873
2820
|
} catch (error) {
|
|
2874
2821
|
console.error("Failed to start MCP server:", error);
|
|
2875
2822
|
process.exit(1);
|
|
2876
2823
|
}
|
|
2877
|
-
}
|
|
2824
|
+
}
|
|
2878
2825
|
|
|
2879
2826
|
/**
|
|
2880
2827
|
* Start the stdio server
|
|
@@ -2904,17 +2851,35 @@ async function main(): Promise<void> {
|
|
|
2904
2851
|
const argv = minimist(process.argv.slice(2), {
|
|
2905
2852
|
string: ['host'],
|
|
2906
2853
|
int: ['port'],
|
|
2907
|
-
boolean: ['
|
|
2854
|
+
boolean: ['stdio', 'http', 'help'],
|
|
2908
2855
|
default: {
|
|
2909
2856
|
host: '127.0.0.1',
|
|
2910
2857
|
port: 8100,
|
|
2858
|
+
stdio: true,
|
|
2911
2859
|
}
|
|
2912
2860
|
});
|
|
2913
2861
|
|
|
2914
|
-
|
|
2862
|
+
// Show help if requested
|
|
2863
|
+
if (argv.help) {
|
|
2864
|
+
console.log(`
|
|
2865
|
+
Hostinger API MCP Server
|
|
2866
|
+
Usage: hostinger-api-mcp [options]
|
|
2867
|
+
Options:
|
|
2868
|
+
--http Use HTTP streaming transport
|
|
2869
|
+
--stdio Use standard input/output transport (default)
|
|
2870
|
+
--host <host> Host to bind to (default: 127.0.0.1)
|
|
2871
|
+
--port <port> Port to bind to (default: 8100)
|
|
2872
|
+
--help Show this help message
|
|
2873
|
+
Environment Variables:
|
|
2874
|
+
API_TOKEN Your Hostinger API token (required)
|
|
2875
|
+
DEBUG Enable debug logging (true/false)
|
|
2876
|
+
`);
|
|
2877
|
+
process.exit(0);
|
|
2878
|
+
}
|
|
2915
2879
|
|
|
2916
|
-
|
|
2917
|
-
|
|
2880
|
+
const server = new MCPServer();
|
|
2881
|
+
if (argv.http) {
|
|
2882
|
+
await server.startHttp(argv.host, argv.port);
|
|
2918
2883
|
} else {
|
|
2919
2884
|
await server.startStdio();
|
|
2920
2885
|
}
|
package/tsconfig.json
CHANGED
package/types.d.ts
CHANGED
|
@@ -1007,23 +1007,6 @@ such as the action name, timestamp, and status.
|
|
|
1007
1007
|
response: any; // Response structure will depend on the API
|
|
1008
1008
|
};
|
|
1009
1009
|
|
|
1010
|
-
/**
|
|
1011
|
-
* This endpoint deletes a specified backup for a virtual machine.
|
|
1012
|
-
*/
|
|
1013
|
-
"undefined": {
|
|
1014
|
-
params: {
|
|
1015
|
-
/**
|
|
1016
|
-
* Virtual Machine ID
|
|
1017
|
-
*/
|
|
1018
|
-
virtualMachineId: number;
|
|
1019
|
-
/**
|
|
1020
|
-
* Backup ID
|
|
1021
|
-
*/
|
|
1022
|
-
backupId: number;
|
|
1023
|
-
};
|
|
1024
|
-
response: any; // Response structure will depend on the API
|
|
1025
|
-
};
|
|
1026
|
-
|
|
1027
1010
|
/**
|
|
1028
1011
|
* This endpoint retrieves a list of backups for a specified virtual machine.
|
|
1029
1012
|
*/
|
|
@@ -1238,6 +1221,10 @@ Be aware, that improper nameserver configuration can lead to the virtual machine
|
|
|
1238
1221
|
* ns2 parameter
|
|
1239
1222
|
*/
|
|
1240
1223
|
ns2?: string;
|
|
1224
|
+
/**
|
|
1225
|
+
* ns3 parameter
|
|
1226
|
+
*/
|
|
1227
|
+
ns3?: string;
|
|
1241
1228
|
};
|
|
1242
1229
|
response: any; // Response structure will depend on the API
|
|
1243
1230
|
};
|