@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 CHANGED
@@ -9,41 +9,33 @@
9
9
  [![codecov](https://codecov.io/gh/StuMason/coolify-mcp/branch/main/graph/badge.svg)](https://codecov.io/gh/StuMason/coolify-mcp)
10
10
  [![MseeP.ai Security Assessment Badge](https://mseep.net/pr/stumason-coolify-mcp-badge.png)](https://mseep.ai/app/stumason-coolify-mcp)
11
11
 
12
- > **The most comprehensive MCP server for Coolify** - 75 tools, 7 workflow prompts, smart diagnostics, and batch operations for managing your self-hosted PaaS through AI assistants.
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 **75 tools** and **7 workflow prompts** for **debugging, management, and deployment**:
19
-
20
- | Category | Tools |
21
- | -------------------- | ------------------------------------------------------------------------------------------------------------- |
22
- | **Infrastructure** | overview, mcp_version (all resources at once) |
23
- | **Diagnostics** | diagnose_app, diagnose_server, find_issues (smart lookup by name/domain/IP) |
24
- | **Batch Operations** | restart_project_apps, bulk_env_update, stop_all_apps, redeploy_project |
25
- | **Servers** | list, get, validate, resources, domains |
26
- | **Projects** | list, get, create, update, delete |
27
- | **Environments** | list, get, create, delete |
28
- | **Applications** | list, get, update, delete, start, stop, restart, logs, env vars (CRUD), create (private-gh, private-key) |
29
- | **Databases** | list, get, create (8 types), delete, start, stop, restart, backups (list, get), backup executions (list, get) |
30
- | **Services** | list, get, create, update, delete, start, stop, restart, env vars (list, create, delete) |
31
- | **Deployments** | list, get, deploy, cancel, list by application |
32
- | **Private Keys** | list, get, create, update, delete |
33
-
34
- ### Workflow Prompts
35
-
36
- Pre-built guided workflows that walk you through common tasks:
37
-
38
- | Prompt | Description |
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 server layer, specifically:
5
- * - get_infrastructure_overview aggregation logic
6
- * - Verification that list tools always use summary mode
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 { jest, describe, it, expect, beforeEach } from '@jest/globals';
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
- jest.clearAllMocks();
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 with valid config', () => {
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
- describe('get_infrastructure_overview behavior', () => {
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('diagnostic tools', () => {
166
- beforeEach(() => {
167
- new CoolifyMcpServer({
168
- baseUrl: 'http://localhost:3000',
169
- accessToken: 'test-token',
170
- });
171
- });
172
- describe('diagnose_app', () => {
173
- it('should call diagnoseApplication with the query', async () => {
174
- mockDiagnoseApplication.mockResolvedValue({
175
- application: {
176
- uuid: 'app-uuid-123',
177
- name: 'test-app',
178
- status: 'running',
179
- fqdn: 'https://test.example.com',
180
- git_repository: 'org/repo',
181
- git_branch: 'main',
182
- },
183
- health: { status: 'healthy', issues: [] },
184
- logs: 'Application started',
185
- environment_variables: {
186
- count: 2,
187
- variables: [{ key: 'NODE_ENV', is_build_time: false }],
188
- },
189
- recent_deployments: [],
190
- });
191
- await mockDiagnoseApplication('test-app');
192
- expect(mockDiagnoseApplication).toHaveBeenCalledWith('test-app');
193
- });
194
- it('should call diagnoseApplication with a domain', async () => {
195
- mockDiagnoseApplication.mockResolvedValue({
196
- application: null,
197
- health: { status: 'unknown', issues: [] },
198
- logs: null,
199
- environment_variables: { count: 0, variables: [] },
200
- recent_deployments: [],
201
- errors: ['Application not found'],
202
- });
203
- await mockDiagnoseApplication('tidylinker.com');
204
- expect(mockDiagnoseApplication).toHaveBeenCalledWith('tidylinker.com');
205
- });
206
- it('should call diagnoseApplication with a UUID', async () => {
207
- mockDiagnoseApplication.mockResolvedValue({
208
- application: {
209
- uuid: 'xs0sgs4gog044s4k4c88kgsc',
210
- name: 'test-app',
211
- status: 'running',
212
- fqdn: null,
213
- git_repository: null,
214
- git_branch: null,
215
- },
216
- health: { status: 'healthy', issues: [] },
217
- logs: null,
218
- environment_variables: { count: 0, variables: [] },
219
- recent_deployments: [],
220
- });
221
- await mockDiagnoseApplication('xs0sgs4gog044s4k4c88kgsc');
222
- expect(mockDiagnoseApplication).toHaveBeenCalledWith('xs0sgs4gog044s4k4c88kgsc');
223
- });
224
- });
225
- describe('diagnose_server', () => {
226
- it('should call diagnoseServer with the query', async () => {
227
- mockDiagnoseServer.mockResolvedValue({
228
- server: {
229
- uuid: 'srv-uuid-123',
230
- name: 'production-server',
231
- ip: '192.168.1.100',
232
- status: 'running',
233
- is_reachable: true,
234
- },
235
- health: { status: 'healthy', issues: [] },
236
- resources: [],
237
- domains: [],
238
- validation: { message: 'Server is reachable' },
239
- });
240
- await mockDiagnoseServer('production-server');
241
- expect(mockDiagnoseServer).toHaveBeenCalledWith('production-server');
242
- });
243
- it('should call diagnoseServer with an IP address', async () => {
244
- mockDiagnoseServer.mockResolvedValue({
245
- server: {
246
- uuid: 'srv-uuid-123',
247
- name: 'production-server',
248
- ip: '192.168.1.100',
249
- status: 'running',
250
- is_reachable: true,
251
- },
252
- health: { status: 'healthy', issues: [] },
253
- resources: [],
254
- domains: [],
255
- validation: { message: 'Server is reachable' },
256
- });
257
- await mockDiagnoseServer('192.168.1.100');
258
- expect(mockDiagnoseServer).toHaveBeenCalledWith('192.168.1.100');
259
- });
260
- it('should call diagnoseServer with a UUID', async () => {
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('version tools', () => {
403
- it('get_mcp_version should return correct version format', () => {
404
- // The VERSION constant is '1.1.0' in mcp-server.ts
405
- // This test verifies the expected output structure
406
- const expectedResponse = {
407
- version: '1.1.0',
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
  });
@@ -1,30 +1,13 @@
1
1
  /**
2
- * Coolify MCP Server
3
- * Model Context Protocol server for Coolify API
4
- *
5
- * Tools focused on debugging, management, and deployment:
6
- * - Servers: list, get, validate, resources, domains
7
- * - Projects: CRUD
8
- * - Environments: CRUD
9
- * - Applications: list, get, update, delete, start/stop/restart, logs, env vars, deploy (private-gh, private-key)
10
- * - Databases: list, get, start/stop/restart
11
- * - Services: list, get, update, start/stop/restart, env vars
12
- * - Deployments: list, get, deploy
13
- *
14
- * Note: @ts-nocheck is required because the MCP SDK's tool() method causes
15
- * TypeScript type instantiation depth errors with 40+ zod-typed tools.
16
- * The client and types are still fully type-checked.
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
  }