@fndchagas/coolify-mcp 0.1.2 → 0.1.4

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,340 @@
1
+ // This file is auto-generated by @hey-api/openapi-ts
2
+ import { z } from 'zod';
3
+ /**
4
+ * Environment Variable model
5
+ */
6
+ export const zEnvironmentVariable = z.object({
7
+ id: z.optional(z.int()),
8
+ uuid: z.optional(z.string()),
9
+ resourceable_type: z.optional(z.string()),
10
+ resourceable_id: z.optional(z.int()),
11
+ is_literal: z.optional(z.boolean()),
12
+ is_multiline: z.optional(z.boolean()),
13
+ is_preview: z.optional(z.boolean()),
14
+ is_runtime: z.optional(z.boolean()),
15
+ is_buildtime: z.optional(z.boolean()),
16
+ is_shared: z.optional(z.boolean()),
17
+ is_shown_once: z.optional(z.boolean()),
18
+ key: z.optional(z.string()),
19
+ value: z.optional(z.string()),
20
+ real_value: z.optional(z.string()),
21
+ version: z.optional(z.string()),
22
+ created_at: z.optional(z.string()),
23
+ updated_at: z.optional(z.string())
24
+ });
25
+ /**
26
+ * Project model
27
+ */
28
+ export const zApplicationDeploymentQueue = z.object({
29
+ id: z.optional(z.int()),
30
+ application_id: z.optional(z.string()),
31
+ deployment_uuid: z.optional(z.string()),
32
+ pull_request_id: z.optional(z.int()),
33
+ force_rebuild: z.optional(z.boolean()),
34
+ commit: z.optional(z.string()),
35
+ status: z.optional(z.string()),
36
+ is_webhook: z.optional(z.boolean()),
37
+ is_api: z.optional(z.boolean()),
38
+ created_at: z.optional(z.string()),
39
+ updated_at: z.optional(z.string()),
40
+ logs: z.optional(z.string()),
41
+ current_process_id: z.optional(z.string()),
42
+ restart_only: z.optional(z.boolean()),
43
+ git_type: z.optional(z.string()),
44
+ server_id: z.optional(z.int()),
45
+ application_name: z.optional(z.string()),
46
+ server_name: z.optional(z.string()),
47
+ deployment_url: z.optional(z.string()),
48
+ destination_id: z.optional(z.string()),
49
+ only_this_server: z.optional(z.boolean()),
50
+ rollback: z.optional(z.boolean()),
51
+ commit_message: z.optional(z.string())
52
+ });
53
+ /**
54
+ * Application model
55
+ */
56
+ export const zApplication = z.object({
57
+ id: z.optional(z.int()),
58
+ description: z.optional(z.string()),
59
+ repository_project_id: z.optional(z.int()),
60
+ uuid: z.optional(z.string()),
61
+ name: z.optional(z.string()),
62
+ fqdn: z.optional(z.string()),
63
+ config_hash: z.optional(z.string()),
64
+ git_repository: z.optional(z.string()),
65
+ git_branch: z.optional(z.string()),
66
+ git_commit_sha: z.optional(z.string()),
67
+ git_full_url: z.optional(z.string()),
68
+ docker_registry_image_name: z.optional(z.string()),
69
+ docker_registry_image_tag: z.optional(z.string()),
70
+ build_pack: z.optional(z.enum([
71
+ 'nixpacks',
72
+ 'static',
73
+ 'dockerfile',
74
+ 'dockercompose'
75
+ ])),
76
+ static_image: z.optional(z.string()),
77
+ install_command: z.optional(z.string()),
78
+ build_command: z.optional(z.string()),
79
+ start_command: z.optional(z.string()),
80
+ ports_exposes: z.optional(z.string()),
81
+ ports_mappings: z.optional(z.string()),
82
+ custom_network_aliases: z.optional(z.string()),
83
+ base_directory: z.optional(z.string()),
84
+ publish_directory: z.optional(z.string()),
85
+ health_check_enabled: z.optional(z.boolean()),
86
+ health_check_path: z.optional(z.string()),
87
+ health_check_port: z.optional(z.string()),
88
+ health_check_host: z.optional(z.string()),
89
+ health_check_method: z.optional(z.string()),
90
+ health_check_return_code: z.optional(z.int()),
91
+ health_check_scheme: z.optional(z.string()),
92
+ health_check_response_text: z.optional(z.string()),
93
+ health_check_interval: z.optional(z.int()),
94
+ health_check_timeout: z.optional(z.int()),
95
+ health_check_retries: z.optional(z.int()),
96
+ health_check_start_period: z.optional(z.int()),
97
+ limits_memory: z.optional(z.string()),
98
+ limits_memory_swap: z.optional(z.string()),
99
+ limits_memory_swappiness: z.optional(z.int()),
100
+ limits_memory_reservation: z.optional(z.string()),
101
+ limits_cpus: z.optional(z.string()),
102
+ limits_cpuset: z.optional(z.string()),
103
+ limits_cpu_shares: z.optional(z.int()),
104
+ status: z.optional(z.string()),
105
+ preview_url_template: z.optional(z.string()),
106
+ destination_type: z.optional(z.string()),
107
+ destination_id: z.optional(z.int()),
108
+ source_id: z.optional(z.int()),
109
+ private_key_id: z.optional(z.int()),
110
+ environment_id: z.optional(z.int()),
111
+ dockerfile: z.optional(z.string()),
112
+ dockerfile_location: z.optional(z.string()),
113
+ custom_labels: z.optional(z.string()),
114
+ dockerfile_target_build: z.optional(z.string()),
115
+ manual_webhook_secret_github: z.optional(z.string()),
116
+ manual_webhook_secret_gitlab: z.optional(z.string()),
117
+ manual_webhook_secret_bitbucket: z.optional(z.string()),
118
+ manual_webhook_secret_gitea: z.optional(z.string()),
119
+ docker_compose_location: z.optional(z.string()),
120
+ docker_compose: z.optional(z.string()),
121
+ docker_compose_raw: z.optional(z.string()),
122
+ docker_compose_domains: z.optional(z.string()),
123
+ docker_compose_custom_start_command: z.optional(z.string()),
124
+ docker_compose_custom_build_command: z.optional(z.string()),
125
+ swarm_replicas: z.optional(z.int()),
126
+ swarm_placement_constraints: z.optional(z.string()),
127
+ custom_docker_run_options: z.optional(z.string()),
128
+ post_deployment_command: z.optional(z.string()),
129
+ post_deployment_command_container: z.optional(z.string()),
130
+ pre_deployment_command: z.optional(z.string()),
131
+ pre_deployment_command_container: z.optional(z.string()),
132
+ watch_paths: z.optional(z.string()),
133
+ custom_healthcheck_found: z.optional(z.boolean()),
134
+ redirect: z.optional(z.enum([
135
+ 'www',
136
+ 'non-www',
137
+ 'both'
138
+ ])),
139
+ created_at: z.optional(z.iso.datetime()),
140
+ updated_at: z.optional(z.iso.datetime()),
141
+ deleted_at: z.optional(z.iso.datetime()),
142
+ compose_parsing_version: z.optional(z.string()),
143
+ custom_nginx_configuration: z.optional(z.string()),
144
+ is_http_basic_auth_enabled: z.optional(z.boolean()),
145
+ http_basic_auth_username: z.optional(z.string()),
146
+ http_basic_auth_password: z.optional(z.string())
147
+ });
148
+ export const zListApplicationsData = z.object({
149
+ body: z.optional(z.never()),
150
+ path: z.optional(z.never()),
151
+ query: z.optional(z.never())
152
+ });
153
+ /**
154
+ * Get all applications.
155
+ */
156
+ export const zListApplicationsResponse = z.array(zApplication);
157
+ export const zGetApplicationByUuidData = z.object({
158
+ body: z.optional(z.never()),
159
+ path: z.object({
160
+ uuid: z.string()
161
+ }),
162
+ query: z.optional(z.never())
163
+ });
164
+ /**
165
+ * Get application by UUID.
166
+ */
167
+ export const zGetApplicationByUuidResponse = zApplication;
168
+ export const zGetApplicationLogsByUuidData = z.object({
169
+ body: z.optional(z.never()),
170
+ path: z.object({
171
+ uuid: z.string()
172
+ }),
173
+ query: z.optional(z.object({
174
+ lines: z.optional(z.int().min(-2147483648, { error: 'Invalid value: Expected int32 to be >= -2147483648' }).max(2147483647, { error: 'Invalid value: Expected int32 to be <= 2147483647' })).default(100)
175
+ }))
176
+ });
177
+ /**
178
+ * Get application logs by UUID.
179
+ */
180
+ export const zGetApplicationLogsByUuidResponse = z.object({
181
+ logs: z.optional(z.string())
182
+ });
183
+ export const zListEnvsByApplicationUuidData = z.object({
184
+ body: z.optional(z.never()),
185
+ path: z.object({
186
+ uuid: z.string()
187
+ }),
188
+ query: z.optional(z.never())
189
+ });
190
+ /**
191
+ * All environment variables by application UUID.
192
+ */
193
+ export const zListEnvsByApplicationUuidResponse = z.array(zEnvironmentVariable);
194
+ export const zUpdateEnvByApplicationUuidData = z.object({
195
+ body: z.object({
196
+ key: z.string(),
197
+ value: z.string(),
198
+ is_preview: z.optional(z.boolean()),
199
+ is_literal: z.optional(z.boolean()),
200
+ is_multiline: z.optional(z.boolean()),
201
+ is_shown_once: z.optional(z.boolean())
202
+ }),
203
+ path: z.object({
204
+ uuid: z.string()
205
+ }),
206
+ query: z.optional(z.never())
207
+ });
208
+ /**
209
+ * Environment variable updated.
210
+ */
211
+ export const zUpdateEnvByApplicationUuidResponse = z.object({
212
+ message: z.optional(z.string())
213
+ });
214
+ export const zCreateEnvByApplicationUuidData = z.object({
215
+ body: z.object({
216
+ key: z.optional(z.string()),
217
+ value: z.optional(z.string()),
218
+ is_preview: z.optional(z.boolean()),
219
+ is_literal: z.optional(z.boolean()),
220
+ is_multiline: z.optional(z.boolean()),
221
+ is_shown_once: z.optional(z.boolean())
222
+ }),
223
+ path: z.object({
224
+ uuid: z.string()
225
+ }),
226
+ query: z.optional(z.never())
227
+ });
228
+ /**
229
+ * Environment variable created.
230
+ */
231
+ export const zCreateEnvByApplicationUuidResponse = z.object({
232
+ uuid: z.optional(z.string())
233
+ });
234
+ export const zListDatabasesData = z.object({
235
+ body: z.optional(z.never()),
236
+ path: z.optional(z.never()),
237
+ query: z.optional(z.never())
238
+ });
239
+ /**
240
+ * Get all databases
241
+ */
242
+ export const zListDatabasesResponse = z.string();
243
+ export const zGetDatabaseByUuidData = z.object({
244
+ body: z.optional(z.never()),
245
+ path: z.object({
246
+ uuid: z.string()
247
+ }),
248
+ query: z.optional(z.never())
249
+ });
250
+ /**
251
+ * Get all databases
252
+ */
253
+ export const zGetDatabaseByUuidResponse = z.string();
254
+ export const zListDeploymentsData = z.object({
255
+ body: z.optional(z.never()),
256
+ path: z.optional(z.never()),
257
+ query: z.optional(z.never())
258
+ });
259
+ /**
260
+ * Get all currently running deployments.
261
+ */
262
+ export const zListDeploymentsResponse = z.array(zApplicationDeploymentQueue);
263
+ export const zGetDeploymentByUuidData = z.object({
264
+ body: z.optional(z.never()),
265
+ path: z.object({
266
+ uuid: z.string()
267
+ }),
268
+ query: z.optional(z.never())
269
+ });
270
+ /**
271
+ * Get deployment by UUID.
272
+ */
273
+ export const zGetDeploymentByUuidResponse = zApplicationDeploymentQueue;
274
+ export const zCancelDeploymentByUuidData = z.object({
275
+ body: z.optional(z.never()),
276
+ path: z.object({
277
+ uuid: z.string()
278
+ }),
279
+ query: z.optional(z.never())
280
+ });
281
+ /**
282
+ * Deployment cancelled successfully.
283
+ */
284
+ export const zCancelDeploymentByUuidResponse = z.object({
285
+ message: z.optional(z.string()),
286
+ deployment_uuid: z.optional(z.string()),
287
+ status: z.optional(z.string())
288
+ });
289
+ export const zDeployByTagOrUuidData = z.object({
290
+ body: z.optional(z.never()),
291
+ path: z.optional(z.never()),
292
+ query: z.optional(z.object({
293
+ tag: z.optional(z.string()),
294
+ uuid: z.optional(z.string()),
295
+ force: z.optional(z.boolean()),
296
+ pr: z.optional(z.int())
297
+ }))
298
+ });
299
+ /**
300
+ * Get deployment(s) UUID's
301
+ */
302
+ export const zDeployByTagOrUuidResponse = z.object({
303
+ deployments: z.optional(z.array(z.object({
304
+ message: z.optional(z.string()),
305
+ resource_uuid: z.optional(z.string()),
306
+ deployment_uuid: z.optional(z.string())
307
+ })))
308
+ });
309
+ export const zListDeploymentsByAppUuidData = z.object({
310
+ body: z.optional(z.never()),
311
+ path: z.object({
312
+ uuid: z.string()
313
+ }),
314
+ query: z.optional(z.object({
315
+ skip: z.optional(z.int().gte(0)).default(0),
316
+ take: z.optional(z.int().gte(1)).default(10)
317
+ }))
318
+ });
319
+ /**
320
+ * List application deployments by using the app uuid.
321
+ */
322
+ export const zListDeploymentsByAppUuidResponse = z.array(zApplication);
323
+ export const zVersionData = z.object({
324
+ body: z.optional(z.never()),
325
+ path: z.optional(z.never()),
326
+ query: z.optional(z.never())
327
+ });
328
+ /**
329
+ * Returns the version of the application
330
+ */
331
+ export const zVersionResponse = z.string();
332
+ export const zListResourcesData = z.object({
333
+ body: z.optional(z.never()),
334
+ path: z.optional(z.never()),
335
+ query: z.optional(z.never())
336
+ });
337
+ /**
338
+ * Get all resources
339
+ */
340
+ export const zListResourcesResponse = z.string();
package/dist/server.js CHANGED
@@ -1,22 +1,40 @@
1
1
  #!/usr/bin/env node
