@masonator/coolify-mcp 1.5.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** - 67 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 **67 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, 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
 
@@ -794,6 +794,87 @@ describe('CoolifyClient', () => {
794
794
  expect(result).toEqual(mockExecution);
795
795
  expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/databases/db-uuid/backups/backup-uuid/executions/exec-uuid', expect.any(Object));
796
796
  });
797
+ it('should create a PostgreSQL database', async () => {
798
+ mockFetch.mockResolvedValueOnce(mockResponse({ uuid: 'pg-uuid' }));
799
+ const result = await client.createPostgresql({
800
+ server_uuid: 'server-uuid',
801
+ project_uuid: 'project-uuid',
802
+ environment_name: 'production',
803
+ postgres_user: 'myuser',
804
+ postgres_db: 'mydb',
805
+ });
806
+ expect(result).toEqual({ uuid: 'pg-uuid' });
807
+ expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/databases/postgresql', expect.objectContaining({ method: 'POST' }));
808
+ });
809
+ it('should create a MySQL database', async () => {
810
+ mockFetch.mockResolvedValueOnce(mockResponse({ uuid: 'mysql-uuid' }));
811
+ const result = await client.createMysql({
812
+ server_uuid: 'server-uuid',
813
+ project_uuid: 'project-uuid',
814
+ mysql_user: 'myuser',
815
+ mysql_database: 'mydb',
816
+ });
817
+ expect(result).toEqual({ uuid: 'mysql-uuid' });
818
+ expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/databases/mysql', expect.objectContaining({ method: 'POST' }));
819
+ });
820
+ it('should create a MariaDB database', async () => {
821
+ mockFetch.mockResolvedValueOnce(mockResponse({ uuid: 'mariadb-uuid' }));
822
+ const result = await client.createMariadb({
823
+ server_uuid: 'server-uuid',
824
+ project_uuid: 'project-uuid',
825
+ });
826
+ expect(result).toEqual({ uuid: 'mariadb-uuid' });
827
+ expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/databases/mariadb', expect.objectContaining({ method: 'POST' }));
828
+ });
829
+ it('should create a MongoDB database', async () => {
830
+ mockFetch.mockResolvedValueOnce(mockResponse({ uuid: 'mongo-uuid' }));
831
+ const result = await client.createMongodb({
832
+ server_uuid: 'server-uuid',
833
+ project_uuid: 'project-uuid',
834
+ mongo_initdb_root_username: 'admin',
835
+ });
836
+ expect(result).toEqual({ uuid: 'mongo-uuid' });
837
+ expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/databases/mongodb', expect.objectContaining({ method: 'POST' }));
838
+ });
839
+ it('should create a Redis database', async () => {
840
+ mockFetch.mockResolvedValueOnce(mockResponse({ uuid: 'redis-uuid' }));
841
+ const result = await client.createRedis({
842
+ server_uuid: 'server-uuid',
843
+ project_uuid: 'project-uuid',
844
+ redis_password: 'secret',
845
+ });
846
+ expect(result).toEqual({ uuid: 'redis-uuid' });
847
+ expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/databases/redis', expect.objectContaining({ method: 'POST' }));
848
+ });
849
+ it('should create a KeyDB database', async () => {
850
+ mockFetch.mockResolvedValueOnce(mockResponse({ uuid: 'keydb-uuid' }));
851
+ const result = await client.createKeydb({
852
+ server_uuid: 'server-uuid',
853
+ project_uuid: 'project-uuid',
854
+ });
855
+ expect(result).toEqual({ uuid: 'keydb-uuid' });
856
+ expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/databases/keydb', expect.objectContaining({ method: 'POST' }));
857
+ });
858
+ it('should create a ClickHouse database', async () => {
859
+ mockFetch.mockResolvedValueOnce(mockResponse({ uuid: 'clickhouse-uuid' }));
860
+ const result = await client.createClickhouse({
861
+ server_uuid: 'server-uuid',
862
+ project_uuid: 'project-uuid',
863
+ clickhouse_admin_user: 'admin',
864
+ });
865
+ expect(result).toEqual({ uuid: 'clickhouse-uuid' });
866
+ expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/databases/clickhouse', expect.objectContaining({ method: 'POST' }));
867
+ });
868
+ it('should create a Dragonfly database', async () => {
869
+ mockFetch.mockResolvedValueOnce(mockResponse({ uuid: 'dragonfly-uuid' }));
870
+ const result = await client.createDragonfly({
871
+ server_uuid: 'server-uuid',
872
+ project_uuid: 'project-uuid',
873
+ dragonfly_password: 'secret',
874
+ });
875
+ expect(result).toEqual({ uuid: 'dragonfly-uuid' });
876
+ expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/databases/dragonfly', expect.objectContaining({ method: 'POST' }));
877
+ });
797
878
  });
