@masonator/coolify-mcp 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,209 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const mcp_server_js_1 = require("../lib/mcp-server.js");
4
+ const globals_1 = require("@jest/globals");
5
+ describe('CoolifyMcpServer', () => {
6
+ const mockConfig = {
7
+ baseUrl: 'https://coolify.test',
8
+ accessToken: 'test-token',
9
+ };
10
+ let server;
11
+ beforeEach(() => {
12
+ server = new mcp_server_js_1.CoolifyMcpServer(mockConfig);
13
+ // Mock validateConnection to prevent actual HTTP calls
14
+ globals_1.jest.spyOn(server['client'], 'validateConnection').mockResolvedValue();
15
+ });
16
+ describe('get_server_resources', () => {
17
+ it('should call client getServerResources', async () => {
18
+ // Mock the method before calling it
19
+ const mockResources = [
20
+ {
21
+ id: 1,
22
+ uuid: 'test-resource-uuid',
23
+ name: 'test-resource',
24
+ type: 'application',
25
+ created_at: '2024-03-20T00:00:00Z',
26
+ updated_at: '2024-03-20T00:00:00Z',
27
+ status: 'running',
28
+ },
29
+ ];
30
+ const spy = globals_1.jest
31
+ .spyOn(server['client'], 'getServerResources')
32
+ .mockResolvedValue(mockResources);
33
+ await server.get_server_resources('test-uuid');
34
+ expect(spy).toHaveBeenCalledWith('test-uuid');
35
+ });
36
+ });
37
+ describe('validate_server', () => {
38
+ it('should call client validateServer', async () => {
39
+ // Mock the method before calling it
40
+ const mockValidation = {
41
+ message: 'Server is valid',
42
+ };
43
+ const spy = globals_1.jest.spyOn(server['client'], 'validateServer').mockResolvedValue(mockValidation);
44
+ await server.validate_server('test-uuid');
45
+ expect(spy).toHaveBeenCalledWith('test-uuid');
46
+ });
47
+ });
48
+ describe('list_projects', () => {
49
+ it('should call client listProjects', async () => {
50
+ const mockProjects = [
51
+ {
52
+ id: 1,
53
+ uuid: 'test-project-uuid',
54
+ name: 'test-project',
55
+ description: 'Test project description',
56
+ environments: [],
57
+ },
58
+ ];
59
+ const spy = globals_1.jest.spyOn(server['client'], 'listProjects').mockResolvedValue(mockProjects);
60
+ await server.list_projects();
61
+ expect(spy).toHaveBeenCalled();
62
+ });
63
+ });
64
+ describe('get_project', () => {
65
+ it('should call client getProject', async () => {
66
+ const mockProject = {
67
+ id: 1,
68
+ uuid: 'test-project-uuid',
69
+ name: 'test-project',
70
+ description: 'Test project description',
71
+ environments: [],
72
+ };
73
+ const spy = globals_1.jest.spyOn(server['client'], 'getProject').mockResolvedValue(mockProject);
74
+ await server.get_project('test-project-uuid');
75
+ expect(spy).toHaveBeenCalledWith('test-project-uuid');
76
+ });
77
+ });
78
+ describe('create_project', () => {
79
+ it('should call client createProject', async () => {
80
+ const mockResponse = { uuid: 'new-project-uuid' };
81
+ const createRequest = {
82
+ name: 'New Project',
83
+ description: 'New project description',
84
+ };
85
+ const spy = globals_1.jest.spyOn(server['client'], 'createProject').mockResolvedValue(mockResponse);
86
+ await server.create_project(createRequest);
87
+ expect(spy).toHaveBeenCalledWith(createRequest);
88
+ });
89
+ });
90
+ describe('update_project', () => {
91
+ it('should call client updateProject', async () => {
92
+ const mockProject = {
93
+ id: 1,
94
+ uuid: 'test-project-uuid',
95
+ name: 'Updated Project',
96
+ description: 'Updated description',
97
+ environments: [],
98
+ };
99
+ const updateRequest = {
100
+ name: 'Updated Project',
101
+ description: 'Updated description',
102
+ };
103
+ const spy = globals_1.jest.spyOn(server['client'], 'updateProject').mockResolvedValue(mockProject);
104
+ await server.update_project('test-project-uuid', updateRequest);
105
+ expect(spy).toHaveBeenCalledWith('test-project-uuid', updateRequest);
106
+ });
107
+ });
108
+ describe('delete_project', () => {
109
+ it('should call client deleteProject', async () => {
110
+ const mockResponse = { message: 'Project deleted successfully' };
111
+ const spy = globals_1.jest.spyOn(server['client'], 'deleteProject').mockResolvedValue(mockResponse);
112
+ await server.delete_project('test-project-uuid');
113
+ expect(spy).toHaveBeenCalledWith('test-project-uuid');
114
+ });
115
+ });
116
+ describe('get_project_environment', () => {
117
+ it('should call client getProjectEnvironment', async () => {
118
+ const mockEnvironment = {
119
+ id: 1,
120
+ uuid: 'test-env-uuid',
121
+ name: 'test-environment',
122
+ project_uuid: 'test-project-uuid',
123
+ created_at: '2024-03-19T12:00:00Z',
124
+ updated_at: '2024-03-19T12:00:00Z',
125
+ };
126
+ const spy = globals_1.jest
127
+ .spyOn(server['client'], 'getProjectEnvironment')
128
+ .mockResolvedValue(mockEnvironment);
129
+ await server.get_project_environment('test-project-uuid', 'test-env-uuid');
130
+ expect(spy).toHaveBeenCalledWith('test-project-uuid', 'test-env-uuid');
131
+ });
132
+ });
133
+ describe('Environment Management', () => {
134
+ const mockEnvironment = {
135
+ id: 1,
136
+ uuid: 'test-env-uuid',
137
+ name: 'test-environment',
138
+ project_uuid: 'test-project-uuid',
139
+ variables: { KEY: 'value' },
140
+ created_at: '2024-03-19T12:00:00Z',
141
+ updated_at: '2024-03-19T12:00:00Z',
142
+ };
143
+ describe('list_environments', () => {
144
+ it('should call client listEnvironments without project UUID', async () => {
145
+ const spy = globals_1.jest
146
+ .spyOn(server['client'], 'listEnvironments')
147
+ .mockResolvedValue([mockEnvironment]);
148
+ await server.list_environments({});
149
+ expect(spy).toHaveBeenCalledWith(undefined);
150
+ });
151
+ it('should call client listEnvironments with project UUID', async () => {
152
+ const spy = globals_1.jest
153
+ .spyOn(server['client'], 'listEnvironments')
154
+ .mockResolvedValue([mockEnvironment]);
155
+ await server.list_environments({ project_uuid: 'test-project-uuid' });
156
+ expect(spy).toHaveBeenCalledWith('test-project-uuid');
157
+ });
158
+ });
159
+ describe('get_environment', () => {
160
+ it('should call client getEnvironment', async () => {
161
+ const spy = globals_1.jest
162
+ .spyOn(server['client'], 'getEnvironment')
163
+ .mockResolvedValue(mockEnvironment);
164
+ await server.get_environment({ uuid: 'test-env-uuid' });
165
+ expect(spy).toHaveBeenCalledWith('test-env-uuid');
166
+ });
167
+ });
168
+ describe('create_environment', () => {
169
+ it('should call client createEnvironment', async () => {
170
+ const createRequest = {
171
+ name: 'test-environment',
172
+ project_uuid: 'test-project-uuid',
173
+ variables: { KEY: 'value' },
174
+ };
175
+ const mockResponse = { uuid: 'test-env-uuid' };
176
+ const spy = globals_1.jest
177
+ .spyOn(server['client'], 'createEnvironment')
178
+ .mockResolvedValue(mockResponse);
179
+ await server.create_environment(createRequest);
180
+ expect(spy).toHaveBeenCalledWith(createRequest);
181
+ });
182
+ });
183
+ describe('update_environment_variables', () => {
184
+ it('should call client updateEnvironmentVariables', async () => {
185
+ const updateRequest = {
186
+ variables: { NEW_KEY: 'new-value' },
187
+ };
188
+ const spy = globals_1.jest
189
+ .spyOn(server['client'], 'updateEnvironmentVariables')
190
+ .mockResolvedValue({ ...mockEnvironment, variables: updateRequest.variables });
191
+ await server.update_environment_variables({
192
+ uuid: 'test-env-uuid',
193
+ variables: updateRequest.variables,
194
+ });
195
+ expect(spy).toHaveBeenCalledWith('test-env-uuid', updateRequest);
196
+ });
197
+ });
198
+ describe('delete_environment', () => {
199
+ it('should call client deleteEnvironment', async () => {
200
+ const mockResponse = { message: 'Environment deleted successfully' };
201
+ const spy = globals_1.jest
202
+ .spyOn(server['client'], 'deleteEnvironment')
203
+ .mockResolvedValue(mockResponse);
204
+ await server.delete_environment({ uuid: 'test-env-uuid' });
205
+ expect(spy).toHaveBeenCalledWith('test-env-uuid');
206
+ });
207
+ });
208
+ });
209
+ });
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
5
+ const mcp_server_js_1 = require("./lib/mcp-server.js");
6
+ async function main() {
7
+ const config = {
8
+ baseUrl: process.env.COOLIFY_BASE_URL || 'http://localhost:3000',
9
+ accessToken: process.env.COOLIFY_ACCESS_TOKEN || '',
10
+ };
11
+ if (!config.accessToken) {
12
+ throw new Error('COOLIFY_ACCESS_TOKEN environment variable is required');
13
+ }
14
+ const server = new mcp_server_js_1.CoolifyMcpServer(config);
15
+ const transport = new stdio_js_1.StdioServerTransport();
16
+ await server.start(transport);
17
+ }
18
+ main().catch((error) => {
19
+ console.error('Fatal error:', error);
20
+ process.exit(1);
21
+ });
@@ -0,0 +1,32 @@
1
+ import { CoolifyConfig, ServerInfo, ServerResources, ServerDomain, ValidationResponse, Project, CreateProjectRequest, UpdateProjectRequest, Environment, CreateEnvironmentRequest, UpdateEnvironmentVariablesRequest } from '../types/coolify.js';
2
+ export declare class CoolifyClient {
3
+ private baseUrl;
4
+ private accessToken;
5
+ constructor(config: CoolifyConfig);
6
+ private request;
7
+ listServers(): Promise<ServerInfo[]>;
8
+ getServer(uuid: string): Promise<ServerInfo>;
9
+ getServerResources(uuid: string): Promise<ServerResources>;
10
+ getServerDomains(uuid: string): Promise<ServerDomain[]>;
11
+ validateServer(uuid: string): Promise<ValidationResponse>;
12
+ validateConnection(): Promise<void>;
13
+ listProjects(): Promise<Project[]>;
14
+ getProject(uuid: string): Promise<Project>;
15
+ createProject(project: CreateProjectRequest): Promise<{
16
+ uuid: string;
17
+ }>;
18
+ updateProject(uuid: string, project: UpdateProjectRequest): Promise<Project>;
19
+ deleteProject(uuid: string): Promise<{
20
+ message: string;
21
+ }>;
22
+ getProjectEnvironment(projectUuid: string, environmentNameOrUuid: string): Promise<Environment>;
23
+ listEnvironments(projectUuid?: string): Promise<Environment[]>;
24
+ getEnvironment(uuid: string): Promise<Environment>;
25
+ createEnvironment(environment: CreateEnvironmentRequest): Promise<{
26
+ uuid: string;
27
+ }>;
28
+ deleteEnvironment(uuid: string): Promise<{
29
+ message: string;
30
+ }>;
31
+ updateEnvironmentVariables(uuid: string, variables: UpdateEnvironmentVariablesRequest): Promise<Environment>;
32
+ }
@@ -0,0 +1,113 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CoolifyClient = void 0;
4
+ class CoolifyClient {
5
+ constructor(config) {
6
+ if (!config.baseUrl) {
7
+ throw new Error('Coolify base URL is required');
8
+ }
9
+ if (!config.accessToken) {
10
+ throw new Error('Coolify access token is required');
11
+ }
12
+ this.baseUrl = config.baseUrl.replace(/\/$/, '');
13
+ this.accessToken = config.accessToken;
14
+ }
15
+ async request(path, options = {}) {
16
+ try {
17
+ const url = `${this.baseUrl}/api/v1${path}`;
18
+ const response = await fetch(url, {
19
+ headers: {
20
+ 'Content-Type': 'application/json',
21
+ Authorization: `Bearer ${this.accessToken}`,
22
+ },
23
+ ...options,
24
+ });
25
+ const data = await response.json();
26
+ if (!response.ok) {
27
+ const error = data;
28
+ throw new Error(error.message || `HTTP ${response.status}: ${response.statusText}`);
29
+ }
30
+ return data;
31
+ }
32
+ catch (error) {
33
+ if (error instanceof TypeError && error.message.includes('fetch')) {
34
+ throw new Error(`Failed to connect to Coolify server at ${this.baseUrl}. Please check if the server is running and the URL is correct.`);
35
+ }
36
+ throw error;
37
+ }
38
+ }
39
+ async listServers() {
40
+ return this.request('/servers');
41
+ }
42
+ async getServer(uuid) {
43
+ return this.request(`/servers/${uuid}`);
44
+ }
45
+ async getServerResources(uuid) {
46
+ return this.request(`/servers/${uuid}/resources`);
47
+ }
48
+ async getServerDomains(uuid) {
49
+ return this.request(`/servers/${uuid}/domains`);
50
+ }
51
+ async validateServer(uuid) {
52
+ return this.request(`/servers/${uuid}/validate`);
53
+ }
54
+ async validateConnection() {
55
+ try {
56
+ await this.listServers();
57
+ }
58
+ catch (error) {
59
+ throw new Error(`Failed to connect to Coolify server: ${error instanceof Error ? error.message : 'Unknown error'}`);
60
+ }
61
+ }
62
+ async listProjects() {
63
+ return this.request('/projects');
64
+ }
65
+ async getProject(uuid) {
66
+ return this.request(`/projects/${uuid}`);
67
+ }
68
+ async createProject(project) {
69
+ return this.request('/projects', {
70
+ method: 'POST',
71
+ body: JSON.stringify(project),
72
+ });
73
+ }
74
+ async updateProject(uuid, project) {
75
+ return this.request(`/projects/${uuid}`, {
76
+ method: 'PATCH',
77
+ body: JSON.stringify(project),
78
+ });
79
+ }
80
+ async deleteProject(uuid) {
81
+ return this.request(`/projects/${uuid}`, {
82
+ method: 'DELETE',
83
+ });
84
+ }
85
+ async getProjectEnvironment(projectUuid, environmentNameOrUuid) {
86
+ return this.request(`/projects/${projectUuid}/${environmentNameOrUuid}`);
87
+ }
88
+ async listEnvironments(projectUuid) {
89
+ const path = projectUuid ? `/environments?project_uuid=${projectUuid}` : '/environments';
90
+ return this.request(path);
91
+ }
92
+ async getEnvironment(uuid) {
93
+ return this.request(`/environments/${uuid}`);
94
+ }
95
+ async createEnvironment(environment) {
96
+ return this.request('/environments', {
97
+ method: 'POST',
98
+ body: JSON.stringify(environment),
99
+ });
100
+ }
101
+ async deleteEnvironment(uuid) {
102
+ return this.request(`/environments/${uuid}`, {
103
+ method: 'DELETE',
104
+ });
105
+ }
106
+ async updateEnvironmentVariables(uuid, variables) {
107
+ return this.request(`/environments/${uuid}/variables`, {
108
+ method: 'PUT',
109
+ body: JSON.stringify(variables),
110
+ });
111
+ }
112
+ }
113
+ exports.CoolifyClient = CoolifyClient;
@@ -0,0 +1,42 @@
1
+ import { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
2
+ import { CoolifyConfig, ServerInfo, ServerResources, ServerDomain, ValidationResponse, Project, CreateProjectRequest, UpdateProjectRequest, Environment, CreateEnvironmentRequest } from '../types/coolify.js';
3
+ export declare class CoolifyMcpServer {
4
+ private server;
5
+ private client;
6
+ constructor(config: CoolifyConfig);
7
+ private setupTools;
8
+ start(transport: Transport): Promise<void>;
9
+ list_servers(): Promise<ServerInfo[]>;
10
+ get_server(uuid: string): Promise<ServerInfo>;
11
+ get_server_resources(uuid: string): Promise<ServerResources>;
12
+ get_server_domains(uuid: string): Promise<ServerDomain[]>;
13
+ validate_server(uuid: string): Promise<ValidationResponse>;
14
+ list_projects(): Promise<Project[]>;
15
+ get_project(uuid: string): Promise<Project>;
16
+ create_project(project: CreateProjectRequest): Promise<{
17
+ uuid: string;
18
+ }>;
19
+ update_project(uuid: string, project: UpdateProjectRequest): Promise<Project>;
20
+ delete_project(uuid: string): Promise<{
21
+ message: string;
22
+ }>;
23
+ get_project_environment(projectUuid: string, environmentNameOrUuid: string): Promise<Environment>;
24
+ list_environments(params: {
25
+ project_uuid?: string;
26
+ }): Promise<Environment[]>;
27
+ get_environment(params: {
28
+ uuid: string;
29
+ }): Promise<Environment>;
30
+ create_environment(params: CreateEnvironmentRequest): Promise<{
31
+ uuid: string;
32
+ }>;
33
+ update_environment_variables(params: {
34
+ uuid: string;
35
+ variables: Record<string, string>;
36
+ }): Promise<Environment>;
37
+ delete_environment(params: {
38
+ uuid: string;
39
+ }): Promise<{
40
+ message: string;
41
+ }>;
42
+ }