@serenichron/mcp-cloudron 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Serenichron
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,189 @@
1
+ # mcp-cloudron
2
+
3
+ MCP server for [Cloudron](https://cloudron.io) instance management. List apps, get status, and manage your self-hosted applications through the Model Context Protocol.
4
+
5
+ ## Features
6
+
7
+ - **List Applications**: Get all installed apps with status, health, and memory usage
8
+ - **Get App Details**: Retrieve detailed information about specific applications
9
+ - **Instance Status**: Check Cloudron version, provider, and configuration
10
+
11
+ ## Installation
12
+
13
+ ```bash
14
+ npm install @serenichron/mcp-cloudron
15
+ ```
16
+
17
+ Or run directly with npx:
18
+
19
+ ```bash
20
+ npx @serenichron/mcp-cloudron
21
+ ```
22
+
23
+ ## Configuration
24
+
25
+ ### Environment Variables
26
+
27
+ | Variable | Required | Description |
28
+ |----------|----------|-------------|
29
+ | `CLOUDRON_BASE_URL` | Yes | Your Cloudron instance URL (e.g., `https://my.cloudron.io`) |
30
+ | `CLOUDRON_API_TOKEN` | Yes | API token from Cloudron Admin Panel |
31
+
32
+ ### Getting an API Token
33
+
34
+ 1. Log in to your Cloudron Admin Panel
35
+ 2. Go to **Settings → API Tokens**
36
+ 3. Click **Create API Token**
37
+ 4. Give it a name (e.g., "MCP Server")
38
+ 5. Copy the generated token
39
+
40
+ ## Usage with Claude Desktop
41
+
42
+ Add to your Claude Desktop configuration (`claude_desktop_config.json`):
43
+
44
+ ```json
45
+ {
46
+ "mcpServers": {
47
+ "cloudron": {
48
+ "command": "npx",
49
+ "args": ["@serenichron/mcp-cloudron"],
50
+ "env": {
51
+ "CLOUDRON_BASE_URL": "https://your-cloudron-instance.com",
52
+ "CLOUDRON_API_TOKEN": "your-api-token"
53
+ }
54
+ }
55
+ }
56
+ }
57
+ ```
58
+
59
+ ## Usage with Docker MCP Gateway
60
+
61
+ Add to your Docker MCP config (`~/.docker/mcp/config.yaml`):
62
+
63
+ ```yaml
64
+ mcpServers:
65
+ cloudron:
66
+ command: npx
67
+ args: ["@serenichron/mcp-cloudron"]
68
+ env:
69
+ CLOUDRON_BASE_URL: "https://your-cloudron-instance.com"
70
+ CLOUDRON_API_TOKEN: "your-api-token"
71
+ ```
72
+
73
+ ## Available Tools
74
+
75
+ ### cloudron_list_apps
76
+
77
+ List all installed applications on the Cloudron instance.
78
+
79
+ **Parameters**: None
80
+
81
+ **Returns**: List of apps with name, domain, ID, state, health, and memory usage.
82
+
83
+ **Example output**:
84
+ ```
85
+ Found 3 apps:
86
+
87
+ WordPress (blog.example.com)
88
+ ID: abc123-def456
89
+ State: installed
90
+ Health: healthy
91
+ Memory: 512 MB
92
+
93
+ GitLab (git.example.com)
94
+ ID: xyz789-uvw012
95
+ State: installed
96
+ Health: healthy
97
+ Memory: 4096 MB
98
+ ```
99
+
100
+ ### cloudron_get_app
101
+
102
+ Get detailed information about a specific application.
103
+
104
+ **Parameters**:
105
+ | Name | Type | Required | Description |
106
+ |------|------|----------|-------------|
107
+ | `appId` | string | Yes | The unique identifier of the application |
108
+
109
+ **Returns**: App details including name, domain, state, health, and memory.
110
+
111
+ ### cloudron_get_status
112
+
113
+ Get the current status and configuration of the Cloudron instance.
114
+
115
+ **Parameters**: None
116
+
117
+ **Returns**: Instance information including name, version, admin URL, provider, and demo mode status.
118
+
119
+ **Example output**:
120
+ ```
121
+ Cloudron Status:
122
+ Name: My Cloudron
123
+ Version: 9.0.13
124
+ Admin URL: my.cloudron.io
125
+ Provider: digitalocean
126
+ Demo Mode: false
127
+ ```
128
+
129
+ ## Development
130
+
131
+ ### Setup
132
+
133
+ ```bash
134
+ git clone https://github.com/serenichron/mcp-cloudron.git
135
+ cd mcp-cloudron
136
+ npm install
137
+ ```
138
+
139
+ ### Build
140
+
141
+ ```bash
142
+ npm run build
143
+ ```
144
+
145
+ ### Run locally
146
+
147
+ ```bash
148
+ export CLOUDRON_BASE_URL="https://your-instance.com"
149
+ export CLOUDRON_API_TOKEN="your-token"
150
+ npm start
151
+ ```
152
+
153
+ ### Test
154
+
155
+ ```bash
156
+ npm test
157
+ ```
158
+
159
+ ## API Reference
160
+
161
+ The server uses the [Cloudron REST API](https://docs.cloudron.io/api/). Currently implemented endpoints:
162
+
163
+ - `GET /api/v1/apps` - List all applications
164
+ - `GET /api/v1/apps/:id` - Get application by ID
165
+ - `GET /api/v1/cloudron/status` - Get instance status
166
+
167
+ ## Roadmap
168
+
169
+ Future versions may include:
170
+
171
+ - [ ] App lifecycle management (start, stop, restart)
172
+ - [ ] Backup operations
173
+ - [ ] User management
174
+ - [ ] Domain configuration
175
+ - [ ] App installation from App Store
176
+
177
+ ## License
178
+
179
+ MIT - See [LICENSE](LICENSE) for details.
180
+
181
+ ## Contributing
182
+
183
+ Contributions welcome! Please open an issue or submit a pull request.
184
+
185
+ ## Related
186
+
187
+ - [Cloudron Documentation](https://docs.cloudron.io/)
188
+ - [Model Context Protocol](https://modelcontextprotocol.io/)
189
+ - [MCP TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk)
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Cloudron API Client
3
+ * MVP scope: listApps + getApp endpoints
4
+ * DI-enabled for testing
5
+ */
6
+ import type { CloudronClientConfig, App, SystemStatus } from './types.js';
7
+ export declare class CloudronClient {
8
+ private readonly baseUrl;
9
+ private readonly token;
10
+ /**
11
+ * Create CloudronClient with DI support
12
+ * @param config - Optional config (defaults to env vars)
13
+ */
14
+ constructor(config?: Partial<CloudronClientConfig>);
15
+ /**
16
+ * Make HTTP request to Cloudron API
17
+ * NO retry logic (deferred to Phase 3 with idempotency keys)
18
+ */
19
+ private makeRequest;
20
+ /**
21
+ * List all installed apps
22
+ * GET /api/v1/apps
23
+ */
24
+ listApps(): Promise<App[]>;
25
+ /**
26
+ * Get a specific app by ID
27
+ * GET /api/v1/apps/:appId
28
+ *
29
+ * Note: API returns app object directly, not wrapped in { app: {...} }
30
+ */
31
+ getApp(appId: string): Promise<App>;
32
+ /**
33
+ * Get Cloudron system status
34
+ * GET /api/v1/cloudron/status
35
+ */
36
+ getStatus(): Promise<SystemStatus>;
37
+ }
38
+ //# sourceMappingURL=cloudron-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloudron-client.d.ts","sourceRoot":"","sources":["../src/cloudron-client.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,GAAG,EAA6B,YAAY,EAAE,MAAM,YAAY,CAAC;AAKrG,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAE/B;;;OAGG;gBACS,MAAM,CAAC,EAAE,OAAO,CAAC,oBAAoB,CAAC;IAelD;;;OAGG;YACW,WAAW;IAiEzB;;;OAGG;IACG,QAAQ,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IAKhC;;;;;OAKG;IACG,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC;IAOzC;;;OAGG;IACG,SAAS,IAAI,OAAO,CAAC,YAAY,CAAC;CAGzC"}
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Cloudron API Client
3
+ * MVP scope: listApps + getApp endpoints
4
+ * DI-enabled for testing
5
+ */
6
+ import { CloudronError, CloudronAuthError, createErrorFromStatus } from './errors.js';
7
+ const DEFAULT_TIMEOUT = 30000;
8
+ export class CloudronClient {
9
+ baseUrl;
10
+ token;
11
+ /**
12
+ * Create CloudronClient with DI support
13
+ * @param config - Optional config (defaults to env vars)
14
+ */
15
+ constructor(config) {
16
+ const baseUrl = config?.baseUrl ?? process.env.CLOUDRON_BASE_URL;
17
+ const token = config?.token ?? process.env.CLOUDRON_API_TOKEN;
18
+ if (!baseUrl) {
19
+ throw new CloudronError('CLOUDRON_BASE_URL not set. Provide via config or environment variable.');
20
+ }
21
+ if (!token) {
22
+ throw new CloudronError('CLOUDRON_API_TOKEN not set. Provide via config or environment variable.');
23
+ }
24
+ this.baseUrl = baseUrl.replace(/\/$/, ''); // Remove trailing slash
25
+ this.token = token;
26
+ }
27
+ /**
28
+ * Make HTTP request to Cloudron API
29
+ * NO retry logic (deferred to Phase 3 with idempotency keys)
30
+ */
31
+ async makeRequest(method, endpoint, body, options) {
32
+ const url = `${this.baseUrl}${endpoint}`;
33
+ const timeout = options?.timeout ?? DEFAULT_TIMEOUT;
34
+ const controller = new AbortController();
35
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
36
+ try {
37
+ const fetchOptions = {
38
+ method,
39
+ headers: {
40
+ 'Authorization': `Bearer ${this.token}`,
41
+ 'Content-Type': 'application/json',
42
+ 'Accept': 'application/json',
43
+ },
44
+ signal: controller.signal,
45
+ };
46
+ if (body !== undefined) {
47
+ fetchOptions.body = JSON.stringify(body);
48
+ }
49
+ const response = await fetch(url, fetchOptions);
50
+ clearTimeout(timeoutId);
51
+ if (!response.ok) {
52
+ const errorBody = await response.text();
53
+ let message = `Cloudron API error: ${response.status} ${response.statusText}`;
54
+ try {
55
+ const parsed = JSON.parse(errorBody);
56
+ if (parsed.message)
57
+ message = parsed.message;
58
+ }
59
+ catch {
60
+ // Use default message if body isn't JSON
61
+ }
62
+ throw createErrorFromStatus(response.status, message);
63
+ }
64
+ return await response.json();
65
+ }
66
+ catch (error) {
67
+ clearTimeout(timeoutId);
68
+ if (error instanceof CloudronError) {
69
+ throw error;
70
+ }
71
+ if (error instanceof Error) {
72
+ if (error.name === 'AbortError') {
73
+ throw new CloudronError(`Request timeout after ${timeout}ms`, undefined, 'TIMEOUT');
74
+ }
75
+ throw new CloudronError(`Network error: ${error.message}`, undefined, 'NETWORK_ERROR');
76
+ }
77
+ throw new CloudronError('Unknown error occurred');
78
+ }
79
+ }
80
+ // ==================== MVP Endpoints ====================
81
+ /**
82
+ * List all installed apps
83
+ * GET /api/v1/apps
84
+ */
85
+ async listApps() {
86
+ const response = await this.makeRequest('GET', '/api/v1/apps');
87
+ return response.apps;
88
+ }
89
+ /**
90
+ * Get a specific app by ID
91
+ * GET /api/v1/apps/:appId
92
+ *
93
+ * Note: API returns app object directly, not wrapped in { app: {...} }
94
+ */
95
+ async getApp(appId) {
96
+ if (!appId) {
97
+ throw new CloudronError('appId is required');
98
+ }
99
+ return await this.makeRequest('GET', `/api/v1/apps/${encodeURIComponent(appId)}`);
100
+ }
101
+ /**
102
+ * Get Cloudron system status
103
+ * GET /api/v1/cloudron/status
104
+ */
105
+ async getStatus() {
106
+ return await this.makeRequest('GET', '/api/v1/cloudron/status');
107
+ }
108
+ }
109
+ //# sourceMappingURL=cloudron-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloudron-client.js","sourceRoot":"","sources":["../src/cloudron-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Cloudron Error Classes
3
+ * MVP scope: Base error + Auth error only
4
+ */
5
+ /** Base error for all Cloudron API errors */
6
+ export declare class CloudronError extends Error {
7
+ readonly statusCode: number | undefined;
8
+ readonly code: string | undefined;
9
+ constructor(message: string, statusCode?: number, code?: string);
10
+ /**
11
+ * Check if error is retryable (for future Phase 3)
12
+ * 429 (rate limit) and 5xx errors are retryable
13
+ * 4xx errors (except 429) are NOT retryable
14
+ */
15
+ isRetryable(): boolean;
16
+ }
17
+ /** Authentication/Authorization error (401/403) */
18
+ export declare class CloudronAuthError extends CloudronError {
19
+ constructor(message?: string, statusCode?: number);
20
+ }
21
+ /**
22
+ * Type guard for CloudronError
23
+ * Usage: if (isCloudronError(error)) { ... }
24
+ */
25
+ export declare function isCloudronError(error: unknown): error is CloudronError;
26
+ /**
27
+ * Create appropriate error from HTTP status code
28
+ * Routes 401/403 to CloudronAuthError, others to CloudronError
29
+ */
30
+ export declare function createErrorFromStatus(statusCode: number, message: string): CloudronError;
31
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,6CAA6C;AAC7C,qBAAa,aAAc,SAAQ,KAAK;IACtC,SAAgB,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/C,SAAgB,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;gBAE7B,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM;IAY/D;;;;OAIG;IACH,WAAW,IAAI,OAAO;CAIvB;AAED,mDAAmD;AACnD,qBAAa,iBAAkB,SAAQ,aAAa;gBAEhD,OAAO,SAAqD,EAC5D,UAAU,SAAM;CAKnB;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,aAAa,CAEtE;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,GACd,aAAa,CAKf"}
package/dist/errors.js ADDED
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Cloudron Error Classes
3
+ * MVP scope: Base error + Auth error only
4
+ */
5
+ /** Base error for all Cloudron API errors */
6
+ export class CloudronError extends Error {
7
+ statusCode;
8
+ code;
9
+ constructor(message, statusCode, code) {
10
+ super(message);
11
+ this.name = 'CloudronError';
12
+ this.statusCode = statusCode ?? undefined;
13
+ this.code = code ?? undefined;
14
+ // Maintains proper stack trace in V8 engines
15
+ if (Error.captureStackTrace) {
16
+ Error.captureStackTrace(this, CloudronError);
17
+ }
18
+ }
19
+ /**
20
+ * Check if error is retryable (for future Phase 3)
21
+ * 429 (rate limit) and 5xx errors are retryable
22
+ * 4xx errors (except 429) are NOT retryable
23
+ */
24
+ isRetryable() {
25
+ if (!this.statusCode)
26
+ return false;
27
+ return this.statusCode === 429 || this.statusCode >= 500;
28
+ }
29
+ }
30
+ /** Authentication/Authorization error (401/403) */
31
+ export class CloudronAuthError extends CloudronError {
32
+ constructor(message = 'Authentication failed. Check CLOUDRON_API_TOKEN.', statusCode = 401) {
33
+ super(message, statusCode, 'AUTH_ERROR');
34
+ this.name = 'CloudronAuthError';
35
+ }
36
+ }
37
+ /**
38
+ * Type guard for CloudronError
39
+ * Usage: if (isCloudronError(error)) { ... }
40
+ */
41
+ export function isCloudronError(error) {
42
+ return error instanceof CloudronError;
43
+ }
44
+ /**
45
+ * Create appropriate error from HTTP status code
46
+ * Routes 401/403 to CloudronAuthError, others to CloudronError
47
+ */
48
+ export function createErrorFromStatus(statusCode, message) {
49
+ if (statusCode === 401 || statusCode === 403) {
50
+ return new CloudronAuthError(message, statusCode);
51
+ }
52
+ return new CloudronError(message, statusCode);
53
+ }
54
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,6CAA6C;AAC7C,MAAM,OAAO,aAAc,SAAQ,KAAK;IACtB,UAAU,CAAqB;IAC/B,IAAI,CAAqB;IAEzC,YAAY,OAAe,EAAE,UAAmB,EAAE,IAAa;QAC7D,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;QAC5B,IAAI,CAAC,UAAU,GAAG,UAAU,IAAI,SAAS,CAAC;QAC1C,IAAI,CAAC,IAAI,GAAG,IAAI,IAAI,SAAS,CAAC;QAE9B,6CAA6C;QAC7C,IAAI,KAAK,CAAC,iBAAiB,EAAE,CAAC;YAC5B,KAAK,CAAC,iBAAiB,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC;IAED;;;;OAIG;IACH,WAAW;QACT,IAAI,CAAC,IAAI,CAAC,UAAU;YAAE,OAAO,KAAK,CAAC;QACnC,OAAO,IAAI,CAAC,UAAU,KAAK,GAAG,IAAI,IAAI,CAAC,UAAU,IAAI,GAAG,CAAC;IAC3D,CAAC;CACF;AAED,mDAAmD;AACnD,MAAM,OAAO,iBAAkB,SAAQ,aAAa;IAClD,YACE,OAAO,GAAG,kDAAkD,EAC5D,UAAU,GAAG,GAAG;QAEhB,KAAK,CAAC,OAAO,EAAE,UAAU,EAAE,YAAY,CAAC,CAAC;QACzC,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;IAClC,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,KAAc;IAC5C,OAAO,KAAK,YAAY,aAAa,CAAC;AACxC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CACnC,UAAkB,EAClB,OAAe;IAEf,IAAI,UAAU,KAAK,GAAG,IAAI,UAAU,KAAK,GAAG,EAAE,CAAC;QAC7C,OAAO,IAAI,iBAAiB,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;IACpD,CAAC;IACD,OAAO,IAAI,aAAa,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;AAChD,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Cloudron MCP Client
3
+ * MVP Phase 2 Implementation
4
+ */
5
+ export { CloudronClient } from './cloudron-client.js';
6
+ export type { CloudronClientConfig, App, AppManifest, AppsResponse, AppResponse, SystemStatus, } from './types.js';
7
+ export { CloudronError, CloudronAuthError, isCloudronError, createErrorFromStatus, } from './errors.js';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAGtD,YAAY,EACV,oBAAoB,EACpB,GAAG,EACH,WAAW,EACX,YAAY,EACZ,WAAW,EACX,YAAY,GACb,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,qBAAqB,GACtB,MAAM,aAAa,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Cloudron MCP Client
3
+ * MVP Phase 2 Implementation
4
+ */
5
+ // Main client
6
+ export { CloudronClient } from './cloudron-client.js';
7
+ // Errors
8
+ export { CloudronError, CloudronAuthError, isCloudronError, createErrorFromStatus, } from './errors.js';
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc;AACd,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAYtD,SAAS;AACT,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,qBAAqB,GACtB,MAAM,aAAa,CAAC"}
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Cloudron MCP Server
4
+ * Provides tools for managing Cloudron instances via MCP protocol
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":";AACA;;;GAGG"}
package/dist/server.js ADDED
@@ -0,0 +1,151 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Cloudron MCP Server
4
+ * Provides tools for managing Cloudron instances via MCP protocol
5
+ */
6
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
7
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
8
+ import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
9
+ import { CloudronClient } from './cloudron-client.js';
10
+ import { isCloudronError } from './errors.js';
11
+ // Tool definitions
12
+ const TOOLS = [
13
+ {
14
+ name: 'cloudron_list_apps',
15
+ description: 'List all installed applications on the Cloudron instance. Returns app details including name, domain, status, and health.',
16
+ inputSchema: {
17
+ type: 'object',
18
+ properties: {},
19
+ required: [],
20
+ },
21
+ },
22
+ {
23
+ name: 'cloudron_get_app',
24
+ description: 'Get detailed information about a specific application by its ID.',
25
+ inputSchema: {
26
+ type: 'object',
27
+ properties: {
28
+ appId: {
29
+ type: 'string',
30
+ description: 'The unique identifier of the application',
31
+ },
32
+ },
33
+ required: ['appId'],
34
+ },
35
+ },
36
+ {
37
+ name: 'cloudron_get_status',
38
+ description: 'Get the current status and configuration of the Cloudron instance.',
39
+ inputSchema: {
40
+ type: 'object',
41
+ properties: {},
42
+ required: [],
43
+ },
44
+ },
45
+ ];
46
+ // Create server instance
47
+ const server = new Server({
48
+ name: 'cloudron-mcp',
49
+ version: '0.1.0',
50
+ }, {
51
+ capabilities: {
52
+ tools: {},
53
+ },
54
+ });
55
+ // Lazy-initialize client (validates env vars on first use)
56
+ let client = null;
57
+ function getClient() {
58
+ if (!client) {
59
+ client = new CloudronClient();
60
+ }
61
+ return client;
62
+ }
63
+ // Format app for display
64
+ function formatApp(app) {
65
+ const fqdn = app.location ? `${app.location}.${app.domain}` : app.domain;
66
+ return `${app.manifest.title} (${fqdn})
67
+ ID: ${app.id}
68
+ State: ${app.installationState}
69
+ Health: ${app.health ?? 'unknown'}
70
+ Memory: ${Math.round(app.memoryLimit / 1024 / 1024)} MB`;
71
+ }
72
+ // Handle list tools request
73
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
74
+ tools: TOOLS,
75
+ }));
76
+ // Handle tool calls
77
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
78
+ const { name, arguments: args } = request.params;
79
+ try {
80
+ const cloudron = getClient();
81
+ switch (name) {
82
+ case 'cloudron_list_apps': {
83
+ const apps = await cloudron.listApps();
84
+ const formatted = apps.map(formatApp).join('\n\n');
85
+ return {
86
+ content: [
87
+ {
88
+ type: 'text',
89
+ text: `Found ${apps.length} apps:\n\n${formatted}`,
90
+ },
91
+ ],
92
+ };
93
+ }
94
+ case 'cloudron_get_app': {
95
+ const appId = args.appId;
96
+ const app = await cloudron.getApp(appId);
97
+ return {
98
+ content: [
99
+ {
100
+ type: 'text',
101
+ text: formatApp(app),
102
+ },
103
+ ],
104
+ };
105
+ }
106
+ case 'cloudron_get_status': {
107
+ const status = await cloudron.getStatus();
108
+ return {
109
+ content: [
110
+ {
111
+ type: 'text',
112
+ text: `Cloudron Status:
113
+ Name: ${status.cloudronName}
114
+ Version: ${status.version}
115
+ Admin URL: ${status.adminFqdn}
116
+ Provider: ${status.provider}
117
+ Demo Mode: ${status.isDemo}`,
118
+ },
119
+ ],
120
+ };
121
+ }
122
+ default:
123
+ return {
124
+ content: [{ type: 'text', text: `Unknown tool: ${name}` }],
125
+ isError: true,
126
+ };
127
+ }
128
+ }
129
+ catch (error) {
130
+ const message = isCloudronError(error)
131
+ ? `Cloudron API Error: ${error.message} (${error.statusCode ?? 'unknown'})`
132
+ : error instanceof Error
133
+ ? error.message
134
+ : 'Unknown error occurred';
135
+ return {
136
+ content: [{ type: 'text', text: message }],
137
+ isError: true,
138
+ };
139
+ }
140
+ });
141
+ // Main entry point
142
+ async function main() {
143
+ const transport = new StdioServerTransport();
144
+ await server.connect(transport);
145
+ console.error('Cloudron MCP server running on stdio');
146
+ }
147
+ main().catch((error) => {
148
+ console.error('Fatal error:', error);
149
+ process.exit(1);
150
+ });
151
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":";AACA;;;GAGG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EACL,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,oCAAoC,CAAC;AAE5C,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAG9C,mBAAmB;AACnB,MAAM,KAAK,GAAG;IACZ;QACE,IAAI,EAAE,oBAAoB;QAC1B,WAAW,EAAE,2HAA2H;QACxI,WAAW,EAAE;YACX,IAAI,EAAE,QAAiB;YACvB,UAAU,EAAE,EAAE;YACd,QAAQ,EAAE,EAAE;SACb;KACF;IACD;QACE,IAAI,EAAE,kBAAkB;QACxB,WAAW,EAAE,kEAAkE;QAC/E,WAAW,EAAE;YACX,IAAI,EAAE,QAAiB;YACvB,UAAU,EAAE;gBACV,KAAK,EAAE;oBACL,IAAI,EAAE,QAAQ;oBACd,WAAW,EAAE,0CAA0C;iBACxD;aACF;YACD,QAAQ,EAAE,CAAC,OAAO,CAAC;SACpB;KACF;IACD;QACE,IAAI,EAAE,qBAAqB;QAC3B,WAAW,EAAE,oEAAoE;QACjF,WAAW,EAAE;YACX,IAAI,EAAE,QAAiB;YACvB,UAAU,EAAE,EAAE;YACd,QAAQ,EAAE,EAAE;SACb;KACF;CACF,CAAC;AAEF,yBAAyB;AACzB,MAAM,MAAM,GAAG,IAAI,MAAM,CACvB;IACE,IAAI,EAAE,cAAc;IACpB,OAAO,EAAE,OAAO;CACjB,EACD;IACE,YAAY,EAAE;QACZ,KAAK,EAAE,EAAE;KACV;CACF,CACF,CAAC;AAEF,2DAA2D;AAC3D,IAAI,MAAM,GAA0B,IAAI,CAAC;AAEzC,SAAS,SAAS;IAChB,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,GAAG,IAAI,cAAc,EAAE,CAAC;IAChC,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,yBAAyB;AACzB,SAAS,SAAS,CAAC,GAAQ;IACzB,MAAM,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC;IACzE,OAAO,GAAG,GAAG,CAAC,QAAQ,CAAC,KAAK,KAAK,IAAI;QAC/B,GAAG,CAAC,EAAE;WACH,GAAG,CAAC,iBAAiB;YACpB,GAAG,CAAC,MAAM,IAAI,SAAS;YACvB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,WAAW,GAAG,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC;AAC3D,CAAC;AAED,4BAA4B;AAC5B,MAAM,CAAC,iBAAiB,CAAC,sBAAsB,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC;IAC5D,KAAK,EAAE,KAAK;CACb,CAAC,CAAC,CAAC;AAEJ,oBAAoB;AACpB,MAAM,CAAC,iBAAiB,CAAC,qBAAqB,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;IAChE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;IAEjD,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,SAAS,EAAE,CAAC;QAE7B,QAAQ,IAAI,EAAE,CAAC;YACb,KAAK,oBAAoB,CAAC,CAAC,CAAC;gBAC1B,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,CAAC;gBACvC,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBACnD,OAAO;oBACL,OAAO,EAAE;wBACP;4BACE,IAAI,EAAE,MAAe;4BACrB,IAAI,EAAE,SAAS,IAAI,CAAC,MAAM,aAAa,SAAS,EAAE;yBACnD;qBACF;iBACF,CAAC;YACJ,CAAC;YAED,KAAK,kBAAkB,CAAC,CAAC,CAAC;gBACxB,MAAM,KAAK,GAAI,IAA0B,CAAC,KAAK,CAAC;gBAChD,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gBACzC,OAAO;oBACL,OAAO,EAAE;wBACP;4BACE,IAAI,EAAE,MAAe;4BACrB,IAAI,EAAE,SAAS,CAAC,GAAG,CAAC;yBACrB;qBACF;iBACF,CAAC;YACJ,CAAC;YAED,KAAK,qBAAqB,CAAC,CAAC,CAAC;gBAC3B,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,SAAS,EAAE,CAAC;gBAC1C,OAAO;oBACL,OAAO,EAAE;wBACP;4BACE,IAAI,EAAE,MAAe;4BACrB,IAAI,EAAE;UACV,MAAM,CAAC,YAAY;aAChB,MAAM,CAAC,OAAO;eACZ,MAAM,CAAC,SAAS;cACjB,MAAM,CAAC,QAAQ;eACd,MAAM,CAAC,MAAM,EAAE;yBACjB;qBACF;iBACF,CAAC;YACJ,CAAC;YAED;gBACE,OAAO;oBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,iBAAiB,IAAI,EAAE,EAAE,CAAC;oBACnE,OAAO,EAAE,IAAI;iBACd,CAAC;QACN,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,OAAO,GAAG,eAAe,CAAC,KAAK,CAAC;YACpC,CAAC,CAAC,uBAAuB,KAAK,CAAC,OAAO,KAAK,KAAK,CAAC,UAAU,IAAI,SAAS,GAAG;YAC3E,CAAC,CAAC,KAAK,YAAY,KAAK;gBACtB,CAAC,CAAC,KAAK,CAAC,OAAO;gBACf,CAAC,CAAC,wBAAwB,CAAC;QAE/B,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;YACnD,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,mBAAmB;AACnB,KAAK,UAAU,IAAI;IACjB,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAChC,OAAO,CAAC,KAAK,CAAC,sCAAsC,CAAC,CAAC;AACxD,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,OAAO,CAAC,KAAK,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC;IACrC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
package/dist/test.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Integration Test for CloudronClient
3
+ * Tests against real Cloudron instance
4
+ */
5
+ export {};
6
+ //# sourceMappingURL=test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test.d.ts","sourceRoot":"","sources":["../src/test.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
package/dist/test.js ADDED
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Integration Test for CloudronClient
3
+ * Tests against real Cloudron instance
4
+ */
5
+ import { CloudronClient, CloudronError, isCloudronError } from './index.js';
6
+ async function runTests() {
7
+ console.log('=== Cloudron MCP Client Integration Test ===\n');
8
+ // Check environment variables
9
+ const baseUrl = process.env.CLOUDRON_BASE_URL;
10
+ const token = process.env.CLOUDRON_API_TOKEN;
11
+ if (!baseUrl || !token) {
12
+ console.error('❌ Missing environment variables:');
13
+ if (!baseUrl)
14
+ console.error(' - CLOUDRON_BASE_URL not set');
15
+ if (!token)
16
+ console.error(' - CLOUDRON_API_TOKEN not set');
17
+ console.error('\nSet these variables and run again.');
18
+ process.exit(1);
19
+ }
20
+ console.log(`📡 Connecting to: ${baseUrl}\n`);
21
+ try {
22
+ // Create client
23
+ const client = new CloudronClient();
24
+ console.log('✅ Client created successfully\n');
25
+ // Test 1: List Apps
26
+ console.log('--- Test 1: listApps() ---');
27
+ const apps = await client.listApps();
28
+ console.log(`✅ Found ${apps.length} apps:`);
29
+ for (const app of apps.slice(0, 5)) { // Show first 5
30
+ const fqdn = app.location ? `${app.location}.${app.domain}` : app.domain;
31
+ console.log(` - ${app.manifest.title} (${fqdn})`);
32
+ console.log(` State: ${app.installationState}, Health: ${app.health}`);
33
+ }
34
+ if (apps.length > 5) {
35
+ console.log(` ... and ${apps.length - 5} more\n`);
36
+ }
37
+ else {
38
+ console.log('');
39
+ }
40
+ // Test 2: Get Single App (if we have any apps)
41
+ if (apps.length > 0) {
42
+ const firstApp = apps[0];
43
+ if (!firstApp) {
44
+ throw new Error('First app is undefined');
45
+ }
46
+ console.log(`--- Test 2: getApp('${firstApp.id}') ---`);
47
+ const app = await client.getApp(firstApp.id);
48
+ console.log(`✅ Retrieved app: ${app.manifest.title}`);
49
+ console.log(` ID: ${app.id}`);
50
+ console.log(` FQDN: ${app.fqdn}`);
51
+ console.log(` Memory: ${app.memoryLimit} bytes`);
52
+ console.log(` Created: ${app.creationTime}\n`);
53
+ }
54
+ // Test 3: Error handling - invalid app ID
55
+ console.log('--- Test 3: Error Handling (invalid app ID) ---');
56
+ try {
57
+ await client.getApp('non-existent-app-id-12345');
58
+ console.log('❌ Expected error but got success');
59
+ }
60
+ catch (error) {
61
+ if (isCloudronError(error)) {
62
+ console.log(`✅ Caught CloudronError as expected`);
63
+ console.log(` Message: ${error.message}`);
64
+ console.log(` Status: ${error.statusCode}`);
65
+ console.log(` Retryable: ${error.isRetryable()}\n`);
66
+ }
67
+ else {
68
+ throw error;
69
+ }
70
+ }
71
+ console.log('=== All Tests Passed! ===');
72
+ }
73
+ catch (error) {
74
+ console.error('\n❌ Test Failed:');
75
+ if (isCloudronError(error)) {
76
+ console.error(` CloudronError: ${error.message}`);
77
+ console.error(` Status: ${error.statusCode}`);
78
+ console.error(` Code: ${error.code}`);
79
+ }
80
+ else if (error instanceof Error) {
81
+ console.error(` Error: ${error.message}`);
82
+ }
83
+ else {
84
+ console.error(' Unknown error:', error);
85
+ }
86
+ process.exit(1);
87
+ }
88
+ }
89
+ runTests();
90
+ //# sourceMappingURL=test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test.js","sourceRoot":"","sources":["../src/test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Cloudron API TypeScript Definitions
3
+ * MVP scope: listApps + getApp endpoints
4
+ */
5
+ /**
6
+ * Configuration for CloudronClient - enables DI for testing
7
+ */
8
+ export interface CloudronClientConfig {
9
+ baseUrl: string;
10
+ token: string;
11
+ }
12
+ /**
13
+ * App manifest subset containing metadata
14
+ */
15
+ export interface AppManifest {
16
+ id: string;
17
+ version: string;
18
+ title: string;
19
+ description: string;
20
+ tagline?: string;
21
+ website?: string;
22
+ author?: string;
23
+ }
24
+ /**
25
+ * Cloudron App representation
26
+ */
27
+ export interface App {
28
+ id: string;
29
+ appStoreId: string;
30
+ installationState: 'pending_install' | 'installed' | 'pending_configure' | 'pending_uninstall' | 'pending_restore' | 'error';
31
+ installationProgress: string;
32
+ runState: 'running' | 'stopped' | 'dead';
33
+ health: 'healthy' | 'unhealthy' | 'unknown';
34
+ location: string;
35
+ domain: string;
36
+ fqdn: string;
37
+ accessRestriction: string | null;
38
+ manifest: AppManifest;
39
+ portBindings: Record<string, number> | null;
40
+ iconUrl: string | null;
41
+ memoryLimit: number;
42
+ creationTime: string;
43
+ }
44
+ /**
45
+ * API response wrapper for listing apps
46
+ */
47
+ export interface AppsResponse {
48
+ apps: App[];
49
+ }
50
+ /**
51
+ * API response wrapper for single app
52
+ */
53
+ export interface AppResponse {
54
+ app: App;
55
+ }
56
+ /**
57
+ * System status response from /api/v1/cloudron/status
58
+ */
59
+ export interface SystemStatus {
60
+ version: string;
61
+ apiServerOrigin: string;
62
+ adminFqdn: string;
63
+ provider: string;
64
+ cloudronName: string;
65
+ isDemo: boolean;
66
+ }
67
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,GAAG;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,iBAAiB,GAAG,WAAW,GAAG,mBAAmB,GAAG,mBAAmB,GAAG,iBAAiB,GAAG,OAAO,CAAC;IAC7H,oBAAoB,EAAE,MAAM,CAAC;IAC7B,QAAQ,EAAE,SAAS,GAAG,SAAS,GAAG,MAAM,CAAC;IACzC,MAAM,EAAE,SAAS,GAAG,WAAW,GAAG,SAAS,CAAC;IAC5C,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,QAAQ,EAAE,WAAW,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;IAC5C,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,GAAG,EAAE,CAAC;CACb;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,GAAG,CAAC;CACV;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,OAAO,CAAC;CACjB"}
package/dist/types.js ADDED
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Cloudron API TypeScript Definitions
3
+ * MVP scope: listApps + getApp endpoints
4
+ */
5
+ export {};
6
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@serenichron/mcp-cloudron",
3
+ "version": "0.1.0",
4
+ "description": "MCP server for Cloudron instance management - list apps, get status, and manage your self-hosted applications",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "bin": {
9
+ "mcp-cloudron": "dist/server.js"
10
+ },
11
+ "files": [
12
+ "dist"
13
+ ],
14
+ "scripts": {
15
+ "build": "tsc",
16
+ "start": "node dist/server.js",
17
+ "dev": "tsx watch src/index.ts",
18
+ "test": "tsx src/test.ts",
19
+ "lint": "tsc --noEmit",
20
+ "clean": "rm -rf dist",
21
+ "prepublishOnly": "npm run build"
22
+ },
23
+ "keywords": [
24
+ "mcp",
25
+ "cloudron",
26
+ "model-context-protocol",
27
+ "ai",
28
+ "automation",
29
+ "self-hosted",
30
+ "server-management",
31
+ "claude",
32
+ "anthropic"
33
+ ],
34
+ "author": "Serenichron <vlad@serenichron.com>",
35
+ "license": "MIT",
36
+ "repository": {
37
+ "type": "git",
38
+ "url": "git+https://github.com/serenichron/mcp-cloudron.git"
39
+ },
40
+ "bugs": {
41
+ "url": "https://github.com/serenichron/mcp-cloudron/issues"
42
+ },
43
+ "homepage": "https://github.com/serenichron/mcp-cloudron#readme",
44
+ "engines": {
45
+ "node": ">=18.0.0"
46
+ },
47
+ "devDependencies": {
48
+ "@types/node": "^24.10.2",
49
+ "tsx": "^4.21.0",
50
+ "typescript": "^5.9.3"
51
+ },
52
+ "dependencies": {
53
+ "@modelcontextprotocol/sdk": "^1.24.3"
54
+ }
55
+ }