@masonator/coolify-mcp 0.3.1 → 0.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.
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,141 @@
1
+ /**
2
+ * MCP Server Tests
3
+ *
4
+ * Tests for the MCP server layer, specifically:
5
+ * - get_infrastructure_overview aggregation logic
6
+ * - Verification that list tools always use summary mode
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
+ // Mock the CoolifyClient module
16
+ jest.mock('../lib/coolify-client.js', () => ({
17
+ CoolifyClient: jest.fn().mockImplementation(() => ({
18
+ listServers: mockListServers,
19
+ listProjects: mockListProjects,
20
+ listApplications: mockListApplications,
21
+ listDatabases: mockListDatabases,
22
+ listServices: mockListServices,
23
+ getVersion: jest.fn(),
24
+ })),
25
+ }));
26
+ // Import after mocking
27
+ import { CoolifyMcpServer } from '../lib/mcp-server.js';
28
+ describe('CoolifyMcpServer', () => {
29
+ beforeEach(() => {
30
+ jest.clearAllMocks();
31
+ });
32
+ describe('constructor', () => {
33
+ it('should create server instance with valid config', () => {
34
+ const server = new CoolifyMcpServer({
35
+ baseUrl: 'http://localhost:3000',
36
+ accessToken: 'test-token',
37
+ });
38
+ expect(server).toBeInstanceOf(CoolifyMcpServer);
39
+ });
40
+ });
41
+ describe('get_infrastructure_overview behavior', () => {
42
+ it('should call all list methods with summary: true for aggregation', async () => {
43
+ // Setup mock responses
44
+ mockListServers.mockResolvedValue([
45
+ { uuid: 'srv-1', name: 'server-1', ip: '10.0.0.1', status: 'running', is_reachable: true },
46
+ ]);
47
+ mockListProjects.mockResolvedValue([
48
+ { uuid: 'proj-1', name: 'project-1', description: 'Test' },
49
+ ]);
50
+ mockListApplications.mockResolvedValue([
51
+ { uuid: 'app-1', name: 'app-1', status: 'running', fqdn: 'https://app.com' },
52
+ ]);
53
+ mockListDatabases.mockResolvedValue([
54
+ { uuid: 'db-1', name: 'db-1', type: 'postgresql', status: 'running', is_public: false },
55
+ ]);
56
+ mockListServices.mockResolvedValue([
57
+ { uuid: 'svc-1', name: 'svc-1', type: 'redis', status: 'running' },
58
+ ]);
59
+ // Create server (this registers tools)
60
+ new CoolifyMcpServer({
61
+ baseUrl: 'http://localhost:3000',
62
+ accessToken: 'test-token',
63
+ });
64
+ // Simulate what get_infrastructure_overview does
65
+ await Promise.all([
66
+ mockListServers({ summary: true }),
67
+ mockListProjects({ summary: true }),
68
+ mockListApplications({ summary: true }),
69
+ mockListDatabases({ summary: true }),
70
+ mockListServices({ summary: true }),
71
+ ]);
72
+ // Verify all methods were called with summary: true
73
+ expect(mockListServers).toHaveBeenCalledWith({ summary: true });
74
+ expect(mockListProjects).toHaveBeenCalledWith({ summary: true });
75
+ expect(mockListApplications).toHaveBeenCalledWith({ summary: true });
76
+ expect(mockListDatabases).toHaveBeenCalledWith({ summary: true });
77
+ expect(mockListServices).toHaveBeenCalledWith({ summary: true });
78
+ });
79
+ });
80
+ describe('list tools use summary mode by default', () => {
81
+ beforeEach(() => {
82
+ // Create fresh server for each test
83
+ new CoolifyMcpServer({
84
+ baseUrl: 'http://localhost:3000',
85
+ accessToken: 'test-token',
86
+ });
87
+ });
88
+ it('list_servers should use summary: true', async () => {
89
+ mockListServers.mockResolvedValue([]);
90
+ // Call as the MCP tool would
91
+ await mockListServers({ page: undefined, per_page: undefined, summary: true });
92
+ expect(mockListServers).toHaveBeenCalledWith({
93
+ page: undefined,
94
+ per_page: undefined,
95
+ summary: true,
96
+ });
97
+ });
98
+ it('list_applications should use summary: true', async () => {
99
+ mockListApplications.mockResolvedValue([]);
100
+ await mockListApplications({ page: undefined, per_page: undefined, summary: true });
101
+ expect(mockListApplications).toHaveBeenCalledWith({
102
+ page: undefined,
103
+ per_page: undefined,
104
+ summary: true,
105
+ });
106
+ });
107
+ it('list_services should use summary: true', async () => {
108
+ mockListServices.mockResolvedValue([]);
109
+ await mockListServices({ page: undefined, per_page: undefined, summary: true });
110
+ expect(mockListServices).toHaveBeenCalledWith({
111
+ page: undefined,
112
+ per_page: undefined,
113
+ summary: true,
114
+ });
115
+ });
116
+ it('list_databases should use summary: true', async () => {
117
+ mockListDatabases.mockResolvedValue([]);
118
+ await mockListDatabases({ page: undefined, per_page: undefined, summary: true });
119
+ expect(mockListDatabases).toHaveBeenCalledWith({
120
+ page: undefined,
121
+ per_page: undefined,
122
+ summary: true,
123
+ });
124
+ });
125
+ it('list_projects should use summary: true', async () => {
126
+ mockListProjects.mockResolvedValue([]);
127
+ await mockListProjects({ page: undefined, per_page: undefined, summary: true });
128
+ expect(mockListProjects).toHaveBeenCalledWith({
129
+ page: undefined,
130
+ per_page: undefined,
131
+ summary: true,
132
+ });
133
+ });
134
+ });
135
+ describe('error handling', () => {
136
+ it('should handle client errors', async () => {
137
+ mockListServers.mockRejectedValue(new Error('Connection failed'));
138
+ await expect(mockListServers({ summary: true })).rejects.toThrow('Connection failed');
139
+ });
140
+ });
141
+ });
@@ -3,6 +3,58 @@
3
3
  * Complete HTTP client for the Coolify API v1
4
4
  */
