Skip to content

Commit 099e4e7

Browse files
authored
Added create_service and delete_service tools (#20)
feat: add create_service and delete_service MCP tools ## Summary - Add `create_service` tool - supports both service types (pocketbase, mysql, etc.) and custom docker_compose_raw - Add `delete_service` tool - with options for cleanup (volumes, networks, configurations) - Make `type` optional in CreateServiceRequest (can use docker_compose_raw instead) - Add test coverage for new options 🤖 Generated with [Claude Code](https://claude.com/claude-code)
1 parent a8c69bb commit 099e4e7

File tree

4 files changed

+117
-4
lines changed

4 files changed

+117
-4
lines changed

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.

src/__tests__/coolify-client.test.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,33 @@ describe('CoolifyClient', () => {
294294
}),
295295
);
296296
});
297+
298+
it('should create a service with docker_compose_raw instead of type', async () => {
299+
const responseData = {
300+
uuid: 'compose-uuid',
301+
domains: ['custom.example.com'],
302+
};
303+
mockFetch.mockResolvedValueOnce(mockResponse(responseData));
304+
305+
const createData: CreateServiceRequest = {
306+
name: 'custom-compose-service',
307+
project_uuid: 'project-uuid',
308+
environment_uuid: 'env-uuid',
309+
server_uuid: 'server-uuid',
310+
docker_compose_raw: 'dmVyc2lvbjogIjMiCnNlcnZpY2VzOgogIGFwcDoKICAgIGltYWdlOiBuZ2lueA==',
311+
};
312+
313+
const result = await client.createService(createData);
314+
315+
expect(result).toEqual(responseData);
316+
expect(mockFetch).toHaveBeenCalledWith(
317+
'http://localhost:3000/api/v1/services',
318+
expect.objectContaining({
319+
method: 'POST',
320+
body: JSON.stringify(createData),
321+
}),
322+
);
323+
});
297324
});
298325

299326
describe('deleteService', () => {
@@ -326,6 +353,24 @@ describe('CoolifyClient', () => {
326353
}),
327354
);
328355
});
356+
357+
it('should delete a service with all options', async () => {
358+
mockFetch.mockResolvedValueOnce(mockResponse({ message: 'Service deleted' }));
359+
360+
await client.deleteService('test-uuid', {
361+
deleteConfigurations: true,
362+
deleteVolumes: true,
363+
dockerCleanup: true,
364+
deleteConnectedNetworks: true,
365+
});
366+
367+
expect(mockFetch).toHaveBeenCalledWith(
368+
'http://localhost:3000/api/v1/services/test-uuid?delete_configurations=true&delete_volumes=true&docker_cleanup=true&delete_connected_networks=true',
369+
expect.objectContaining({
370+
method: 'DELETE',
371+
}),
372+
);
373+
});
329374
});
330375

331376
describe('applications', () => {

src/lib/mcp-server.ts

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -425,7 +425,7 @@ export class CoolifyMcpServer extends McpServer {
425425
);
426426

427427
// =========================================================================
428-
// Services (9 tools)
428+
// Services (11 tools)
429429
// =========================================================================
430430
this.tool(
431431
'list_services',
@@ -449,6 +449,73 @@ export class CoolifyMcpServer extends McpServer {
449449
async ({ uuid }) => wrapHandler(() => this.client.getService(uuid)),
450450
);
451451

452+
this.tool(
453+
'create_service',
454+
'Create a one-click service (e.g., pocketbase, mysql, redis, wordpress, etc.). Use type OR docker_compose_raw, not both.',
455+
{
456+
type: z
457+
.string()
458+
.optional()
459+
.describe(
460+
'Service type (e.g., pocketbase, mysql, redis, postgresql, mongodb, wordpress, etc.)',
461+
),
462+
server_uuid: z.string().describe('Server UUID'),
463+
project_uuid: z.string().describe('Project UUID'),
464+
environment_name: z.string().optional().describe('Environment name (e.g., production)'),
465+
environment_uuid: z
466+
.string()
467+
.optional()
468+
.describe('Environment UUID (alternative to environment_name)'),
469+
name: z.string().optional().describe('Service name'),
470+
description: z.string().optional().describe('Service description'),
471+
destination_uuid: z.string().optional().describe('Destination UUID'),
472+
instant_deploy: z.boolean().optional().describe('Deploy immediately after creation'),
473+
docker_compose_raw: z
474+
.string()
475+
.optional()
476+
.describe(
477+
'Base64 encoded docker-compose YAML with SERVICE_FQDN_* env var for custom domain (alternative to type)',
478+
),
479+
},
480+
async (args) => wrapHandler(() => this.client.createService(args)),
481+
);
482+
483+
this.tool(
484+
'delete_service',
485+
'Delete a service',
486+
{
487+
uuid: z.string().describe('Service UUID'),
488+
delete_configurations: z
489+
.boolean()
490+
.optional()
491+
.describe('Delete configurations (default: true)'),
492+
delete_volumes: z.boolean().optional().describe('Delete volumes (default: true)'),
493+
docker_cleanup: z
494+
.boolean()
495+
.optional()
496+
.describe('Clean up Docker resources (default: true)'),
497+
delete_connected_networks: z
498+
.boolean()
499+
.optional()
500+
.describe('Delete connected networks (default: true)'),
501+
},
502+
async ({
503+
uuid,
504+
delete_configurations,
505+
delete_volumes,
506+
docker_cleanup,
507+
delete_connected_networks,
508+
}) =>
509+
wrapHandler(() =>
510+
this.client.deleteService(uuid, {
511+
deleteConfigurations: delete_configurations,
512+
deleteVolumes: delete_volumes,
513+
dockerCleanup: docker_cleanup,
514+
deleteConnectedNetworks: delete_connected_networks,
515+
}),
516+
),
517+
);
518+
452519
this.tool(
453520
'update_service',
454521
'Update a service (IMPORTANT: See UpdateServiceRequest type docs for Traefik basic auth requirements)',

src/types/coolify.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -601,7 +601,7 @@ export interface Service {
601601
}
602602

603603
export interface CreateServiceRequest {
604-
type: ServiceType;
604+
type?: ServiceType;
605605
name?: string;
606606
description?: string;
607607
project_uuid: string;
@@ -610,6 +610,7 @@ export interface CreateServiceRequest {
610610
server_uuid: string;
611611
destination_uuid?: string;
612612
instant_deploy?: boolean;
613+
docker_compose_raw?: string; // Base64 encoded docker-compose YAML (alternative to type)
613614
}
614615

615616
/**

0 commit comments

Comments
 (0)