2
+ import { createRequire } from 'node:module';
2
3
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
4
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
5
  import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
5
6
  import express from 'express';
6
7
  import { registerCoolifyTools } from './tools/coolify.js';
7
8
  import { COOLIFY_OPENAPI_REF, COOLIFY_STRICT_VERSION, MCP_HTTP_PORT, MCP_TRANSPORT, } from './config.js';
8
- import { getVersion } from './coolify/client.js';
9
+ import { initializeClient } from './coolify/client.js';
10
+ import { version } from './generated/sdk.gen.js';
11
+ const require = createRequire(import.meta.url);
12
+ const { version: MCP_VERSION } = require('../package.json');
9
13
  const server = new McpServer({
10
14
  name: 'coolify-mcp',
11
- version: '0.1.2',
15
+ version: MCP_VERSION,
12
16
  });
17
+ initializeClient();
13
18
  registerCoolifyTools(server);
14
19
  function normalizeVersion(value) {
15
20
  return value.replace(/^v/i, '');
16
21
  }
22
+ function extractVersion(data) {
23
+ if (typeof data === 'string')
24
+ return data;
25
+ if (data && typeof data === 'object' && 'version' in data) {
26
+ return String(data.version);
27
+ }
28
+ return 'unknown';
29
+ }
17
30
  async function checkVersion() {
18
31
  try {
19
- const current = await getVersion();
32
+ const result = await version();
33
+ if ('error' in result && result.error) {
34
+ throw new Error('Failed to fetch version');
35
+ }
36
+ const data = 'data' in result ? result.data : undefined;
37
+ const current = extractVersion(data);
20
38
  if (normalizeVersion(current) !== normalizeVersion(COOLIFY_OPENAPI_REF)) {
21
39
  const message = `Coolify version mismatch. Server=${current}, OpenAPI=${COOLIFY_OPENAPI_REF}.`;
22
40
  if (COOLIFY_STRICT_VERSION) {
@@ -1,130 +1,133 @@
1
- import * as z from 'zod';
2
- import { request } from '../coolify/client.js';
3
- import { COOLIFY_ALLOW_WRITE } from '../config.js';
4
- function ensureWriteAllowed() {
1
+ import { COOLIFY_ALLOW_WRITE } from "../config.js";
2
+ import * as sdk from "../generated/sdk.gen.js";
3
+ import * as z from "../generated/zod.gen.js";
4
+ async function unwrap(promise) {
5
+ const result = await promise;
6
+ if (result.error) {
7
+ const msg = typeof result.error === "object" &&
8
+ result.error !== null &&
9
+ "message" in result.error
10
+ ? String(result.error.message)
11
+ : "API request failed";
12
+ throw new Error(msg);
13
+ }
14
+ return result.data;
15
+ }
16
+ // Convert any data to Record for structuredContent (single conversion point)
17
+ function toRecord(data) {
18
+ if (data === null || data === undefined)
19
+ return {};
20
+ if (typeof data === "object" && !Array.isArray(data)) {
21
+ return data;
22
+ }
23
+ // Wrap primitives and arrays in a data property
24
+ return { data };
25
+ }
26
+ const ok = (text, data) => ({
27
+ content: [{ type: "text", text }],
28
+ structuredContent: toRecord(data),
29
+ });
30
+ const list = (text, items) => ok(text, { items });
31
+ function requireWrite() {
5
32
  if (!COOLIFY_ALLOW_WRITE) {
6
- throw new Error('Write operations are disabled (COOLIFY_ALLOW_WRITE=false).');
33
+ throw new Error("Write operations are disabled (COOLIFY_ALLOW_WRITE=false).");
7
34
  }
8
35
  }
9
36
  export function registerCoolifyTools(server) {
10
- server.registerTool('coolify.listResources', {
11
- title: 'List resources',
12
- description: 'List Coolify resources (apps, databases, etc).',
37
+ server.registerTool("coolify.listResources", {
38
+ title: "List resources",
39
+ description: "List all Coolify resources.",
13
40
  inputSchema: {},
14
- outputSchema: { resources: z.array(z.unknown()) },
15
- }, async () => {
16
- const data = await request('GET', '/api/v1/resources');
17
- return {
18
- content: [{ type: 'text', text: 'Resources fetched.' }],
19
- structuredContent: { resources: data },
20
- };
21
- });
22
- server.registerTool('coolify.getApplication', {
23
- title: 'Get application',
24
- description: 'Get application details by UUID.',
25
- inputSchema: { uuid: z.string() },
26
- outputSchema: { application: z.unknown() },
27
- }, async ({ uuid }) => {
28
- const data = await request('GET', `/api/v1/applications/${uuid}`);
29
- return {
30
- content: [{ type: 'text', text: `Application ${uuid} fetched.` }],
31
- structuredContent: { application: data },
32
- };
33
- });
34
- server.registerTool('coolify.listEnvs', {
35
- title: 'List application env vars',
36
- description: 'List environment variables for an application.',
37
- inputSchema: { appUuid: z.string() },
38
- outputSchema: { envs: z.array(z.unknown()) },
39
- }, async ({ appUuid }) => {
40
- const data = await request('GET', `/api/v1/applications/${appUuid}/envs`);
41
- return {
42
- content: [{ type: 'text', text: `Env vars for ${appUuid} fetched.` }],
43
- structuredContent: { envs: data },
44
- };
45
- });
46
- server.registerTool('coolify.upsertEnv', {
47
- title: 'Upsert environment variable',
48
- description: 'Upsert an environment variable for an application.',
41
+ }, async () => list("Resources fetched.", await unwrap(sdk.listResources())));
42
+ server.registerTool("coolify.listApplications", {
43
+ title: "List applications",
44
+ description: "List all Coolify applications.",
45
+ inputSchema: {},
46
+ }, async () => list("Applications fetched.", await unwrap(sdk.listApplications())));
47
+ server.registerTool("coolify.listDatabases", {
48
+ title: "List databases",
49
+ description: "List all Coolify databases.",
50
+ inputSchema: {},
51
+ }, async () => list("Databases fetched.", await unwrap(sdk.listDatabases())));
52
+ server.registerTool("coolify.listDeployments", {
53
+ title: "List deployments",
54
+ description: "List currently running deployments.",
55
+ inputSchema: {},
56
+ }, async () => list("Running deployments fetched.", await unwrap(sdk.listDeployments())));
57
+ server.registerTool("coolify.listEnvs", {
58
+ title: "List env vars",
59
+ description: "List environment variables for an application.",
60
+ inputSchema: z.zListEnvsByApplicationUuidData.shape.path.shape,
61
+ }, async ({ uuid }) => list(`Env vars for ${uuid} fetched.`, await unwrap(sdk.listEnvsByApplicationUuid({ path: { uuid } }))));
62
+ server.registerTool("coolify.listAppDeployments", {
63
+ title: "List app deployments",
64
+ description: "List deployments for an application with pagination (skip/take).",
49
65
  inputSchema: {
50
- appUuid: z.string(),
51
- key: z.string(),
52
- value: z.string(),
53
- is_buildtime: z.boolean().optional(),
54
- is_runtime: z.boolean().optional(),
66
+ ...z.zListDeploymentsByAppUuidData.shape.path.shape,
67
+ ...z.zListDeploymentsByAppUuidData.shape.query.unwrap().shape,
55
68
  },
56
- outputSchema: { env: z.unknown() },
57
- }, async ({ appUuid, key, value, is_buildtime = true, is_runtime = true }) => {
58
- ensureWriteAllowed();
59
- const data = await request('PATCH', `/api/v1/applications/${appUuid}/envs`, {
60
- body: { key, value, is_buildtime, is_runtime },
61
- });
62
- return {
63
- content: [{ type: 'text', text: `Env ${key} upserted for ${appUuid}.` }],
64
- structuredContent: { env: data },
65
- };
66
- });
67
- server.registerTool('coolify.deploy', {
68
- title: 'Trigger deploy',
69
- description: 'Trigger a deployment for an application.',
70
- inputSchema: { appUuid: z.string(), force: z.boolean().optional() },
71
- outputSchema: { deployment: z.unknown() },
72
- }, async ({ appUuid, force = true }) => {
73
- ensureWriteAllowed();
74
- const data = await request('POST', '/api/v1/deploy', {
75
- query: { uuid: appUuid, force: String(force) },
76
- });
77
- return {
78
- content: [{ type: 'text', text: `Deploy triggered for ${appUuid}.` }],
79
- structuredContent: { deployment: data },
80
- };
81
- });
82
- server.registerTool('coolify.getDeployment', {
83
- title: 'Get deployment',
84
- description: 'Get deployment status by UUID.',
85
- inputSchema: { deploymentUuid: z.string() },
86
- outputSchema: { deployment: z.unknown() },
87
- }, async ({ deploymentUuid }) => {
88
- const data = await request('GET', `/api/v1/deployments/${deploymentUuid}`);
89
- return {
90
- content: [{ type: 'text', text: `Deployment ${deploymentUuid} fetched.` }],
91
- structuredContent: { deployment: data },
92
- };
69
+ }, async ({ uuid, ...query }) => list(`Deployments for ${uuid} fetched.`, await unwrap(sdk.listDeploymentsByAppUuid({ path: { uuid }, query }))));
70
+ server.registerTool("coolify.getApplication", {
71
+ title: "Get application",
72
+ description: "Get application details by UUID.",
73
+ inputSchema: z.zGetApplicationByUuidData.shape.path.shape,
74
+ }, async ({ uuid }) => ok(`Application ${uuid} fetched.`, await unwrap(sdk.getApplicationByUuid({ path: { uuid } }))));
75
+ server.registerTool("coolify.getDatabase", {
76
+ title: "Get database",
77
+ description: "Get database details by UUID.",
78
+ inputSchema: z.zGetDatabaseByUuidData.shape.path.shape,
79
+ }, async ({ uuid }) => ok(`Database ${uuid} fetched.`, await unwrap(sdk.getDatabaseByUuid({ path: { uuid } }))));
80
+ server.registerTool("coolify.getDeployment", {
81
+ title: "Get deployment",
82
+ description: "Get deployment status and logs by UUID.",
83
+ inputSchema: z.zGetDeploymentByUuidData.shape.path.shape,
84
+ }, async ({ uuid }) => ok(`Deployment ${uuid} fetched.`, await unwrap(sdk.getDeploymentByUuid({ path: { uuid } }))));
85
+ server.registerTool("coolify.getLogs", {
86
+ title: "Get logs",
87
+ description: "Fetch runtime logs for an application.",
88
+ inputSchema: {
89
+ ...z.zGetApplicationLogsByUuidData.shape.path.shape,
90
+ ...z.zGetApplicationLogsByUuidData.shape.query.unwrap().shape,
91
+ },
92
+ }, async ({ uuid, ...query }) => ok("Logs fetched.", await unwrap(sdk.getApplicationLogsByUuid({ path: { uuid }, query }))));
93
+ server.registerTool("coolify.createEnv", {
94
+ title: "Create env var",
95
+ description: "Create a new environment variable for an application.",
96
+ inputSchema: {
97
+ ...z.zCreateEnvByApplicationUuidData.shape.path.shape,
98
+ ...z.zCreateEnvByApplicationUuidData.shape.body.shape,
99
+ },
100
+ }, async ({ uuid, ...body }) => {
101
+ requireWrite();
102
+ const data = await unwrap(sdk.createEnvByApplicationUuid({ path: { uuid }, body }));
103
+ return ok(`Env var ${body.key} created.`, data);
93
104
  });
94
- server.registerTool('coolify.getLogs', {
95
- title: 'Get application logs',
96
- description: 'Fetch runtime logs for an application.',
97
- inputSchema: { appUuid: z.string() },
98
- outputSchema: { logs: z.string() },
99
- }, async ({ appUuid }) => {
100
- const data = await request('GET', `/api/v1/applications/${appUuid}/logs`);
101
- return {
102
- content: [{ type: 'text', text: 'Logs fetched.' }],
103
- structuredContent: { logs: data.logs ?? '' },
104
- };
105
+ server.registerTool("coolify.updateEnv", {
106
+ title: "Update env var",
107
+ description: "Update an existing environment variable for an application.",
108
+ inputSchema: {
109
+ ...z.zUpdateEnvByApplicationUuidData.shape.path.shape,
110
+ ...z.zUpdateEnvByApplicationUuidData.shape.body.shape,
111
+ },
112
+ }, async ({ uuid, ...body }) => {
113
+ requireWrite();
114
+ const data = await unwrap(sdk.updateEnvByApplicationUuid({ path: { uuid }, body }));
115
+ return ok(`Env var ${body.key} updated.`, data);
105
116
  });
106
- server.registerTool('coolify.listDatabases', {
107
- title: 'List databases',
108
- description: 'List Coolify databases.',
109
- inputSchema: {},
110
- outputSchema: { databases: z.array(z.unknown()) },
111
- }, async () => {
112
- const data = await request('GET', '/api/v1/databases');
113
- return {
114
- content: [{ type: 'text', text: 'Databases fetched.' }],
115
- structuredContent: { databases: data },
116
- };
117
+ server.registerTool("coolify.deploy", {
118
+ title: "Trigger deploy",
119
+ description: "Trigger a deployment for an application by UUID or tag.",
120
+ inputSchema: z.zDeployByTagOrUuidData.shape.query.unwrap().shape,
121
+ }, async (query) => {
122
+ requireWrite();
123
+ return ok("Deployment triggered.", await unwrap(sdk.deployByTagOrUuid({ query })));
117
124
  });
118
- server.registerTool('coolify.getDatabase', {
119
- title: 'Get database',
120
- description: 'Get database details by UUID.',
121
- inputSchema: { uuid: z.string() },
122
- outputSchema: { database: z.unknown() },
125
+ server.registerTool("coolify.cancelDeployment", {
126
+ title: "Cancel deployment",
127
+ description: "Cancel a running deployment by UUID.",
128
+ inputSchema: z.zCancelDeploymentByUuidData.shape.path.shape,
123
129
  }, async ({ uuid }) => {
124
- const data = await request('GET', `/api/v1/databases/${uuid}`);
125
- return {
126
- content: [{ type: 'text', text: `Database ${uuid} fetched.` }],
127
- structuredContent: { database: data },
128
- };
130
+ requireWrite();
131
+ return ok(`Deployment ${uuid} cancelled.`, await unwrap(sdk.cancelDeploymentByUuid({ path: { uuid } })));
129
132
  });
130
133
  }