Coolify MCP Server
by StuMason
#!/usr/bin/env node
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
import { z } from "zod";
import { zodToJsonSchema } from "zod-to-json-schema";
const server = new Server(
{
name: "coolify",
version: "1.0.0",
},
{
capabilities: {
tools: {},
},
}
);
// Helper function for API calls
async function coolifyApiCall(endpoint: string, method: string = 'GET', body?: any): Promise<any> {
const baseUrl = process.env.COOLIFY_BASE_URL?.replace(/\/$/, '') || 'https://coolify.stuartmason.co.uk';
const url = `${baseUrl}/api/v1${endpoint}`;
const response = await fetch(url, {
method,
headers: {
'Authorization': `Bearer ${process.env.COOLIFY_ACCESS_TOKEN}`,
'Content-Type': 'application/json',
},
body: body ? JSON.stringify(body) : undefined,
});
if (!response.ok) {
const errorBody = await response.json().catch(() => ({}));
throw new Error(JSON.stringify({
error: `Coolify API error: ${response.status} ${response.statusText}`,
status: response.status,
details: errorBody
}));
}
return await response.json();
}
// Schema Definitions
const UuidSchema = z.object({
uuid: z.string().describe("Resource UUID"),
});
const DeploySchema = z.object({
tag: z.string().optional().describe("Tag name(s). Comma separated list is accepted"),
uuid: z.string().optional().describe("Resource UUID(s). Comma separated list is accepted"),
force: z.boolean().optional().describe("Force rebuild (without cache)"),
});
const ApplicationUpdateSchema = z.object({
health_check_enabled: z.boolean().optional(),
health_check_path: z.string().optional(),
health_check_port: z.string().nullable().optional(),
health_check_host: z.string().nullable().optional(),
health_check_method: z.string().optional(),
health_check_return_code: z.number().optional(),
health_check_scheme: z.string().optional(),
health_check_response_text: z.string().nullable().optional(),
health_check_interval: z.number().optional(),
health_check_timeout: z.number().optional(),
health_check_retries: z.number().optional(),
health_check_start_period: z.number().optional(),
// Add other updateable fields
name: z.string().optional(),
description: z.string().optional(),
domains: z.string().optional(),
});
// Set up request handlers
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "list-resources",
description: "Retrieve a comprehensive list of all resources managed by Coolify. This includes applications, services, databases, and deployments.",
inputSchema: zodToJsonSchema(z.object({})),
},
{
name: "list-applications",
description: "Fetch a list of all applications currently managed by Coolify. This provides an overview of all deployed applications.",
inputSchema: zodToJsonSchema(z.object({})),
},
{
name: "get-application",
description: "Retrieve detailed information about a specific application using its UUID. This includes the application's status, configuration, and deployment details.",
inputSchema: zodToJsonSchema(UuidSchema),
},
{
name: "start-application",
description: "Start a specific application using its UUID. This initiates the application and makes it available for use.",
inputSchema: zodToJsonSchema(UuidSchema),
},
{
name: "stop-application",
description: "Stop a specific application using its UUID. This halts the application and makes it unavailable.",
inputSchema: zodToJsonSchema(UuidSchema),
},
{
name: "restart-application",
description: "Restart a specific application using its UUID. This stops and then starts the application, applying any configuration changes.",
inputSchema: zodToJsonSchema(UuidSchema),
},
{
name: "list-services",
description: "Retrieve a list of all services managed by Coolify. This includes external services and microservices.",
inputSchema: zodToJsonSchema(z.object({})),
},
{
name: "list-databases",
description: "Fetch a list of all databases managed by Coolify. This provides an overview of all database instances.",
inputSchema: zodToJsonSchema(z.object({})),
},
{
name: "list-deployments",
description: "Retrieve a list of all running deployments in Coolify. This includes details about the deployment status and history.",
inputSchema: zodToJsonSchema(z.object({})),
},
{
name: "deploy",
description: "Deploy an application or service using a tag or UUID. This allows you to deploy new versions or updates to your applications.",
inputSchema: zodToJsonSchema(DeploySchema),
},
{
name: "update-application",
description: "Update the settings of a specific application, such as health check configurations. This allows you to modify the application's behavior and monitoring settings.",
inputSchema: zodToJsonSchema(z.object({
uuid: z.string().describe("Resource UUID"),
settings: ApplicationUpdateSchema
})),
},
// Additional tools can be added here
],
};
});
server.setRequestHandler(CallToolRequestSchema, async (request) => {
try {
switch (request.params.name) {
case "list-resources": {
const resources = await coolifyApiCall('/resources');
return {
content: [{
type: "text",
text: JSON.stringify(resources, null, 2)
}]
};
}
case "list-applications": {
const apps = await coolifyApiCall('/applications');
return {
content: [{
type: "text",
text: JSON.stringify(apps, null, 2)
}]
};
}
case "get-application": {
const { uuid } = UuidSchema.parse(request.params.arguments);
const app = await coolifyApiCall(`/applications/${uuid}`);
return {
content: [{
type: "text",
text: JSON.stringify(app, null, 2)
}]
};
}
case "start-application": {
const { uuid } = UuidSchema.parse(request.params.arguments);
const result = await coolifyApiCall(`/applications/${uuid}/start`);
return {
content: [{
type: "text",
text: JSON.stringify(result, null, 2)
}]
};
}
case "stop-application": {
const { uuid } = UuidSchema.parse(request.params.arguments);
const result = await coolifyApiCall(`/applications/${uuid}/stop`);
return {
content: [{
type: "text",
text: JSON.stringify(result, null, 2)
}]
};
}
case "update-application": {
const { uuid, settings } = request.params.arguments as { uuid: string; settings: any };
const result = await coolifyApiCall(`/applications/${uuid}`, 'PATCH', settings);
return {
content: [{
type: "text",
text: JSON.stringify(result, null, 2)
}]
};
}
case "restart-application": {
const { uuid } = UuidSchema.parse(request.params.arguments);
const result = await coolifyApiCall(`/applications/${uuid}/restart`);
return {
content: [{
type: "text",
text: JSON.stringify(result, null, 2)
}]
};
}
case "list-services": {
const services = await coolifyApiCall('/services');
return {
content: [{
type: "text",
text: JSON.stringify(services, null, 2)
}]
};
}
case "list-databases": {
const databases = await coolifyApiCall('/databases');
return {
content: [{
type: "text",
text: JSON.stringify(databases, null, 2)
}]
};
}
case "list-deployments": {
const deployments = await coolifyApiCall('/deployments');
return {
content: [{
type: "text",
text: JSON.stringify(deployments, null, 2)
}]
};
}
case "deploy": {
const params = DeploySchema.parse(request.params.arguments);
const queryParams = new URLSearchParams();
if (params.tag) queryParams.append('tag', params.tag);
if (params.uuid) queryParams.append('uuid', params.uuid);
if (params.force) queryParams.append('force', 'true');
const result = await coolifyApiCall(`/deploy?${queryParams.toString()}`);
return {
content: [{
type: "text",
text: JSON.stringify(result, null, 2)
}]
};
}
default:
throw new Error(`Unknown tool: ${request.params.name}`);
}
} catch (error) {
if (error instanceof z.ZodError) {
throw new Error(`Invalid input: ${JSON.stringify(error.errors)}`);
}
throw error;
}
});
// Start server
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Coolify MCP Server running on stdio");
}
main().catch((error) => {
console.error("Fatal error:", error);
process.exit(1);
});