@masonator/coolify-mcp 1.6.0 → 2.0.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 -30
- package/dist/__tests__/mcp-server.test.js +116 -398
- package/dist/lib/mcp-server.d.ts +2 -19
- package/dist/lib/mcp-server.js +436 -613
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -9,41 +9,33 @@
|
|
|
9
9
|
[](https://codecov.io/gh/StuMason/coolify-mcp)
|
|
10
10
|
[](https://mseep.ai/app/stumason-coolify-mcp)
|
|
11
11
|
|
|
12
|
-
> **The most comprehensive MCP server for Coolify** -
|
|
12
|
+
> **The most comprehensive MCP server for Coolify** - 34 optimized tools, smart diagnostics, and batch operations for managing your self-hosted PaaS through AI assistants.
|
|
13
13
|
|
|
14
14
|
A Model Context Protocol (MCP) server for [Coolify](https://coolify.io/), enabling AI assistants to manage and debug your Coolify instances through natural language.
|
|
15
15
|
|
|
16
16
|
## Features
|
|
17
17
|
|
|
18
|
-
This MCP server provides **
|
|
19
|
-
|
|
20
|
-
| Category | Tools
|
|
21
|
-
| -------------------- |
|
|
22
|
-
| **Infrastructure** |
|
|
23
|
-
| **Diagnostics** | diagnose_app
|
|
24
|
-
| **Batch Operations** | restart_project_apps
|
|
25
|
-
| **Servers** |
|
|
26
|
-
| **Projects** | list, get, create, update, delete
|
|
27
|
-
| **Environments** | list, get, create, delete
|
|
28
|
-
| **Applications** |
|
|
29
|
-
| **Databases** |
|
|
30
|
-
| **Services** |
|
|
31
|
-
| **
|
|
32
|
-
| **
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
| ------------------ | ----------------------------------------------------------- |
|
|
40
|
-
| `debug-app` | Debug an application - gathers logs, status, env vars |
|
|
41
|
-
| `health-check` | Full infrastructure health analysis |
|
|
42
|
-
| `deploy-app` | Step-by-step deployment wizard from Git repository |
|
|
43
|
-
| `troubleshoot-ssl` | SSL/TLS certificate diagnosis workflow |
|
|
44
|
-
| `restart-project` | Safely restart all apps in a project with status monitoring |
|
|
45
|
-
| `env-audit` | Audit and compare environment variables across applications |
|
|
46
|
-
| `backup-status` | Check database backup status and history |
|
|
18
|
+
This MCP server provides **34 token-optimized tools** for **debugging, management, and deployment**:
|
|
19
|
+
|
|
20
|
+
| Category | Tools |
|
|
21
|
+
| -------------------- | ----------------------------------------------------------------------------------------- |
|
|
22
|
+
| **Infrastructure** | `get_infrastructure_overview`, `get_mcp_version`, `get_version` |
|
|
23
|
+
| **Diagnostics** | `diagnose_app`, `diagnose_server`, `find_issues` |
|
|
24
|
+
| **Batch Operations** | `restart_project_apps`, `bulk_env_update`, `stop_all_apps`, `redeploy_project` |
|
|
25
|
+
| **Servers** | `list_servers`, `get_server`, `validate_server`, `server_resources`, `server_domains` |
|
|
26
|
+
| **Projects** | `projects` (list, get, create, update, delete via action param) |
|
|
27
|
+
| **Environments** | `environments` (list, get, create, delete via action param) |
|
|
28
|
+
| **Applications** | `list_applications`, `get_application`, `application` (CRUD), `application_logs` |
|
|
29
|
+
| **Databases** | `list_databases`, `get_database`, `database` (create 8 types, delete), `database_backups` |
|
|
30
|
+
| **Services** | `list_services`, `get_service`, `service` (create, update, delete) |
|
|
31
|
+
| **Control** | `control` (start/stop/restart for apps, databases, services) |
|
|
32
|
+
| **Env Vars** | `env_vars` (CRUD for application and service env vars) |
|
|
33
|
+
| **Deployments** | `list_deployments`, `deploy`, `deployment` (get, cancel, list_for_app) |
|
|
34
|
+
| **Private Keys** | `private_keys` (list, get, create, update, delete via action param) |
|
|
35
|
+
|
|
36
|
+
### v2.0.0 Token Diet
|
|
37
|
+
|
|
38
|
+
v2.0.0 reduced token usage by **85%** (from ~43,000 to ~6,600 tokens) by consolidating related operations into single tools with action parameters. This prevents context window exhaustion in AI assistants.
|
|
47
39
|
|
|
48
40
|
## Installation
|
|
49
41
|
|
|
@@ -1,414 +1,132 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* MCP Server Tests
|
|
2
|
+
* MCP Server Tests v2.0.0
|
|
3
3
|
*
|
|
4
|
-
* Tests for the MCP
|
|
5
|
-
* -
|
|
6
|
-
*
|
|
4
|
+
* Tests for the consolidated MCP tool layer.
|
|
5
|
+
* CoolifyClient methods are fully tested in coolify-client.test.ts (174 tests).
|
|
6
|
+
* These tests verify MCP server instantiation and structure.
|
|
7
7
|
*/
|
|
8
|
-
import {
|
|
9
|
-
// Create typed mock functions
|
|
10
|
-
const mockListServers = jest.fn();
|
|
11
|
-
const mockListProjects = jest.fn();
|
|
12
|
-
const mockListApplications = jest.fn();
|
|
13
|
-
const mockListDatabases = jest.fn();
|
|
14
|
-
const mockListServices = jest.fn();
|
|
15
|
-
const mockDiagnoseApplication = jest.fn();
|
|
16
|
-
const mockDiagnoseServer = jest.fn();
|
|
17
|
-
const mockFindInfrastructureIssues = jest.fn();
|
|
18
|
-
const mockDeleteDatabase = jest.fn();
|
|
19
|
-
const mockCreatePostgresql = jest.fn();
|
|
20
|
-
const mockCreateMysql = jest.fn();
|
|
21
|
-
const mockCreateMariadb = jest.fn();
|
|
22
|
-
const mockCreateMongodb = jest.fn();
|
|
23
|
-
const mockCreateRedis = jest.fn();
|
|
24
|
-
const mockCreateKeydb = jest.fn();
|
|
25
|
-
const mockCreateClickhouse = jest.fn();
|
|
26
|
-
const mockCreateDragonfly = jest.fn();
|
|
27
|
-
// Mock the CoolifyClient module
|
|
28
|
-
jest.mock('../lib/coolify-client.js', () => ({
|
|
29
|
-
CoolifyClient: jest.fn().mockImplementation(() => ({
|
|
30
|
-
listServers: mockListServers,
|
|
31
|
-
listProjects: mockListProjects,
|
|
32
|
-
listApplications: mockListApplications,
|
|
33
|
-
listDatabases: mockListDatabases,
|
|
34
|
-
listServices: mockListServices,
|
|
35
|
-
diagnoseApplication: mockDiagnoseApplication,
|
|
36
|
-
diagnoseServer: mockDiagnoseServer,
|
|
37
|
-
findInfrastructureIssues: mockFindInfrastructureIssues,
|
|
38
|
-
deleteDatabase: mockDeleteDatabase,
|
|
39
|
-
createPostgresql: mockCreatePostgresql,
|
|
40
|
-
createMysql: mockCreateMysql,
|
|
41
|
-
createMariadb: mockCreateMariadb,
|
|
42
|
-
createMongodb: mockCreateMongodb,
|
|
43
|
-
createRedis: mockCreateRedis,
|
|
44
|
-
createKeydb: mockCreateKeydb,
|
|
45
|
-
createClickhouse: mockCreateClickhouse,
|
|
46
|
-
createDragonfly: mockCreateDragonfly,
|
|
47
|
-
getVersion: jest.fn(),
|
|
48
|
-
})),
|
|
49
|
-
}));
|
|
50
|
-
// Import after mocking
|
|
8
|
+
import { describe, it, expect, beforeEach } from '@jest/globals';
|
|
51
9
|
import { CoolifyMcpServer } from '../lib/mcp-server.js';
|
|
52
|
-
describe('CoolifyMcpServer', () => {
|
|
10
|
+
describe('CoolifyMcpServer v2', () => {
|
|
11
|
+
let server;
|
|
53
12
|
beforeEach(() => {
|
|
54
|
-
|
|
13
|
+
server = new CoolifyMcpServer({
|
|
14
|
+
baseUrl: 'http://localhost:3000',
|
|
15
|
+
accessToken: 'test-token',
|
|
16
|
+
});
|
|
55
17
|
});
|
|
56
18
|
describe('constructor', () => {
|
|
57
|
-
it('should create server instance
|
|
58
|
-
const server = new CoolifyMcpServer({
|
|
59
|
-
baseUrl: 'http://localhost:3000',
|
|
60
|
-
accessToken: 'test-token',
|
|
61
|
-
});
|
|
19
|
+
it('should create server instance', () => {
|
|
62
20
|
expect(server).toBeInstanceOf(CoolifyMcpServer);
|
|
63
21
|
});
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
it('should call all list methods with summary: true for aggregation', async () => {
|
|
67
|
-
// Setup mock responses
|
|
68
|
-
mockListServers.mockResolvedValue([
|
|
69
|
-
{ uuid: 'srv-1', name: 'server-1', ip: '10.0.0.1', status: 'running', is_reachable: true },
|
|
70
|
-
]);
|
|
71
|
-
mockListProjects.mockResolvedValue([
|
|
72
|
-
{ uuid: 'proj-1', name: 'project-1', description: 'Test' },
|
|
73
|
-
]);
|
|
74
|
-
mockListApplications.mockResolvedValue([
|
|
75
|
-
{ uuid: 'app-1', name: 'app-1', status: 'running', fqdn: 'https://app.com' },
|
|
76
|
-
]);
|
|
77
|
-
mockListDatabases.mockResolvedValue([
|
|
78
|
-
{ uuid: 'db-1', name: 'db-1', type: 'postgresql', status: 'running', is_public: false },
|
|
79
|
-
]);
|
|
80
|
-
mockListServices.mockResolvedValue([
|
|
81
|
-
{ uuid: 'svc-1', name: 'svc-1', type: 'redis', status: 'running' },
|
|
82
|
-
]);
|
|
83
|
-
// Create server (this registers tools)
|
|
84
|
-
new CoolifyMcpServer({
|
|
85
|
-
baseUrl: 'http://localhost:3000',
|
|
86
|
-
accessToken: 'test-token',
|
|
87
|
-
});
|
|
88
|
-
// Simulate what get_infrastructure_overview does
|
|
89
|
-
await Promise.all([
|
|
90
|
-
mockListServers({ summary: true }),
|
|
91
|
-
mockListProjects({ summary: true }),
|
|
92
|
-
mockListApplications({ summary: true }),
|
|
93
|
-
mockListDatabases({ summary: true }),
|
|
94
|
-
mockListServices({ summary: true }),
|
|
95
|
-
]);
|
|
96
|
-
// Verify all methods were called with summary: true
|
|
97
|
-
expect(mockListServers).toHaveBeenCalledWith({ summary: true });
|
|
98
|
-
expect(mockListProjects).toHaveBeenCalledWith({ summary: true });
|
|
99
|
-
expect(mockListApplications).toHaveBeenCalledWith({ summary: true });
|
|
100
|
-
expect(mockListDatabases).toHaveBeenCalledWith({ summary: true });
|
|
101
|
-
expect(mockListServices).toHaveBeenCalledWith({ summary: true });
|
|
102
|
-
});
|
|
103
|
-
});
|
|
104
|
-
describe('list tools use summary mode by default', () => {
|
|
105
|
-
beforeEach(() => {
|
|
106
|
-
// Create fresh server for each test
|
|
107
|
-
new CoolifyMcpServer({
|
|
108
|
-
baseUrl: 'http://localhost:3000',
|
|
109
|
-
accessToken: 'test-token',
|
|
110
|
-
});
|
|
111
|
-
});
|
|
112
|
-
it('list_servers should use summary: true', async () => {
|
|
113
|
-
mockListServers.mockResolvedValue([]);
|
|
114
|
-
// Call as the MCP tool would
|
|
115
|
-
await mockListServers({ page: undefined, per_page: undefined, summary: true });
|
|
116
|
-
expect(mockListServers).toHaveBeenCalledWith({
|
|
117
|
-
page: undefined,
|
|
118
|
-
per_page: undefined,
|
|
119
|
-
summary: true,
|
|
120
|
-
});
|
|
121
|
-
});
|
|
122
|
-
it('list_applications should use summary: true', async () => {
|
|
123
|
-
mockListApplications.mockResolvedValue([]);
|
|
124
|
-
await mockListApplications({ page: undefined, per_page: undefined, summary: true });
|
|
125
|
-
expect(mockListApplications).toHaveBeenCalledWith({
|
|
126
|
-
page: undefined,
|
|
127
|
-
per_page: undefined,
|
|
128
|
-
summary: true,
|
|
129
|
-
});
|
|
130
|
-
});
|
|
131
|
-
it('list_services should use summary: true', async () => {
|
|
132
|
-
mockListServices.mockResolvedValue([]);
|
|
133
|
-
await mockListServices({ page: undefined, per_page: undefined, summary: true });
|
|
134
|
-
expect(mockListServices).toHaveBeenCalledWith({
|
|
135
|
-
page: undefined,
|
|
136
|
-
per_page: undefined,
|
|
137
|
-
summary: true,
|
|
138
|
-
});
|
|
139
|
-
});
|
|
140
|
-
it('list_databases should use summary: true', async () => {
|
|
141
|
-
mockListDatabases.mockResolvedValue([]);
|
|
142
|
-
await mockListDatabases({ page: undefined, per_page: undefined, summary: true });
|
|
143
|
-
expect(mockListDatabases).toHaveBeenCalledWith({
|
|
144
|
-
page: undefined,
|
|
145
|
-
per_page: undefined,
|
|
146
|
-
summary: true,
|
|
147
|
-
});
|
|
148
|
-
});
|
|
149
|
-
it('list_projects should use summary: true', async () => {
|
|
150
|
-
mockListProjects.mockResolvedValue([]);
|
|
151
|
-
await mockListProjects({ page: undefined, per_page: undefined, summary: true });
|
|
152
|
-
expect(mockListProjects).toHaveBeenCalledWith({
|
|
153
|
-
page: undefined,
|
|
154
|
-
per_page: undefined,
|
|
155
|
-
summary: true,
|
|
156
|
-
});
|
|
157
|
-
});
|
|
158
|
-
});
|
|
159
|
-
describe('error handling', () => {
|
|
160
|
-
it('should handle client errors', async () => {
|
|
161
|
-
mockListServers.mockRejectedValue(new Error('Connection failed'));
|
|
162
|
-
await expect(mockListServers({ summary: true })).rejects.toThrow('Connection failed');
|
|
22
|
+
it('should be an MCP server with connect method', () => {
|
|
23
|
+
expect(typeof server.connect).toBe('function');
|
|
163
24
|
});
|
|
164
25
|
});
|
|
165
|
-
describe('
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
mockDiagnoseServer.mockResolvedValue({
|
|
262
|
-
server: {
|
|
263
|
-
uuid: 'ggkk8w4c08gw48oowsg4g0oc',
|
|
264
|
-
name: 'coolify-apps',
|
|
265
|
-
ip: '10.0.0.1',
|
|
266
|
-
status: 'running',
|
|
267
|
-
is_reachable: true,
|
|
268
|
-
},
|
|
269
|
-
health: { status: 'healthy', issues: [] },
|
|
270
|
-
resources: [],
|
|
271
|
-
domains: [],
|
|
272
|
-
validation: { message: 'Server is reachable' },
|
|
273
|
-
});
|
|
274
|
-
await mockDiagnoseServer('ggkk8w4c08gw48oowsg4g0oc');
|
|
275
|
-
expect(mockDiagnoseServer).toHaveBeenCalledWith('ggkk8w4c08gw48oowsg4g0oc');
|
|
276
|
-
});
|
|
277
|
-
});
|
|
278
|
-
describe('find_issues', () => {
|
|
279
|
-
it('should call findInfrastructureIssues', async () => {
|
|
280
|
-
mockFindInfrastructureIssues.mockResolvedValue({
|
|
281
|
-
summary: {
|
|
282
|
-
total_issues: 2,
|
|
283
|
-
unhealthy_applications: 1,
|
|
284
|
-
unhealthy_databases: 0,
|
|
285
|
-
unhealthy_services: 1,
|
|
286
|
-
unreachable_servers: 0,
|
|
287
|
-
},
|
|
288
|
-
issues: [
|
|
289
|
-
{
|
|
290
|
-
type: 'application',
|
|
291
|
-
uuid: 'app-1',
|
|
292
|
-
name: 'broken-app',
|
|
293
|
-
issue: 'Application is unhealthy',
|
|
294
|
-
status: 'exited:unhealthy',
|
|
295
|
-
},
|
|
296
|
-
{
|
|
297
|
-
type: 'service',
|
|
298
|
-
uuid: 'svc-1',
|
|
299
|
-
name: 'broken-service',
|
|
300
|
-
issue: 'Service has exited',
|
|
301
|
-
status: 'exited',
|
|
302
|
-
},
|
|
303
|
-
],
|
|
304
|
-
});
|
|
305
|
-
await mockFindInfrastructureIssues();
|
|
306
|
-
expect(mockFindInfrastructureIssues).toHaveBeenCalled();
|
|
307
|
-
});
|
|
308
|
-
it('should return empty issues when infrastructure is healthy', async () => {
|
|
309
|
-
mockFindInfrastructureIssues.mockResolvedValue({
|
|
310
|
-
summary: {
|
|
311
|
-
total_issues: 0,
|
|
312
|
-
unhealthy_applications: 0,
|
|
313
|
-
unhealthy_databases: 0,
|
|
314
|
-
unhealthy_services: 0,
|
|
315
|
-
unreachable_servers: 0,
|
|
316
|
-
},
|
|
317
|
-
issues: [],
|
|
318
|
-
});
|
|
319
|
-
const result = await mockFindInfrastructureIssues();
|
|
320
|
-
expect(result.summary.total_issues).toBe(0);
|
|
321
|
-
expect(result.issues).toHaveLength(0);
|
|
322
|
-
});
|
|
323
|
-
});
|
|
324
|
-
describe('delete_database', () => {
|
|
325
|
-
it('should call deleteDatabase with uuid', async () => {
|
|
326
|
-
mockDeleteDatabase.mockResolvedValue({ message: 'Database deletion request queued.' });
|
|
327
|
-
await mockDeleteDatabase('db-uuid-123');
|
|
328
|
-
expect(mockDeleteDatabase).toHaveBeenCalledWith('db-uuid-123');
|
|
329
|
-
});
|
|
330
|
-
it('should call deleteDatabase with delete_volumes option', async () => {
|
|
331
|
-
mockDeleteDatabase.mockResolvedValue({ message: 'Database deletion request queued.' });
|
|
332
|
-
await mockDeleteDatabase('db-uuid-123', { deleteVolumes: true });
|
|
333
|
-
expect(mockDeleteDatabase).toHaveBeenCalledWith('db-uuid-123', { deleteVolumes: true });
|
|
334
|
-
});
|
|
335
|
-
});
|
|
336
|
-
describe('database creation tools', () => {
|
|
337
|
-
const baseParams = {
|
|
338
|
-
server_uuid: 'server-uuid',
|
|
339
|
-
project_uuid: 'project-uuid',
|
|
340
|
-
environment_name: 'production',
|
|
341
|
-
};
|
|
342
|
-
it('should call createPostgresql with correct params', async () => {
|
|
343
|
-
mockCreatePostgresql.mockResolvedValue({ uuid: 'pg-uuid' });
|
|
344
|
-
await mockCreatePostgresql({ ...baseParams, postgres_user: 'myuser' });
|
|
345
|
-
expect(mockCreatePostgresql).toHaveBeenCalledWith({
|
|
346
|
-
...baseParams,
|
|
347
|
-
postgres_user: 'myuser',
|
|
348
|
-
});
|
|
349
|
-
});
|
|
350
|
-
it('should call createMysql with correct params', async () => {
|
|
351
|
-
mockCreateMysql.mockResolvedValue({ uuid: 'mysql-uuid' });
|
|
352
|
-
await mockCreateMysql({ ...baseParams, mysql_user: 'myuser' });
|
|
353
|
-
expect(mockCreateMysql).toHaveBeenCalledWith({
|
|
354
|
-
...baseParams,
|
|
355
|
-
mysql_user: 'myuser',
|
|
356
|
-
});
|
|
357
|
-
});
|
|
358
|
-
it('should call createMariadb with correct params', async () => {
|
|
359
|
-
mockCreateMariadb.mockResolvedValue({ uuid: 'mariadb-uuid' });
|
|
360
|
-
await mockCreateMariadb(baseParams);
|
|
361
|
-
expect(mockCreateMariadb).toHaveBeenCalledWith(baseParams);
|
|
362
|
-
});
|
|
363
|
-
it('should call createMongodb with correct params', async () => {
|
|
364
|
-
mockCreateMongodb.mockResolvedValue({ uuid: 'mongo-uuid' });
|
|
365
|
-
await mockCreateMongodb({ ...baseParams, mongo_initdb_root_username: 'admin' });
|
|
366
|
-
expect(mockCreateMongodb).toHaveBeenCalledWith({
|
|
367
|
-
...baseParams,
|
|
368
|
-
mongo_initdb_root_username: 'admin',
|
|
369
|
-
});
|
|
370
|
-
});
|
|
371
|
-
it('should call createRedis with correct params', async () => {
|
|
372
|
-
mockCreateRedis.mockResolvedValue({ uuid: 'redis-uuid' });
|
|
373
|
-
await mockCreateRedis({ ...baseParams, redis_password: 'secret' });
|
|
374
|
-
expect(mockCreateRedis).toHaveBeenCalledWith({
|
|
375
|
-
...baseParams,
|
|
376
|
-
redis_password: 'secret',
|
|
377
|
-
});
|
|
378
|
-
});
|
|
379
|
-
it('should call createKeydb with correct params', async () => {
|
|
380
|
-
mockCreateKeydb.mockResolvedValue({ uuid: 'keydb-uuid' });
|
|
381
|
-
await mockCreateKeydb(baseParams);
|
|
382
|
-
expect(mockCreateKeydb).toHaveBeenCalledWith(baseParams);
|
|
383
|
-
});
|
|
384
|
-
it('should call createClickhouse with correct params', async () => {
|
|
385
|
-
mockCreateClickhouse.mockResolvedValue({ uuid: 'clickhouse-uuid' });
|
|
386
|
-
await mockCreateClickhouse({ ...baseParams, clickhouse_admin_user: 'admin' });
|
|
387
|
-
expect(mockCreateClickhouse).toHaveBeenCalledWith({
|
|
388
|
-
...baseParams,
|
|
389
|
-
clickhouse_admin_user: 'admin',
|
|
390
|
-
});
|
|
391
|
-
});
|
|
392
|
-
it('should call createDragonfly with correct params', async () => {
|
|
393
|
-
mockCreateDragonfly.mockResolvedValue({ uuid: 'dragonfly-uuid' });
|
|
394
|
-
await mockCreateDragonfly({ ...baseParams, dragonfly_password: 'secret' });
|
|
395
|
-
expect(mockCreateDragonfly).toHaveBeenCalledWith({
|
|
396
|
-
...baseParams,
|
|
397
|
-
dragonfly_password: 'secret',
|
|
398
|
-
});
|
|
399
|
-
});
|
|
26
|
+
describe('client', () => {
|
|
27
|
+
it('should have client instance', () => {
|
|
28
|
+
const client = server['client'];
|
|
29
|
+
expect(client).toBeDefined();
|
|
30
|
+
});
|
|
31
|
+
it('should have all required client methods', () => {
|
|
32
|
+
const client = server['client'];
|
|
33
|
+
// Core methods
|
|
34
|
+
expect(typeof client.getVersion).toBe('function');
|
|
35
|
+
// Server operations
|
|
36
|
+
expect(typeof client.listServers).toBe('function');
|
|
37
|
+
expect(typeof client.getServer).toBe('function');
|
|
38
|
+
expect(typeof client.getServerResources).toBe('function');
|
|
39
|
+
expect(typeof client.getServerDomains).toBe('function');
|
|
40
|
+
expect(typeof client.validateServer).toBe('function');
|
|
41
|
+
// Project operations
|
|
42
|
+
expect(typeof client.listProjects).toBe('function');
|
|
43
|
+
expect(typeof client.getProject).toBe('function');
|
|
44
|
+
expect(typeof client.createProject).toBe('function');
|
|
45
|
+
expect(typeof client.updateProject).toBe('function');
|
|
46
|
+
expect(typeof client.deleteProject).toBe('function');
|
|
47
|
+
// Environment operations
|
|
48
|
+
expect(typeof client.listProjectEnvironments).toBe('function');
|
|
49
|
+
expect(typeof client.getProjectEnvironment).toBe('function');
|
|
50
|
+
expect(typeof client.createProjectEnvironment).toBe('function');
|
|
51
|
+
expect(typeof client.deleteProjectEnvironment).toBe('function');
|
|
52
|
+
// Application operations
|
|
53
|
+
expect(typeof client.listApplications).toBe('function');
|
|
54
|
+
expect(typeof client.getApplication).toBe('function');
|
|
55
|
+
expect(typeof client.createApplicationPrivateGH).toBe('function');
|
|
56
|
+
expect(typeof client.createApplicationPrivateKey).toBe('function');
|
|
57
|
+
expect(typeof client.updateApplication).toBe('function');
|
|
58
|
+
expect(typeof client.deleteApplication).toBe('function');
|
|
59
|
+
expect(typeof client.getApplicationLogs).toBe('function');
|
|
60
|
+
// Control operations
|
|
61
|
+
expect(typeof client.startApplication).toBe('function');
|
|
62
|
+
expect(typeof client.stopApplication).toBe('function');
|
|
63
|
+
expect(typeof client.restartApplication).toBe('function');
|
|
64
|
+
expect(typeof client.startDatabase).toBe('function');
|
|
65
|
+
expect(typeof client.stopDatabase).toBe('function');
|
|
66
|
+
expect(typeof client.restartDatabase).toBe('function');
|
|
67
|
+
expect(typeof client.startService).toBe('function');
|
|
68
|
+
expect(typeof client.stopService).toBe('function');
|
|
69
|
+
expect(typeof client.restartService).toBe('function');
|
|
70
|
+
// Database operations
|
|
71
|
+
expect(typeof client.listDatabases).toBe('function');
|
|
72
|
+
expect(typeof client.getDatabase).toBe('function');
|
|
73
|
+
expect(typeof client.deleteDatabase).toBe('function');
|
|
74
|
+
expect(typeof client.createPostgresql).toBe('function');
|
|
75
|
+
expect(typeof client.createMysql).toBe('function');
|
|
76
|
+
expect(typeof client.createMariadb).toBe('function');
|
|
77
|
+
expect(typeof client.createMongodb).toBe('function');
|
|
78
|
+
expect(typeof client.createRedis).toBe('function');
|
|
79
|
+
expect(typeof client.createKeydb).toBe('function');
|
|
80
|
+
expect(typeof client.createClickhouse).toBe('function');
|
|
81
|
+
expect(typeof client.createDragonfly).toBe('function');
|
|
82
|
+
// Service operations
|
|
83
|
+
expect(typeof client.listServices).toBe('function');
|
|
84
|
+
expect(typeof client.getService).toBe('function');
|
|
85
|
+
expect(typeof client.createService).toBe('function');
|
|
86
|
+
expect(typeof client.updateService).toBe('function');
|
|
87
|
+
expect(typeof client.deleteService).toBe('function');
|
|
88
|
+
// Environment variable operations
|
|
89
|
+
expect(typeof client.listApplicationEnvVars).toBe('function');
|
|
90
|
+
expect(typeof client.createApplicationEnvVar).toBe('function');
|
|
91
|
+
expect(typeof client.updateApplicationEnvVar).toBe('function');
|
|
92
|
+
expect(typeof client.deleteApplicationEnvVar).toBe('function');
|
|
93
|
+
expect(typeof client.listServiceEnvVars).toBe('function');
|
|
94
|
+
expect(typeof client.createServiceEnvVar).toBe('function');
|
|
95
|
+
expect(typeof client.deleteServiceEnvVar).toBe('function');
|
|
96
|
+
// Deployment operations
|
|
97
|
+
expect(typeof client.listDeployments).toBe('function');
|
|
98
|
+
expect(typeof client.getDeployment).toBe('function');
|
|
99
|
+
expect(typeof client.deployByTagOrUuid).toBe('function');
|
|
100
|
+
expect(typeof client.listApplicationDeployments).toBe('function');
|
|
101
|
+
expect(typeof client.cancelDeployment).toBe('function');
|
|
102
|
+
// Private key operations
|
|
103
|
+
expect(typeof client.listPrivateKeys).toBe('function');
|
|
104
|
+
expect(typeof client.getPrivateKey).toBe('function');
|
|
105
|
+
expect(typeof client.createPrivateKey).toBe('function');
|
|
106
|
+
expect(typeof client.updatePrivateKey).toBe('function');
|
|
107
|
+
expect(typeof client.deletePrivateKey).toBe('function');
|
|
108
|
+
// Backup operations
|
|
109
|
+
expect(typeof client.listDatabaseBackups).toBe('function');
|
|
110
|
+
expect(typeof client.getDatabaseBackup).toBe('function');
|
|
111
|
+
expect(typeof client.listBackupExecutions).toBe('function');
|
|
112
|
+
expect(typeof client.getBackupExecution).toBe('function');
|
|
113
|
+
// Diagnostic operations
|
|
114
|
+
expect(typeof client.diagnoseApplication).toBe('function');
|
|
115
|
+
expect(typeof client.diagnoseServer).toBe('function');
|
|
116
|
+
expect(typeof client.findInfrastructureIssues).toBe('function');
|
|
117
|
+
// Batch operations
|
|
118
|
+
expect(typeof client.restartProjectApps).toBe('function');
|
|
119
|
+
expect(typeof client.bulkEnvUpdate).toBe('function');
|
|
120
|
+
expect(typeof client.stopAllApps).toBe('function');
|
|
121
|
+
expect(typeof client.redeployProjectApps).toBe('function');
|
|
400
122
|
});
|
|
401
123
|
});
|
|
402
|
-
describe('
|
|
403
|
-
it('
|
|
404
|
-
|
|
405
|
-
//
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
name: '@masonator/coolify-mcp',
|
|
409
|
-
};
|
|
410
|
-
expect(expectedResponse.version).toBe('1.1.0');
|
|
411
|
-
expect(expectedResponse.name).toBe('@masonator/coolify-mcp');
|
|
124
|
+
describe('server configuration', () => {
|
|
125
|
+
it('should store baseUrl and accessToken in client', () => {
|
|
126
|
+
const client = server['client'];
|
|
127
|
+
// CoolifyClient stores base URL without /api/v1 suffix
|
|
128
|
+
expect(client['baseUrl']).toBe('http://localhost:3000');
|
|
129
|
+
expect(client['accessToken']).toBe('test-token');
|
|
412
130
|
});
|
|
413
131
|
});
|
|
414
132
|
});
|
package/dist/lib/mcp-server.d.ts
CHANGED
|
@@ -1,30 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Coolify MCP Server
|
|
3
|
-
*
|
|
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.
|
|
2
|
+
* Coolify MCP Server v2.0.0
|
|
3
|
+
* Consolidated tools for efficient token usage
|
|
17
4
|
*/
|
|
18
5
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
19
6
|
import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
|
|
20
7
|
import type { CoolifyConfig } from '../types/coolify.js';
|
|
21
|
-
/**
|
|
22
|
-
* Coolify MCP Server
|
|
23
|
-
*/
|
|
24
8
|
export declare class CoolifyMcpServer extends McpServer {
|
|
25
9
|
private readonly client;
|
|
26
10
|
constructor(config: CoolifyConfig);
|
|
27
11
|
connect(transport: Transport): Promise<void>;
|
|
28
12
|
private registerTools;
|
|
29
|
-
private registerPrompts;
|
|
30
13
|
}
|