798
879
  // =========================================================================
799
880
  // Service endpoints - extended coverage
@@ -1,333 +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
- // Mock the CoolifyClient module
20
- jest.mock('../lib/coolify-client.js', () => ({
21
- CoolifyClient: jest.fn().mockImplementation(() => ({
22
- listServers: mockListServers,
23
- listProjects: mockListProjects,
24
- listApplications: mockListApplications,
25
- listDatabases: mockListDatabases,
26
- listServices: mockListServices,
27
- diagnoseApplication: mockDiagnoseApplication,
28
- diagnoseServer: mockDiagnoseServer,
29
- findInfrastructureIssues: mockFindInfrastructureIssues,
30
- deleteDatabase: mockDeleteDatabase,
31
- getVersion: jest.fn(),
32
- })),
33
- }));
34
- // Import after mocking
8
+ import { describe, it, expect, beforeEach } from '@jest/globals';
35
9
  import { CoolifyMcpServer } from '../lib/mcp-server.js';
36
- describe('CoolifyMcpServer', () => {
10
+ describe('CoolifyMcpServer v2', () => {
11
+ let server;
37
12
  beforeEach(() => {
38
- jest.clearAllMocks();
13
+ server = new CoolifyMcpServer({
14
+ baseUrl: 'http://localhost:3000',
15
+ accessToken: 'test-token',
16
+ });
39
17
  });
40
18
  describe('constructor', () => {
41
- it('should create server instance with valid config', () => {
42
- const server = new CoolifyMcpServer({
43
- baseUrl: 'http://localhost:3000',
44
- accessToken: 'test-token',
45
- });
19
+ it('should create server instance', () => {
46
20
  expect(server).toBeInstanceOf(CoolifyMcpServer);
47
21
  });
48
- });
49
- describe('get_infrastructure_overview behavior', () => {
50
- it('should call all list methods with summary: true for aggregation', async () => {
51
- // Setup mock responses
52
- mockListServers.mockResolvedValue([
53
- { uuid: 'srv-1', name: 'server-1', ip: '10.0.0.1', status: 'running', is_reachable: true },
54
- ]);
55
- mockListProjects.mockResolvedValue([
56
- { uuid: 'proj-1', name: 'project-1', description: 'Test' },
57
- ]);
58
- mockListApplications.mockResolvedValue([
59
- { uuid: 'app-1', name: 'app-1', status: 'running', fqdn: 'https://app.com' },
60
- ]);
61
- mockListDatabases.mockResolvedValue([
62
- { uuid: 'db-1', name: 'db-1', type: 'postgresql', status: 'running', is_public: false },
63
- ]);
64
- mockListServices.mockResolvedValue([
65
- { uuid: 'svc-1', name: 'svc-1', type: 'redis', status: 'running' },
66
- ]);
67
- // Create server (this registers tools)
68
- new CoolifyMcpServer({
69
- baseUrl: 'http://localhost:3000',
70
- accessToken: 'test-token',
71
- });
72
- // Simulate what get_infrastructure_overview does
73
- await Promise.all([
74
- mockListServers({ summary: true }),
75
- mockListProjects({ summary: true }),
76
- mockListApplications({ summary: true }),
77
- mockListDatabases({ summary: true }),
78
- mockListServices({ summary: true }),
79
- ]);
80
- // Verify all methods were called with summary: true
81
- expect(mockListServers).toHaveBeenCalledWith({ summary: true });
82
- expect(mockListProjects).toHaveBeenCalledWith({ summary: true });
83
- expect(mockListApplications).toHaveBeenCalledWith({ summary: true });
84
- expect(mockListDatabases).toHaveBeenCalledWith({ summary: true });
85
- expect(mockListServices).toHaveBeenCalledWith({ summary: true });
86
- });
87
- });
88
- describe('list tools use summary mode by default', () => {
89
- beforeEach(() => {
90
- // Create fresh server for each test
91
- new CoolifyMcpServer({
92
- baseUrl: 'http://localhost:3000',
93
- accessToken: 'test-token',
94
- });
95
- });
96
- it('list_servers should use summary: true', async () => {
97
- mockListServers.mockResolvedValue([]);
98
- // Call as the MCP tool would
99
- await mockListServers({ page: undefined, per_page: undefined, summary: true });
100
- expect(mockListServers).toHaveBeenCalledWith({
101
- page: undefined,
102
- per_page: undefined,
103
- summary: true,
104
- });
105
- });
106
- it('list_applications should use summary: true', async () => {
107
- mockListApplications.mockResolvedValue([]);
108
- await mockListApplications({ page: undefined, per_page: undefined, summary: true });
109
- expect(mockListApplications).toHaveBeenCalledWith({
110
- page: undefined,
111
- per_page: undefined,
112
- summary: true,
113
- });
114
- });
115
- it('list_services should use summary: true', async () => {
116
- mockListServices.mockResolvedValue([]);
117
- await mockListServices({ page: undefined, per_page: undefined, summary: true });
118
- expect(mockListServices).toHaveBeenCalledWith({
119
- page: undefined,
120
- per_page: undefined,
121
- summary: true,
122
- });
123
- });
124
- it('list_databases should use summary: true', async () => {
125
- mockListDatabases.mockResolvedValue([]);
126
- await mockListDatabases({ page: undefined, per_page: undefined, summary: true });
127
- expect(mockListDatabases).toHaveBeenCalledWith({
128
- page: undefined,
129
- per_page: undefined,
130
- summary: true,
131
- });
132
- });
133
- it('list_projects should use summary: true', async () => {
134
- mockListProjects.mockResolvedValue([]);
135
- await mockListProjects({ page: undefined, per_page: undefined, summary: true });
136
- expect(mockListProjects).toHaveBeenCalledWith({
137
- page: undefined,
138
- per_page: undefined,
139
- summary: true,
140
- });
141
- });
142
- });
143
- describe('error handling', () => {
144
- it('should handle client errors', async () => {
145
- mockListServers.mockRejectedValue(new Error('Connection failed'));
146
- 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');
147
24
  });
148
25
  });
149
- describe('diagnostic tools', () => {
150
- beforeEach(() => {
151
- new CoolifyMcpServer({
152
- baseUrl: 'http://localhost:3000',
153
- accessToken: 'test-token',
154
- });
155
- });
156
- describe('diagnose_app', () => {
157
- it('should call diagnoseApplication with the query', async () => {
158
- mockDiagnoseApplication.mockResolvedValue({
159
- application: {
160
- uuid: 'app-uuid-123',
161
- name: 'test-app',
162
- status: 'running',
163
- fqdn: 'https://test.example.com',
164
- git_repository: 'org/repo',
165
- git_branch: 'main',
166
- },
167
- health: { status: 'healthy', issues: [] },
168
- logs: 'Application started',
169
- environment_variables: {
170
- count: 2,
171
- variables: [{ key: 'NODE_ENV', is_build_time: false }],
172
- },
173
- recent_deployments: [],
174
- });
175
- await mockDiagnoseApplication('test-app');
176
- expect(mockDiagnoseApplication).toHaveBeenCalledWith('test-app');
177
- });
178
- it('should call diagnoseApplication with a domain', async () => {
179
- mockDiagnoseApplication.mockResolvedValue({
180
- application: null,
181
- health: { status: 'unknown', issues: [] },
182
- logs: null,
183
- environment_variables: { count: 0, variables: [] },
184
- recent_deployments: [],
185
- errors: ['Application not found'],
186
- });
187
- await mockDiagnoseApplication('tidylinker.com');
188
- expect(mockDiagnoseApplication).toHaveBeenCalledWith('tidylinker.com');
189
- });
190
- it('should call diagnoseApplication with a UUID', async () => {
191
- mockDiagnoseApplication.mockResolvedValue({
192
- application: {
193
- uuid: 'xs0sgs4gog044s4k4c88kgsc',
194
- name: 'test-app',
195
- status: 'running',
196
- fqdn: null,
197
- git_repository: null,
198
- git_branch: null,
199
- },
200
- health: { status: 'healthy', issues: [] },
201
- logs: null,
202
- environment_variables: { count: 0, variables: [] },
203
- recent_deployments: [],
204
- });
205
- await mockDiagnoseApplication('xs0sgs4gog044s4k4c88kgsc');
206
- expect(mockDiagnoseApplication).toHaveBeenCalledWith('xs0sgs4gog044s4k4c88kgsc');
207
- });
208
- });
209
- describe('diagnose_server', () => {
210
- it('should call diagnoseServer with the query', async () => {
211
- mockDiagnoseServer.mockResolvedValue({
212
- server: {
213
- uuid: 'srv-uuid-123',
214
- name: 'production-server',
215
- ip: '192.168.1.100',
216
- status: 'running',
217
- is_reachable: true,
218
- },
219
- health: { status: 'healthy', issues: [] },
220
- resources: [],
221
- domains: [],
222
- validation: { message: 'Server is reachable' },
223
- });
224
- await mockDiagnoseServer('production-server');
225
- expect(mockDiagnoseServer).toHaveBeenCalledWith('production-server');
226
- });
227
- it('should call diagnoseServer with an IP address', async () => {
228
- mockDiagnoseServer.mockResolvedValue({
229
- server: {
230
- uuid: 'srv-uuid-123',
231
- name: 'production-server',
232
- ip: '192.168.1.100',
233
- status: 'running',
234
- is_reachable: true,
235
- },
236
- health: { status: 'healthy', issues: [] },
237
- resources: [],
238
- domains: [],
239
- validation: { message: 'Server is reachable' },
240
- });
241
- await mockDiagnoseServer('192.168.1.100');
242
- expect(mockDiagnoseServer).toHaveBeenCalledWith('192.168.1.100');
243
- });
244
- it('should call diagnoseServer with a UUID', async () => {
245
- mockDiagnoseServer.mockResolvedValue({
246
- server: {
247
- uuid: 'ggkk8w4c08gw48oowsg4g0oc',
248
- name: 'coolify-apps',
249
- ip: '10.0.0.1',
250
- status: 'running',
251
- is_reachable: true,
252
- },
253
- health: { status: 'healthy', issues: [] },
254
- resources: [],
255
- domains: [],
256
- validation: { message: 'Server is reachable' },
257
- });
258
- await mockDiagnoseServer('ggkk8w4c08gw48oowsg4g0oc');
259
- expect(mockDiagnoseServer).toHaveBeenCalledWith('ggkk8w4c08gw48oowsg4g0oc');
260
- });
261
- });
262
- describe('find_issues', () => {
263
- it('should call findInfrastructureIssues', async () => {
264
- mockFindInfrastructureIssues.mockResolvedValue({
265
- summary: {
266
- total_issues: 2,
267
- unhealthy_applications: 1,
268
- unhealthy_databases: 0,
269
- unhealthy_services: 1,
270
- unreachable_servers: 0,
271
- },
272
- issues: [
273
- {
274
- type: 'application',
275
- uuid: 'app-1',
276
- name: 'broken-app',
277
- issue: 'Application is unhealthy',
278
- status: 'exited:unhealthy',
279
- },
280
- {
281
- type: 'service',
282
- uuid: 'svc-1',
283
- name: 'broken-service',
284
- issue: 'Service has exited',
285
- status: 'exited',
286
- },
287
- ],
288
- });
289
- await mockFindInfrastructureIssues();
290
- expect(mockFindInfrastructureIssues).toHaveBeenCalled();
291
- });
292
- it('should return empty issues when infrastructure is healthy', async () => {
293
- mockFindInfrastructureIssues.mockResolvedValue({
294
- summary: {
295
- total_issues: 0,
296
- unhealthy_applications: 0,
297
- unhealthy_databases: 0,
298
- unhealthy_services: 0,
299
- unreachable_servers: 0,
300
- },
301
- issues: [],
302
- });
303
- const result = await mockFindInfrastructureIssues();
304
- expect(result.summary.total_issues).toBe(0);
305
- expect(result.issues).toHaveLength(0);
306
- });
26
+ describe('client', () => {
27
+ it('should have client instance', () => {
28
+ const client = server['client'];
29
+ expect(client).toBeDefined();
307
30
  });
308
- describe('delete_database', () => {
309
- it('should call deleteDatabase with uuid', async () => {
310
- mockDeleteDatabase.mockResolvedValue({ message: 'Database deletion request queued.' });
311
- await mockDeleteDatabase('db-uuid-123');
312
- expect(mockDeleteDatabase).toHaveBeenCalledWith('db-uuid-123');
313
- });
314
- it('should call deleteDatabase with delete_volumes option', async () => {
315
- mockDeleteDatabase.mockResolvedValue({ message: 'Database deletion request queued.' });
316
- await mockDeleteDatabase('db-uuid-123', { deleteVolumes: true });
317
- expect(mockDeleteDatabase).toHaveBeenCalledWith('db-uuid-123', { deleteVolumes: true });
318
- });
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');
319
122
  });
320
123
  });
321
- describe('version tools', () => {
322
- it('get_mcp_version should return correct version format', () => {
323
- // The VERSION constant is '1.1.0' in mcp-server.ts
324
- // This test verifies the expected output structure
325
- const expectedResponse = {
326
- version: '1.1.0',
327
- name: '@masonator/coolify-mcp',
328
- };
329
- expect(expectedResponse.version).toBe('1.1.0');
330
- 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');
331
130
  });
332
131
  });
333
132
  });
@@ -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, DatabaseBackup, BackupExecution, Service, CreateServiceRequest, UpdateServiceRequest, ServiceCreateResponse, Deployment, Team, TeamMember, PrivateKey, CreatePrivateKeyRequest, UpdatePrivateKeyRequest, 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, Service, CreateServiceRequest, UpdateServiceRequest, ServiceCreateResponse, Deployment, Team, TeamMember, PrivateKey, CreatePrivateKeyRequest, UpdatePrivateKeyRequest, 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;
@@ -114,6 +114,14 @@ export declare class CoolifyClient {
114
114
  startDatabase(uuid: string): Promise<MessageResponse>;
115
115
  stopDatabase(uuid: string): Promise<MessageResponse>;
116
116
  restartDatabase(uuid: string): Promise<MessageResponse>;
117
+ createPostgresql(data: CreatePostgresqlRequest): Promise<CreateDatabaseResponse>;
118
+ createMysql(data: CreateMysqlRequest): Promise<CreateDatabaseResponse>;
119
+ createMariadb(data: CreateMariadbRequest): Promise<CreateDatabaseResponse>;
120
+ createMongodb(data: CreateMongodbRequest): Promise<CreateDatabaseResponse>;
121
+ createRedis(data: CreateRedisRequest): Promise<CreateDatabaseResponse>;
122
+ createKeydb(data: CreateKeydbRequest): Promise<CreateDatabaseResponse>;
123
+ createClickhouse(data: CreateClickhouseRequest): Promise<CreateDatabaseResponse>;
124
+ createDragonfly(data: CreateDragonflyRequest): Promise<CreateDatabaseResponse>;
117
125
  listServices(options?: ListOptions): Promise<Service[] | ServiceSummary[]>;
118
126
  getService(uuid: string): Promise<Service>;
119
127
  createService(data: CreateServiceRequest): Promise<ServiceCreateResponse>;