@masonator/coolify-mcp 0.3.1 → 0.7.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.
@@ -19,8 +19,8 @@
19
19
  // @ts-nocheck
20
20
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
21
21
  import { z } from 'zod';
22
- import { CoolifyClient } from './coolify-client.js';
23
- const VERSION = '0.3.1';
22
+ import { CoolifyClient, } from './coolify-client.js';
23
+ const VERSION = '0.7.0';
24
24
  /** Wrap tool handler with consistent error handling */
25
25
  function wrapHandler(fn) {
26
26
  return fn()
@@ -55,10 +55,53 @@ export class CoolifyMcpServer extends McpServer {
55
55
  registerTools() {
56
56
  // Version
57
57
  this.tool('get_version', 'Get Coolify API version', {}, async () => wrapHandler(() => this.client.getVersion()));
58
+ // Infrastructure Overview - high-level view of all resources
59
+ this.tool('get_infrastructure_overview', 'Get a high-level overview of all infrastructure (servers, projects, applications, databases, services). Returns counts and summaries. Start here to understand the infrastructure.', {}, async () => wrapHandler(async () => {
60
+ const results = await Promise.allSettled([
61
+ this.client.listServers({ summary: true }),
62
+ this.client.listProjects({ summary: true }),
63
+ this.client.listApplications({ summary: true }),
64
+ this.client.listDatabases({ summary: true }),
65
+ this.client.listServices({ summary: true }),
66
+ ]);
67
+ const extract = (result) => result.status === 'fulfilled' ? result.value : [];
68
+ const servers = extract(results[0]);
69
+ const projects = extract(results[1]);
70
+ const applications = extract(results[2]);
71
+ const databases = extract(results[3]);
72
+ const services = extract(results[4]);
73
+ const errors = results
74
+ .map((r, i) => {
75
+ if (r.status === 'rejected') {
76
+ const names = ['servers', 'projects', 'applications', 'databases', 'services'];
77
+ return `${names[i]}: ${r.reason instanceof Error ? r.reason.message : String(r.reason)}`;
78
+ }
79
+ return null;
80
+ })
81
+ .filter(Boolean);
82
+ return {
83
+ summary: {
84
+ servers: servers.length,
85
+ projects: projects.length,
86
+ applications: applications.length,
87
+ databases: databases.length,
88
+ services: services.length,
89
+ },
90
+ servers,
91
+ projects,
92
+ applications,
93
+ databases,
94
+ services,
95
+ ...(errors.length > 0 && { errors }),
96
+ };
97
+ }));
58
98
  // =========================================================================
59
99
  // Servers (5 tools)
60
100
  // =========================================================================
61
- this.tool('list_servers', 'List all servers', {}, async () => wrapHandler(() => this.client.listServers()));
101
+ this.tool('list_servers', 'List all servers (returns summary: uuid, name, ip, status). Use get_server for full details.', {
102
+ page: z.number().optional().describe('Page number for pagination'),
103
+ per_page: z.number().optional().describe('Items per page (default: all)'),
104
+ }, async ({ page, per_page }) => wrapHandler(() => this.client.listServers({ page, per_page, summary: true })));
62
105
  this.tool('get_server', 'Get server details', { uuid: z.string().describe('Server UUID') }, async ({ uuid }) => wrapHandler(() => this.client.getServer(uuid)));
63
106
  this.tool('get_server_resources', 'Get resources running on a server', { uuid: z.string().describe('Server UUID') }, async ({ uuid }) => wrapHandler(() => this.client.getServerResources(uuid)));
64
107
  this.tool('get_server_domains', 'Get domains configured on a server', { uuid: z.string().describe('Server UUID') }, async ({ uuid }) => wrapHandler(() => this.client.getServerDomains(uuid)));
@@ -66,7 +109,10 @@ export class CoolifyMcpServer extends McpServer {
66
109
  // =========================================================================
67
110
  // Projects (5 tools)
68
111
  // =========================================================================
69
- this.tool('list_projects', 'List all projects', {}, async () => wrapHandler(() => this.client.listProjects()));
112
+ this.tool('list_projects', 'List all projects (returns summary: uuid, name, description). Use get_project for full details.', {
113
+ page: z.number().optional().describe('Page number for pagination'),
114
+ per_page: z.number().optional().describe('Items per page (default: all)'),
115
+ }, async ({ page, per_page }) => wrapHandler(() => this.client.listProjects({ page, per_page, summary: true })));
70
116
  this.tool('get_project', 'Get project details', { uuid: z.string().describe('Project UUID') }, async ({ uuid }) => wrapHandler(() => this.client.getProject(uuid)));
71
117
  this.tool('create_project', 'Create a new project', {
72
118
  name: z.string().describe('Project name'),
@@ -95,7 +141,10 @@ export class CoolifyMcpServer extends McpServer {
95
141
  // =========================================================================
96
142
  // Applications (15 tools)
97
143
  // =========================================================================
98
- this.tool('list_applications', 'List all applications', {}, async () => wrapHandler(() => this.client.listApplications()));
144
+ this.tool('list_applications', 'List all applications (returns summary: uuid, name, status, fqdn, git_repository). Use get_application for full details.', {
145
+ page: z.number().optional().describe('Page number for pagination'),
146
+ per_page: z.number().optional().describe('Items per page (default: all)'),
147
+ }, async ({ page, per_page }) => wrapHandler(() => this.client.listApplications({ page, per_page, summary: true })));
99
148
  this.tool('get_application', 'Get application details', { uuid: z.string().describe('Application UUID') }, async ({ uuid }) => wrapHandler(() => this.client.getApplication(uuid)));
100
149
  this.tool('create_application_private_gh', 'Create app from private GitHub repo (GitHub App)', {
101
150
  project_uuid: z.string().describe('Project UUID'),
@@ -163,16 +212,64 @@ export class CoolifyMcpServer extends McpServer {
163
212
  // =========================================================================
164
213
  // Databases (5 tools)
165
214
  // =========================================================================
166
- this.tool('list_databases', 'List all databases', {}, async () => wrapHandler(() => this.client.listDatabases()));
215
+ this.tool('list_databases', 'List all databases (returns summary: uuid, name, type, status). Use get_database for full details.', {
216
+ page: z.number().optional().describe('Page number for pagination'),
217
+ per_page: z.number().optional().describe('Items per page (default: all)'),
218
+ }, async ({ page, per_page }) => wrapHandler(() => this.client.listDatabases({ page, per_page, summary: true })));
167
219
  this.tool('get_database', 'Get database details', { uuid: z.string().describe('Database UUID') }, async ({ uuid }) => wrapHandler(() => this.client.getDatabase(uuid)));
168
220
  this.tool('start_database', 'Start a database', { uuid: z.string().describe('Database UUID') }, async ({ uuid }) => wrapHandler(() => this.client.startDatabase(uuid)));
169
221
  this.tool('stop_database', 'Stop a database', { uuid: z.string().describe('Database UUID') }, async ({ uuid }) => wrapHandler(() => this.client.stopDatabase(uuid)));
170
222
  this.tool('restart_database', 'Restart a database', { uuid: z.string().describe('Database UUID') }, async ({ uuid }) => wrapHandler(() => this.client.restartDatabase(uuid)));
171
223
  // =========================================================================
172
- // Services (9 tools)
224
+ // Services (11 tools)
173
225
  // =========================================================================
174
- this.tool('list_services', 'List all services', {}, async () => wrapHandler(() => this.client.listServices()));
226
+ this.tool('list_services', 'List all services (returns summary: uuid, name, type, status, domains). Use get_service for full details.', {
227
+ page: z.number().optional().describe('Page number for pagination'),
228
+ per_page: z.number().optional().describe('Items per page (default: all)'),
229
+ }, async ({ page, per_page }) => wrapHandler(() => this.client.listServices({ page, per_page, summary: true })));
175
230
  this.tool('get_service', 'Get service details', { uuid: z.string().describe('Service UUID') }, async ({ uuid }) => wrapHandler(() => this.client.getService(uuid)));
231
+ this.tool('create_service', 'Create a one-click service (e.g., pocketbase, mysql, redis, wordpress, etc.). Use type OR docker_compose_raw, not both.', {
232
+ type: z
233
+ .string()
234
+ .optional()
235
+ .describe('Service type (e.g., pocketbase, mysql, redis, postgresql, mongodb, wordpress, etc.)'),
236
+ server_uuid: z.string().describe('Server UUID'),
237
+ project_uuid: z.string().describe('Project UUID'),
238
+ environment_name: z.string().optional().describe('Environment name (e.g., production)'),
239
+ environment_uuid: z
240
+ .string()
241
+ .optional()
242
+ .describe('Environment UUID (alternative to environment_name)'),
243
+ name: z.string().optional().describe('Service name'),
244
+ description: z.string().optional().describe('Service description'),
245
+ destination_uuid: z.string().optional().describe('Destination UUID'),
246
+ instant_deploy: z.boolean().optional().describe('Deploy immediately after creation'),
247
+ docker_compose_raw: z
248
+ .string()
249
+ .optional()
250
+ .describe('Base64 encoded docker-compose YAML with SERVICE_FQDN_* env var for custom domain (alternative to type)'),
251
+ }, async (args) => wrapHandler(() => this.client.createService(args)));
252
+ this.tool('delete_service', 'Delete a service', {
253
+ uuid: z.string().describe('Service UUID'),
254
+ delete_configurations: z
255
+ .boolean()
256
+ .optional()
257
+ .describe('Delete configurations (default: true)'),
258
+ delete_volumes: z.boolean().optional().describe('Delete volumes (default: true)'),
259
+ docker_cleanup: z
260
+ .boolean()
261
+ .optional()
262
+ .describe('Clean up Docker resources (default: true)'),
263
+ delete_connected_networks: z
264
+ .boolean()
265
+ .optional()
266
+ .describe('Delete connected networks (default: true)'),
267
+ }, async ({ uuid, delete_configurations, delete_volumes, docker_cleanup, delete_connected_networks, }) => wrapHandler(() => this.client.deleteService(uuid, {
268
+ deleteConfigurations: delete_configurations,
269
+ deleteVolumes: delete_volumes,
270
+ dockerCleanup: docker_cleanup,
271
+ deleteConnectedNetworks: delete_connected_networks,
272
+ })));
176
273
  this.tool('update_service', 'Update a service (IMPORTANT: See UpdateServiceRequest type docs for Traefik basic auth requirements)', {
177
274
  uuid: z.string().describe('Service UUID'),
178
275
  name: z.string().optional().describe('Service name'),
@@ -199,12 +296,50 @@ export class CoolifyMcpServer extends McpServer {
199
296
  // =========================================================================
200
297
  // Deployments (4 tools)
201
298
  // =========================================================================
202
- this.tool('list_deployments', 'List running deployments', {}, async () => wrapHandler(() => this.client.listDeployments()));
299
+ this.tool('list_deployments', 'List running deployments (returns summary: uuid, deployment_uuid, application_name, status). Use get_deployment for full details.', {
300
+ page: z.number().optional().describe('Page number for pagination'),
301
+ per_page: z.number().optional().describe('Items per page (default: all)'),
302
+ }, async ({ page, per_page }) => wrapHandler(() => this.client.listDeployments({ page, per_page, summary: true })));
203
303
  this.tool('get_deployment', 'Get deployment details', { uuid: z.string().describe('Deployment UUID') }, async ({ uuid }) => wrapHandler(() => this.client.getDeployment(uuid)));
204
304
  this.tool('deploy', 'Deploy by tag or UUID', {
205
305
  tag_or_uuid: z.string().describe('Tag or UUID'),
206
306
  force: z.boolean().optional().describe('Force rebuild'),
207
307
  }, async ({ tag_or_uuid, force }) => wrapHandler(() => this.client.deployByTagOrUuid(tag_or_uuid, force)));
208
308
  this.tool('list_application_deployments', 'List deployments for an application', { uuid: z.string().describe('Application UUID') }, async ({ uuid }) => wrapHandler(() => this.client.listApplicationDeployments(uuid)));
309
+ this.tool('cancel_deployment', 'Cancel a running deployment', { uuid: z.string().describe('Deployment UUID') }, async ({ uuid }) => wrapHandler(() => this.client.cancelDeployment(uuid)));
310
+ // =========================================================================
311
+ // Private Keys (5 tools)
312
+ // =========================================================================
313
+ this.tool('list_private_keys', 'List all private keys (SSH keys for deployments)', {}, async () => wrapHandler(() => this.client.listPrivateKeys()));
314
+ this.tool('get_private_key', 'Get private key details', { uuid: z.string().describe('Private key UUID') }, async ({ uuid }) => wrapHandler(() => this.client.getPrivateKey(uuid)));
315
+ this.tool('create_private_key', 'Create a new private key for deployments', {
316
+ private_key: z.string().describe('The private key content (PEM format)'),
317
+ name: z.string().optional().describe('Name for the key'),
318
+ description: z.string().optional().describe('Description'),
319
+ }, async (args) => wrapHandler(() => this.client.createPrivateKey(args)));
320
+ this.tool('update_private_key', 'Update a private key', {
321
+ uuid: z.string().describe('Private key UUID'),
322
+ name: z.string().optional().describe('Name for the key'),
323
+ description: z.string().optional().describe('Description'),
324
+ private_key: z.string().optional().describe('The private key content (PEM format)'),
325
+ }, async ({ uuid, ...data }) => wrapHandler(() => this.client.updatePrivateKey(uuid, data)));
326
+ this.tool('delete_private_key', 'Delete a private key', { uuid: z.string().describe('Private key UUID') }, async ({ uuid }) => wrapHandler(() => this.client.deletePrivateKey(uuid)));
327
+ // =========================================================================
328
+ // Database Backups (4 tools)
329
+ // =========================================================================
330
+ this.tool('list_database_backups', 'List scheduled backups for a database', { uuid: z.string().describe('Database UUID') }, async ({ uuid }) => wrapHandler(() => this.client.listDatabaseBackups(uuid)));
331
+ this.tool('get_database_backup', 'Get details of a scheduled backup', {
332
+ database_uuid: z.string().describe('Database UUID'),
333
+ backup_uuid: z.string().describe('Scheduled backup UUID'),
334
+ }, async ({ database_uuid, backup_uuid }) => wrapHandler(() => this.client.getDatabaseBackup(database_uuid, backup_uuid)));
335
+ this.tool('list_backup_executions', 'List execution history for a scheduled backup', {
336
+ database_uuid: z.string().describe('Database UUID'),
337
+ backup_uuid: z.string().describe('Scheduled backup UUID'),
338
+ }, async ({ database_uuid, backup_uuid }) => wrapHandler(() => this.client.listBackupExecutions(database_uuid, backup_uuid)));
339
+ this.tool('get_backup_execution', 'Get details of a specific backup execution', {
340
+ database_uuid: z.string().describe('Database UUID'),
341
+ backup_uuid: z.string().describe('Scheduled backup UUID'),
342
+ execution_uuid: z.string().describe('Backup execution UUID'),
343
+ }, async ({ database_uuid, backup_uuid, execution_uuid }) => wrapHandler(() => this.client.getBackupExecution(database_uuid, backup_uuid, execution_uuid)));
209
344
  }
210
345
  }
@@ -300,6 +300,9 @@ export interface UpdateApplicationRequest {
300
300
  limits_memory?: string;
301
301
  limits_memory_swap?: string;
302
302
  limits_cpus?: string;
303
+ is_http_basic_auth_enabled?: boolean;
304
+ http_basic_auth_username?: string;
305
+ http_basic_auth_password?: string;
303
306
  }
304
307
  export interface ApplicationActionResponse {
305
308
  message: string;
@@ -470,6 +473,17 @@ export interface CreateDatabaseBackupRequest {
470
473
  backup_retention?: number;
471
474
  backup_retention_days?: number;
472
475
  }
476
+ export interface BackupExecution {
477
+ id: number;
478
+ uuid: string;
479
+ scheduled_database_backup_id: number;
480
+ status: 'pending' | 'running' | 'success' | 'failed';
481
+ message?: string;
482
+ size?: number;
483
+ filename?: string;
484
+ created_at: string;
485
+ updated_at: string;
486
+ }
473
487
  /**
474
488
  * Available one-click service types in Coolify.
475
489
  * This is a string type to avoid TypeScript memory issues with large const arrays.
@@ -497,7 +511,7 @@ export interface Service {
497
511
  updated_at: string;
498
512
  }
499
513
  export interface CreateServiceRequest {
500
- type: ServiceType;
514
+ type?: ServiceType;
501
515
  name?: string;
502
516
  description?: string;
503
517
  project_uuid: string;
@@ -506,6 +520,7 @@ export interface CreateServiceRequest {
506
520
  server_uuid: string;
507
521
  destination_uuid?: string;
508
522
  instant_deploy?: boolean;
523
+ docker_compose_raw?: string;
509
524
  }
510
525
  /**
511
526
  * CRITICAL: When updating services with Traefik basic auth labels
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@masonator/coolify-mcp",
3
3
  "scope": "@masonator",
4
- "version": "0.3.1",
4
+ "version": "0.7.0",
5
5
  "description": "MCP server implementation for Coolify",
6
6
  "type": "module",
7
7
  "main": "./dist/index.js",
@@ -61,5 +61,9 @@
61
61
  },
62
62
  "engines": {
63
63
  "node": ">=18"
64
+ },
65
+ "lint-staged": {
66
+ "*.{ts,js,json,md,yaml,yml}": "prettier --write",
67
+ "*.ts": "eslint --fix"
64
68
  }
65
69
  }