#!/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 { CoolifyConfig, CoolifyApiClient } from './client/coolify-api.js';
import { logger } from './utils/logger.js';
// Import tools
import {
listProjectsTool,
createProjectTool,
handleListProjects,
handleCreateProject,
} from './tools/projects.js';
import {
listAppsTool,
getAppTool,
createAppTool,
updateAppTool,
deleteAppTool,
handleListApps,
handleGetApp,
handleCreateApp,
handleUpdateApp,
handleDeleteApp,
} from './tools/applications.js';
import {
deployAppTool,
getDeploymentStatusTool,
getDeploymentLogsTool,
handleDeployApp,
handleGetDeploymentStatus,
handleGetDeploymentLogs,
} from './tools/deployments.js';
import {
deployTemplateTool,
listTemplatesTool,
handleDeployTemplate,
handleListTemplates,
} from './tools/templates.js';
import {
checkQuotaTool,
checkNameConflictsTool,
handleCheckQuota,
handleCheckNameConflicts,
} from './tools/safety.js';
// Load configuration from environment variables
function loadConfig(): CoolifyConfig {
const apiUrl = process.env.COOLIFY_API_URL;
const apiToken = process.env.COOLIFY_API_TOKEN;
if (!apiUrl) {
throw new Error('COOLIFY_API_URL environment variable is required');
}
if (!apiToken) {
throw new Error('COOLIFY_API_TOKEN environment variable is required');
}
return {
apiUrl: apiUrl.replace(/\/$/, ''), // Remove trailing slash
apiToken,
defaultTeamId: process.env.COOLIFY_DEFAULT_TEAM_ID,
maxAppsPerProject: process.env.COOLIFY_MAX_APPS_PER_PROJECT
? parseInt(process.env.COOLIFY_MAX_APPS_PER_PROJECT, 10)
: undefined,
};
}
// Initialize the server
async function main() {
try {
const config = loadConfig();
const apiClient = new CoolifyApiClient(config);
const server = new Server(
{
name: 'coolify-mcp-server',
version: '1.0.0',
},
{
capabilities: {
tools: {},
},
}
);
// List all available tools
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
// Projects
listProjectsTool,
createProjectTool,
// Applications
listAppsTool,
getAppTool,
createAppTool,
updateAppTool,
deleteAppTool,
// Deployments
deployAppTool,
getDeploymentStatusTool,
getDeploymentLogsTool,
// Templates
deployTemplateTool,
listTemplatesTool,
// Safety
checkQuotaTool,
checkNameConflictsTool,
],
};
});
// Handle tool calls
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
switch (name) {
// Projects
case 'coolify.list_projects':
return { content: [{ type: 'text', text: JSON.stringify(await handleListProjects(args, apiClient)) }] };
case 'coolify.create_project':
return { content: [{ type: 'text', text: JSON.stringify(await handleCreateProject(args, apiClient)) }] };
// Applications
case 'coolify.list_apps':
return { content: [{ type: 'text', text: JSON.stringify(await handleListApps(args, apiClient)) }] };
case 'coolify.get_app':
return { content: [{ type: 'text', text: JSON.stringify(await handleGetApp(args, apiClient)) }] };
case 'coolify.create_app':
return { content: [{ type: 'text', text: JSON.stringify(await handleCreateApp(args, apiClient)) }] };
case 'coolify.update_app':
return { content: [{ type: 'text', text: JSON.stringify(await handleUpdateApp(args, apiClient)) }] };
case 'coolify.delete_app':
return { content: [{ type: 'text', text: JSON.stringify(await handleDeleteApp(args, apiClient)) }] };
// Deployments
case 'coolify.deploy_app':
return { content: [{ type: 'text', text: JSON.stringify(await handleDeployApp(args, apiClient)) }] };
case 'coolify.get_deployment_status':
return { content: [{ type: 'text', text: JSON.stringify(await handleGetDeploymentStatus(args, apiClient)) }] };
case 'coolify.get_deployment_logs':
return { content: [{ type: 'text', text: JSON.stringify(await handleGetDeploymentLogs(args, apiClient)) }] };
// Templates
case 'coolify.deploy_template':
return { content: [{ type: 'text', text: JSON.stringify(await handleDeployTemplate(args, apiClient)) }] };
case 'coolify.list_templates':
return { content: [{ type: 'text', text: JSON.stringify(await handleListTemplates(args)) }] };
// Safety
case 'coolify.check_quota':
return { content: [{ type: 'text', text: JSON.stringify(await handleCheckQuota(args, apiClient)) }] };
case 'coolify.check_name_conflicts':
return { content: [{ type: 'text', text: JSON.stringify(await handleCheckNameConflicts(args, apiClient)) }] };
default:
throw new Error(`Unknown tool: ${name}`);
}
} catch (error: any) {
logger.error(`Tool execution failed for ${name}`, { error: error.message });
return {
content: [
{
type: 'text',
text: JSON.stringify({
success: false,
error: {
code: error.code || 'EXECUTION_ERROR',
message: error.message,
},
}),
},
],
};
}
});
// Start the server
const transport = new StdioServerTransport();
await server.connect(transport);
logger.info('Coolify MCP Server started successfully');
logger.info(`Connected to Coolify API at ${config.apiUrl}`);
// Graceful shutdown
process.on('SIGINT', async () => {
logger.info('Shutting down Coolify MCP Server...');
await server.close();
process.exit(0);
});
process.on('SIGTERM', async () => {
logger.info('Shutting down Coolify MCP Server...');
await server.close();
process.exit(0);
});
} catch (error: any) {
logger.error('Failed to start server', { error: error.message });
process.exit(1);
}
}
// Start the server
main().catch((error) => {
logger.error('Server startup error', { error });
process.exit(1);
});