Skip to content

Commit a45770e

Browse files
StuMasonclaude
andauthored
feat: v2.1.0 - Add full database backup management (#60)
* feat: add full backup management to MCP - Add create/update/delete actions to database_backups tool - Add CreateDatabaseBackupRequest and UpdateDatabaseBackupRequest types - Add createDatabaseBackup, updateDatabaseBackup, deleteDatabaseBackup client methods - Support all backup configuration parameters: - frequency, enabled, save_s3, s3_storage_uuid - database_backup_retention_days_locally/s3 - database_backup_retention_amount_locally/s3 - databases_to_backup, dump_all Enables full database backup lifecycle management via MCP API. * 2.1.0 * fix: add missing tests and documentation for backup management - Add tests for createDatabaseBackup, updateDatabaseBackup, deleteDatabaseBackup in coolify-client.test.ts - Add method existence tests in mcp-server.test.ts - Add CHANGELOG.md entry for v2.1.0 with full backup management details - Update CLAUDE.md with testing requirements and codecov reminder Addresses PR review feedback: - Codecov coverage restored to 98.25% - All new client methods now have proper test coverage - Documentation updated per CLAUDE.md standards 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent 283ec9c commit a45770e

File tree

9 files changed

+190
-12
lines changed

9 files changed

+190
-12
lines changed

CHANGELOG.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,32 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [2.1.0] - 2026-01-07
9+
10+
### Added
11+
12+
- **Full Database Backup Management** - Complete lifecycle management for database backup schedules:
13+
- `database_backups` tool now supports `create`, `update`, and `delete` actions
14+
- Configure backup frequency (hourly, daily, weekly, monthly)
15+
- Set retention policies (days or amount limits for local and S3 storage)
16+
- Enable/disable backup schedules without deletion
17+
- S3 storage integration for off-server backups
18+
- All backup configuration parameters supported:
19+
- `frequency` - Cron expression or predefined schedule
20+
- `enabled` - Enable/disable the backup schedule
21+
- `save_s3` - Store backups in S3-compatible storage
22+
- `s3_storage_uuid` - Which S3 storage to use
23+
- `database_backup_retention_days_locally` - Days to keep backups locally (0 = unlimited)
24+
- `database_backup_retention_days_s3` - Days to keep backups in S3 (0 = unlimited)
25+
- `database_backup_retention_amount_locally` - Number of most recent backups to keep locally
26+
- `database_backup_retention_amount_s3` - Number of most recent backups to keep in S3
27+
- `databases_to_backup` - Specific databases to backup (for applicable database types)
28+
- `dump_all` - Dump all databases (for applicable database types)
29+
30+
### Changed
31+
32+
- `database_backups` tool actions expanded from 4 to 7: `list_schedules`, `get_schedule`, `list_executions`, `get_execution`, `create`, `update`, `delete`
33+
834
## [2.0.0] - 2026-01-06
935

1036
### Breaking Changes - Token Diet Release 🏋️

CLAUDE.md

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,25 @@ List endpoints return summaries (uuid, name, status) not full objects. This redu
4848
2. Add types to `src/types/coolify.ts`
4949
3. Add client method with explicit return type
5050
4. Add MCP tool to `src/lib/mcp-server.ts`
51-
5. Add mocked test
51+
5. Add mocked tests (required for codecov coverage)
52+
53+
### Testing Requirements
54+
55+
**IMPORTANT**: All new client methods MUST have test coverage to pass codecov checks.
56+
57+
When adding new client methods, you must add:
58+
59+
1. **Client method tests** in `src/__tests__/coolify-client.test.ts`:
60+
- Test the HTTP method (GET, POST, PATCH, DELETE)
61+
- Test the endpoint path
62+
- Test the request body if applicable
63+
- Follow the existing test patterns in the file
64+
65+
2. **Method existence tests** in `src/__tests__/mcp-server.test.ts`:
66+
- Add `expect(typeof client.methodName).toBe('function');` in the appropriate section
67+
- Ensures the method is properly exported and accessible
68+
69+
**codecov will fail PRs with uncovered lines.** Always run `npm test` before committing.
5270

5371
### Client Method Example
5472

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@masonator/coolify-mcp",
33
"scope": "@masonator",
4-
"version": "2.0.0",
4+
"version": "2.1.0",
55
"description": "MCP server implementation for Coolify",
66
"type": "module",
77
"main": "./dist/index.js",

src/__tests__/coolify-client.test.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1152,6 +1152,55 @@ describe('CoolifyClient', () => {
11521152
);
11531153
});
11541154

