@masonator/coolify-mcp 2.5.0 → 2.6.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/README.md +22 -0
- package/dist/__tests__/coolify-client.test.js +145 -2
- package/dist/__tests__/mcp-server.test.js +123 -1
- package/dist/lib/coolify-client.d.ts +18 -2
- package/dist/lib/coolify-client.js +58 -4
- package/dist/lib/mcp-server.d.ts +7 -1
- package/dist/lib/mcp-server.js +132 -19
- package/dist/types/coolify.d.ts +31 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -106,6 +106,28 @@ The Coolify API returns extremely verbose responses - a single application can c
|
|
|
106
106
|
| list_services | ~367KB | ~1.2KB | **99%** |
|
|
107
107
|
| list_servers | ~4KB | ~0.4KB | **90%** |
|
|
108
108
|
| list_application_envs | ~3KB/var | ~0.1KB/var | **97%** |
|
|
109
|
+
| deployment get | ~13KB | ~1KB | **92%** |
|
|
110
|
+
|
|
111
|
+
### HATEOAS-style Response Actions
|
|
112
|
+
|
|
113
|
+
Responses include contextual `_actions` suggesting relevant next steps:
|
|
114
|
+
|
|
115
|
+
```json
|
|
116
|
+
{
|
|
117
|
+
"data": { "uuid": "abc123", "status": "running" },
|
|
118
|
+
"_actions": [
|
|
119
|
+
{ "tool": "application_logs", "args": { "uuid": "abc123" }, "hint": "View logs" },
|
|
120
|
+
{
|
|
121
|
+
"tool": "control",
|
|
122
|
+
"args": { "resource": "application", "action": "restart", "uuid": "abc123" },
|
|
123
|
+
"hint": "Restart"
|
|
124
|
+
}
|
|
125
|
+
],
|
|
126
|
+
"_pagination": { "next": { "tool": "list_applications", "args": { "page": 2 } } }
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
This helps AI assistants understand logical next steps without consuming extra tokens.
|
|
109
131
|
|
|
110
132
|
### Recommended Workflow
|
|
111
133
|
|
|
@@ -84,6 +84,10 @@ describe('CoolifyClient', () => {
|
|
|
84
84
|
deployment_uuid: 'dep-123',
|
|
85
85
|
application_name: 'test-app',
|
|
86
86
|
status: 'finished',
|
|
87
|
+
force_rebuild: false,
|
|
88
|
+
is_webhook: false,
|
|
89
|
+
is_api: true,
|
|
90
|
+
restart_only: false,
|
|
87
91
|
created_at: '2024-01-01',
|
|
88
92
|
updated_at: '2024-01-01',
|
|
89
93
|
};
|
|
@@ -342,6 +346,18 @@ describe('CoolifyClient', () => {
|
|
|
342
346
|
expect(result).toEqual({ message: 'Deployed' });
|
|
343
347
|
expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/deploy?tag=my-tag&force=true', expect.any(Object));
|
|
344
348
|
});
|
|
349
|
+
it('should deploy by Coolify UUID (24 char alphanumeric)', async () => {
|
|
350
|
+
mockFetch.mockResolvedValueOnce(mockResponse({ message: 'Deployed' }));
|
|
351
|
+
// Coolify-style UUID: 24 lowercase alphanumeric chars
|
|
352
|
+
await client.deployByTagOrUuid('xs0sgs4gog044s4k4c88kgsc', false);
|
|
353
|
+
expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/deploy?uuid=xs0sgs4gog044s4k4c88kgsc&force=false', expect.any(Object));
|
|
354
|
+
});
|
|
355
|
+
it('should deploy by standard UUID format', async () => {
|
|
356
|
+
mockFetch.mockResolvedValueOnce(mockResponse({ message: 'Deployed' }));
|
|
357
|
+
// Standard UUID format with hyphens
|
|
358
|
+
await client.deployByTagOrUuid('a1b2c3d4-e5f6-7890-abcd-ef1234567890', true);
|
|
359
|
+
expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/deploy?uuid=a1b2c3d4-e5f6-7890-abcd-ef1234567890&force=true', expect.any(Object));
|
|
360
|
+
});
|
|
345
361
|
});
|
|
346
362
|
describe('private keys', () => {
|
|
347
363
|
it('should list private keys', async () => {
|
|
@@ -574,6 +590,98 @@ describe('CoolifyClient', () => {
|
|
|
574
590
|
expect(result).toEqual(mockEnvironment);
|
|
575
591
|
expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/projects/proj-uuid/production', expect.any(Object));
|
|
576
592
|
});
|
|
593
|
+
it('should get project environment with missing database types', async () => {
|
|
594
|
+
// Use environment_id to match (what real API uses)
|
|
595
|
+
const mockDbSummaries = [
|
|
596
|
+
{
|
|
597
|
+
uuid: 'pg-uuid',
|
|
598
|
+
name: 'pg-db',
|
|
599
|
+
type: 'postgresql',
|
|
600
|
+
status: 'running',
|
|
601
|
+
is_public: false,
|
|
602
|
+
environment_id: 1,
|
|
603
|
+
},
|
|
604
|
+
{
|
|
605
|
+
uuid: 'dragonfly-uuid',
|
|
606
|
+
name: 'dragonfly-cache',
|
|
607
|
+
type: 'standalone-dragonfly',
|
|
608
|
+
status: 'running',
|
|
609
|
+
is_public: false,
|
|
610
|
+
environment_id: 1,
|
|
611
|
+
},
|
|
612
|
+
{
|
|
613
|
+
uuid: 'other-env-db',
|
|
614
|
+
name: 'other-db',
|
|
615
|
+
type: 'standalone-keydb',
|
|
616
|
+
status: 'running',
|
|
617
|
+
is_public: false,
|
|
618
|
+
environment_id: 999, // different env
|
|
619
|
+
},
|
|
620
|
+
];
|
|
621
|
+
mockFetch
|
|
622
|
+
.mockResolvedValueOnce(mockResponse(mockEnvironment))
|
|
623
|
+
.mockResolvedValueOnce(mockResponse(mockDbSummaries));
|
|
624
|
+
const result = await client.getProjectEnvironmentWithDatabases('proj-uuid', 'production');
|
|
625
|
+
expect(result.uuid).toBe('env-uuid');
|
|
626
|
+
expect(result.dragonflys).toHaveLength(1);
|
|
627
|
+
expect(result.dragonflys[0].uuid).toBe('dragonfly-uuid');
|
|
628
|
+
expect(result.keydbs).toBeUndefined(); // other-env-db is in different env
|
|
629
|
+
});
|
|
630
|
+
it('should match databases by environment_uuid fallback', async () => {
|
|
631
|
+
const mockDbSummaries = [
|
|
632
|
+
{
|
|
633
|
+
uuid: 'keydb-uuid',
|
|
634
|
+
name: 'keydb-cache',
|
|
635
|
+
type: 'standalone-keydb',
|
|
636
|
+
status: 'running',
|
|
637
|
+
is_public: false,
|
|
638
|
+
environment_uuid: 'env-uuid', // matching by uuid
|
|
639
|
+
},
|
|
640
|
+
];
|
|
641
|
+
mockFetch
|
|
642
|
+
.mockResolvedValueOnce(mockResponse(mockEnvironment))
|
|
643
|
+
.mockResolvedValueOnce(mockResponse(mockDbSummaries));
|
|
644
|
+
const result = await client.getProjectEnvironmentWithDatabases('proj-uuid', 'production');
|
|
645
|
+
expect(result.keydbs).toHaveLength(1);
|
|
646
|
+
expect(result.keydbs[0].uuid).toBe('keydb-uuid');
|
|
647
|
+
});
|
|
648
|
+
it('should match databases by environment_name fallback', async () => {
|
|
649
|
+
const mockDbSummaries = [
|
|
650
|
+
{
|
|
651
|
+
uuid: 'clickhouse-uuid',
|
|
652
|
+
name: 'clickhouse-analytics',
|
|
653
|
+
type: 'standalone-clickhouse',
|
|
654
|
+
status: 'running',
|
|
655
|
+
is_public: false,
|
|
656
|
+
environment_name: 'production', // matching by name
|
|
657
|
+
},
|
|
658
|
+
];
|
|
659
|
+
mockFetch
|
|
660
|
+
.mockResolvedValueOnce(mockResponse(mockEnvironment))
|
|
661
|
+
.mockResolvedValueOnce(mockResponse(mockDbSummaries));
|
|
662
|
+
const result = await client.getProjectEnvironmentWithDatabases('proj-uuid', 'production');
|
|
663
|
+
expect(result.clickhouses).toHaveLength(1);
|
|
664
|
+
expect(result.clickhouses[0].uuid).toBe('clickhouse-uuid');
|
|
665
|
+
});
|
|
666
|
+
it('should not add empty arrays when no missing DB types exist', async () => {
|
|
667
|
+
const mockDbSummaries = [
|
|
668
|
+
{
|
|
669
|
+
uuid: 'pg-uuid',
|
|
670
|
+
name: 'pg-db',
|
|
671
|
+
type: 'postgresql', // not a "missing" type
|
|
672
|
+
status: 'running',
|
|
673
|
+
is_public: false,
|
|
674
|
+
environment_id: 1,
|
|
675
|
+
},
|
|
676
|
+
];
|
|
677
|
+
mockFetch
|
|
678
|
+
.mockResolvedValueOnce(mockResponse(mockEnvironment))
|
|
679
|
+
.mockResolvedValueOnce(mockResponse(mockDbSummaries));
|
|
680
|
+
const result = await client.getProjectEnvironmentWithDatabases('proj-uuid', 'production');
|
|
681
|
+
expect(result.dragonflys).toBeUndefined();
|
|
682
|
+
expect(result.keydbs).toBeUndefined();
|
|
683
|
+
expect(result.clickhouses).toBeUndefined();
|
|
684
|
+
});
|
|
577
685
|
it('should create a project environment', async () => {
|
|
578
686
|
mockFetch.mockResolvedValueOnce(mockResponse({ uuid: 'new-env-uuid' }));
|
|
579
687
|
const result = await client.createProjectEnvironment('proj-uuid', { name: 'staging' });
|
|
@@ -1165,12 +1273,47 @@ describe('CoolifyClient', () => {
|
|
|
1165
1273
|
},
|
|
1166
1274
|
]);
|
|
1167
1275
|
});
|
|
1168
|
-
it('should get a deployment', async () => {
|
|
1276
|
+
it('should get a deployment (essential by default, no logs)', async () => {
|
|
1169
1277
|
mockFetch.mockResolvedValueOnce(mockResponse(mockDeployment));
|
|
1170
1278
|
const result = await client.getDeployment('dep-uuid');
|
|
1171
|
-
|
|
1279
|
+
// By default, returns DeploymentEssential without logs
|
|
1280
|
+
expect(result).toEqual({
|
|
1281
|
+
uuid: 'dep-uuid',
|
|
1282
|
+
deployment_uuid: 'dep-123',
|
|
1283
|
+
application_uuid: undefined,
|
|
1284
|
+
application_name: 'test-app',
|
|
1285
|
+
server_name: undefined,
|
|
1286
|
+
status: 'finished',
|
|
1287
|
+
commit: undefined,
|
|
1288
|
+
force_rebuild: false,
|
|
1289
|
+
is_webhook: false,
|
|
1290
|
+
is_api: true,
|
|
1291
|
+
created_at: '2024-01-01',
|
|
1292
|
+
updated_at: '2024-01-01',
|
|
1293
|
+
logs_available: false,
|
|
1294
|
+
logs_info: undefined,
|
|
1295
|
+
});
|
|
1172
1296
|
expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/deployments/dep-uuid', expect.any(Object));
|
|
1173
1297
|
});
|
|
1298
|
+
it('should get a deployment with logs when includeLogs is true', async () => {
|
|
1299
|
+
const deploymentWithLogs = { ...mockDeployment, logs: 'Build started...' };
|
|
1300
|
+
mockFetch.mockResolvedValueOnce(mockResponse(deploymentWithLogs));
|
|
1301
|
+
const result = await client.getDeployment('dep-uuid', { includeLogs: true });
|
|
1302
|
+
// With includeLogs: true, returns full Deployment with logs
|
|
1303
|
+
expect(result).toEqual(deploymentWithLogs);
|
|
1304
|
+
expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/deployments/dep-uuid', expect.any(Object));
|
|
1305
|
+
});
|
|
1306
|
+
it('should include logs_info when deployment has logs but includeLogs is false', async () => {
|
|
1307
|
+
const deploymentWithLogs = { ...mockDeployment, logs: 'Build started...' };
|
|
1308
|
+
mockFetch.mockResolvedValueOnce(mockResponse(deploymentWithLogs));
|
|
1309
|
+
const result = await client.getDeployment('dep-uuid');
|
|
1310
|
+
// Should have logs_info indicating logs are available
|
|
1311
|
+
expect(result).toMatchObject({
|
|
1312
|
+
uuid: 'dep-uuid',
|
|
1313
|
+
logs_available: true,
|
|
1314
|
+
logs_info: 'Logs available (16 chars). Use lines param to retrieve.',
|
|
1315
|
+
});
|
|
1316
|
+
});
|
|
1174
1317
|
it('should list application deployments', async () => {
|
|
1175
1318
|
mockFetch.mockResolvedValueOnce(mockResponse([mockDeployment]));
|
|
1176
1319
|
const result = await client.listApplicationDeployments('app-uuid');
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* These tests verify MCP server instantiation and structure.
|
|
7
7
|
*/
|
|
8
8
|
import { describe, it, expect, beforeEach } from '@jest/globals';
|
|
9
|
-
import { CoolifyMcpServer, truncateLogs } from '../lib/mcp-server.js';
|
|
9
|
+
import { CoolifyMcpServer, truncateLogs, getApplicationActions, getDeploymentActions, getPagination, } from '../lib/mcp-server.js';
|
|
10
10
|
describe('CoolifyMcpServer v2', () => {
|
|
11
11
|
let server;
|
|
12
12
|
beforeEach(() => {
|
|
@@ -47,6 +47,7 @@ describe('CoolifyMcpServer v2', () => {
|
|
|
47
47
|
// Environment operations
|
|
48
48
|
expect(typeof client.listProjectEnvironments).toBe('function');
|
|
49
49
|
expect(typeof client.getProjectEnvironment).toBe('function');
|
|
50
|
+
expect(typeof client.getProjectEnvironmentWithDatabases).toBe('function');
|
|
50
51
|
expect(typeof client.createProjectEnvironment).toBe('function');
|
|
51
52
|
expect(typeof client.deleteProjectEnvironment).toBe('function');
|
|
52
53
|
// Application operations
|
|
@@ -186,3 +187,124 @@ describe('truncateLogs', () => {
|
|
|
186
187
|
expect(result.length).toBe(100);
|
|
187
188
|
});
|
|
188
189
|
});
|
|
190
|
+
// =============================================================================
|
|
191
|
+
// Action Generators Tests
|
|
192
|
+
// =============================================================================
|
|
193
|
+
describe('getApplicationActions', () => {
|
|
194
|
+
it('should return view logs action for all apps', () => {
|
|
195
|
+
const actions = getApplicationActions('app-uuid', 'stopped');
|
|
196
|
+
expect(actions).toContainEqual({
|
|
197
|
+
tool: 'application_logs',
|
|
198
|
+
args: { uuid: 'app-uuid' },
|
|
199
|
+
hint: 'View logs',
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
it('should return restart/stop actions for running apps', () => {
|
|
203
|
+
const actions = getApplicationActions('app-uuid', 'running');
|
|
204
|
+
expect(actions).toContainEqual({
|
|
205
|
+
tool: 'control',
|
|
206
|
+
args: { resource: 'application', action: 'restart', uuid: 'app-uuid' },
|
|
207
|
+
hint: 'Restart',
|
|
208
|
+
});
|
|
209
|
+
expect(actions).toContainEqual({
|
|
210
|
+
tool: 'control',
|
|
211
|
+
args: { resource: 'application', action: 'stop', uuid: 'app-uuid' },
|
|
212
|
+
hint: 'Stop',
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
it('should return start action for stopped apps', () => {
|
|
216
|
+
const actions = getApplicationActions('app-uuid', 'stopped');
|
|
217
|
+
expect(actions).toContainEqual({
|
|
218
|
+
tool: 'control',
|
|
219
|
+
args: { resource: 'application', action: 'start', uuid: 'app-uuid' },
|
|
220
|
+
hint: 'Start',
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
it('should handle running:healthy status', () => {
|
|
224
|
+
const actions = getApplicationActions('app-uuid', 'running:healthy');
|
|
225
|
+
expect(actions.some((a) => a.hint === 'Restart')).toBe(true);
|
|
226
|
+
expect(actions.some((a) => a.hint === 'Stop')).toBe(true);
|
|
227
|
+
});
|
|
228
|
+
it('should handle undefined status', () => {
|
|
229
|
+
const actions = getApplicationActions('app-uuid', undefined);
|
|
230
|
+
expect(actions).toContainEqual({
|
|
231
|
+
tool: 'control',
|
|
232
|
+
args: { resource: 'application', action: 'start', uuid: 'app-uuid' },
|
|
233
|
+
hint: 'Start',
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
describe('getDeploymentActions', () => {
|
|
238
|
+
it('should return cancel action for in_progress deployments', () => {
|
|
239
|
+
const actions = getDeploymentActions('dep-uuid', 'in_progress', 'app-uuid');
|
|
240
|
+
expect(actions).toContainEqual({
|
|
241
|
+
tool: 'deployment',
|
|
242
|
+
args: { action: 'cancel', uuid: 'dep-uuid' },
|
|
243
|
+
hint: 'Cancel',
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
it('should return cancel action for queued deployments', () => {
|
|
247
|
+
const actions = getDeploymentActions('dep-uuid', 'queued', 'app-uuid');
|
|
248
|
+
expect(actions).toContainEqual({
|
|
249
|
+
tool: 'deployment',
|
|
250
|
+
args: { action: 'cancel', uuid: 'dep-uuid' },
|
|
251
|
+
hint: 'Cancel',
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
it('should return app actions when appUuid provided', () => {
|
|
255
|
+
const actions = getDeploymentActions('dep-uuid', 'finished', 'app-uuid');
|
|
256
|
+
expect(actions).toContainEqual({
|
|
257
|
+
tool: 'get_application',
|
|
258
|
+
args: { uuid: 'app-uuid' },
|
|
259
|
+
hint: 'View app',
|
|
260
|
+
});
|
|
261
|
+
expect(actions).toContainEqual({
|
|
262
|
+
tool: 'application_logs',
|
|
263
|
+
args: { uuid: 'app-uuid' },
|
|
264
|
+
hint: 'App logs',
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
it('should not return cancel for finished deployments', () => {
|
|
268
|
+
const actions = getDeploymentActions('dep-uuid', 'finished', 'app-uuid');
|
|
269
|
+
expect(actions.some((a) => a.hint === 'Cancel')).toBe(false);
|
|
270
|
+
});
|
|
271
|
+
it('should return empty actions when no appUuid and not in_progress', () => {
|
|
272
|
+
const actions = getDeploymentActions('dep-uuid', 'finished', undefined);
|
|
273
|
+
expect(actions).toEqual([]);
|
|
274
|
+
});
|
|
275
|
+
});
|
|
276
|
+
describe('getPagination', () => {
|
|
277
|
+
it('should return undefined when count is less than perPage and page is 1', () => {
|
|
278
|
+
const result = getPagination('list_apps', 1, 50, 30);
|
|
279
|
+
expect(result).toBeUndefined();
|
|
280
|
+
});
|
|
281
|
+
it('should return next when count equals or exceeds perPage', () => {
|
|
282
|
+
const result = getPagination('list_apps', 1, 50, 50);
|
|
283
|
+
expect(result).toEqual({
|
|
284
|
+
next: { tool: 'list_apps', args: { page: 2, per_page: 50 } },
|
|
285
|
+
});
|
|
286
|
+
});
|
|
287
|
+
it('should return both prev and next for middle pages', () => {
|
|
288
|
+
const result = getPagination('list_apps', 2, 50, 50);
|
|
289
|
+
expect(result).toEqual({
|
|
290
|
+
prev: { tool: 'list_apps', args: { page: 1, per_page: 50 } },
|
|
291
|
+
next: { tool: 'list_apps', args: { page: 3, per_page: 50 } },
|
|
292
|
+
});
|
|
293
|
+
});
|
|
294
|
+
it('should return prev when page > 1 and count < perPage', () => {
|
|
295
|
+
const result = getPagination('list_apps', 3, 50, 20);
|
|
296
|
+
expect(result).toEqual({
|
|
297
|
+
prev: { tool: 'list_apps', args: { page: 2, per_page: 50 } },
|
|
298
|
+
});
|
|
299
|
+
});
|
|
300
|
+
it('should use default page and perPage when undefined', () => {
|
|
301
|
+
const result = getPagination('list_apps', undefined, undefined, 100);
|
|
302
|
+
expect(result).toEqual({
|
|
303
|
+
next: { tool: 'list_apps', args: { page: 2, per_page: 50 } },
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
it('should return undefined when count is undefined', () => {
|
|
307
|
+
const result = getPagination('list_apps', 1, 50, undefined);
|
|
308
|
+
expect(result).toBeUndefined();
|
|
309
|
+
});
|
|
310
|
+
});
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Coolify API Client
|
|
3
3
|
* Complete HTTP client for the Coolify API v1
|
|
4
4
|
*/
|
|
5
|
-
import type { CoolifyConfig, DeleteOptions, MessageResponse, UuidResponse, Server, ServerResource, ServerDomain, ServerValidation, CreateServerRequest, UpdateServerRequest, Project, CreateProjectRequest, UpdateProjectRequest, Environment, CreateEnvironmentRequest, Application, CreateApplicationPublicRequest, CreateApplicationPrivateGHRequest, CreateApplicationPrivateKeyRequest, CreateApplicationDockerfileRequest, CreateApplicationDockerImageRequest, CreateApplicationDockerComposeRequest, UpdateApplicationRequest, ApplicationActionResponse, EnvironmentVariable, EnvVarSummary, CreateEnvVarRequest, UpdateEnvVarRequest, BulkUpdateEnvVarsRequest, Database, UpdateDatabaseRequest, CreatePostgresqlRequest, CreateMysqlRequest, CreateMariadbRequest, CreateMongodbRequest, CreateRedisRequest, CreateKeydbRequest, CreateClickhouseRequest, CreateDragonflyRequest, CreateDatabaseResponse, DatabaseBackup, BackupExecution, CreateDatabaseBackupRequest, UpdateDatabaseBackupRequest, Service, CreateServiceRequest, UpdateServiceRequest, ServiceCreateResponse, Deployment, Team, TeamMember, PrivateKey, CreatePrivateKeyRequest, UpdatePrivateKeyRequest, GitHubApp, CreateGitHubAppRequest, UpdateGitHubAppRequest, GitHubAppUpdateResponse, CloudToken, CreateCloudTokenRequest, UpdateCloudTokenRequest, CloudTokenValidation, Version, ApplicationDiagnostic, ServerDiagnostic, InfrastructureIssuesReport, BatchOperationResult } from '../types/coolify.js';
|
|
5
|
+
import type { CoolifyConfig, DeleteOptions, MessageResponse, UuidResponse, Server, ServerResource, ServerDomain, ServerValidation, CreateServerRequest, UpdateServerRequest, Project, CreateProjectRequest, UpdateProjectRequest, Environment, CreateEnvironmentRequest, Application, CreateApplicationPublicRequest, CreateApplicationPrivateGHRequest, CreateApplicationPrivateKeyRequest, CreateApplicationDockerfileRequest, CreateApplicationDockerImageRequest, CreateApplicationDockerComposeRequest, UpdateApplicationRequest, ApplicationActionResponse, EnvironmentVariable, EnvVarSummary, CreateEnvVarRequest, UpdateEnvVarRequest, BulkUpdateEnvVarsRequest, Database, UpdateDatabaseRequest, CreatePostgresqlRequest, CreateMysqlRequest, CreateMariadbRequest, CreateMongodbRequest, CreateRedisRequest, CreateKeydbRequest, CreateClickhouseRequest, CreateDragonflyRequest, CreateDatabaseResponse, DatabaseBackup, BackupExecution, CreateDatabaseBackupRequest, UpdateDatabaseBackupRequest, Service, CreateServiceRequest, UpdateServiceRequest, ServiceCreateResponse, Deployment, DeploymentEssential, Team, TeamMember, PrivateKey, CreatePrivateKeyRequest, UpdatePrivateKeyRequest, GitHubApp, CreateGitHubAppRequest, UpdateGitHubAppRequest, GitHubAppUpdateResponse, CloudToken, CreateCloudTokenRequest, UpdateCloudTokenRequest, CloudTokenValidation, Version, ApplicationDiagnostic, ServerDiagnostic, InfrastructureIssuesReport, BatchOperationResult } from '../types/coolify.js';
|
|
6
6
|
export interface ListOptions {
|
|
7
7
|
page?: number;
|
|
8
8
|
per_page?: number;
|
|
@@ -35,6 +35,9 @@ export interface DatabaseSummary {
|
|
|
35
35
|
type: string;
|
|
36
36
|
status: string;
|
|
37
37
|
is_public: boolean;
|
|
38
|
+
environment_uuid?: string;
|
|
39
|
+
environment_name?: string;
|
|
40
|
+
environment_id?: number;
|
|
38
41
|
}
|
|
39
42
|
export interface ServiceSummary {
|
|
40
43
|
uuid: string;
|
|
@@ -89,6 +92,17 @@ export declare class CoolifyClient {
|
|
|
89
92
|
deleteProject(uuid: string): Promise<MessageResponse>;
|
|
90
93
|
listProjectEnvironments(projectUuid: string): Promise<Environment[]>;
|
|
91
94
|
getProjectEnvironment(projectUuid: string, environmentNameOrUuid: string): Promise<Environment>;
|
|
95
|
+
/**
|
|
96
|
+
* Get environment with missing database types (dragonfly, keydb, clickhouse).
|
|
97
|
+
* Coolify API omits these from the environment endpoint - we cross-reference
|
|
98
|
+
* with listDatabases using lightweight summaries.
|
|
99
|
+
* @see https://github.com/StuMason/coolify-mcp/issues/88
|
|
100
|
+
*/
|
|
101
|
+
getProjectEnvironmentWithDatabases(projectUuid: string, environmentNameOrUuid: string): Promise<Environment & {
|
|
102
|
+
dragonflys?: DatabaseSummary[];
|
|
103
|
+
keydbs?: DatabaseSummary[];
|
|
104
|
+
clickhouses?: DatabaseSummary[];
|
|
105
|
+
}>;
|
|
92
106
|
createProjectEnvironment(projectUuid: string, data: CreateEnvironmentRequest): Promise<UuidResponse>;
|
|
93
107
|
deleteProjectEnvironment(projectUuid: string, environmentNameOrUuid: string): Promise<MessageResponse>;
|
|
94
108
|
listApplications(options?: ListOptions): Promise<Application[] | ApplicationSummary[]>;
|
|
@@ -143,7 +157,9 @@ export declare class CoolifyClient {
|
|
|
143
157
|
updateServiceEnvVar(uuid: string, data: UpdateEnvVarRequest): Promise<MessageResponse>;
|
|
144
158
|
deleteServiceEnvVar(uuid: string, envUuid: string): Promise<MessageResponse>;
|
|
145
159
|
listDeployments(options?: ListOptions): Promise<Deployment[] | DeploymentSummary[]>;
|
|
146
|
-
getDeployment(uuid: string
|
|
160
|
+
getDeployment(uuid: string, options?: {
|
|
161
|
+
includeLogs?: boolean;
|
|
162
|
+
}): Promise<Deployment | DeploymentEssential>;
|
|
147
163
|
deployByTagOrUuid(tagOrUuid: string, force?: boolean): Promise<MessageResponse>;
|
|
148
164
|
listApplicationDeployments(appUuid: string): Promise<Deployment[]>;
|
|
149
165
|
listTeams(): Promise<Team[]>;
|
|
@@ -38,12 +38,17 @@ function toApplicationSummary(app) {
|
|
|
38
38
|
};
|
|
39
39
|
}
|
|
40
40
|
function toDatabaseSummary(db) {
|
|
41
|
+
// API returns database_type not type, and environment_id not environment_uuid
|
|
42
|
+
const raw = db;
|
|
41
43
|
return {
|
|
42
44
|
uuid: db.uuid,
|
|
43
45
|
name: db.name,
|
|
44
|
-
type: db.type,
|
|
46
|
+
type: db.type || raw.database_type,
|
|
45
47
|
status: db.status,
|
|
46
48
|
is_public: db.is_public,
|
|
49
|
+
environment_uuid: db.environment_uuid,
|
|
50
|
+
environment_name: db.environment_name,
|
|
51
|
+
environment_id: raw.environment_id,
|
|
47
52
|
};
|
|
48
53
|
}
|
|
49
54
|
function toServiceSummary(svc) {
|
|
@@ -64,6 +69,26 @@ function toDeploymentSummary(dep) {
|
|
|
64
69
|
created_at: dep.created_at,
|
|
65
70
|
};
|
|
66
71
|
}
|
|
72
|
+
function toDeploymentEssential(dep) {
|
|
73
|
+
return {
|
|
74
|
+
uuid: dep.uuid,
|
|
75
|
+
deployment_uuid: dep.deployment_uuid,
|
|
76
|
+
application_uuid: dep.application_uuid,
|
|
77
|
+
application_name: dep.application_name,
|
|
78
|
+
server_name: dep.server_name,
|
|
79
|
+
status: dep.status,
|
|
80
|
+
commit: dep.commit,
|
|
81
|
+
force_rebuild: dep.force_rebuild,
|
|
82
|
+
is_webhook: dep.is_webhook,
|
|
83
|
+
is_api: dep.is_api,
|
|
84
|
+
created_at: dep.created_at,
|
|
85
|
+
updated_at: dep.updated_at,
|
|
86
|
+
logs_available: !!dep.logs,
|
|
87
|
+
logs_info: dep.logs
|
|
88
|
+
? `Logs available (${dep.logs.length} chars). Use lines param to retrieve.`
|
|
89
|
+
: undefined,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
67
92
|
function toProjectSummary(proj) {
|
|
68
93
|
return {
|
|
69
94
|
uuid: proj.uuid,
|
|
@@ -256,6 +281,32 @@ export class CoolifyClient {
|
|
|
256
281
|
async getProjectEnvironment(projectUuid, environmentNameOrUuid) {
|
|
257
282
|
return this.request(`/projects/${projectUuid}/${environmentNameOrUuid}`);
|
|
258
283
|
}
|
|
284
|
+
/**
|
|
285
|
+
* Get environment with missing database types (dragonfly, keydb, clickhouse).
|
|
286
|
+
* Coolify API omits these from the environment endpoint - we cross-reference
|
|
287
|
+
* with listDatabases using lightweight summaries.
|
|
288
|
+
* @see https://github.com/StuMason/coolify-mcp/issues/88
|
|
289
|
+
*/
|
|
290
|
+
async getProjectEnvironmentWithDatabases(projectUuid, environmentNameOrUuid) {
|
|
291
|
+
const [environment, dbSummaries] = await Promise.all([
|
|
292
|
+
this.getProjectEnvironment(projectUuid, environmentNameOrUuid),
|
|
293
|
+
this.listDatabases({ summary: true }),
|
|
294
|
+
]);
|
|
295
|
+
// Filter for this environment's missing database types
|
|
296
|
+
// API uses environment_id, not environment_uuid
|
|
297
|
+
const envDbs = dbSummaries.filter((db) => db.environment_id === environment.id ||
|
|
298
|
+
db.environment_uuid === environment.uuid ||
|
|
299
|
+
db.environment_name === environment.name);
|
|
300
|
+
const dragonflys = envDbs.filter((db) => db.type?.includes('dragonfly'));
|
|
301
|
+
const keydbs = envDbs.filter((db) => db.type?.includes('keydb'));
|
|
302
|
+
const clickhouses = envDbs.filter((db) => db.type?.includes('clickhouse'));
|
|
303
|
+
return {
|
|
304
|
+
...environment,
|
|
305
|
+
...(dragonflys.length > 0 && { dragonflys }),
|
|
306
|
+
...(keydbs.length > 0 && { keydbs }),
|
|
307
|
+
...(clickhouses.length > 0 && { clickhouses }),
|
|
308
|
+
};
|
|
309
|
+
}
|
|
259
310
|
async createProjectEnvironment(projectUuid, data) {
|
|
260
311
|
return this.request(`/projects/${projectUuid}/environments`, {
|
|
261
312
|
method: 'POST',
|
|
@@ -569,11 +620,14 @@ export class CoolifyClient {
|
|
|
569
620
|
? deployments.map(toDeploymentSummary)
|
|
570
621
|
: deployments;
|
|
571
622
|
}
|
|
572
|
-
async getDeployment(uuid) {
|
|
573
|
-
|
|
623
|
+
async getDeployment(uuid, options) {
|
|
624
|
+
const deployment = await this.request(`/deployments/${uuid}`);
|
|
625
|
+
return options?.includeLogs ? deployment : toDeploymentEssential(deployment);
|
|
574
626
|
}
|
|
575
627
|
async deployByTagOrUuid(tagOrUuid, force = false) {
|
|
576
|
-
|
|
628
|
+
// Detect if the value looks like a UUID or a tag name
|
|
629
|
+
const param = this.isLikelyUuid(tagOrUuid) ? 'uuid' : 'tag';
|
|
630
|
+
return this.request(`/deploy?${param}=${encodeURIComponent(tagOrUuid)}&force=${force}`, { method: 'GET' });
|
|
577
631
|
}
|
|
578
632
|
async listApplicationDeployments(appUuid) {
|
|
579
633
|
return this.request(`/applications/${appUuid}/deployments`);
|
package/dist/lib/mcp-server.d.ts
CHANGED
|
@@ -4,12 +4,18 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
6
6
|
import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
|
|
7
|
-
import type { CoolifyConfig } from '../types/coolify.js';
|
|
7
|
+
import type { CoolifyConfig, ResponseAction, ResponsePagination } from '../types/coolify.js';
|
|
8
8
|
/**
|
|
9
9
|
* Truncate logs by line count and character count.
|
|
10
10
|
* Exported for testing.
|
|
11
11
|
*/
|
|
12
12
|
export declare function truncateLogs(logs: string, lineLimit?: number, charLimit?: number): string;
|
|
13
|
+
/** Generate contextual actions for an application based on its status */
|
|
14
|
+
export declare function getApplicationActions(uuid: string, status?: string): ResponseAction[];
|
|
15
|
+
/** Generate contextual actions for a deployment */
|
|
16
|
+
export declare function getDeploymentActions(uuid: string, status: string, appUuid?: string): ResponseAction[];
|
|
17
|
+
/** Generate pagination info for list endpoints */
|
|
18
|
+
export declare function getPagination(tool: string, page?: number, perPage?: number, count?: number): ResponsePagination | undefined;
|
|
13
19
|
export declare class CoolifyMcpServer extends McpServer {
|
|
14
20
|
private readonly client;
|
|
15
21
|
constructor(config: CoolifyConfig);
|
package/dist/lib/mcp-server.js
CHANGED
|
@@ -39,6 +39,82 @@ export function truncateLogs(logs, lineLimit = 200, charLimit = 50000) {
|
|
|
39
39
|
}
|
|
40
40
|
return truncatedLogs;
|
|
41
41
|
}
|
|
42
|
+
// =============================================================================
|
|
43
|
+
// Action Generators for HATEOAS-style responses
|
|
44
|
+
// =============================================================================
|
|
45
|
+
/** Generate contextual actions for an application based on its status */
|
|
46
|
+
export function getApplicationActions(uuid, status) {
|
|
47
|
+
const actions = [
|
|
48
|
+
{ tool: 'application_logs', args: { uuid }, hint: 'View logs' },
|
|
49
|
+
];
|
|
50
|
+
const s = (status || '').toLowerCase();
|
|
51
|
+
if (s.includes('running')) {
|
|
52
|
+
actions.push({
|
|
53
|
+
tool: 'control',
|
|
54
|
+
args: { resource: 'application', action: 'restart', uuid },
|
|
55
|
+
hint: 'Restart',
|
|
56
|
+
});
|
|
57
|
+
actions.push({
|
|
58
|
+
tool: 'control',
|
|
59
|
+
args: { resource: 'application', action: 'stop', uuid },
|
|
60
|
+
hint: 'Stop',
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
actions.push({
|
|
65
|
+
tool: 'control',
|
|
66
|
+
args: { resource: 'application', action: 'start', uuid },
|
|
67
|
+
hint: 'Start',
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
return actions;
|
|
71
|
+
}
|
|
72
|
+
/** Generate contextual actions for a deployment */
|
|
73
|
+
export function getDeploymentActions(uuid, status, appUuid) {
|
|
74
|
+
const actions = [];
|
|
75
|
+
if (status === 'in_progress' || status === 'queued') {
|
|
76
|
+
actions.push({ tool: 'deployment', args: { action: 'cancel', uuid }, hint: 'Cancel' });
|
|
77
|
+
}
|
|
78
|
+
if (appUuid) {
|
|
79
|
+
actions.push({ tool: 'get_application', args: { uuid: appUuid }, hint: 'View app' });
|
|
80
|
+
actions.push({ tool: 'application_logs', args: { uuid: appUuid }, hint: 'App logs' });
|
|
81
|
+
}
|
|
82
|
+
return actions;
|
|
83
|
+
}
|
|
84
|
+
/** Generate pagination info for list endpoints */
|
|
85
|
+
export function getPagination(tool, page, perPage, count) {
|
|
86
|
+
const p = page ?? 1;
|
|
87
|
+
const pp = perPage ?? 50;
|
|
88
|
+
if (!count || count < pp) {
|
|
89
|
+
return p > 1 ? { prev: { tool, args: { page: p - 1, per_page: pp } } } : undefined;
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
...(p > 1 && { prev: { tool, args: { page: p - 1, per_page: pp } } }),
|
|
93
|
+
next: { tool, args: { page: p + 1, per_page: pp } },
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
/** Wrap handler with error handling and HATEOAS actions */
|
|
97
|
+
function wrapWithActions(fn, getActions, getPaginationFn) {
|
|
98
|
+
return fn()
|
|
99
|
+
.then((result) => {
|
|
100
|
+
const actions = getActions?.(result) ?? [];
|
|
101
|
+
const pagination = getPaginationFn?.(result);
|
|
102
|
+
const response = { data: result };
|
|
103
|
+
if (actions.length > 0)
|
|
104
|
+
response._actions = actions;
|
|
105
|
+
if (pagination)
|
|
106
|
+
response._pagination = pagination;
|
|
107
|
+
return { content: [{ type: 'text', text: JSON.stringify(response, null, 2) }] };
|
|
108
|
+
})
|
|
109
|
+
.catch((error) => ({
|
|
110
|
+
content: [
|
|
111
|
+
{
|
|
112
|
+
type: 'text',
|
|
113
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
}));
|
|
117
|
+
}
|
|
42
118
|
export class CoolifyMcpServer extends McpServer {
|
|
43
119
|
constructor(config) {
|
|
44
120
|
super({ name: 'coolify', version: VERSION });
|
|
@@ -150,7 +226,7 @@ export class CoolifyMcpServer extends McpServer {
|
|
|
150
226
|
// =========================================================================
|
|
151
227
|
// Environments (1 tool - consolidated CRUD)
|
|
152
228
|
// =========================================================================
|
|
153
|
-
this.tool('environments', 'Manage environments: list/get/create/delete', {
|
|
229
|
+
this.tool('environments', 'Manage environments: list/get/create/delete (get includes dragonfly/keydb/clickhouse DBs missing from API)', {
|
|
154
230
|
action: z.enum(['list', 'get', 'create', 'delete']),
|
|
155
231
|
project_uuid: z.string(),
|
|
156
232
|
name: z.string().optional(),
|
|
@@ -162,7 +238,8 @@ export class CoolifyMcpServer extends McpServer {
|
|
|
162
238
|
case 'get':
|
|
163
239
|
if (!name)
|
|
164
240
|
return { content: [{ type: 'text', text: 'Error: name required' }] };
|
|
165
|
-
|
|
241
|
+
// Use enhanced method that includes missing DB types (#88)
|
|
242
|
+
return wrap(() => this.client.getProjectEnvironmentWithDatabases(project_uuid, name));
|
|
166
243
|
case 'create':
|
|
167
244
|
if (!name)
|
|
168
245
|
return { content: [{ type: 'text', text: 'Error: name required' }] };
|
|
@@ -176,8 +253,8 @@ export class CoolifyMcpServer extends McpServer {
|
|
|
176
253
|
// =========================================================================
|
|
177
254
|
// Applications (4 tools)
|
|
178
255
|
// =========================================================================
|
|
179
|
-
this.tool('list_applications', 'List apps (summary)', { page: z.number().optional(), per_page: z.number().optional() }, async ({ page, per_page }) =>
|
|
180
|
-
this.tool('get_application', 'App details', { uuid: z.string() }, async ({ uuid }) =>
|
|
256
|
+
this.tool('list_applications', 'List apps (summary)', { page: z.number().optional(), per_page: z.number().optional() }, async ({ page, per_page }) => wrapWithActions(() => this.client.listApplications({ page, per_page, summary: true }), undefined, (result) => getPagination('list_applications', page, per_page, result.length)));
|
|
257
|
+
this.tool('get_application', 'App details', { uuid: z.string() }, async ({ uuid }) => wrapWithActions(() => this.client.getApplication(uuid), (app) => getApplicationActions(app.uuid, app.status)));
|
|
181
258
|
this.tool('application', 'Manage app: create/update/delete', {
|
|
182
259
|
action: z.enum([
|
|
183
260
|
'create_public',
|
|
@@ -504,7 +581,36 @@ export class CoolifyMcpServer extends McpServer {
|
|
|
504
581
|
restart: (u) => this.client.restartService(u),
|
|
505
582
|
},
|
|
506
583
|
};
|
|
507
|
-
|
|
584
|
+
// Generate contextual actions based on resource type and action taken
|
|
585
|
+
const getControlActions = () => {
|
|
586
|
+
const actions = [];
|
|
587
|
+
if (resource === 'application') {
|
|
588
|
+
actions.push({ tool: 'application_logs', args: { uuid }, hint: 'View logs' });
|
|
589
|
+
actions.push({ tool: 'get_application', args: { uuid }, hint: 'Check status' });
|
|
590
|
+
if (action === 'start' || action === 'restart') {
|
|
591
|
+
actions.push({
|
|
592
|
+
tool: 'control',
|
|
593
|
+
args: { resource: 'application', action: 'stop', uuid },
|
|
594
|
+
hint: 'Stop',
|
|
595
|
+
});
|
|
596
|
+
}
|
|
597
|
+
else {
|
|
598
|
+
actions.push({
|
|
599
|
+
tool: 'control',
|
|
600
|
+
args: { resource: 'application', action: 'start', uuid },
|
|
601
|
+
hint: 'Start',
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
else if (resource === 'database') {
|
|
606
|
+
actions.push({ tool: 'get_database', args: { uuid }, hint: 'Check status' });
|
|
607
|
+
}
|
|
608
|
+
else if (resource === 'service') {
|
|
609
|
+
actions.push({ tool: 'get_service', args: { uuid }, hint: 'Check status' });
|
|
610
|
+
}
|
|
611
|
+
return actions;
|
|
612
|
+
};
|
|
613
|
+
return wrapWithActions(() => methods[resource][action](uuid), getControlActions);
|
|
508
614
|
});
|
|
509
615
|
// =========================================================================
|
|
510
616
|
// Environment Variables (1 tool - consolidated)
|
|
@@ -516,8 +622,7 @@ export class CoolifyMcpServer extends McpServer {
|
|
|
516
622
|
key: z.string().optional(),
|
|
517
623
|
value: z.string().optional(),
|
|
518
624
|
env_uuid: z.string().optional(),
|
|
519
|
-
|
|
520
|
-
}, async ({ resource, action, uuid, key, value, env_uuid, is_build_time }) => {
|
|
625
|
+
}, async ({ resource, action, uuid, key, value, env_uuid }) => {
|
|
521
626
|
if (resource === 'application') {
|
|
522
627
|
switch (action) {
|
|
523
628
|
case 'list':
|
|
@@ -525,7 +630,8 @@ export class CoolifyMcpServer extends McpServer {
|
|
|
525
630
|
case 'create':
|
|
526
631
|
if (!key || !value)
|
|
527
632
|
return { content: [{ type: 'text', text: 'Error: key, value required' }] };
|
|
528
|
-
|
|
633
|
+
// Note: is_build_time is not passed - Coolify API rejects it for create action
|
|
634
|
+
return wrap(() => this.client.createApplicationEnvVar(uuid, { key, value }));
|
|
529
635
|
case 'update':
|
|
530
636
|
if (!key || !value)
|
|
531
637
|
return { content: [{ type: 'text', text: 'Error: key, value required' }] };
|
|
@@ -560,23 +666,30 @@ export class CoolifyMcpServer extends McpServer {
|
|
|
560
666
|
// =========================================================================
|
|
561
667
|
// Deployments (3 tools)
|
|
562
668
|
// =========================================================================
|
|
563
|
-
this.tool('list_deployments', 'List deployments (summary)', { page: z.number().optional(), per_page: z.number().optional() }, async ({ page, per_page }) =>
|
|
564
|
-
this.tool('deploy', 'Deploy by tag/UUID', { tag_or_uuid: z.string(), force: z.boolean().optional() }, async ({ tag_or_uuid, force }) =>
|
|
565
|
-
this.tool('deployment', 'Manage deployment: get/cancel/list_for_app (logs
|
|
669
|
+
this.tool('list_deployments', 'List deployments (summary)', { page: z.number().optional(), per_page: z.number().optional() }, async ({ page, per_page }) => wrapWithActions(() => this.client.listDeployments({ page, per_page, summary: true }), undefined, (result) => getPagination('list_deployments', page, per_page, result.length)));
|
|
670
|
+
this.tool('deploy', 'Deploy by tag/UUID', { tag_or_uuid: z.string(), force: z.boolean().optional() }, async ({ tag_or_uuid, force }) => wrapWithActions(() => this.client.deployByTagOrUuid(tag_or_uuid, force), () => [{ tool: 'list_deployments', args: {}, hint: 'Check deployment status' }]));
|
|
671
|
+
this.tool('deployment', 'Manage deployment: get/cancel/list_for_app (logs excluded by default, use lines param to include)', {
|
|
566
672
|
action: z.enum(['get', 'cancel', 'list_for_app']),
|
|
567
673
|
uuid: z.string(),
|
|
568
|
-
lines: z.number().optional(), //
|
|
674
|
+
lines: z.number().optional(), // Include logs truncated to last N lines (omit for no logs)
|
|
569
675
|
max_chars: z.number().optional(), // Limit log output to last N chars (default: 50000)
|
|
570
676
|
}, async ({ action, uuid, lines, max_chars }) => {
|
|
571
677
|
switch (action) {
|
|
572
678
|
case 'get':
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
deployment
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
679
|
+
// If lines param specified, include logs and truncate
|
|
680
|
+
if (lines !== undefined) {
|
|
681
|
+
return wrapWithActions(async () => {
|
|
682
|
+
const deployment = (await this.client.getDeployment(uuid, {
|
|
683
|
+
includeLogs: true,
|
|
684
|
+
}));
|
|
685
|
+
if (deployment.logs) {
|
|
686
|
+
deployment.logs = truncateLogs(deployment.logs, lines, max_chars ?? 50000);
|
|
687
|
+
}
|
|
688
|
+
return deployment;
|
|
689
|
+
}, (dep) => getDeploymentActions(dep.uuid, dep.status, dep.application_uuid));
|
|
690
|
+
}
|
|
691
|
+
// Otherwise return essential info without logs
|
|
692
|
+
return wrapWithActions(() => this.client.getDeployment(uuid), (dep) => getDeploymentActions(dep.uuid, dep.status, dep.application_uuid));
|
|
580
693
|
case 'cancel':
|
|
581
694
|
return wrap(() => this.client.cancelDeployment(uuid));
|
|
582
695
|
case 'list_for_app':
|
package/dist/types/coolify.d.ts
CHANGED
|
@@ -901,3 +901,34 @@ export interface BatchOperationResult {
|
|
|
901
901
|
error: string;
|
|
902
902
|
}>;
|
|
903
903
|
}
|
|
904
|
+
export interface ResponseAction {
|
|
905
|
+
tool: string;
|
|
906
|
+
args: Record<string, string | number | boolean>;
|
|
907
|
+
hint: string;
|
|
908
|
+
}
|
|
909
|
+
export interface ResponsePagination {
|
|
910
|
+
next?: {
|
|
911
|
+
tool: string;
|
|
912
|
+
args: Record<string, number>;
|
|
913
|
+
};
|
|
914
|
+
prev?: {
|
|
915
|
+
tool: string;
|
|
916
|
+
args: Record<string, number>;
|
|
917
|
+
};
|
|
918
|
+
}
|
|
919
|
+
export interface DeploymentEssential {
|
|
920
|
+
uuid: string;
|
|
921
|
+
deployment_uuid: string;
|
|
922
|
+
application_uuid?: string;
|
|
923
|
+
application_name?: string;
|
|
924
|
+
server_name?: string;
|
|
925
|
+
status: string;
|
|
926
|
+
commit?: string;
|
|
927
|
+
force_rebuild: boolean;
|
|
928
|
+
is_webhook: boolean;
|
|
929
|
+
is_api: boolean;
|
|
930
|
+
created_at: string;
|
|
931
|
+
updated_at: string;
|
|
932
|
+
logs_available?: boolean;
|
|
933
|
+
logs_info?: string;
|
|
934
|
+
}
|