@masonator/coolify-mcp 0.2.18 → 0.3.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.
@@ -0,0 +1,211 @@
1
+ /**
2
+ * Coolify MCP Server
3
+ * Model Context Protocol server for Coolify API
4
+ *
5
+ * Tools focused on debugging, management, and deployment:
6
+ * - Servers: list, get, validate, resources, domains
7
+ * - Projects: CRUD
8
+ * - Environments: CRUD
9
+ * - Applications: list, get, update, delete, start/stop/restart, logs, env vars, deploy (private-gh, private-key)
10
+ * - Databases: list, get, start/stop/restart
11
+ * - Services: list, get, update, start/stop/restart, env vars
12
+ * - Deployments: list, get, deploy
13
+ *
14
+ * Note: @ts-nocheck is required because the MCP SDK's tool() method causes
15
+ * TypeScript type instantiation depth errors with 40+ zod-typed tools.
16
+ * The client and types are still fully type-checked.
17
+ */
18
+ /* eslint-disable @typescript-eslint/ban-ts-comment */
19
+ // @ts-nocheck
20
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
21
+ import { z } from 'zod';
22
+ import { CoolifyClient } from './coolify-client.js';
23
+ const VERSION = '0.3.0';
24
+ /** Wrap tool handler with consistent error handling */
25
+ function wrapHandler(fn) {
26
+ return fn()
27
+ .then((result) => ({
28
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
29
+ }))
30
+ .catch((error) => ({
31
+ content: [
32
+ {
33
+ type: 'text',
34
+ text: `Error: ${error instanceof Error ? error.message : String(error)}`,
35
+ },
36
+ ],
37
+ }));
38
+ }
39
+ /**
40
+ * Coolify MCP Server
41
+ */
42
+ export class CoolifyMcpServer extends McpServer {
43
+ constructor(config) {
44
+ super({
45
+ name: 'coolify',
46
+ version: VERSION,
47
+ capabilities: { tools: {} },
48
+ });
49
+ this.client = new CoolifyClient(config);
50
+ this.registerTools();
51
+ }
52
+ async connect(transport) {
53
+ await this.client.validateConnection();
54
+ await super.connect(transport);
55
+ }
56
+ registerTools() {
57
+ // Version
58
+ this.tool('get_version', 'Get Coolify API version', {}, async () => wrapHandler(() => this.client.getVersion()));
59
+ // =========================================================================
60
+ // Servers (5 tools)
61
+ // =========================================================================
62
+ this.tool('list_servers', 'List all servers', {}, async () => wrapHandler(() => this.client.listServers()));
63
+ this.tool('get_server', 'Get server details', { uuid: z.string().describe('Server UUID') }, async ({ uuid }) => wrapHandler(() => this.client.getServer(uuid)));
64
+ this.tool('get_server_resources', 'Get resources running on a server', { uuid: z.string().describe('Server UUID') }, async ({ uuid }) => wrapHandler(() => this.client.getServerResources(uuid)));
65
+ 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
+ this.tool('validate_server', 'Validate server connection', { uuid: z.string().describe('Server UUID') }, async ({ uuid }) => wrapHandler(() => this.client.validateServer(uuid)));
67
+ // =========================================================================
68
+ // Projects (5 tools)
69
+ // =========================================================================
70
+ this.tool('list_projects', 'List all projects', {}, async () => wrapHandler(() => this.client.listProjects()));
71
+ this.tool('get_project', 'Get project details', { uuid: z.string().describe('Project UUID') }, async ({ uuid }) => wrapHandler(() => this.client.getProject(uuid)));
72
+ this.tool('create_project', 'Create a new project', {
73
+ name: z.string().describe('Project name'),
74
+ description: z.string().optional().describe('Description'),
75
+ }, async (args) => wrapHandler(() => this.client.createProject(args)));
76
+ this.tool('update_project', 'Update a project', {
77
+ uuid: z.string().describe('Project UUID'),
78
+ name: z.string().optional().describe('Project name'),
79
+ description: z.string().optional().describe('Description'),
80
+ }, async ({ uuid, ...data }) => wrapHandler(() => this.client.updateProject(uuid, data)));
81
+ this.tool('delete_project', 'Delete a project', { uuid: z.string().describe('Project UUID') }, async ({ uuid }) => wrapHandler(() => this.client.deleteProject(uuid)));
82
+ // =========================================================================
83
+ // Environments (4 tools)
84
+ // =========================================================================
85
+ this.tool('list_environments', 'List environments in a project', { project_uuid: z.string().describe('Project UUID') }, async ({ project_uuid }) => wrapHandler(() => this.client.listProjectEnvironments(project_uuid)));
86
+ this.tool('get_environment', 'Get environment details', {
87
+ project_uuid: z.string().describe('Project UUID'),
88
+ environment: z.string().describe('Environment name or UUID'),
89
+ }, async ({ project_uuid, environment }) => wrapHandler(() => this.client.getProjectEnvironment(project_uuid, environment)));
90
+ this.tool('create_environment', 'Create environment in a project', {
91
+ project_uuid: z.string().describe('Project UUID'),
92
+ name: z.string().describe('Environment name'),
93
+ description: z.string().optional().describe('Description'),
94
+ }, async ({ project_uuid, ...data }) => wrapHandler(() => this.client.createProjectEnvironment(project_uuid, data)));
95
+ this.tool('delete_environment', 'Delete an environment', { environment_uuid: z.string().describe('Environment UUID') }, async ({ environment_uuid }) => wrapHandler(() => this.client.deleteProjectEnvironment(environment_uuid)));
96
+ // =========================================================================
97
+ // Applications (15 tools)
98
+ // =========================================================================
99
+ this.tool('list_applications', 'List all applications', {}, async () => wrapHandler(() => this.client.listApplications()));
100
+ this.tool('get_application', 'Get application details', { uuid: z.string().describe('Application UUID') }, async ({ uuid }) => wrapHandler(() => this.client.getApplication(uuid)));
101
+ this.tool('create_application_private_gh', 'Create app from private GitHub repo (GitHub App)', {
102
+ project_uuid: z.string().describe('Project UUID'),
103
+ server_uuid: z.string().describe('Server UUID'),
104
+ github_app_uuid: z.string().describe('GitHub App UUID'),
105
+ git_repository: z.string().describe('Repository (org/repo)'),
106
+ git_branch: z.string().describe('Branch'),
107
+ environment_name: z.string().optional().describe('Environment name'),
108
+ destination_uuid: z.string().optional().describe('Destination UUID'),
109
+ build_pack: z.string().optional().describe('Build pack'),
110
+ ports_exposes: z.string().optional().describe('Ports to expose'),
111
+ }, async (args) => wrapHandler(() => this.client.createApplicationPrivateGH(args)));
112
+ this.tool('create_application_private_key', 'Create app from private repo using deploy key', {
113
+ project_uuid: z.string().describe('Project UUID'),
114
+ server_uuid: z.string().describe('Server UUID'),
115
+ private_key_uuid: z.string().describe('Private key UUID'),
116
+ git_repository: z.string().describe('Repository URL'),
117
+ git_branch: z.string().describe('Branch'),
118
+ environment_name: z.string().optional().describe('Environment name'),
119
+ destination_uuid: z.string().optional().describe('Destination UUID'),
120
+ build_pack: z.string().optional().describe('Build pack'),
121
+ ports_exposes: z.string().optional().describe('Ports to expose'),
122
+ }, async (args) => wrapHandler(() => this.client.createApplicationPrivateKey(args)));
123
+ this.tool('update_application', 'Update an application', {
124
+ uuid: z.string().describe('Application UUID'),
125
+ name: z.string().optional().describe('Name'),
126
+ description: z.string().optional().describe('Description'),
127
+ fqdn: z.string().optional().describe('Domain'),
128
+ git_branch: z.string().optional().describe('Git branch'),
129
+ is_http_basic_auth_enabled: z
130
+ .boolean()
131
+ .optional()
132
+ .describe('Enable HTTP basic authentication'),
133
+ http_basic_auth_username: z.string().optional().describe('HTTP basic auth username'),
134
+ http_basic_auth_password: z.string().optional().describe('HTTP basic auth password'),
135
+ }, async ({ uuid, ...data }) => wrapHandler(() => this.client.updateApplication(uuid, data)));
136
+ this.tool('delete_application', 'Delete an application', {
137
+ uuid: z.string().describe('Application UUID'),
138
+ delete_volumes: z.boolean().optional().describe('Delete volumes'),
139
+ }, async ({ uuid, delete_volumes }) => wrapHandler(() => this.client.deleteApplication(uuid, { deleteVolumes: delete_volumes })));
140
+ this.tool('start_application', 'Start an application', { uuid: z.string().describe('Application UUID') }, async ({ uuid }) => wrapHandler(() => this.client.startApplication(uuid)));
141
+ this.tool('stop_application', 'Stop an application', { uuid: z.string().describe('Application UUID') }, async ({ uuid }) => wrapHandler(() => this.client.stopApplication(uuid)));
142
+ this.tool('restart_application', 'Restart an application', { uuid: z.string().describe('Application UUID') }, async ({ uuid }) => wrapHandler(() => this.client.restartApplication(uuid)));
143
+ this.tool('get_application_logs', 'Get application logs', {
144
+ uuid: z.string().describe('Application UUID'),
145
+ lines: z.number().optional().describe('Number of lines'),
146
+ }, async ({ uuid, lines }) => wrapHandler(() => this.client.getApplicationLogs(uuid, lines)));
147
+ // Application env vars
148
+ this.tool('list_application_envs', 'List application environment variables', { uuid: z.string().describe('Application UUID') }, async ({ uuid }) => wrapHandler(() => this.client.listApplicationEnvVars(uuid)));
149
+ this.tool('create_application_env', 'Create application environment variable', {
150
+ uuid: z.string().describe('Application UUID'),
151
+ key: z.string().describe('Variable key'),
152
+ value: z.string().describe('Variable value'),
153
+ is_build_time: z.boolean().optional().describe('Build time variable'),
154
+ }, async ({ uuid, ...data }) => wrapHandler(() => this.client.createApplicationEnvVar(uuid, data)));
155
+ this.tool('update_application_env', 'Update application environment variable', {
156
+ uuid: z.string().describe('Application UUID'),
157
+ key: z.string().describe('Variable key'),
158
+ value: z.string().describe('Variable value'),
159
+ }, async ({ uuid, ...data }) => wrapHandler(() => this.client.updateApplicationEnvVar(uuid, data)));
160
+ this.tool('delete_application_env', 'Delete application environment variable', {
161
+ uuid: z.string().describe('Application UUID'),
162
+ env_uuid: z.string().describe('Env variable UUID'),
163
+ }, async ({ uuid, env_uuid }) => wrapHandler(() => this.client.deleteApplicationEnvVar(uuid, env_uuid)));
164
+ // =========================================================================
165
+ // Databases (5 tools)
166
+ // =========================================================================
167
+ this.tool('list_databases', 'List all databases', {}, async () => wrapHandler(() => this.client.listDatabases()));
168
+ this.tool('get_database', 'Get database details', { uuid: z.string().describe('Database UUID') }, async ({ uuid }) => wrapHandler(() => this.client.getDatabase(uuid)));
169
+ this.tool('start_database', 'Start a database', { uuid: z.string().describe('Database UUID') }, async ({ uuid }) => wrapHandler(() => this.client.startDatabase(uuid)));
170
+ this.tool('stop_database', 'Stop a database', { uuid: z.string().describe('Database UUID') }, async ({ uuid }) => wrapHandler(() => this.client.stopDatabase(uuid)));
171
+ this.tool('restart_database', 'Restart a database', { uuid: z.string().describe('Database UUID') }, async ({ uuid }) => wrapHandler(() => this.client.restartDatabase(uuid)));
172
+ // =========================================================================
173
+ // Services (9 tools)
174
+ // =========================================================================
175
+ this.tool('list_services', 'List all services', {}, async () => wrapHandler(() => this.client.listServices()));
176
+ this.tool('get_service', 'Get service details', { uuid: z.string().describe('Service UUID') }, async ({ uuid }) => wrapHandler(() => this.client.getService(uuid)));
177
+ this.tool('update_service', 'Update a service (IMPORTANT: See UpdateServiceRequest type docs for Traefik basic auth requirements)', {
178
+ uuid: z.string().describe('Service UUID'),
179
+ name: z.string().optional().describe('Service name'),
180
+ description: z.string().optional().describe('Description'),
181
+ docker_compose_raw: z
182
+ .string()
183
+ .optional()
184
+ .describe('Base64 encoded docker-compose YAML. CRITICAL FOR BASIC AUTH: (1) Manually disable label escaping in Coolify UI first (no API). (2) Use $$ in htpasswd hashes even with escaping disabled (Traefik requirement). (3) Generate: htpasswd -nb user pass, then replace $ with $$.'),
185
+ }, async ({ uuid, ...data }) => wrapHandler(() => this.client.updateService(uuid, data)));
186
+ this.tool('start_service', 'Start a service', { uuid: z.string().describe('Service UUID') }, async ({ uuid }) => wrapHandler(() => this.client.startService(uuid)));
187
+ this.tool('stop_service', 'Stop a service', { uuid: z.string().describe('Service UUID') }, async ({ uuid }) => wrapHandler(() => this.client.stopService(uuid)));
188
+ this.tool('restart_service', 'Restart a service', { uuid: z.string().describe('Service UUID') }, async ({ uuid }) => wrapHandler(() => this.client.restartService(uuid)));
189
+ // Service env vars
190
+ this.tool('list_service_envs', 'List service environment variables', { uuid: z.string().describe('Service UUID') }, async ({ uuid }) => wrapHandler(() => this.client.listServiceEnvVars(uuid)));
191
+ this.tool('create_service_env', 'Create service environment variable', {
192
+ uuid: z.string().describe('Service UUID'),
193
+ key: z.string().describe('Variable key'),
194
+ value: z.string().describe('Variable value'),
195
+ }, async ({ uuid, ...data }) => wrapHandler(() => this.client.createServiceEnvVar(uuid, data)));
196
+ this.tool('delete_service_env', 'Delete service environment variable', {
197
+ uuid: z.string().describe('Service UUID'),
198
+ env_uuid: z.string().describe('Env variable UUID'),
199
+ }, async ({ uuid, env_uuid }) => wrapHandler(() => this.client.deleteServiceEnvVar(uuid, env_uuid)));
200
+ // =========================================================================
201
+ // Deployments (4 tools)
202
+ // =========================================================================
203
+ this.tool('list_deployments', 'List running deployments', {}, async () => wrapHandler(() => this.client.listDeployments()));
204
+ this.tool('get_deployment', 'Get deployment details', { uuid: z.string().describe('Deployment UUID') }, async ({ uuid }) => wrapHandler(() => this.client.getDeployment(uuid)));
205
+ this.tool('deploy', 'Deploy by tag or UUID', {
206
+ tag_or_uuid: z.string().describe('Tag or UUID'),
207
+ force: z.boolean().optional().describe('Force rebuild'),
208
+ }, async ({ tag_or_uuid, force }) => wrapHandler(() => this.client.deployByTagOrUuid(tag_or_uuid, force)));
209
+ this.tool('list_application_deployments', 'List deployments for an application', { uuid: z.string().describe('Application UUID') }, async ({ uuid }) => wrapHandler(() => this.client.listApplicationDeployments(uuid)));
210
+ }
211
+ }