1155+
it('should create a database backup', async () => {
1156+
const mockBackup = { uuid: 'backup-uuid', frequency: '0 0 * * *', enabled: true };
1157+
mockFetch.mockResolvedValueOnce(mockResponse(mockBackup));
1158+
1159+
const result = await client.createDatabaseBackup('db-uuid', {
1160+
frequency: '0 0 * * *',
1161+
enabled: true,
1162+
save_s3: true,
1163+
s3_storage_uuid: 'storage-uuid',
1164+
database_backup_retention_days_locally: 7,
1165+
database_backup_retention_days_s3: 7,
1166+
});
1167+
1168+
expect(result).toEqual(mockBackup);
1169+
expect(mockFetch).toHaveBeenCalledWith(
1170+
'http://localhost:3000/api/v1/databases/db-uuid/backups',
1171+
expect.objectContaining({ method: 'POST' }),
1172+
);
1173+
});
1174+
1175+
it('should update a database backup', async () => {
1176+
const mockData = { message: 'Backup updated' };
1177+
mockFetch.mockResolvedValueOnce(mockResponse(mockData));
1178+
1179+
const result = await client.updateDatabaseBackup('db-uuid', 'backup-uuid', {
1180+
enabled: false,
1181+
frequency: '0 2 * * *',
1182+
});
1183+
1184+
expect(result).toEqual(mockData);
1185+
expect(mockFetch).toHaveBeenCalledWith(
1186+
'http://localhost:3000/api/v1/databases/db-uuid/backups/backup-uuid',
1187+
expect.objectContaining({ method: 'PATCH' }),
1188+
);
1189+
});
1190+
1191+
it('should delete a database backup', async () => {
1192+
const mockData = { message: 'Backup deleted' };
1193+
mockFetch.mockResolvedValueOnce(mockResponse(mockData));
1194+
1195+
const result = await client.deleteDatabaseBackup('db-uuid', 'backup-uuid');
1196+
1197+
expect(result).toEqual(mockData);
1198+
expect(mockFetch).toHaveBeenCalledWith(
1199+
'http://localhost:3000/api/v1/databases/db-uuid/backups/backup-uuid',
1200+
expect.objectContaining({ method: 'DELETE' }),
1201+
);
1202+
});
1203+
11551204
it('should list backup executions', async () => {
11561205
const mockExecutions = [{ uuid: 'exec-uuid', status: 'success' }];
11571206
mockFetch.mockResolvedValueOnce(mockResponse(mockExecutions));

src/__tests__/mcp-server.test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,9 @@ describe('CoolifyMcpServer v2', () => {
126126
// Backup operations
127127
expect(typeof client.listDatabaseBackups).toBe('function');
128128
expect(typeof client.getDatabaseBackup).toBe('function');
129+
expect(typeof client.createDatabaseBackup).toBe('function');
130+
expect(typeof client.updateDatabaseBackup).toBe('function');
131+
expect(typeof client.deleteDatabaseBackup).toBe('function');
129132
expect(typeof client.listBackupExecutions).toBe('function');
130133
expect(typeof client.getBackupExecution).toBe('function');
131134

src/lib/coolify-client.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ import type {
5353
CreateDatabaseResponse,
5454
DatabaseBackup,
5555
BackupExecution,
56+
CreateDatabaseBackupRequest,
57+
UpdateDatabaseBackupRequest,
5658
// Service types
5759
Service,
5860
CreateServiceRequest,
@@ -962,6 +964,33 @@ export class CoolifyClient {
962964
);
963965
}
964966

967+
async createDatabaseBackup(
968+
databaseUuid: string,
969+
data: CreateDatabaseBackupRequest,
970+
): Promise<DatabaseBackup> {
971+
return this.request<DatabaseBackup>(`/databases/${databaseUuid}/backups`, {
972+
method: 'POST',
973+
body: JSON.stringify(data),
974+
});
975+
}
976+
977+
async updateDatabaseBackup(
978+
databaseUuid: string,
979+
backupUuid: string,
980+
data: UpdateDatabaseBackupRequest,
981+
): Promise<MessageResponse> {
982+
return this.request<MessageResponse>(`/databases/${databaseUuid}/backups/${backupUuid}`, {
983+
method: 'PATCH',
984+
body: JSON.stringify(data),
985+
});
986+
}
987+
988+
async deleteDatabaseBackup(databaseUuid: string, backupUuid: string): Promise<MessageResponse> {
989+
return this.request<MessageResponse>(`/databases/${databaseUuid}/backups/${backupUuid}`, {
990+
method: 'DELETE',
991+
});
992+
}
993+
965994
// ===========================================================================
966995
// Deployment Control endpoints
967996
// ===========================================================================

src/lib/mcp-server.ts

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Coolify MCP Server v2.0.0
2+
* Coolify MCP Server v2.1.0
33
* Consolidated tools for efficient token usage
44
*/
55
/* eslint-disable @typescript-eslint/no-explicit-any */
@@ -17,7 +17,7 @@ import {
1717
} from './coolify-client.js';
1818
import type { CoolifyConfig } from '../types/coolify.js';
1919

20-
const VERSION = '2.0.0';
20+
const VERSION = '2.1.0';
2121

2222
/** Wrap handler with error handling */
2323
function wrap<T>(
@@ -666,14 +666,34 @@ export class CoolifyMcpServer extends McpServer {
666666
// =========================================================================
667667
this.tool(
668668
'database_backups',
669-
'Manage backups: list_schedules/get_schedule/list_executions/get_execution',
669+
'Manage backups: list_schedules/get_schedule/list_executions/get_execution/create/update/delete',
670670
{
671-
action: z.enum(['list_schedules', 'get_schedule', 'list_executions', 'get_execution']),
671+
action: z.enum([
672+
'list_schedules',
673+
'get_schedule',
674+
'list_executions',
675+
'get_execution',
676+
'create',
677+
'update',
678+
'delete',
679+
]),
672680
database_uuid: z.string(),
673681
backup_uuid: z.string().optional(),
674682
execution_uuid: z.string().optional(),
683+
// Backup configuration parameters
684+
frequency: z.string().optional(),
685+
enabled: z.boolean().optional(),
686+
save_s3: z.boolean().optional(),
687+
s3_storage_uuid: z.string().optional(),
688+
databases_to_backup: z.string().optional(),
689+
dump_all: z.boolean().optional(),
690+
database_backup_retention_days_locally: z.number().optional(),
691+
database_backup_retention_days_s3: z.number().optional(),
692+
database_backup_retention_amount_locally: z.number().optional(),
693+
database_backup_retention_amount_s3: z.number().optional(),
675694
},
676-
async ({ action, database_uuid, backup_uuid, execution_uuid }) => {
695+
async (args) => {
696+
const { action, database_uuid, backup_uuid, execution_uuid, ...backupData } = args;
677697
switch (action) {
678698
case 'list_schedules':
679699
return wrap(() => this.client.listDatabaseBackups(database_uuid));
@@ -695,6 +715,25 @@ export class CoolifyMcpServer extends McpServer {
695715
return wrap(() =>
696716
this.client.getBackupExecution(database_uuid, backup_uuid, execution_uuid),
697717
);
718+
case 'create':
719+
if (!args.frequency)
720+
return { content: [{ type: 'text' as const, text: 'Error: frequency required' }] };
721+
return wrap(() =>
722+
this.client.createDatabaseBackup(database_uuid, {
723+
...backupData,
724+
frequency: args.frequency!,
725+
}),
726+
);
727+
case 'update':
728+
if (!backup_uuid)
729+
return { content: [{ type: 'text' as const, text: 'Error: backup_uuid required' }] };
730+
return wrap(() =>
731+
this.client.updateDatabaseBackup(database_uuid, backup_uuid, backupData),
732+
);
733+
case 'delete':
734+
if (!backup_uuid)
735+
return { content: [{ type: 'text' as const, text: 'Error: backup_uuid required' }] };
736+
return wrap(() => this.client.deleteDatabaseBackup(database_uuid, backup_uuid));
698737
}
699738
},
700739
);

src/types/coolify.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -651,9 +651,23 @@ export interface CreateDatabaseBackupRequest {
651651
s3_storage_uuid?: string;
652652
databases_to_backup?: string;
653653
dump_all?: boolean;
654-
backup_now?: boolean;
655-
backup_retention?: number;
656-
backup_retention_days?: number;
654+
database_backup_retention_days_locally?: number;
655+
database_backup_retention_days_s3?: number;
656+
database_backup_retention_amount_locally?: number;
657+
database_backup_retention_amount_s3?: number;
658+
}
659+
660+
export interface UpdateDatabaseBackupRequest {
661+
frequency?: string;
662+
enabled?: boolean;
663+
save_s3?: boolean;
664+
s3_storage_uuid?: string;
665+
databases_to_backup?: string;
666+
dump_all?: boolean;
667+
database_backup_retention_days_locally?: number;
668+
database_backup_retention_days_s3?: number;
669+
database_backup_retention_amount_locally?: number;
670+
database_backup_retention_amount_s3?: number;
657671
}
658672

659673
export interface BackupExecution {

0 commit comments

Comments
 (0)