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>