Coolify MCP Server

by StuMason
Verified
This file is a merged representation of the entire codebase, combining all repository files into a single document. Generated by Repomix on: 2025-03-08T15:17:09.667Z <file_summary> This section contains a summary of this file. <purpose> This file contains a packed representation of the entire repository's contents. It is designed to be easily consumable by AI systems for analysis, code review, or other automated processes. </purpose> <file_format> The content is organized as follows: 1. This summary section 2. Repository information 3. Repository structure 4. Repository files, each consisting of: - File path as an attribute - Full contents of the file </file_format> <usage_guidelines> - This file should be treated as read-only. Any changes should be made to the original repository files, not this packed version. - When processing this file, use the file path to distinguish between different files in the repository. - Be aware that this file may contain sensitive information. Handle it with the same level of security as you would the original repository. </usage_guidelines> <notes> - Some files may have been excluded based on .gitignore rules and Repomix's configuration. - Binary files are not included in this packed representation. Please refer to the Repository Structure section for a complete list of file paths, including binary files. </notes> <additional_info> For more information about Repomix, visit: https://github.com/yamadashy/repomix </additional_info> </file_summary> <repository_structure> src/ __tests__/ resources/ application-resources.test.ts database-resources.test.ts deployment-resources.test.ts service-resources.test.ts coolify-client.test.ts lib/ coolify-client.ts mcp-server.ts resource.ts resources/ application-resources.ts database-resources.ts deployment-resources.ts index.ts service-resources.ts types/ coolify.ts index.ts .eslintrc.json .gitignore .lintstagedrc.json .markdownlint-cli2.jsonc .prettierrc .repomixignore debug.js jest.config.js package.json README.md tsconfig.json </repository_structure> <repository_files> This section contains the contents of the repository's files. <file path="src/__tests__/resources/application-resources.test.ts"> import { ApplicationResources } from '../../resources/application-resources.js'; import { CoolifyClient } from '../../lib/coolify-client.js'; import { jest } from '@jest/globals'; jest.mock('../../lib/coolify-client.js'); describe('ApplicationResources', () => { let resources: ApplicationResources; let mockClient: jest.Mocked<CoolifyClient>; beforeEach(() => { mockClient = { deployApplication: jest.fn(), } as unknown as jest.Mocked<CoolifyClient>; resources = new ApplicationResources(mockClient); }); describe('listApplications', () => { it('should throw not implemented error', async () => { await expect(resources.listApplications()).rejects.toThrow('Not implemented'); }); }); describe('getApplication', () => { it('should throw not implemented error', async () => { await expect(resources.getApplication('test-id')).rejects.toThrow('Not implemented'); }); }); describe('createApplication', () => { it('should throw not implemented error', async () => { await expect(resources.createApplication({ name: 'test-app' })).rejects.toThrow( 'Not implemented', ); }); }); describe('deleteApplication', () => { it('should throw not implemented error', async () => { await expect(resources.deleteApplication('test-id')).rejects.toThrow('Not implemented'); }); }); }); </file> <file path="src/__tests__/resources/database-resources.test.ts"> import { DatabaseResources } from '../../resources/database-resources.js'; import { CoolifyClient } from '../../lib/coolify-client.js'; import { PostgresDatabase } from '../../types/coolify.js'; import { jest } from '@jest/globals'; jest.mock('../../lib/coolify-client.js'); describe('DatabaseResources', () => { let mockClient: jest.Mocked<CoolifyClient>; let resources: DatabaseResources; const mockDatabase: PostgresDatabase = { id: 1, uuid: 'test-uuid', name: 'test-db', description: 'test description', type: 'postgresql', status: 'running', created_at: '2024-01-01', updated_at: '2024-01-01', is_public: false, image: 'postgres:latest', postgres_user: 'test', postgres_password: 'test', postgres_db: 'test', }; beforeEach(() => { mockClient = { listDatabases: jest.fn(), getDatabase: jest.fn(), updateDatabase: jest.fn(), deleteDatabase: jest.fn(), } as unknown as jest.Mocked<CoolifyClient>; resources = new DatabaseResources(mockClient); }); describe('listDatabases', () => { it('should return a list of databases', async () => { mockClient.listDatabases.mockResolvedValue([mockDatabase]); const result = await resources.listDatabases(); expect(result).toEqual([mockDatabase]); expect(mockClient.listDatabases).toHaveBeenCalled(); }); }); describe('getDatabase', () => { it('should return a database by uuid', async () => { mockClient.getDatabase.mockResolvedValue(mockDatabase); const result = await resources.getDatabase('test-uuid'); expect(result).toEqual(mockDatabase); expect(mockClient.getDatabase).toHaveBeenCalledWith('test-uuid'); }); }); describe('updateDatabase', () => { it('should update a database', async () => { const updateData = { name: 'updated-db', description: 'updated description', }; mockClient.updateDatabase.mockResolvedValue({ ...mockDatabase, ...updateData }); const result = await resources.updateDatabase('test-uuid', updateData); expect(result).toEqual({ ...mockDatabase, ...updateData }); expect(mockClient.updateDatabase).toHaveBeenCalledWith('test-uuid', updateData); }); }); describe('deleteDatabase', () => { it('should delete a database', async () => { const mockResponse = { message: 'Database deleted successfully' }; mockClient.deleteDatabase.mockResolvedValue(mockResponse); const result = await resources.deleteDatabase('test-uuid', {}); expect(result).toEqual(mockResponse); expect(mockClient.deleteDatabase).toHaveBeenCalledWith('test-uuid', {}); }); }); }); </file> <file path="src/__tests__/resources/deployment-resources.test.ts"> import { DeploymentResources } from '../../resources/deployment-resources.js'; import { CoolifyClient } from '../../lib/coolify-client.js'; import { jest } from '@jest/globals'; jest.mock('../../lib/coolify-client.js'); describe('DeploymentResources', () => { let mockClient: jest.Mocked<CoolifyClient>; let resources: DeploymentResources; const mockDeployment = { id: 1, uuid: 'test-uuid', status: 'running', created_at: '2024-01-01', updated_at: '2024-01-01', application_uuid: 'app-uuid', environment_uuid: 'env-uuid', }; beforeEach(() => { mockClient = { deployApplication: jest.fn(), } as unknown as jest.Mocked<CoolifyClient>; resources = new DeploymentResources(mockClient); }); describe('listDeployments', () => { it('should throw not implemented error', async () => { await expect(resources.listDeployments()).rejects.toThrow('Not implemented'); }); }); describe('getDeployment', () => { it('should throw not implemented error', async () => { await expect(resources.getDeployment('test-id')).rejects.toThrow('Not implemented'); }); }); describe('deploy', () => { it('should deploy an application', async () => { mockClient.deployApplication.mockResolvedValue(mockDeployment); const result = await resources.deploy({ uuid: 'test-uuid' }); expect(result).toEqual(mockDeployment); expect(mockClient.deployApplication).toHaveBeenCalledWith('test-uuid'); }); it('should handle deployment errors', async () => { const error = new Error('Deployment failed'); mockClient.deployApplication.mockRejectedValue(error); await expect(resources.deploy({ uuid: 'test-uuid' })).rejects.toThrow('Deployment failed'); expect(mockClient.deployApplication).toHaveBeenCalledWith('test-uuid'); }); }); }); </file> <file path="src/__tests__/resources/service-resources.test.ts"> import { ServiceResources } from '../../resources/service-resources.js'; import { CoolifyClient } from '../../lib/coolify-client.js'; import { Service, ServiceType } from '../../types/coolify.js'; import { jest } from '@jest/globals'; jest.mock('../../lib/coolify-client.js'); describe('ServiceResources', () => { let mockClient: jest.Mocked<CoolifyClient>; let resources: ServiceResources; const mockService: Service = { id: 1, uuid: 'test-uuid', name: 'test-service', description: 'test description', type: 'code-server', status: 'running', created_at: '2024-01-01', updated_at: '2024-01-01', project_uuid: 'project-uuid', environment_name: 'test-env', environment_uuid: 'env-uuid', server_uuid: 'server-uuid', domains: ['test.com'], }; beforeEach(() => { mockClient = { listServices: jest.fn(), getService: jest.fn(), createService: jest.fn(), deleteService: jest.fn(), } as unknown as jest.Mocked<CoolifyClient>; resources = new ServiceResources(mockClient); }); describe('listServices', () => { it('should return a list of services', async () => { mockClient.listServices.mockResolvedValue([mockService]); const result = await resources.listServices(); expect(result).toEqual([mockService]); expect(mockClient.listServices).toHaveBeenCalled(); }); }); describe('getService', () => { it('should return a service by uuid', async () => { mockClient.getService.mockResolvedValue(mockService); const result = await resources.getService('test-uuid'); expect(result).toEqual(mockService); expect(mockClient.getService).toHaveBeenCalledWith('test-uuid'); }); }); describe('createService', () => { it('should create a new service', async () => { const createData = { name: 'new-service', type: 'code-server' as ServiceType, project_uuid: 'project-uuid', environment_name: 'test-env', environment_uuid: 'env-uuid', server_uuid: 'server-uuid', }; const mockResponse = { uuid: 'new-uuid', domains: ['new-service.test.com'], }; mockClient.createService.mockResolvedValue(mockResponse); const result = await resources.createService(createData); expect(result).toEqual(mockResponse); expect(mockClient.createService).toHaveBeenCalledWith(createData); }); }); describe('deleteService', () => { it('should delete a service', async () => { const mockResponse = { message: 'Service deleted' }; mockClient.deleteService.mockResolvedValue(mockResponse); const result = await resources.deleteService('test-uuid'); expect(result).toEqual(mockResponse); expect(mockClient.deleteService).toHaveBeenCalledWith('test-uuid', undefined); }); }); }); </file> <file path="src/__tests__/coolify-client.test.ts"> import { jest } from '@jest/globals'; import { CoolifyClient } from '../lib/coolify-client.js'; import { ServiceType, CreateServiceRequest } from '../types/coolify.js'; const mockFetch = jest.fn() as any; describe('CoolifyClient', () => { let client: CoolifyClient; const mockServers = [ { id: 1, uuid: 'test-uuid', name: 'test-server', status: 'running', }, ]; const mockServerInfo = { id: 1, uuid: 'test-uuid', name: 'test-server', status: 'running', }; const mockServerResources = { resources: [ { name: 'memory', value: '2GB', }, { name: 'disk', value: '20GB', }, ], }; const mockService = { id: 1, uuid: 'test-uuid', name: 'test-service', type: 'code-server' as ServiceType, status: 'running', created_at: '2024-01-01', updated_at: '2024-01-01', }; const errorResponse = { message: 'Resource not found', }; beforeEach(() => { mockFetch.mockClear(); (global as any).fetch = mockFetch; client = new CoolifyClient({ baseUrl: 'http://localhost:3000', accessToken: 'test-api-key', }); }); describe('listServers', () => { it('should return a list of servers', async () => { mockFetch.mockImplementationOnce( async () => ({ ok: true, json: async () => mockServers, }) as Response, ); const servers = await client.listServers(); expect(servers).toEqual(mockServers); expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/servers', { headers: { 'Content-Type': 'application/json', Authorization: 'Bearer test-api-key', }, }); }); it('should handle errors', async () => { mockFetch.mockImplementationOnce(() => Promise.resolve({ ok: false, json: async () => errorResponse, } as Response), ); await expect(client.listServers()).rejects.toThrow('Resource not found'); }); }); describe('getServer', () => { it('should get server info', async () => { mockFetch.mockImplementationOnce(() => Promise.resolve({ ok: true, json: async () => mockServerInfo, } as Response), ); const result = await client.getServer('test-uuid'); expect(result).toEqual(mockServerInfo); expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/servers/test-uuid', { headers: { 'Content-Type': 'application/json', Authorization: 'Bearer test-api-key', }, }); }); it('should handle errors', async () => { mockFetch.mockImplementationOnce(() => Promise.resolve({ ok: false, json: async () => errorResponse, } as Response), ); await expect(client.getServer('test-uuid')).rejects.toThrow('Resource not found'); }); }); describe('getServerResources', () => { it('should get server resources', async () => { mockFetch.mockImplementationOnce(() => Promise.resolve({ ok: true, json: async () => mockServerResources, } as Response), ); const result = await client.getServerResources('test-uuid'); expect(result).toEqual(mockServerResources); expect(mockFetch).toHaveBeenCalledWith( 'http://localhost:3000/api/v1/servers/test-uuid/resources', { headers: { 'Content-Type': 'application/json', Authorization: 'Bearer test-api-key', }, }, ); }); it('should handle errors', async () => { mockFetch.mockImplementationOnce(() => Promise.resolve({ ok: false, json: async () => errorResponse, } as Response), ); await expect(client.getServerResources('test-uuid')).rejects.toThrow('Resource not found'); }); }); describe('listServices', () => { it('should list services', async () => { mockFetch.mockImplementationOnce(() => Promise.resolve({ ok: true, json: () => Promise.resolve([mockService]), } as Response), ); const result = await client.listServices(); expect(result).toEqual([mockService]); expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/services', { headers: { 'Content-Type': 'application/json', Authorization: 'Bearer test-api-key', }, }); }); }); describe('getService', () => { it('should get service info', async () => { mockFetch.mockImplementationOnce(() => Promise.resolve({ ok: true, json: () => Promise.resolve(mockService), } as Response), ); const result = await client.getService('test-uuid'); expect(result).toEqual(mockService); expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/services/test-uuid', { headers: { 'Content-Type': 'application/json', Authorization: 'Bearer test-api-key', }, }); }); }); describe('createService', () => { it('should create a service', async () => { mockFetch.mockImplementationOnce(() => Promise.resolve({ ok: true, json: () => Promise.resolve({ uuid: 'test-uuid', domains: ['test.com'], }), } as Response), ); const createData: CreateServiceRequest = { name: 'test-service', type: 'code-server', project_uuid: 'project-uuid', environment_uuid: 'env-uuid', server_uuid: 'server-uuid', }; const result = await client.createService(createData); expect(result).toEqual({ uuid: 'test-uuid', domains: ['test.com'], }); expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/services', { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: 'Bearer test-api-key', }, body: JSON.stringify(createData), }); }); }); describe('deleteService', () => { it('should delete a service', async () => { mockFetch.mockImplementationOnce(() => Promise.resolve({ ok: true, json: () => Promise.resolve({ message: 'Service deleted' }), } as Response), ); const result = await client.deleteService('test-uuid'); expect(result).toEqual({ message: 'Service deleted' }); expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/services/test-uuid', { method: 'DELETE', headers: { 'Content-Type': 'application/json', Authorization: 'Bearer test-api-key', }, }); }); }); describe('error handling', () => { it('should handle network errors', async () => { const errorMessage = 'Network error'; mockFetch.mockImplementationOnce(() => Promise.reject(new Error(errorMessage))); await expect(client.listServers()).rejects.toThrow(errorMessage); }); }); }); </file> <file path="src/lib/coolify-client.ts"> import { CoolifyConfig, ErrorResponse, ServerInfo, ServerResources, ServerDomain, ValidationResponse, Project, CreateProjectRequest, UpdateProjectRequest, Environment, Deployment, Database, DatabaseUpdateRequest, Service, CreateServiceRequest, DeleteServiceOptions, } from '../types/coolify.js'; export class CoolifyClient { private baseUrl: string; private accessToken: string; constructor(config: CoolifyConfig) { if (!config.baseUrl) { throw new Error('Coolify base URL is required'); } if (!config.accessToken) { throw new Error('Coolify access token is required'); } this.baseUrl = config.baseUrl.replace(/\/$/, ''); this.accessToken = config.accessToken; } private async request<T>(path: string, options: RequestInit = {}): Promise<T> { try { const url = `${this.baseUrl}/api/v1${path}`; const response = await fetch(url, { headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${this.accessToken}`, }, ...options, }); const data = await response.json(); if (!response.ok) { const error = data as ErrorResponse; throw new Error(error.message || `HTTP ${response.status}: ${response.statusText}`); } return data as T; } catch (error) { if (error instanceof TypeError && error.message.includes('fetch')) { throw new Error( `Failed to connect to Coolify server at ${this.baseUrl}. Please check if the server is running and the URL is correct.`, ); } throw error; } } async listServers(): Promise<ServerInfo[]> { return this.request<ServerInfo[]>('/servers'); } async getServer(uuid: string): Promise<ServerInfo> { return this.request<ServerInfo>(`/servers/${uuid}`); } async getServerResources(uuid: string): Promise<ServerResources> { return this.request<ServerResources>(`/servers/${uuid}/resources`); } async getServerDomains(uuid: string): Promise<ServerDomain[]> { return this.request<ServerDomain[]>(`/servers/${uuid}/domains`); } async validateServer(uuid: string): Promise<ValidationResponse> { return this.request<ValidationResponse>(`/servers/${uuid}/validate`); } async validateConnection(): Promise<void> { try { await this.listServers(); } catch (error) { throw new Error( `Failed to connect to Coolify server: ${error instanceof Error ? error.message : 'Unknown error'}`, ); } } async listProjects(): Promise<Project[]> { return this.request<Project[]>('/projects'); } async getProject(uuid: string): Promise<Project> { return this.request<Project>(`/projects/${uuid}`); } async createProject(project: CreateProjectRequest): Promise<{ uuid: string }> { return this.request<{ uuid: string }>('/projects', { method: 'POST', body: JSON.stringify(project), }); } async updateProject(uuid: string, project: UpdateProjectRequest): Promise<Project> { return this.request<Project>(`/projects/${uuid}`, { method: 'PATCH', body: JSON.stringify(project), }); } async deleteProject(uuid: string): Promise<{ message: string }> { return this.request<{ message: string }>(`/projects/${uuid}`, { method: 'DELETE', }); } async getProjectEnvironment( projectUuid: string, environmentNameOrUuid: string, ): Promise<Environment> { return this.request<Environment>(`/projects/${projectUuid}/${environmentNameOrUuid}`); } async deployApplication(uuid: string): Promise<Deployment> { const response = await this.request<Deployment>(`/applications/${uuid}/deploy`, { method: 'POST', }); return response; } async listDatabases(): Promise<Database[]> { return this.request<Database[]>('/databases'); } async getDatabase(uuid: string): Promise<Database> { return this.request<Database>(`/databases/${uuid}`); } async updateDatabase(uuid: string, data: DatabaseUpdateRequest): Promise<Database> { return this.request<Database>(`/databases/${uuid}`, { method: 'PATCH', body: JSON.stringify(data), }); } async deleteDatabase( uuid: string, options?: { deleteConfigurations?: boolean; deleteVolumes?: boolean; dockerCleanup?: boolean; deleteConnectedNetworks?: boolean; }, ): Promise<{ message: string }> { const queryParams = new URLSearchParams(); if (options) { if (options.deleteConfigurations !== undefined) { queryParams.set('delete_configurations', options.deleteConfigurations.toString()); } if (options.deleteVolumes !== undefined) { queryParams.set('delete_volumes', options.deleteVolumes.toString()); } if (options.dockerCleanup !== undefined) { queryParams.set('docker_cleanup', options.dockerCleanup.toString()); } if (options.deleteConnectedNetworks !== undefined) { queryParams.set('delete_connected_networks', options.deleteConnectedNetworks.toString()); } } const queryString = queryParams.toString(); const url = queryString ? `/databases/${uuid}?${queryString}` : `/databases/${uuid}`; return this.request<{ message: string }>(url, { method: 'DELETE', }); } async listServices(): Promise<Service[]> { return this.request<Service[]>('/services'); } async getService(uuid: string): Promise<Service> { return this.request<Service>(`/services/${uuid}`); } async createService(data: CreateServiceRequest): Promise<{ uuid: string; domains: string[] }> { return this.request<{ uuid: string; domains: string[] }>('/services', { method: 'POST', body: JSON.stringify(data), }); } async deleteService(uuid: string, options?: DeleteServiceOptions): Promise<{ message: string }> { const queryParams = new URLSearchParams(); if (options) { if (options.deleteConfigurations !== undefined) { queryParams.set('delete_configurations', options.deleteConfigurations.toString()); } if (options.deleteVolumes !== undefined) { queryParams.set('delete_volumes', options.deleteVolumes.toString()); } if (options.dockerCleanup !== undefined) { queryParams.set('docker_cleanup', options.dockerCleanup.toString()); } if (options.deleteConnectedNetworks !== undefined) { queryParams.set('delete_connected_networks', options.deleteConnectedNetworks.toString()); } } const queryString = queryParams.toString(); const url = queryString ? `/services/${uuid}?${queryString}` : `/services/${uuid}`; return this.request<{ message: string }>(url, { method: 'DELETE', }); } // Add more methods as needed for other endpoints } </file> <file path="src/lib/mcp-server.ts"> import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { Transport } from '@modelcontextprotocol/sdk/shared/transport.js'; import { CoolifyClient } from './coolify-client.js'; import debug from 'debug'; import { z } from 'zod'; import type { ServerInfo, ServerResources, ServerDomain, ValidationResponse, Project, CreateProjectRequest, UpdateProjectRequest, Environment, Deployment, Database, DatabaseUpdateRequest, Service, CreateServiceRequest, DeleteServiceOptions, } from '../types/coolify.js'; const log = debug('coolify:mcp'); // Define valid service types const serviceTypes = [ 'activepieces', 'appsmith', 'appwrite', 'authentik', 'babybuddy', 'budge', 'changedetection', 'chatwoot', 'classicpress-with-mariadb', 'classicpress-with-mysql', 'classicpress-without-database', 'cloudflared', 'code-server', 'dashboard', 'directus', 'directus-with-postgresql', 'docker-registry', 'docuseal', 'docuseal-with-postgres', 'dokuwiki', 'duplicati', 'emby', 'embystat', 'fider', 'filebrowser', 'firefly', 'formbricks', 'ghost', 'gitea', 'gitea-with-mariadb', 'gitea-with-mysql', 'gitea-with-postgresql', 'glance', 'glances', 'glitchtip', 'grafana', 'grafana-with-postgresql', 'grocy', 'heimdall', 'homepage', 'jellyfin', 'kuzzle', 'listmonk', 'logto', 'mediawiki', 'meilisearch', 'metabase', 'metube', 'minio', 'moodle', 'n8n', 'n8n-with-postgresql', 'next-image-transformation', 'nextcloud', 'nocodb', 'odoo', 'openblocks', 'pairdrop', 'penpot', 'phpmyadmin', 'pocketbase', 'posthog', 'reactive-resume', 'rocketchat', 'shlink', 'slash', 'snapdrop', 'statusnook', 'stirling-pdf', 'supabase', 'syncthing', 'tolgee', 'trigger', 'trigger-with-external-database', 'twenty', 'umami', 'unleash-with-postgresql', 'unleash-without-database', 'uptime-kuma', 'vaultwarden', 'vikunja', 'weblate', 'whoogle', 'wordpress-with-mariadb', 'wordpress-with-mysql', 'wordpress-without-database' ] as const; export class CoolifyMcpServer extends McpServer { private client: CoolifyClient; constructor(config: { baseUrl: string; accessToken: string }) { super({ name: 'coolify', version: '0.1.18', capabilities: { tools: true } }); log('Initializing server with config: %o', config); this.client = new CoolifyClient(config); this.setupTools(); } private setupTools(): void { log('Setting up tools'); this.tool('list_servers', 'List all Coolify servers', {}, async (_args, _extra) => { const servers = await this.client.listServers(); return { content: [{ type: 'text', text: JSON.stringify(servers, null, 2) }] }; }); this.tool('get_server', 'Get details about a specific Coolify server', { uuid: z.string() }, async (args, _extra) => { const server = await this.client.getServer(args.uuid); return { content: [{ type: 'text', text: JSON.stringify(server, null, 2) }] }; }); this.tool('get_server_resources', 'Get the current resources running on a specific Coolify server', { uuid: z.string() }, async (args, _extra) => { const resources = await this.client.getServerResources(args.uuid); return { content: [{ type: 'text', text: JSON.stringify(resources, null, 2) }] }; }); this.tool('get_server_domains', 'Get domains for a specific Coolify server', { uuid: z.string() }, async (args, _extra) => { const domains = await this.client.getServerDomains(args.uuid); return { content: [{ type: 'text', text: JSON.stringify(domains, null, 2) }] }; }); this.tool('validate_server', 'Validate a specific Coolify server', { uuid: z.string() }, async (args, _extra) => { const validation = await this.client.validateServer(args.uuid); return { content: [{ type: 'text', text: JSON.stringify(validation, null, 2) }] }; }); this.tool('list_projects', 'List all Coolify projects', {}, async (_args, _extra) => { const projects = await this.client.listProjects(); return { content: [{ type: 'text', text: JSON.stringify(projects, null, 2) }] }; }); this.tool('get_project', 'Get details about a specific Coolify project', { uuid: z.string() }, async (args, _extra) => { const project = await this.client.getProject(args.uuid); return { content: [{ type: 'text', text: JSON.stringify(project, null, 2) }] }; }); this.tool('create_project', 'Create a new Coolify project', { name: z.string(), description: z.string().optional() }, async (args, _extra) => { const result = await this.client.createProject({ name: args.name, description: args.description }); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; }); this.tool('update_project', 'Update an existing Coolify project', { uuid: z.string(), name: z.string(), description: z.string().optional() }, async (args, _extra) => { const { uuid, ...updateData } = args; const result = await this.client.updateProject(uuid, updateData); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; }); this.tool('delete_project', 'Delete a Coolify project', { uuid: z.string() }, async (args, _extra) => { const result = await this.client.deleteProject(args.uuid); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; }); this.tool('get_project_environment', 'Get environment details for a Coolify project', { project_uuid: z.string(), environment_name_or_uuid: z.string() }, async (args, _extra) => { const environment = await this.client.getProjectEnvironment(args.project_uuid, args.environment_name_or_uuid); return { content: [{ type: 'text', text: JSON.stringify(environment, null, 2) }] }; }); this.tool('list_databases', 'List all Coolify databases', {}, async (_args, _extra) => { const databases = await this.client.listDatabases(); return { content: [{ type: 'text', text: JSON.stringify(databases, null, 2) }] }; }); this.tool('get_database', 'Get details about a specific Coolify database', { uuid: z.string() }, async (args, _extra) => { const database = await this.client.getDatabase(args.uuid); return { content: [{ type: 'text', text: JSON.stringify(database, null, 2) }] }; }); this.tool('update_database', 'Update a Coolify database', { uuid: z.string(), data: z.record(z.unknown()) }, async (args, _extra) => { const result = await this.client.updateDatabase(args.uuid, args.data); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; }); const deleteOptionsSchema = { deleteConfigurations: z.boolean().optional(), deleteVolumes: z.boolean().optional(), dockerCleanup: z.boolean().optional(), deleteConnectedNetworks: z.boolean().optional() }; this.tool('delete_database', 'Delete a Coolify database', { uuid: z.string(), options: z.object(deleteOptionsSchema).optional() }, async (args, _extra) => { const result = await this.client.deleteDatabase(args.uuid, args.options); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; }); this.tool('deploy_application', 'Deploy a Coolify application', { uuid: z.string() }, async (args, _extra) => { const result = await this.client.deployApplication(args.uuid); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; }); this.tool('list_services', 'List all Coolify services', {}, async (_args, _extra) => { const services = await this.client.listServices(); return { content: [{ type: 'text', text: JSON.stringify(services, null, 2) }] }; }); this.tool('get_service', 'Get details about a specific Coolify service', { uuid: z.string() }, async (args, _extra) => { const service = await this.client.getService(args.uuid); return { content: [{ type: 'text', text: JSON.stringify(service, null, 2) }] }; }); this.tool('create_service', 'Create a new Coolify service', { type: z.enum(serviceTypes), project_uuid: z.string(), server_uuid: z.string(), name: z.string().optional(), description: z.string().optional(), environment_name: z.string().optional(), environment_uuid: z.string().optional(), destination_uuid: z.string().optional(), instant_deploy: z.boolean().optional() }, async (args, _extra) => { const result = await this.client.createService(args); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; }); this.tool('delete_service', 'Delete a Coolify service', { uuid: z.string(), options: z.object(deleteOptionsSchema).optional() }, async (args, _extra) => { const result = await this.client.deleteService(args.uuid, args.options); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; }); } async connect(transport: Transport): Promise<void> { log('Starting server...'); log('Validating connection...'); await this.client.validateConnection(); await super.connect(transport); log('Server started successfully'); } async list_servers(): Promise<ServerInfo[]> { return this.client.listServers(); } async get_server(uuid: string): Promise<ServerInfo> { return this.client.getServer(uuid); } async get_server_resources(uuid: string): Promise<ServerResources> { return this.client.getServerResources(uuid); } async get_server_domains(uuid: string): Promise<ServerDomain[]> { return this.client.getServerDomains(uuid); } async validate_server(uuid: string): Promise<ValidationResponse> { return this.client.validateServer(uuid); } async list_projects(): Promise<Project[]> { return this.client.listProjects(); } async get_project(uuid: string): Promise<Project> { return this.client.getProject(uuid); } async create_project(project: CreateProjectRequest): Promise<{ uuid: string }> { return this.client.createProject(project); } async update_project(uuid: string, project: UpdateProjectRequest): Promise<Project> { return this.client.updateProject(uuid, project); } async delete_project(uuid: string): Promise<{ message: string }> { return this.client.deleteProject(uuid); } async get_project_environment( projectUuid: string, environmentNameOrUuid: string, ): Promise<Environment> { return this.client.getProjectEnvironment(projectUuid, environmentNameOrUuid); } async deploy_application(params: { uuid: string }): Promise<Deployment> { return this.client.deployApplication(params.uuid); } async list_databases(): Promise<Database[]> { return this.client.listDatabases(); } async get_database(uuid: string): Promise<Database> { return this.client.getDatabase(uuid); } async update_database(uuid: string, data: DatabaseUpdateRequest): Promise<Database> { return this.client.updateDatabase(uuid, data); } async delete_database( uuid: string, options?: { deleteConfigurations?: boolean; deleteVolumes?: boolean; dockerCleanup?: boolean; deleteConnectedNetworks?: boolean; }, ): Promise<{ message: string }> { return this.client.deleteDatabase(uuid, options); } async list_services(): Promise<Service[]> { return this.client.listServices(); } async get_service(uuid: string): Promise<Service> { return this.client.getService(uuid); } async create_service(data: CreateServiceRequest): Promise<{ uuid: string; domains: string[] }> { return this.client.createService(data); } async delete_service(uuid: string, options?: DeleteServiceOptions): Promise<{ message: string }> { return this.client.deleteService(uuid, options); } } </file> <file path="src/lib/resource.ts"> import 'reflect-metadata'; /** * Metadata key for storing the resource URI */ const RESOURCE_URI_KEY = Symbol('resourceUri'); /** * Decorator for marking methods as MCP resources. * @param uri The URI pattern for the resource */ export function Resource(uri: string): MethodDecorator { return function ( target: object, propertyKey: string | symbol, descriptor: PropertyDescriptor, ): PropertyDescriptor { // Store the URI pattern in the method's metadata Reflect.defineMetadata(RESOURCE_URI_KEY, uri, target, propertyKey); return descriptor; }; } /** * Get the resource URI for a decorated method * @param target The class instance or constructor * @param propertyKey The method name * @returns The resource URI or undefined if not a resource */ export function getResourceUri(target: object, propertyKey: string | symbol): string | undefined { return Reflect.getMetadata(RESOURCE_URI_KEY, target, propertyKey); } </file> <file path="src/resources/application-resources.ts"> import { Resource } from '../lib/resource.js'; import { CoolifyClient } from '../lib/coolify-client.js'; import { Application, CreateApplicationRequest } from '../types/coolify.js'; export class ApplicationResources { private client: CoolifyClient; constructor(client: CoolifyClient) { this.client = client; } @Resource('coolify/applications/list') async listApplications(): Promise<Application[]> { // TODO: Implement listApplications in CoolifyClient throw new Error('Not implemented'); } @Resource('coolify/applications/{id}') async getApplication(_id: string): Promise<Application> { // TODO: Implement getApplication in CoolifyClient throw new Error('Not implemented'); } @Resource('coolify/applications/create') async createApplication(_data: CreateApplicationRequest): Promise<{ uuid: string }> { // TODO: Implement createApplication in CoolifyClient throw new Error('Not implemented'); } @Resource('coolify/applications/{id}/delete') async deleteApplication(_id: string): Promise<{ message: string }> { // TODO: Implement deleteApplication in CoolifyClient throw new Error('Not implemented'); } } </file> <file path="src/resources/database-resources.ts"> import { Resource } from '../lib/resource.js'; import { CoolifyClient } from '../lib/coolify-client.js'; import { Database, DatabaseUpdateRequest } from '../types/coolify.js'; export class DatabaseResources { private client: CoolifyClient; constructor(client: CoolifyClient) { this.client = client; } @Resource('coolify/databases/list') async listDatabases(): Promise<Database[]> { return this.client.listDatabases(); } @Resource('coolify/databases/{id}') async getDatabase(id: string): Promise<Database> { return this.client.getDatabase(id); } @Resource('coolify/databases/{id}/update') async updateDatabase(id: string, data: DatabaseUpdateRequest): Promise<Database> { return this.client.updateDatabase(id, data); } @Resource('coolify/databases/{id}/delete') async deleteDatabase( id: string, options?: { deleteConfigurations?: boolean; deleteVolumes?: boolean; dockerCleanup?: boolean; deleteConnectedNetworks?: boolean; }, ): Promise<{ message: string }> { return this.client.deleteDatabase(id, options); } } </file> <file path="src/resources/deployment-resources.ts"> import { Resource } from '../lib/resource.js'; import { CoolifyClient } from '../lib/coolify-client.js'; import { Deployment } from '../types/coolify.js'; export class DeploymentResources { private client: CoolifyClient; constructor(client: CoolifyClient) { this.client = client; } @Resource('coolify/deployments/list') async listDeployments(): Promise<Deployment[]> { // TODO: Implement listDeployments in CoolifyClient throw new Error('Not implemented'); } @Resource('coolify/deployments/{id}') async getDeployment(_id: string): Promise<Deployment> { // TODO: Implement getDeployment in CoolifyClient throw new Error('Not implemented'); } @Resource('coolify/deploy') async deploy(params: { uuid: string; forceRebuild?: boolean }): Promise<Deployment> { return this.client.deployApplication(params.uuid); } } </file> <file path="src/resources/index.ts"> export * from './database-resources.js'; export * from './deployment-resources.js'; export * from './application-resources.js'; export * from './service-resources.js'; </file> <file path="src/resources/service-resources.ts"> import { Resource } from '../lib/resource.js'; import { CoolifyClient } from '../lib/coolify-client.js'; import { Service, CreateServiceRequest, DeleteServiceOptions } from '../types/coolify.js'; export class ServiceResources { private client: CoolifyClient; constructor(client: CoolifyClient) { this.client = client; } @Resource('coolify/services/list') async listServices(): Promise<Service[]> { return this.client.listServices(); } @Resource('coolify/services/{id}') async getService(id: string): Promise<Service> { return this.client.getService(id); } @Resource('coolify/services/create') async createService(data: CreateServiceRequest): Promise<{ uuid: string; domains: string[] }> { return this.client.createService(data); } @Resource('coolify/services/{id}/delete') async deleteService(id: string, options?: DeleteServiceOptions): Promise<{ message: string }> { return this.client.deleteService(id, options); } } </file> <file path="src/types/coolify.ts"> export interface CoolifyConfig { baseUrl: string; accessToken: string; } export interface ServerInfo { uuid: string; name: string; status: 'running' | 'stopped' | 'error'; version: string; resources: { cpu: number; memory: number; disk: number; }; } export interface ResourceStatus { id: number; uuid: string; name: string; type: string; created_at: string; updated_at: string; status: string; } export type ServerResources = ResourceStatus[]; export interface ErrorResponse { error: string; status: number; message: string; } export interface ServerDomain { ip: string; domains: string[]; } export interface ValidationResponse { message: string; } export interface Environment { id: number; uuid: string; name: string; project_uuid: string; variables?: Record<string, string>; created_at: string; updated_at: string; } export interface Project { id: number; uuid: string; name: string; description?: string; environments?: Environment[]; } export interface CreateProjectRequest { name: string; description?: string; } export interface UpdateProjectRequest { name?: string; description?: string; } export interface LogEntry { timestamp: string; level: string; message: string; } export interface Deployment { id: number; uuid: string; application_uuid: string; status: string; created_at: string; updated_at: string; } export interface DatabaseBase { id: number; uuid: string; name: string; description?: string; type: | 'postgresql' | 'mysql' | 'mariadb' | 'mongodb' | 'redis' | 'keydb' | 'clickhouse' | 'dragonfly'; status: 'running' | 'stopped' | 'error'; created_at: string; updated_at: string; is_public: boolean; public_port?: number; image: string; limits?: { memory?: string; memory_swap?: string; memory_swappiness?: number; memory_reservation?: string; cpus?: string; cpuset?: string; cpu_shares?: number; }; } export interface PostgresDatabase extends DatabaseBase { type: 'postgresql'; postgres_user: string; postgres_password: string; postgres_db: string; postgres_initdb_args?: string; postgres_host_auth_method?: string; postgres_conf?: string; } export interface MySQLDatabase extends DatabaseBase { type: 'mysql'; mysql_root_password: string; mysql_user?: string; mysql_password?: string; mysql_database?: string; } export interface MariaDBDatabase extends DatabaseBase { type: 'mariadb'; mariadb_root_password: string; mariadb_user?: string; mariadb_password?: string; mariadb_database?: string; mariadb_conf?: string; } export interface MongoDBDatabase extends DatabaseBase { type: 'mongodb'; mongo_initdb_root_username: string; mongo_initdb_root_password: string; mongo_initdb_database?: string; mongo_conf?: string; } export interface RedisDatabase extends DatabaseBase { type: 'redis'; redis_password?: string; redis_conf?: string; } export interface KeyDBDatabase extends DatabaseBase { type: 'keydb'; keydb_password?: string; keydb_conf?: string; } export interface ClickhouseDatabase extends DatabaseBase { type: 'clickhouse'; clickhouse_admin_user: string; clickhouse_admin_password: string; } export interface DragonflyDatabase extends DatabaseBase { type: 'dragonfly'; dragonfly_password: string; } export type Database = | PostgresDatabase | MySQLDatabase | MariaDBDatabase | MongoDBDatabase | RedisDatabase | KeyDBDatabase | ClickhouseDatabase | DragonflyDatabase; export interface DatabaseUpdateRequest { name?: string; description?: string; image?: string; is_public?: boolean; public_port?: number; limits_memory?: string; limits_memory_swap?: string; limits_memory_swappiness?: number; limits_memory_reservation?: string; limits_cpus?: string; limits_cpuset?: string; limits_cpu_shares?: number; postgres_user?: string; postgres_password?: string; postgres_db?: string; postgres_initdb_args?: string; postgres_host_auth_method?: string; postgres_conf?: string; clickhouse_admin_user?: string; clickhouse_admin_password?: string; dragonfly_password?: string; redis_password?: string; redis_conf?: string; keydb_password?: string; keydb_conf?: string; mariadb_conf?: string; mariadb_root_password?: string; mariadb_user?: string; mariadb_password?: string; mariadb_database?: string; mongo_conf?: string; mongo_initdb_root_username?: string; mongo_initdb_root_password?: string; mongo_initdb_database?: string; mysql_root_password?: string; mysql_password?: string; mysql_user?: string; mysql_database?: string; } export type ServiceType = | 'activepieces' | 'appsmith' | 'appwrite' | 'authentik' | 'babybuddy' | 'budge' | 'changedetection' | 'chatwoot' | 'classicpress-with-mariadb' | 'classicpress-with-mysql' | 'classicpress-without-database' | 'cloudflared' | 'code-server' | 'dashboard' | 'directus' | 'directus-with-postgresql' | 'docker-registry' | 'docuseal' | 'docuseal-with-postgres' | 'dokuwiki' | 'duplicati' | 'emby' | 'embystat' | 'fider' | 'filebrowser' | 'firefly' | 'formbricks' | 'ghost' | 'gitea' | 'gitea-with-mariadb' | 'gitea-with-mysql' | 'gitea-with-postgresql' | 'glance' | 'glances' | 'glitchtip' | 'grafana' | 'grafana-with-postgresql' | 'grocy' | 'heimdall' | 'homepage' | 'jellyfin' | 'kuzzle' | 'listmonk' | 'logto' | 'mediawiki' | 'meilisearch' | 'metabase' | 'metube' | 'minio' | 'moodle' | 'n8n' | 'n8n-with-postgresql' | 'next-image-transformation' | 'nextcloud' | 'nocodb' | 'odoo' | 'openblocks' | 'pairdrop' | 'penpot' | 'phpmyadmin' | 'pocketbase' | 'posthog' | 'reactive-resume' | 'rocketchat' | 'shlink' | 'slash' | 'snapdrop' | 'statusnook' | 'stirling-pdf' | 'supabase' | 'syncthing' | 'tolgee' | 'trigger' | 'trigger-with-external-database' | 'twenty' | 'umami' | 'unleash-with-postgresql' | 'unleash-without-database' | 'uptime-kuma' | 'vaultwarden' | 'vikunja' | 'weblate' | 'whoogle' | 'wordpress-with-mariadb' | 'wordpress-with-mysql' | 'wordpress-without-database'; export interface Service { id: number; uuid: string; name: string; description?: string; type: ServiceType; status: 'running' | 'stopped' | 'error'; created_at: string; updated_at: string; project_uuid: string; environment_name: string; environment_uuid: string; server_uuid: string; destination_uuid?: string; domains?: string[]; } export interface CreateServiceRequest { type: ServiceType; name?: string; description?: string; project_uuid: string; environment_name?: string; environment_uuid?: string; server_uuid: string; destination_uuid?: string; instant_deploy?: boolean; } export interface DeleteServiceOptions { deleteConfigurations?: boolean; deleteVolumes?: boolean; dockerCleanup?: boolean; deleteConnectedNetworks?: boolean; } export interface Application { uuid: string; name: string; // Add other application properties as needed } export interface CreateApplicationRequest { name: string; // Add other required fields for application creation } </file> <file path="src/index.ts"> #!/usr/bin/env node import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CoolifyMcpServer } from './lib/mcp-server.js'; import { CoolifyConfig } from './types/coolify.js'; declare const process: NodeJS.Process; async function main(): Promise<void> { const config: CoolifyConfig = { baseUrl: process.env.COOLIFY_BASE_URL || 'http://localhost:3000', accessToken: process.env.COOLIFY_ACCESS_TOKEN || '', }; if (!config.accessToken) { throw new Error('COOLIFY_ACCESS_TOKEN environment variable is required'); } const server = new CoolifyMcpServer(config); const transport = new StdioServerTransport(); await server.connect(transport); } main().catch((error) => { console.error('Fatal error:', error); process.exit(1); }); </file> <file path=".eslintrc.json"> { "env": { "node": true, "es2021": true }, "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended", "prettier"], "parser": "@typescript-eslint/parser", "parserOptions": { "ecmaVersion": "latest", "sourceType": "module" }, "plugins": ["@typescript-eslint"], "rules": { "@typescript-eslint/explicit-function-return-type": "warn", "@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }], "@typescript-eslint/no-explicit-any": "warn" } } </file> <file path=".gitignore"> # Dependencies node_modules/ # Build output dist/ build/ /lib/ coverage/ # IDE and editor files .idea/ .vscode/ *.swp *.swo .DS_Store Thumbs.db # Environment variables .env .env.local .env.*.local # Logs logs/ *.log npm-debug.log* yarn-debug.log* yarn-error.log* # Test coverage coverage/ .nyc_output/ # Optional npm cache directory .npm # Optional eslint cache .eslintcache # Optional REPL history .node_repl_history # Output of 'npm pack' *.tgz # Yarn Integrity file .yarn-integrity </file> <file path=".lintstagedrc.json"> { "src/**/*.ts": ["eslint --fix", "prettier --write"], "*.json": ["prettier --write"], "*.md": ["prettier --write", "markdownlint-cli2"] } </file> <file path=".markdownlint-cli2.jsonc"> { "config": { "line-length": false, "no-duplicate-heading": false, "no-inline-html": false, }, "ignores": ["node_modules", "dist"], } </file> <file path=".prettierrc"> { "semi": true, "trailingComma": "all", "singleQuote": true, "printWidth": 100, "tabWidth": 2 } </file> <file path=".repomixignore"> .cursor/ .github/ .husky/ docs/ package-lock.json </file> <file path="debug.js"> import { CoolifyMcpServer } from './dist/lib/mcp-server.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; // Enable debug logging process.env.DEBUG = '*'; const server = new CoolifyMcpServer({ baseUrl: 'https://coolify.dev', // Replace with your actual Coolify URL accessToken: 'your-actual-token' // Replace with your actual Coolify token }); const transport = new StdioServerTransport(); await server.connect(transport); </file> <file path="jest.config.js"> /** @type {import('ts-jest').JestConfigWithTsJest} */ export default { preset: 'ts-jest', testEnvironment: 'node', moduleNameMapper: { '^(\\.{1,2}/.*)\\.js$': '$1', }, transform: { '^.+\\.tsx?$': [ 'ts-jest', { useESM: true, }, ], }, extensionsToTreatAsEsm: ['.ts'], testPathIgnorePatterns: ['/node_modules/', '/dist/', '\\.d\\.ts$'], }; </file> <file path="package.json"> { "name": "@masonator/coolify-mcp", "scope": "@masonator", "version": "0.2.2", "description": "MCP server implementation for Coolify", "type": "module", "main": "./dist/index.js", "types": "./dist/index.d.ts", "exports": { ".": { "import": "./dist/index.js", "require": "./dist/index.cjs", "types": "./dist/index.d.ts" } }, "bin": { "coolify-mcp": "dist/index.js" }, "files": [ "dist" ], "scripts": { "build": "tsc && shx chmod +x dist/*.js", "dev": "tsc --watch", "test": "NODE_OPTIONS=--experimental-vm-modules jest", "test:watch": "NODE_OPTIONS=--experimental-vm-modules jest --watch", "test:coverage": "NODE_OPTIONS=--experimental-vm-modules jest --coverage", "lint": "eslint . --ext .ts", "lint:fix": "eslint . --ext .ts --fix", "format": "prettier --write .", "format:check": "prettier --check .", "prepare": "husky", "prepublishOnly": "npm test && npm run lint", "start": "node dist/index.js" }, "keywords": [ "coolify", "mcp", "model-context-protocol" ], "author": "Stuart Mason", "license": "MIT", "dependencies": { "@modelcontextprotocol/sdk": "^1.6.1", "@modelcontextprotocol/server-github": "^2025.1.23", "reflect-metadata": "^0.2.2", "zod": "^3.24.2" }, "devDependencies": { "@types/debug": "^4.1.12", "@types/jest": "^29.5.14", "@types/node": "^20.17.23", "@typescript-eslint/eslint-plugin": "^7.18.0", "@typescript-eslint/parser": "^7.18.0", "eslint": "^8.56.0", "eslint-config-prettier": "^9.1.0", "husky": "^9.0.11", "jest": "^29.7.0", "lint-staged": "^15.2.2", "markdownlint-cli2": "^0.12.1", "prettier": "^3.5.3", "shx": "^0.3.4", "ts-jest": "^29.2.6", "typescript": "^5.8.2" }, "engines": { "node": ">=18" } } </file> <file path="README.md"> # Coolify MCP Server A Model Context Protocol (MCP) server implementation for [Coolify](https://coolify.io/), enabling AI assistants to interact with your Coolify instances through natural language. ## Example Prompts Here are example prompts you can use with MCP-compatible AI assistants to interact with your Coolify instance: ### Server Management ``` # List and Inspect Servers - Show me all Coolify servers in my instance - What's the status of server {uuid}? - Show me the resources running on server {uuid} - What domains are configured for server {uuid}? - Can you validate the connection to server {uuid}? # Resource Monitoring - How much CPU and memory is server {uuid} using? - List all resources running on server {uuid} - Show me the current status of all servers ``` ### Project Management ``` # Project Operations - List all my Coolify projects - Create a new project called "my-webapp" with description "My web application" - Show me the details of project {uuid} - Update project {uuid} to change its name to "new-name" - Delete project {uuid} # Environment Management - Show me the environments in project {uuid} - Get details of the production environment in project {uuid} - What variables are set in the staging environment of project {uuid}? ``` ### Application and Service Management ``` # Application Management - List all applications - Show me details of application {uuid} - Create a new application called "my-nodejs-app" - Delete application {uuid} # Service Operations - Show me all running services - Create a new WordPress service: - Name: my-blog - Project UUID: {project_uuid} - Server UUID: {server_uuid} - Type: wordpress-with-mysql - What's the status of service {uuid}? - Delete service {uuid} and clean up its resources ``` ### Database Management ``` # Database Operations - List all databases - Show me the configuration of database {uuid} - Update database {uuid}: - Increase memory limit to 1GB - Change public port to 5432 - Update password - Delete database {uuid} and clean up volumes # Database Types - Create a PostgreSQL database - Set up a Redis instance - Configure a MongoDB database - Initialize a MySQL database ``` ### Deployment Management ``` # Deployment Operations - Show me all active deployments - What's the status of deployment {uuid}? - Deploy application {uuid} - Force rebuild and deploy application {uuid} - List recent deployments for application {uuid} ``` ## Installation ### Prerequisites - Node.js >= 18 - A running Coolify instance - Coolify API access token ### Setup in AI Tools #### Claude Desktop ```json "coolify": { "command": "npx", "args": [ "-y", "@masonator/coolify-mcp" ], "env": { "COOLIFY_ACCESS_TOKEN": "0|your-secret-token", "COOLIFY_BASE_URL": "https://your-coolify-instance.com" } } ``` #### Cursor ```bash env COOLIFY_ACCESS_TOKEN:0|your-secret-token COOLIFY_BASE_URL:https://your-coolify-instance.com npx -y @stumason/coolify-mcp ``` ## Development ### Local Setup ```bash # Clone the repository git clone https://github.com/stumason/coolify-mcp.git cd coolify-mcp # Install dependencies npm install # Build the project npm run build # Run tests npm test ``` ### Environment Variables ```bash # Required COOLIFY_ACCESS_TOKEN=your_access_token_here # Optional (defaults to http://localhost:3000) COOLIFY_BASE_URL=https://your.coolify.instance ``` ## API Reference ### Resource Types #### Application ```typescript interface Application { uuid: string; name: string; // Additional properties based on your Coolify instance } ``` #### Service ```typescript interface Service { id: number; uuid: string; name: string; type: ServiceType; // Various types like 'wordpress', 'mysql', etc. status: 'running' | 'stopped' | 'error'; project_uuid: string; environment_uuid: string; server_uuid: string; domains?: string[]; } ``` #### Database ```typescript interface Database { id: number; uuid: string; name: string; type: 'postgresql' | 'mysql' | 'mongodb' | 'redis' | /* other types */; status: 'running' | 'stopped' | 'error'; is_public: boolean; public_port?: number; // Additional configuration based on database type } ``` #### Deployment ```typescript interface Deployment { id: number; uuid: string; application_uuid: string; status: string; created_at: string; updated_at: string; } ``` ## Contributing Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change. ## License MIT ## Support For support, please: 1. Check the [issues](https://github.com/stumason/coolify-mcp/issues) page 2. Create a new issue if needed 3. Join the Coolify community </file> <file path="tsconfig.json"> { "compilerOptions": { "target": "ES2020", "module": "NodeNext", "moduleResolution": "NodeNext", "declaration": true, "outDir": "./dist", "strict": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, "experimentalDecorators": true, "emitDecoratorMetadata": true, "allowJs": true, "resolveJsonModule": true }, "include": ["src"], "exclude": ["node_modules", "dist", "tests"] } </file> </repository_files>