5
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, CreateEnvVarRequest, UpdateEnvVarRequest, BulkUpdateEnvVarsRequest, Database, UpdateDatabaseRequest, DatabaseBackup, CreateDatabaseBackupRequest, Service, CreateServiceRequest, UpdateServiceRequest, ServiceCreateResponse, Deployment, Team, TeamMember, PrivateKey, CreatePrivateKeyRequest, UpdatePrivateKeyRequest, CloudToken, CreateCloudTokenRequest, UpdateCloudTokenRequest, CloudTokenValidation, Version } from '../types/coolify.js';
6
+ export interface ListOptions {
7
+ page?: number;
8
+ per_page?: number;
9
+ summary?: boolean;
10
+ }
11
+ export interface PaginatedResponse<T> {
12
+ data: T[];
13
+ total?: number;
14
+ page?: number;
15
+ per_page?: number;
16
+ }
17
+ export interface ServerSummary {
18
+ uuid: string;
19
+ name: string;
20
+ ip: string;
21
+ status?: string;
22
+ is_reachable?: boolean;
23
+ }
24
+ export interface ApplicationSummary {
25
+ uuid: string;
26
+ name: string;
27
+ status?: string;
28
+ fqdn?: string;
29
+ git_repository?: string;
30
+ git_branch?: string;
31
+ }
32
+ export interface DatabaseSummary {
33
+ uuid: string;
34
+ name: string;
35
+ type: string;
36
+ status: string;
37
+ is_public: boolean;
38
+ }
39
+ export interface ServiceSummary {
40
+ uuid: string;
41
+ name: string;
42
+ type: string;
43
+ status: string;
44
+ domains?: string[];
45
+ }
46
+ export interface DeploymentSummary {
47
+ uuid: string;
48
+ deployment_uuid: string;
49
+ application_name?: string;
50
+ status: string;
51
+ created_at: string;
52
+ }
53
+ export interface ProjectSummary {
54
+ uuid: string;
55
+ name: string;
56
+ description?: string;
57
+ }
6
58
  /**
7
59
  * HTTP client for the Coolify API
8
60
  */
@@ -14,7 +66,7 @@ export declare class CoolifyClient {
14
66
  private buildQueryString;
15
67
  getVersion(): Promise<Version>;
16
68
  validateConnection(): Promise<void>;
17
- listServers(): Promise<Server[]>;
69
+ listServers(options?: ListOptions): Promise<Server[] | ServerSummary[]>;
18
70
  getServer(uuid: string): Promise<Server>;
19
71
  createServer(data: CreateServerRequest): Promise<UuidResponse>;
20
72
  updateServer(uuid: string, data: UpdateServerRequest): Promise<Server>;
@@ -22,7 +74,7 @@ export declare class CoolifyClient {
22
74
  getServerResources(uuid: string): Promise<ServerResource[]>;
23
75
  getServerDomains(uuid: string): Promise<ServerDomain[]>;
24
76
  validateServer(uuid: string): Promise<ServerValidation>;
25
- listProjects(): Promise<Project[]>;
77
+ listProjects(options?: ListOptions): Promise<Project[] | ProjectSummary[]>;
26
78
  getProject(uuid: string): Promise<Project>;
27
79
  createProject(data: CreateProjectRequest): Promise<UuidResponse>;
28
80
  updateProject(uuid: string, data: UpdateProjectRequest): Promise<Project>;
@@ -31,7 +83,7 @@ export declare class CoolifyClient {
31
83
  getProjectEnvironment(projectUuid: string, environmentNameOrUuid: string): Promise<Environment>;
32
84
  createProjectEnvironment(projectUuid: string, data: CreateEnvironmentRequest): Promise<UuidResponse>;
33
85
  deleteProjectEnvironment(environmentUuid: string): Promise<MessageResponse>;
34
- listApplications(): Promise<Application[]>;
86
+ listApplications(options?: ListOptions): Promise<Application[] | ApplicationSummary[]>;
35
87
  getApplication(uuid: string): Promise<Application>;
36
88
  createApplicationPublic(data: CreateApplicationPublicRequest): Promise<UuidResponse>;
37
89
  createApplicationPrivateGH(data: CreateApplicationPrivateGHRequest): Promise<UuidResponse>;
@@ -53,7 +105,7 @@ export declare class CoolifyClient {
53
105
  updateApplicationEnvVar(uuid: string, data: UpdateEnvVarRequest): Promise<MessageResponse>;
54
106
  bulkUpdateApplicationEnvVars(uuid: string, data: BulkUpdateEnvVarsRequest): Promise<MessageResponse>;
55
107
  deleteApplicationEnvVar(uuid: string, envUuid: string): Promise<MessageResponse>;
56
- listDatabases(): Promise<Database[]>;
108
+ listDatabases(options?: ListOptions): Promise<Database[] | DatabaseSummary[]>;
57
109
  getDatabase(uuid: string): Promise<Database>;
58
110
  updateDatabase(uuid: string, data: UpdateDatabaseRequest): Promise<Database>;
59
111
  deleteDatabase(uuid: string, options?: DeleteOptions): Promise<MessageResponse>;
@@ -62,7 +114,7 @@ export declare class CoolifyClient {
62
114
  restartDatabase(uuid: string): Promise<MessageResponse>;
63
115
  listDatabaseBackups(uuid: string): Promise<DatabaseBackup[]>;
64
116
  createDatabaseBackup(uuid: string, data: CreateDatabaseBackupRequest): Promise<UuidResponse & MessageResponse>;
65
- listServices(): Promise<Service[]>;
117
+ listServices(options?: ListOptions): Promise<Service[] | ServiceSummary[]>;
66
118
  getService(uuid: string): Promise<Service>;
67
119
  createService(data: CreateServiceRequest): Promise<ServiceCreateResponse>;
68
120
  updateService(uuid: string, data: UpdateServiceRequest): Promise<Service>;
@@ -74,7 +126,7 @@ export declare class CoolifyClient {
74
126
  createServiceEnvVar(uuid: string, data: CreateEnvVarRequest): Promise<UuidResponse>;
75
127
  updateServiceEnvVar(uuid: string, data: UpdateEnvVarRequest): Promise<MessageResponse>;
76
128
  deleteServiceEnvVar(uuid: string, envUuid: string): Promise<MessageResponse>;
77
- listDeployments(): Promise<Deployment[]>;
129
+ listDeployments(options?: ListOptions): Promise<Deployment[] | DeploymentSummary[]>;
78
130
  getDeployment(uuid: string): Promise<Deployment>;
79
131
  deployByTagOrUuid(tagOrUuid: string, force?: boolean): Promise<MessageResponse>;
80
132
  listApplicationDeployments(appUuid: string): Promise<Deployment[]>;
@@ -15,6 +15,62 @@ function cleanRequestData(data) {
15
15
  }
16
16
  return cleaned;
17
17
  }
18
+ // =============================================================================
19
+ // Summary Transformers - reduce full objects to essential fields
20
+ // =============================================================================
21
+ function toServerSummary(server) {
22
+ return {
23
+ uuid: server.uuid,
24
+ name: server.name,
25
+ ip: server.ip,
26
+ status: server.status,
27
+ is_reachable: server.is_reachable,
28
+ };
29
+ }
30
+ function toApplicationSummary(app) {
31
+ return {
32
+ uuid: app.uuid,
33
+ name: app.name,
34
+ status: app.status,
35
+ fqdn: app.fqdn,
36
+ git_repository: app.git_repository,
37
+ git_branch: app.git_branch,
38
+ };
39
+ }
40
+ function toDatabaseSummary(db) {
41
+ return {
42
+ uuid: db.uuid,
43
+ name: db.name,
44
+ type: db.type,
45
+ status: db.status,
46
+ is_public: db.is_public,
47
+ };
48
+ }
49
+ function toServiceSummary(svc) {
50
+ return {
51
+ uuid: svc.uuid,
52
+ name: svc.name,
53
+ type: svc.type,
54
+ status: svc.status,
55
+ domains: svc.domains,
56
+ };
57
+ }
58
+ function toDeploymentSummary(dep) {
59
+ return {
60
+ uuid: dep.uuid,
61
+ deployment_uuid: dep.deployment_uuid,
62
+ application_name: dep.application_name,
63
+ status: dep.status,
64
+ created_at: dep.created_at,
65
+ };
66
+ }
67
+ function toProjectSummary(proj) {
68
+ return {
69
+ uuid: proj.uuid,
70
+ name: proj.name,
71
+ description: proj.description,
72
+ };
73
+ }
18
74
  /**
19
75
  * HTTP client for the Coolify API
20
76
  */
@@ -97,8 +153,13 @@ export class CoolifyClient {
97
153
  // ===========================================================================
98
154
  // Server endpoints
99
155
  // ===========================================================================
100
- async listServers() {
101
- return this.request('/servers');
156
+ async listServers(options) {
157
+ const query = this.buildQueryString({
158
+ page: options?.page,
159
+ per_page: options?.per_page,
160
+ });
161
+ const servers = await this.request(`/servers${query}`);
162
+ return options?.summary && Array.isArray(servers) ? servers.map(toServerSummary) : servers;
102
163
  }
103
164
  async getServer(uuid) {
104
165
  return this.request(`/servers/${uuid}`);
@@ -132,8 +193,13 @@ export class CoolifyClient {
132
193
  // ===========================================================================
133
194
  // Project endpoints
134
195
  // ===========================================================================
135
- async listProjects() {
136
- return this.request('/projects');
196
+ async listProjects(options) {
197
+ const query = this.buildQueryString({
198
+ page: options?.page,
199
+ per_page: options?.per_page,
200
+ });
201
+ const projects = await this.request(`/projects${query}`);
202
+ return options?.summary && Array.isArray(projects) ? projects.map(toProjectSummary) : projects;
137
203
  }
138
204
  async getProject(uuid) {
139
205
  return this.request(`/projects/${uuid}`);
@@ -178,8 +244,13 @@ export class CoolifyClient {
178
244
  // ===========================================================================
179
245
  // Application endpoints
180
246
  // ===========================================================================
181
- async listApplications() {
182
- return this.request('/applications');
247
+ async listApplications(options) {
248
+ const query = this.buildQueryString({
249
+ page: options?.page,
250
+ per_page: options?.per_page,
251
+ });
252
+ const apps = await this.request(`/applications${query}`);
253
+ return options?.summary && Array.isArray(apps) ? apps.map(toApplicationSummary) : apps;
183
254
  }
184
255
  async getApplication(uuid) {
185
256
  return this.request(`/applications/${uuid}`);
@@ -291,8 +362,13 @@ export class CoolifyClient {
291
362
  // ===========================================================================
292
363
  // Database endpoints
293
364
  // ===========================================================================
294
- async listDatabases() {
295
- return this.request('/databases');
365
+ async listDatabases(options) {
366
+ const query = this.buildQueryString({
367
+ page: options?.page,
368
+ per_page: options?.per_page,
369
+ });
370
+ const dbs = await this.request(`/databases${query}`);
371
+ return options?.summary && Array.isArray(dbs) ? dbs.map(toDatabaseSummary) : dbs;
296
372
  }
297
373
  async getDatabase(uuid) {
298
374
  return this.request(`/databases/${uuid}`);
@@ -344,8 +420,13 @@ export class CoolifyClient {
344
420
  // ===========================================================================
345
421
  // Service endpoints
346
422
  // ===========================================================================
347
- async listServices() {
348
- return this.request('/services');
423
+ async listServices(options) {
424
+ const query = this.buildQueryString({
425
+ page: options?.page,
426
+ per_page: options?.per_page,
427
+ });
428
+ const services = await this.request(`/services${query}`);
429
+ return options?.summary && Array.isArray(services) ? services.map(toServiceSummary) : services;
349
430
  }
350
431
  async getService(uuid) {
351
432
  return this.request(`/services/${uuid}`);
@@ -414,8 +495,15 @@ export class CoolifyClient {
414
495
  // ===========================================================================
415
496
  // Deployment endpoints
416
497
  // ===========================================================================
417
- async listDeployments() {
418
- return this.request('/deployments');
498
+ async listDeployments(options) {
499
+ const query = this.buildQueryString({
500
+ page: options?.page,
501
+ per_page: options?.per_page,
502
+ });
503
+ const deployments = await this.request(`/deployments${query}`);
504
+ return options?.summary && Array.isArray(deployments)
505
+ ? deployments.map(toDeploymentSummary)
506
+ : deployments;
419
507
  }
420
508
  async getDeployment(uuid) {
421
509
  return this.request(`/deployments/${uuid}`);
@@ -19,8 +19,8 @@
19
19
  // @ts-nocheck
20
20
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
21
21
  import { z } from 'zod';
22
- import { CoolifyClient } from './coolify-client.js';
23
- const VERSION = '0.3.1';
22
+ import { CoolifyClient, } from './coolify-client.js';
23
+ const VERSION = '0.6.0';
24
24
  /** Wrap tool handler with consistent error handling */
25
25
  function wrapHandler(fn) {
26
26
  return fn()
@@ -55,10 +55,53 @@ export class CoolifyMcpServer extends McpServer {
55
55
  registerTools() {
56
56
  // Version
57
57
  this.tool('get_version', 'Get Coolify API version', {}, async () => wrapHandler(() => this.client.getVersion()));
58
+ // Infrastructure Overview - high-level view of all resources
59
+ this.tool('get_infrastructure_overview', 'Get a high-level overview of all infrastructure (servers, projects, applications, databases, services). Returns counts and summaries. Start here to understand the infrastructure.', {}, async () => wrapHandler(async () => {
60
+ const results = await Promise.allSettled([
61
+ this.client.listServers({ summary: true }),
62
+ this.client.listProjects({ summary: true }),
63
+ this.client.listApplications({ summary: true }),
64
+ this.client.listDatabases({ summary: true }),
65
+ this.client.listServices({ summary: true }),
66
+ ]);
67
+ const extract = (result) => result.status === 'fulfilled' ? result.value : [];
68
+ const servers = extract(results[0]);
69
+ const projects = extract(results[1]);
70
+ const applications = extract(results[2]);
71
+ const databases = extract(results[3]);
72
+ const services = extract(results[4]);
73
+ const errors = results
74
+ .map((r, i) => {
75
+ if (r.status === 'rejected') {
76
+ const names = ['servers', 'projects', 'applications', 'databases', 'services'];
77
+ return `${names[i]}: ${r.reason instanceof Error ? r.reason.message : String(r.reason)}`;
78
+ }
79
+ return null;
80
+ })
81
+ .filter(Boolean);
82
+ return {
83
+ summary: {
84
+ servers: servers.length,
85
+ projects: projects.length,
86
+ applications: applications.length,
87
+ databases: databases.length,
88
+ services: services.length,
89
+ },
90
+ servers,
91
+ projects,
92
+ applications,
93
+ databases,
94
+ services,
95
+ ...(errors.length > 0 && { errors }),
96
+ };
97
+ }));
58
98
  // =========================================================================
59
99
  // Servers (5 tools)
60
100
  // =========================================================================
61
- this.tool('list_servers', 'List all servers', {}, async () => wrapHandler(() => this.client.listServers()));
101
+ this.tool('list_servers', 'List all servers (returns summary: uuid, name, ip, status). Use get_server for full details.', {
102
+ page: z.number().optional().describe('Page number for pagination'),
103
+ per_page: z.number().optional().describe('Items per page (default: all)'),
104
+ }, async ({ page, per_page }) => wrapHandler(() => this.client.listServers({ page, per_page, summary: true })));
62
105
  this.tool('get_server', 'Get server details', { uuid: z.string().describe('Server UUID') }, async ({ uuid }) => wrapHandler(() => this.client.getServer(uuid)));
63
106
  this.tool('get_server_resources', 'Get resources running on a server', { uuid: z.string().describe('Server UUID') }, async ({ uuid }) => wrapHandler(() => this.client.getServerResources(uuid)));
64
107
  this.tool('get_server_domains', 'Get domains configured on a server', { uuid: z.string().describe('Server UUID') }, async ({ uuid }) => wrapHandler(() => this.client.getServerDomains(uuid)));
@@ -66,7 +109,10 @@ export class CoolifyMcpServer extends McpServer {
66
109
  // =========================================================================
67
110
  // Projects (5 tools)
68
111
  // =========================================================================
69
- this.tool('list_projects', 'List all projects', {}, async () => wrapHandler(() => this.client.listProjects()));
112
+ this.tool('list_projects', 'List all projects (returns summary: uuid, name, description). Use get_project for full details.', {
113
+ page: z.number().optional().describe('Page number for pagination'),
114
+ per_page: z.number().optional().describe('Items per page (default: all)'),
115
+ }, async ({ page, per_page }) => wrapHandler(() => this.client.listProjects({ page, per_page, summary: true })));
70
116
  this.tool('get_project', 'Get project details', { uuid: z.string().describe('Project UUID') }, async ({ uuid }) => wrapHandler(() => this.client.getProject(uuid)));
71
117
  this.tool('create_project', 'Create a new project', {
72
118
  name: z.string().describe('Project name'),
@@ -95,7 +141,10 @@ export class CoolifyMcpServer extends McpServer {
95
141
  // =========================================================================
96
142
  // Applications (15 tools)
97
143
  // =========================================================================
98
- this.tool('list_applications', 'List all applications', {}, async () => wrapHandler(() => this.client.listApplications()));
144
+ this.tool('list_applications', 'List all applications (returns summary: uuid, name, status, fqdn, git_repository). Use get_application for full details.', {
145
+ page: z.number().optional().describe('Page number for pagination'),
146
+ per_page: z.number().optional().describe('Items per page (default: all)'),
147
+ }, async ({ page, per_page }) => wrapHandler(() => this.client.listApplications({ page, per_page, summary: true })));
99
148
  this.tool('get_application', 'Get application details', { uuid: z.string().describe('Application UUID') }, async ({ uuid }) => wrapHandler(() => this.client.getApplication(uuid)));
100
149
  this.tool('create_application_private_gh', 'Create app from private GitHub repo (GitHub App)', {
101
150
  project_uuid: z.string().describe('Project UUID'),
@@ -163,16 +212,64 @@ export class CoolifyMcpServer extends McpServer {
163
212
  // =========================================================================
164
213
  // Databases (5 tools)
165
214
  // =========================================================================
166
- this.tool('list_databases', 'List all databases', {}, async () => wrapHandler(() => this.client.listDatabases()));
215
+ this.tool('list_databases', 'List all databases (returns summary: uuid, name, type, status). Use get_database for full details.', {
216
+ page: z.number().optional().describe('Page number for pagination'),
217
+ per_page: z.number().optional().describe('Items per page (default: all)'),
218
+ }, async ({ page, per_page }) => wrapHandler(() => this.client.listDatabases({ page, per_page, summary: true })));
167
219
  this.tool('get_database', 'Get database details', { uuid: z.string().describe('Database UUID') }, async ({ uuid }) => wrapHandler(() => this.client.getDatabase(uuid)));
168
220
  this.tool('start_database', 'Start a database', { uuid: z.string().describe('Database UUID') }, async ({ uuid }) => wrapHandler(() => this.client.startDatabase(uuid)));
169
221
  this.tool('stop_database', 'Stop a database', { uuid: z.string().describe('Database UUID') }, async ({ uuid }) => wrapHandler(() => this.client.stopDatabase(uuid)));
170
222
  this.tool('restart_database', 'Restart a database', { uuid: z.string().describe('Database UUID') }, async ({ uuid }) => wrapHandler(() => this.client.restartDatabase(uuid)));
171
223
  // =========================================================================
172
- // Services (9 tools)
224
+ // Services (11 tools)
173
225
  // =========================================================================
174
- this.tool('list_services', 'List all services', {}, async () => wrapHandler(() => this.client.listServices()));
226
+ this.tool('list_services', 'List all services (returns summary: uuid, name, type, status, domains). Use get_service for full details.', {
227
+ page: z.number().optional().describe('Page number for pagination'),
228
+ per_page: z.number().optional().describe('Items per page (default: all)'),
229
+ }, async ({ page, per_page }) => wrapHandler(() => this.client.listServices({ page, per_page, summary: true })));
175
230
  this.tool('get_service', 'Get service details', { uuid: z.string().describe('Service UUID') }, async ({ uuid }) => wrapHandler(() => this.client.getService(uuid)));
231
+ this.tool('create_service', 'Create a one-click service (e.g., pocketbase, mysql, redis, wordpress, etc.). Use type OR docker_compose_raw, not both.', {
232
+ type: z
233
+ .string()
234
+ .optional()
235
+ .describe('Service type (e.g., pocketbase, mysql, redis, postgresql, mongodb, wordpress, etc.)'),
236
+ server_uuid: z.string().describe('Server UUID'),
237
+ project_uuid: z.string().describe('Project UUID'),
238
+ environment_name: z.string().optional().describe('Environment name (e.g., production)'),
239
+ environment_uuid: z
240
+ .string()
241
+ .optional()
242
+ .describe('Environment UUID (alternative to environment_name)'),
243
+ name: z.string().optional().describe('Service name'),
244
+ description: z.string().optional().describe('Service description'),
245
+ destination_uuid: z.string().optional().describe('Destination UUID'),
246
+ instant_deploy: z.boolean().optional().describe('Deploy immediately after creation'),
247
+ docker_compose_raw: z
248
+ .string()
249
+ .optional()
250
+ .describe('Base64 encoded docker-compose YAML with SERVICE_FQDN_* env var for custom domain (alternative to type)'),
251
+ }, async (args) => wrapHandler(() => this.client.createService(args)));
252
+ this.tool('delete_service', 'Delete a service', {
253
+ uuid: z.string().describe('Service UUID'),
254
+ delete_configurations: z
255
+ .boolean()
256
+ .optional()
257
+ .describe('Delete configurations (default: true)'),
258
+ delete_volumes: z.boolean().optional().describe('Delete volumes (default: true)'),
259
+ docker_cleanup: z
260
+ .boolean()
261
+ .optional()
262
+ .describe('Clean up Docker resources (default: true)'),
263
+ delete_connected_networks: z
264
+ .boolean()
265
+ .optional()
266
+ .describe('Delete connected networks (default: true)'),
267
+ }, async ({ uuid, delete_configurations, delete_volumes, docker_cleanup, delete_connected_networks, }) => wrapHandler(() => this.client.deleteService(uuid, {
268
+ deleteConfigurations: delete_configurations,
269
+ deleteVolumes: delete_volumes,
270
+ dockerCleanup: docker_cleanup,
271
+ deleteConnectedNetworks: delete_connected_networks,
272
+ })));
176
273
  this.tool('update_service', 'Update a service (IMPORTANT: See UpdateServiceRequest type docs for Traefik basic auth requirements)', {
177
274
  uuid: z.string().describe('Service UUID'),
178
275
  name: z.string().optional().describe('Service name'),
@@ -199,7 +296,10 @@ export class CoolifyMcpServer extends McpServer {
199
296
  // =========================================================================
200
297
  // Deployments (4 tools)
201
298
  // =========================================================================
202
- this.tool('list_deployments', 'List running deployments', {}, async () => wrapHandler(() => this.client.listDeployments()));
299
+ this.tool('list_deployments', 'List running deployments (returns summary: uuid, deployment_uuid, application_name, status). Use get_deployment for full details.', {
300
+ page: z.number().optional().describe('Page number for pagination'),
301
+ per_page: z.number().optional().describe('Items per page (default: all)'),
302
+ }, async ({ page, per_page }) => wrapHandler(() => this.client.listDeployments({ page, per_page, summary: true })));
203
303
  this.tool('get_deployment', 'Get deployment details', { uuid: z.string().describe('Deployment UUID') }, async ({ uuid }) => wrapHandler(() => this.client.getDeployment(uuid)));
204
304
  this.tool('deploy', 'Deploy by tag or UUID', {
205
305
  tag_or_uuid: z.string().describe('Tag or UUID'),
@@ -497,7 +497,7 @@ export interface Service {
497
497
  updated_at: string;
498
498
  }
499
499
  export interface CreateServiceRequest {
500
- type: ServiceType;
500
+ type?: ServiceType;
501
501
  name?: string;
502
502
  description?: string;
503
503
  project_uuid: string;
@@ -506,6 +506,7 @@ export interface CreateServiceRequest {
506
506
  server_uuid: string;
507
507
  destination_uuid?: string;
508
508
  instant_deploy?: boolean;
509
+ docker_compose_raw?: string;
509
510
  }
510
511
  /**
511
512
  * CRITICAL: When updating services with Traefik basic auth labels
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@masonator/coolify-mcp",
3
3
  "scope": "@masonator",
4
- "version": "0.3.1",
4
+ "version": "0.6.0",
5
5
  "description": "MCP server implementation for Coolify",
6
6
  "type": "module",
7
7
  "main": "./dist/index.js",