#
tokens: 44920/50000 49/56 files (page 1/5)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 1 of 5. Use http://codebase.md/stumason/coolify-mcp?lines=true&page={x} to view the full context.

# Directory Structure

```
├── .cursor
│   └── rules
│       ├── 000-cursor-rules.mdc
│       ├── 801-feature-workflow.mdc
│       ├── 802-coolify-mcp-workflow.mdc
│       └── 803-npm-publish-workflow.mdc
├── .eslintrc.json
├── .github
│   └── workflows
│       └── ci.yml
├── .gitignore
├── .lintstagedrc.json
├── .markdownlint-cli2.jsonc
├── .prettierrc
├── .repomixignore
├── debug.js
├── docs
│   ├── coolify-openapi.yaml
│   ├── features
│   │   ├── 001-core-server-setup.md
│   │   ├── 002-server-info-resource.md
│   │   ├── 003-project-management.md
│   │   ├── 004-environment-management.md
│   │   ├── 005-application-deployment.md
│   │   ├── 006-database-management.md
│   │   ├── 007-service-management.md
│   │   ├── 008-mcp-resources-implementation.md
│   │   ├── 009-mcp-prompts-implementation.md
│   │   ├── 010-private-key-management.md
│   │   ├── 011-team-management.md
│   │   ├── 012-backup-management.md
│   │   ├── 013-npx-config-fix.md
│   │   └── future-adrs.md
│   ├── mcp-example-clients.md
│   ├── mcp-js-readme.md
│   └── openapi-chunks
│       ├── applications-api.yaml
│       ├── databases-api.yaml
│       ├── deployments-api.yaml
│       ├── private-keys-api.yaml
│       ├── projects-api.yaml
│       ├── resources-api.yaml
│       ├── schemas.yaml
│       ├── servers-api.yaml
│       ├── services-api.yaml
│       ├── teams-api.yaml
│       └── untagged-api.yaml
├── jest.config.js
├── package-lock.json
├── package.json
├── README.md
├── repomix-output.xml
├── src
│   ├── __tests__
│   │   ├── coolify-client.test.ts
│   │   └── resources
│   │       ├── application-resources.test.ts
│   │       ├── database-resources.test.ts
│   │       ├── deployment-resources.test.ts
│   │       └── service-resources.test.ts
│   ├── index.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
└── tsconfig.json
```

# Files

--------------------------------------------------------------------------------
/.repomixignore:
--------------------------------------------------------------------------------

```
1 | .cursor/
2 | .github/
3 | .husky/
4 | docs/
5 | package-lock.json
6 | 
7 | 
```

--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------

```
1 | {
2 |   "semi": true,
3 |   "trailingComma": "all",
4 |   "singleQuote": true,
5 |   "printWidth": 100,
6 |   "tabWidth": 2
7 | }
8 | 
```

--------------------------------------------------------------------------------
/.lintstagedrc.json:
--------------------------------------------------------------------------------

```json
1 | {
2 |   "src/**/*.ts": ["eslint --fix", "prettier --write"],
3 |   "*.json": ["prettier --write"],
4 |   "*.md": ["prettier --write", "markdownlint-cli2"]
5 | }
6 | 
```

--------------------------------------------------------------------------------
/.markdownlint-cli2.jsonc:
--------------------------------------------------------------------------------

```
1 | {
2 |   "config": {
3 |     "line-length": false,
4 |     "no-duplicate-heading": false,
5 |     "no-inline-html": false,
6 |   },
7 |   "ignores": ["node_modules", "dist"],
8 | }
9 | 
```

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
 1 | # Dependencies
 2 | node_modules/
 3 | 
 4 | # Build output
 5 | dist/
 6 | build/
 7 | /lib/
 8 | coverage/
 9 | 
10 | # IDE and editor files
11 | .idea/
12 | .vscode/
13 | *.swp
14 | *.swo
15 | .DS_Store
16 | Thumbs.db
17 | 
18 | # Environment variables
19 | .env
20 | .env.local
21 | .env.*.local
22 | 
23 | # Logs
24 | logs/
25 | *.log
26 | npm-debug.log*
27 | yarn-debug.log*
28 | yarn-error.log*
29 | 
30 | # Test coverage
31 | coverage/
32 | .nyc_output/
33 | 
34 | # Optional npm cache directory
35 | .npm
36 | 
37 | # Optional eslint cache
38 | .eslintcache
39 | 
40 | # Optional REPL history
41 | .node_repl_history
42 | 
43 | # Output of 'npm pack'
44 | *.tgz
45 | 
46 | # Yarn Integrity file
47 | .yarn-integrity 
```

--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "env": {
 3 |     "node": true,
 4 |     "es2021": true
 5 |   },
 6 |   "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended", "prettier"],
 7 |   "parser": "@typescript-eslint/parser",
 8 |   "parserOptions": {
 9 |     "ecmaVersion": "latest",
10 |     "sourceType": "module"
11 |   },
12 |   "plugins": ["@typescript-eslint"],
13 |   "rules": {
14 |     "@typescript-eslint/explicit-function-return-type": "warn",
15 |     "@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }],
16 |     "@typescript-eslint/no-explicit-any": "warn"
17 |   }
18 | }
19 | 
```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
  1 | # Coolify MCP Server
  2 | 
  3 | A Model Context Protocol (MCP) server implementation for [Coolify](https://coolify.io/), enabling AI assistants to interact with your Coolify instances through natural language.
  4 | 
  5 | ## Example Prompts
  6 | 
  7 | Here are example prompts you can use with MCP-compatible AI assistants to interact with your Coolify instance:
  8 | 
  9 | ### Server Management
 10 | 
 11 | ```
 12 | # List and Inspect Servers
 13 | - Show me all Coolify servers in my instance
 14 | - What's the status of server {uuid}?
 15 | - Show me the resources running on server {uuid}
 16 | - What domains are configured for server {uuid}?
 17 | - Can you validate the connection to server {uuid}?
 18 | 
 19 | # Resource Monitoring
 20 | - How much CPU and memory is server {uuid} using?
 21 | - List all resources running on server {uuid}
 22 | - Show me the current status of all servers
 23 | ```
 24 | 
 25 | ### Project Management
 26 | 
 27 | ```
 28 | # Project Operations
 29 | - List all my Coolify projects
 30 | - Create a new project called "my-webapp" with description "My web application"
 31 | - Show me the details of project {uuid}
 32 | - Update project {uuid} to change its name to "new-name"
 33 | - Delete project {uuid}
 34 | 
 35 | # Environment Management
 36 | - Show me the environments in project {uuid}
 37 | - Get details of the production environment in project {uuid}
 38 | - What variables are set in the staging environment of project {uuid}?
 39 | ```
 40 | 
 41 | ### Application and Service Management
 42 | 
 43 | ```
 44 | # Application Management
 45 | - List all applications
 46 | - Show me details of application {uuid}
 47 | - Create a new application called "my-nodejs-app"
 48 | - Delete application {uuid}
 49 | 
 50 | # Service Operations
 51 | - Show me all running services
 52 | - Create a new WordPress service:
 53 |   - Name: my-blog
 54 |   - Project UUID: {project_uuid}
 55 |   - Server UUID: {server_uuid}
 56 |   - Type: wordpress-with-mysql
 57 | - What's the status of service {uuid}?
 58 | - Delete service {uuid} and clean up its resources
 59 | ```
 60 | 
 61 | ### Database Management
 62 | 
 63 | ```
 64 | # Database Operations
 65 | - List all databases
 66 | - Show me the configuration of database {uuid}
 67 | - Update database {uuid}:
 68 |   - Increase memory limit to 1GB
 69 |   - Change public port to 5432
 70 |   - Update password
 71 | - Delete database {uuid} and clean up volumes
 72 | 
 73 | # Database Types
 74 | - Create a PostgreSQL database
 75 | - Set up a Redis instance
 76 | - Configure a MongoDB database
 77 | - Initialize a MySQL database
 78 | ```
 79 | 
 80 | ### Deployment Management
 81 | 
 82 | ```
 83 | # Deployment Operations
 84 | - Show me all active deployments
 85 | - What's the status of deployment {uuid}?
 86 | - Deploy application {uuid}
 87 | - Force rebuild and deploy application {uuid}
 88 | - List recent deployments for application {uuid}
 89 | ```
 90 | 
 91 | ## Installation
 92 | 
 93 | ### Prerequisites
 94 | 
 95 | - Node.js >= 18
 96 | - A running Coolify instance
 97 | - Coolify API access token
 98 | 
 99 | ### Setup in AI Tools
100 | 
101 | #### Claude Desktop
102 | 
103 | ```json
104 | "coolify": {
105 |     "command": "npx",
106 |     "args": [
107 |         "-y", "@masonator/coolify-mcp"
108 |     ],
109 |     "env": {
110 |         "COOLIFY_ACCESS_TOKEN": "0|your-secret-token",
111 |         "COOLIFY_BASE_URL": "https://your-coolify-instance.com"
112 |     }
113 | }
114 | ```
115 | 
116 | #### Cursor
117 | 
118 | ```bash
119 | env COOLIFY_ACCESS_TOKEN:0|your-secret-token COOLIFY_BASE_URL:https://your-coolify-instance.com npx -y @stumason/coolify-mcp
120 | ```
121 | 
122 | ## Development
123 | 
124 | ### Local Setup
125 | 
126 | ```bash
127 | # Clone the repository
128 | git clone https://github.com/stumason/coolify-mcp.git
129 | cd coolify-mcp
130 | 
131 | # Install dependencies
132 | npm install
133 | 
134 | # Build the project
135 | npm run build
136 | 
137 | # Run tests
138 | npm test
139 | ```
140 | 
141 | ### Environment Variables
142 | 
143 | ```bash
144 | # Required
145 | COOLIFY_ACCESS_TOKEN=your_access_token_here
146 | 
147 | # Optional (defaults to http://localhost:3000)
148 | COOLIFY_BASE_URL=https://your.coolify.instance
149 | ```
150 | 
151 | ## API Reference
152 | 
153 | ### Resource Types
154 | 
155 | #### Application
156 | 
157 | ```typescript
158 | interface Application {
159 |   uuid: string;
160 |   name: string;
161 |   // Additional properties based on your Coolify instance
162 | }
163 | ```
164 | 
165 | #### Service
166 | 
167 | ```typescript
168 | interface Service {
169 |   id: number;
170 |   uuid: string;
171 |   name: string;
172 |   type: ServiceType; // Various types like 'wordpress', 'mysql', etc.
173 |   status: 'running' | 'stopped' | 'error';
174 |   project_uuid: string;
175 |   environment_uuid: string;
176 |   server_uuid: string;
177 |   domains?: string[];
178 | }
179 | ```
180 | 
181 | #### Database
182 | 
183 | ```typescript
184 | interface Database {
185 |   id: number;
186 |   uuid: string;
187 |   name: string;
188 |   type: 'postgresql' | 'mysql' | 'mongodb' | 'redis' | /* other types */;
189 |   status: 'running' | 'stopped' | 'error';
190 |   is_public: boolean;
191 |   public_port?: number;
192 |   // Additional configuration based on database type
193 | }
194 | ```
195 | 
196 | #### Deployment
197 | 
198 | ```typescript
199 | interface Deployment {
200 |   id: number;
201 |   uuid: string;
202 |   application_uuid: string;
203 |   status: string;
204 |   created_at: string;
205 |   updated_at: string;
206 | }
207 | ```
208 | 
209 | ## Contributing
210 | 
211 | 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.
212 | 
213 | ## License
214 | 
215 | MIT
216 | 
217 | ## Support
218 | 
219 | For support, please:
220 | 
221 | 1. Check the [issues](https://github.com/stumason/coolify-mcp/issues) page
222 | 2. Create a new issue if needed
223 | 3. Join the Coolify community
224 | 
```

--------------------------------------------------------------------------------
/docs/features/012-backup-management.md:
--------------------------------------------------------------------------------

```markdown
1 | 
```

--------------------------------------------------------------------------------
/src/resources/index.ts:
--------------------------------------------------------------------------------

```typescript
1 | export * from './database-resources.js';
2 | export * from './deployment-resources.js';
3 | export * from './application-resources.js';
4 | export * from './service-resources.js';
5 | 
```

--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------

```javascript
 1 | /** @type {import('ts-jest').JestConfigWithTsJest} */
 2 | export default {
 3 |   preset: 'ts-jest',
 4 |   testEnvironment: 'node',
 5 |   moduleNameMapper: {
 6 |     '^(\\.{1,2}/.*)\\.js$': '$1',
 7 |   },
 8 |   transform: {
 9 |     '^.+\\.tsx?$': [
10 |       'ts-jest',
11 |       {
12 |         useESM: true,
13 |       },
14 |     ],
15 |   },
16 |   extensionsToTreatAsEsm: ['.ts'],
17 |   testPathIgnorePatterns: ['/node_modules/', '/dist/', '\\.d\\.ts$'],
18 | };
19 | 
```

--------------------------------------------------------------------------------
/debug.js:
--------------------------------------------------------------------------------

```javascript
 1 | import { CoolifyMcpServer } from './dist/lib/mcp-server.js';
 2 | import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
 3 | 
 4 | // Enable debug logging
 5 | process.env.DEBUG = '*';
 6 | 
 7 | const server = new CoolifyMcpServer({
 8 |     baseUrl: 'https://coolify.dev',  // Replace with your actual Coolify URL
 9 |     accessToken: 'your-actual-token'   // Replace with your actual Coolify token
10 | });
11 | 
12 | const transport = new StdioServerTransport();
13 | await server.connect(transport); 
```

--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "compilerOptions": {
 3 |     "target": "ES2020",
 4 |     "module": "NodeNext",
 5 |     "moduleResolution": "NodeNext",
 6 |     "declaration": true,
 7 |     "outDir": "./dist",
 8 |     "strict": true,
 9 |     "esModuleInterop": true,
10 |     "skipLibCheck": true,
11 |     "forceConsistentCasingInFileNames": true,
12 |     "experimentalDecorators": true,
13 |     "emitDecoratorMetadata": true,
14 |     "allowJs": true,
15 |     "resolveJsonModule": true
16 |   },
17 |   "include": ["src"],
18 |   "exclude": ["node_modules", "dist", "tests"]
19 | }
20 | 
```

--------------------------------------------------------------------------------
/docs/openapi-chunks/resources-api.yaml:
--------------------------------------------------------------------------------

```yaml
 1 | openapi: 3.1.0
 2 | info:
 3 |   title: Coolify
 4 |   version: '0.1'
 5 | paths:
 6 |   /resources:
 7 |     get:
 8 |       tags:
 9 |         - Resources
10 |       summary: List
11 |       description: Get all resources.
12 |       operationId: list-resources
13 |       responses:
14 |         '200':
15 |           description: Get all resources
16 |           content:
17 |             application/json:
18 |               schema:
19 |                 type: string
20 |               example: Content is very complex. Will be implemented later.
21 |         '400':
22 |           $ref: '#/components/responses/400'
23 |         '401':
24 |           $ref: '#/components/responses/401'
25 |       security:
26 |         - bearerAuth: []
27 | 
```

--------------------------------------------------------------------------------
/docs/features/001-core-server-setup.md:
--------------------------------------------------------------------------------

```markdown
 1 | # ADR 001: Core Server Setup
 2 | 
 3 | ## Context
 4 | 
 5 | Need basic MCP server implementation that can authenticate with Coolify and handle basic operations.
 6 | 
 7 | ## Implementation Checklist
 8 | 
 9 | - [x] Project structure setup
10 | 
11 |   - [x] TypeScript configuration
12 |   - [x] ESLint + Prettier
13 |   - [x] Jest/Vitest setup
14 |   - [x] Basic GitHub Actions CI
15 | 
16 | - [x] Core MCP Server
17 | 
18 |   - [x] Basic server class implementation
19 |   - [x] Environment configuration (COOLIFY_ACCESS_TOKEN, COOLIFY_BASE_URL)
20 |   - [x] Coolify API client wrapper
21 |   - [x] Error handling structure
22 | 
23 | - [x] Testing Infrastructure
24 |   - [x] Mock Coolify API responses
25 |   - [x] Basic integration test framework
26 | 
27 | ## Dependencies
28 | 
29 | - None (This is our first implementation)
30 | 
```

--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------

```yaml
 1 | name: CI
 2 | 
 3 | on:
 4 |   push:
 5 |     branches: [main]
 6 |   pull_request:
 7 |     branches: [main]
 8 | 
 9 | jobs:
10 |   build:
11 |     runs-on: ubuntu-latest
12 | 
13 |     strategy:
14 |       matrix:
15 |         node-version: [18.x, 20.x]
16 | 
17 |     steps:
18 |       - uses: actions/checkout@v4
19 | 
20 |       - name: Use Node.js ${{ matrix.node-version }}
21 |         uses: actions/setup-node@v4
22 |         with:
23 |           node-version: ${{ matrix.node-version }}
24 |           cache: 'npm'
25 | 
26 |       - name: Install dependencies
27 |         run: npm ci
28 | 
29 |       - name: Security audit
30 |         run: npm audit --audit-level=high
31 | 
32 |       - name: Format check
33 |         run: npx prettier --check .
34 | 
35 |       - name: Lint
36 |         run: npm run lint
37 | 
38 |       - name: Build
39 |         run: npm run build
40 | 
41 |       - name: Test
42 |         run: npm test
43 | 
```

--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------

```typescript
 1 | #!/usr/bin/env node
 2 | 
 3 | import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
 4 | import { CoolifyMcpServer } from './lib/mcp-server.js';
 5 | import { CoolifyConfig } from './types/coolify.js';
 6 | 
 7 | declare const process: NodeJS.Process;
 8 | 
 9 | async function main(): Promise<void> {
10 |   const config: CoolifyConfig = {
11 |     baseUrl: process.env.COOLIFY_BASE_URL || 'http://localhost:3000',
12 |     accessToken: process.env.COOLIFY_ACCESS_TOKEN || '',
13 |   };
14 | 
15 |   if (!config.accessToken) {
16 |     throw new Error('COOLIFY_ACCESS_TOKEN environment variable is required');
17 |   }
18 | 
19 |   const server = new CoolifyMcpServer(config);
20 |   const transport = new StdioServerTransport();
21 | 
22 |   await server.connect(transport);
23 | }
24 | 
25 | main().catch((error) => {
26 |   console.error('Fatal error:', error);
27 |   process.exit(1);
28 | });
29 | 
```

--------------------------------------------------------------------------------
/src/resources/deployment-resources.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { Resource } from '../lib/resource.js';
 2 | import { CoolifyClient } from '../lib/coolify-client.js';
 3 | import { Deployment } from '../types/coolify.js';
 4 | 
 5 | export class DeploymentResources {
 6 |   private client: CoolifyClient;
 7 | 
 8 |   constructor(client: CoolifyClient) {
 9 |     this.client = client;
10 |   }
11 | 
12 |   @Resource('coolify/deployments/list')
13 |   async listDeployments(): Promise<Deployment[]> {
14 |     // TODO: Implement listDeployments in CoolifyClient
15 |     throw new Error('Not implemented');
16 |   }
17 | 
18 |   @Resource('coolify/deployments/{id}')
19 |   async getDeployment(_id: string): Promise<Deployment> {
20 |     // TODO: Implement getDeployment in CoolifyClient
21 |     throw new Error('Not implemented');
22 |   }
23 | 
24 |   @Resource('coolify/deploy')
25 |   async deploy(params: { uuid: string; forceRebuild?: boolean }): Promise<Deployment> {
26 |     return this.client.deployApplication(params.uuid);
27 |   }
28 | }
29 | 
```

--------------------------------------------------------------------------------
/src/lib/resource.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import 'reflect-metadata';
 2 | 
 3 | /**
 4 |  * Metadata key for storing the resource URI
 5 |  */
 6 | const RESOURCE_URI_KEY = Symbol('resourceUri');
 7 | 
 8 | /**
 9 |  * Decorator for marking methods as MCP resources.
10 |  * @param uri The URI pattern for the resource
11 |  */
12 | export function Resource(uri: string): MethodDecorator {
13 |   return function (
14 |     target: object,
15 |     propertyKey: string | symbol,
16 |     descriptor: PropertyDescriptor,
17 |   ): PropertyDescriptor {
18 |     // Store the URI pattern in the method's metadata
19 |     Reflect.defineMetadata(RESOURCE_URI_KEY, uri, target, propertyKey);
20 |     return descriptor;
21 |   };
22 | }
23 | 
24 | /**
25 |  * Get the resource URI for a decorated method
26 |  * @param target The class instance or constructor
27 |  * @param propertyKey The method name
28 |  * @returns The resource URI or undefined if not a resource
29 |  */
30 | export function getResourceUri(target: object, propertyKey: string | symbol): string | undefined {
31 |   return Reflect.getMetadata(RESOURCE_URI_KEY, target, propertyKey);
32 | }
33 | 
```

--------------------------------------------------------------------------------
/src/resources/service-resources.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { Resource } from '../lib/resource.js';
 2 | import { CoolifyClient } from '../lib/coolify-client.js';
 3 | import { Service, CreateServiceRequest, DeleteServiceOptions } from '../types/coolify.js';
 4 | 
 5 | export class ServiceResources {
 6 |   private client: CoolifyClient;
 7 | 
 8 |   constructor(client: CoolifyClient) {
 9 |     this.client = client;
10 |   }
11 | 
12 |   @Resource('coolify/services/list')
13 |   async listServices(): Promise<Service[]> {
14 |     return this.client.listServices();
15 |   }
16 | 
17 |   @Resource('coolify/services/{id}')
18 |   async getService(id: string): Promise<Service> {
19 |     return this.client.getService(id);
20 |   }
21 | 
22 |   @Resource('coolify/services/create')
23 |   async createService(data: CreateServiceRequest): Promise<{ uuid: string; domains: string[] }> {
24 |     return this.client.createService(data);
25 |   }
26 | 
27 |   @Resource('coolify/services/{id}/delete')
28 |   async deleteService(id: string, options?: DeleteServiceOptions): Promise<{ message: string }> {
29 |     return this.client.deleteService(id, options);
30 |   }
31 | }
32 | 
```

--------------------------------------------------------------------------------
/docs/features/002-server-info-resource.md:
--------------------------------------------------------------------------------

```markdown
 1 | # ADR 002: Server Information Resources
 2 | 
 3 | ## Context
 4 | 
 5 | First useful feature - ability to get server information and status through MCP resources.
 6 | 
 7 | ## API Endpoints Used
 8 | 
 9 | - GET `/servers` (Line ~500)
10 | 
11 |   - Lists all servers
12 |   - Response: Array of Server objects
13 |   - Auth: Bearer token required
14 | 
15 | - GET `/servers/{uuid}` (Line ~550)
16 | 
17 |   - Get server details
18 |   - Response: Server object with status
19 |   - Auth: Bearer token required
20 | 
21 | - GET `/servers/{uuid}/status` (Line ~600)
22 |   - Get server health and resource usage
23 |   - Response: ServerStatus object
24 |   - Auth: Bearer token required
25 | 
26 | ## Implementation Checklist
27 | 
28 | - [x] Basic Resource Implementation
29 | 
30 |   - [x] Server info resource (resources://coolify/server/info)
31 |     - [x] Basic server details
32 |     - [x] Version information
33 |   - [x] Server status resource (resources://coolify/server/status)
34 |     - [x] Health check
35 |     - [x] Resource usage
36 | 
37 | - [x] Resource Testing
38 |   - [x] Unit tests for resource formatters
39 |   - [x] Integration tests with mock data
40 |   - [x] Live test with real Coolify instance
41 | 
42 | ## Dependencies
43 | 
44 | - ADR 001 (Core Server Setup)
45 | 
```

--------------------------------------------------------------------------------
/src/resources/database-resources.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { Resource } from '../lib/resource.js';
 2 | import { CoolifyClient } from '../lib/coolify-client.js';
 3 | import { Database, DatabaseUpdateRequest } from '../types/coolify.js';
 4 | 
 5 | export class DatabaseResources {
 6 |   private client: CoolifyClient;
 7 | 
 8 |   constructor(client: CoolifyClient) {
 9 |     this.client = client;
10 |   }
11 | 
12 |   @Resource('coolify/databases/list')
13 |   async listDatabases(): Promise<Database[]> {
14 |     return this.client.listDatabases();
15 |   }
16 | 
17 |   @Resource('coolify/databases/{id}')
18 |   async getDatabase(id: string): Promise<Database> {
19 |     return this.client.getDatabase(id);
20 |   }
21 | 
22 |   @Resource('coolify/databases/{id}/update')
23 |   async updateDatabase(id: string, data: DatabaseUpdateRequest): Promise<Database> {
24 |     return this.client.updateDatabase(id, data);
25 |   }
26 | 
27 |   @Resource('coolify/databases/{id}/delete')
28 |   async deleteDatabase(
29 |     id: string,
30 |     options?: {
31 |       deleteConfigurations?: boolean;
32 |       deleteVolumes?: boolean;
33 |       dockerCleanup?: boolean;
34 |       deleteConnectedNetworks?: boolean;
35 |     },
36 |   ): Promise<{ message: string }> {
37 |     return this.client.deleteDatabase(id, options);
38 |   }
39 | }
40 | 
```

--------------------------------------------------------------------------------
/src/resources/application-resources.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { Resource } from '../lib/resource.js';
 2 | import { CoolifyClient } from '../lib/coolify-client.js';
 3 | import { Application, CreateApplicationRequest } from '../types/coolify.js';
 4 | 
 5 | export class ApplicationResources {
 6 |   private client: CoolifyClient;
 7 | 
 8 |   constructor(client: CoolifyClient) {
 9 |     this.client = client;
10 |   }
11 | 
12 |   @Resource('coolify/applications/list')
13 |   async listApplications(): Promise<Application[]> {
14 |     // TODO: Implement listApplications in CoolifyClient
15 |     throw new Error('Not implemented');
16 |   }
17 | 
18 |   @Resource('coolify/applications/{id}')
19 |   async getApplication(_id: string): Promise<Application> {
20 |     // TODO: Implement getApplication in CoolifyClient
21 |     throw new Error('Not implemented');
22 |   }
23 | 
24 |   @Resource('coolify/applications/create')
25 |   async createApplication(_data: CreateApplicationRequest): Promise<{ uuid: string }> {
26 |     // TODO: Implement createApplication in CoolifyClient
27 |     throw new Error('Not implemented');
28 |   }
29 | 
30 |   @Resource('coolify/applications/{id}/delete')
31 |   async deleteApplication(_id: string): Promise<{ message: string }> {
32 |     // TODO: Implement deleteApplication in CoolifyClient
33 |     throw new Error('Not implemented');
34 |   }
35 | }
36 | 
```

--------------------------------------------------------------------------------
/src/__tests__/resources/application-resources.test.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { ApplicationResources } from '../../resources/application-resources.js';
 2 | import { CoolifyClient } from '../../lib/coolify-client.js';
 3 | import { jest } from '@jest/globals';
 4 | 
 5 | jest.mock('../../lib/coolify-client.js');
 6 | 
 7 | describe('ApplicationResources', () => {
 8 |   let resources: ApplicationResources;
 9 |   let mockClient: jest.Mocked<CoolifyClient>;
10 | 
11 |   beforeEach(() => {
12 |     mockClient = {
13 |       deployApplication: jest.fn(),
14 |     } as unknown as jest.Mocked<CoolifyClient>;
15 | 
16 |     resources = new ApplicationResources(mockClient);
17 |   });
18 | 
19 |   describe('listApplications', () => {
20 |     it('should throw not implemented error', async () => {
21 |       await expect(resources.listApplications()).rejects.toThrow('Not implemented');
22 |     });
23 |   });
24 | 
25 |   describe('getApplication', () => {
26 |     it('should throw not implemented error', async () => {
27 |       await expect(resources.getApplication('test-id')).rejects.toThrow('Not implemented');
28 |     });
29 |   });
30 | 
31 |   describe('createApplication', () => {
32 |     it('should throw not implemented error', async () => {
33 |       await expect(resources.createApplication({ name: 'test-app' })).rejects.toThrow(
34 |         'Not implemented',
35 |       );
36 |     });
37 |   });
38 | 
39 |   describe('deleteApplication', () => {
40 |     it('should throw not implemented error', async () => {
41 |       await expect(resources.deleteApplication('test-id')).rejects.toThrow('Not implemented');
42 |     });
43 |   });
44 | });
45 | 
```

--------------------------------------------------------------------------------
/docs/features/011-team-management.md:
--------------------------------------------------------------------------------

```markdown
 1 | # ADR 011: Team Management
 2 | 
 3 | ## Context
 4 | 
 5 | Implementation of team management features through MCP resources, allowing users to manage teams and team members in Coolify.
 6 | 
 7 | ## API Endpoints Used
 8 | 
 9 | - GET `/teams` (List)
10 | 
11 |   - Lists all teams
12 |   - Response: Array of Team objects
13 |   - Auth: Bearer token required
14 | 
15 | - GET `/teams/{id}` (Get)
16 | 
17 |   - Get team details by ID
18 |   - Response: Team object
19 |   - Auth: Bearer token required
20 | 
21 | - GET `/teams/{id}/members` (List Members)
22 | 
23 |   - Get team members by team ID
24 |   - Response: Array of User objects
25 |   - Auth: Bearer token required
26 | 
27 | - GET `/teams/current` (Get Current)
28 | 
29 |   - Get currently authenticated team
30 |   - Response: Team object
31 |   - Auth: Bearer token required
32 | 
33 | - GET `/teams/current/members` (Get Current Members)
34 |   - Get currently authenticated team members
35 |   - Response: Array of User objects
36 |   - Auth: Bearer token required
37 | 
38 | ## Implementation Checklist
39 | 
40 | - [ ] Basic Team Management
41 | 
42 |   - [ ] List teams resource
43 |   - [ ] Get team details
44 |   - [ ] List team members
45 |   - [ ] Get current team
46 |   - [ ] Get current team members
47 | 
48 | - [ ] Team Features
49 | 
50 |   - [ ] Team information display
51 |   - [ ] Member list management
52 |   - [ ] Team permissions handling
53 |   - [ ] Current team context
54 | 
55 | - [ ] Resource Testing
56 |   - [ ] Unit tests for team operations
57 |   - [ ] Integration tests with mock data
58 |   - [ ] Live test with real Coolify instance
59 |   - [ ] Permission testing
60 | 
61 | ## Dependencies
62 | 
63 | - ADR 001 (Core Server Setup)
64 | - ADR 002 (Server Information Resources)
65 | 
```

--------------------------------------------------------------------------------
/docs/features/003-project-management.md:
--------------------------------------------------------------------------------

```markdown
 1 | # ADR 003: Project Management
 2 | 
 3 | ## Context
 4 | 
 5 | Basic project management functionality - first interactive feature allowing users to create and manage projects.
 6 | 
 7 | ## API Endpoints Used
 8 | 
 9 | - GET `/projects` (Line ~800)
10 | 
11 |   - Lists all projects
12 |   - Response: Array of Project objects
13 |   - Auth: Bearer token required
14 | 
15 | - POST `/projects` (Line ~850)
16 | 
17 |   - Create new project
18 |   - Request body: { name: string, description?: string }
19 |   - Response: Project object
20 |   - Auth: Bearer token required
21 | 
22 | - GET `/projects/{uuid}` (Line ~900)
23 | 
24 |   - Get project details
25 |   - Response: Project object with relationships
26 |   - Auth: Bearer token required
27 | 
28 | - DELETE `/projects/{uuid}` (Line ~950)
29 | 
30 |   - Delete project
31 |   - Response: 204 No Content
32 |   - Auth: Bearer token required
33 | 
34 | - PUT `/projects/{uuid}` (Line ~1000)
35 |   - Update project
36 |   - Request body: { name?: string, description?: string }
37 |   - Response: Updated Project object
38 |   - Auth: Bearer token required
39 | 
40 | ## Implementation Checklist
41 | 
42 | - [x] Project List Resource
43 | 
44 |   - [x] resources://coolify/projects/list implementation
45 |   - [x] Pagination support
46 |   - [x] Basic filtering
47 | 
48 | - [x] Project Management Tools
49 | 
50 |   - [x] createProject tool
51 |   - [x] deleteProject tool
52 |   - [x] updateProject tool
53 | 
54 | - [x] Project Detail Resource
55 | 
56 |   - [x] resources://coolify/projects/{id} implementation
57 |   - [x] Project status and configuration
58 | 
59 | - [x] Testing
60 |   - [x] CRUD operation tests
61 |   - [x] Error handling tests
62 |   - [x] Resource format tests
63 | 
64 | ## Dependencies
65 | 
66 | - ADR 001 (Core Server Setup)
67 | - ADR 002 (Server Information Resources)
68 | 
```

--------------------------------------------------------------------------------
/docs/features/010-private-key-management.md:
--------------------------------------------------------------------------------

```markdown
 1 | # ADR 010: Private Key Management
 2 | 
 3 | ## Context
 4 | 
 5 | Implementation of private key management features through MCP resources, allowing users to manage SSH keys for server access and deployment.
 6 | 
 7 | ## API Endpoints Used
 8 | 
 9 | - GET `/security/keys` (List)
10 | 
11 |   - Lists all private keys
12 |   - Response: Array of PrivateKey objects
13 |   - Auth: Bearer token required
14 | 
15 | - POST `/security/keys` (Create)
16 | 
17 |   - Create a new private key
18 |   - Required fields:
19 |     - private_key
20 |   - Optional fields:
21 |     - name
22 |     - description
23 |   - Response: { uuid: string }
24 |   - Auth: Bearer token required
25 | 
26 | - GET `/security/keys/{uuid}` (Get)
27 | 
28 |   - Get private key details
29 |   - Response: PrivateKey object
30 |   - Auth: Bearer token required
31 | 
32 | - PATCH `/security/keys` (Update)
33 | 
34 |   - Update a private key
35 |   - Required fields:
36 |     - private_key
37 |   - Optional fields:
38 |     - name
39 |     - description
40 |   - Response: { uuid: string }
41 |   - Auth: Bearer token required
42 | 
43 | - DELETE `/security/keys/{uuid}` (Delete)
44 |   - Delete a private key
45 |   - Response: { message: string }
46 |   - Auth: Bearer token required
47 | 
48 | ## Implementation Checklist
49 | 
50 | - [ ] Basic Key Management
51 | 
52 |   - [ ] List private keys resource
53 |   - [ ] Get private key details
54 |   - [ ] Create private key
55 |   - [ ] Update private key
56 |   - [ ] Delete private key
57 | 
58 | - [ ] Security Features
59 | 
60 |   - [ ] Secure key storage
61 |   - [ ] Key validation
62 |   - [ ] Usage tracking
63 |   - [ ] Access control
64 | 
65 | - [ ] Resource Testing
66 |   - [ ] Unit tests for key operations
67 |   - [ ] Integration tests with mock data
68 |   - [ ] Live test with real Coolify instance
69 |   - [ ] Security testing
70 | 
71 | ## Dependencies
72 | 
73 | - ADR 001 (Core Server Setup)
74 | - ADR 002 (Server Information Resources)
75 | 
```

--------------------------------------------------------------------------------
/docs/features/004-environment-management.md:
--------------------------------------------------------------------------------

```markdown
 1 | # ADR 004: Environment Management
 2 | 
 3 | ## Context
 4 | 
 5 | Environment management within projects - allows retrieving environment information and deploying applications within environments.
 6 | 
 7 | ## API Endpoints Used
 8 | 
 9 | - GET `/projects/{uuid}/{environment_name_or_uuid}`
10 | 
11 |   - Get environment details by project UUID and environment name/UUID
12 |   - Response: Environment object
13 |   - Auth: Bearer token required
14 | 
15 | - POST `/applications/{uuid}/deploy`
16 |   - Deploy an application using its UUID
17 |   - Response: Deployment object
18 |   - Auth: Bearer token required
19 | 
20 | Note: Environment creation and management is handled through the Projects API. Environments are created and configured as part of project setup.
21 | 
22 | ## Implementation Status
23 | 
24 | ### Completed
25 | 
26 | - [x] Environment Detail Resource
27 | 
28 |   - [x] GET project environment endpoint implemented
29 |   - [x] Client method: `getProjectEnvironment`
30 |   - [x] MCP tool: `get_project_environment`
31 | 
32 | - [x] Application Deployment
33 |   - [x] Deploy application endpoint implemented
34 |   - [x] Client method: `deployApplication`
35 |   - [x] MCP tool: `deploy_application`
36 | 
37 | ### Environment Schema
38 | 
39 | ```typescript
40 | interface Environment {
41 |   id: number;
42 |   name: string;
43 |   project_id: number;
44 |   created_at: string;
45 |   updated_at: string;
46 |   description: string;
47 | }
48 | ```
49 | 
50 | ## Dependencies
51 | 
52 | - ADR 001 (Core Server Setup)
53 | - ADR 003 (Project Management)
54 | 
55 | ## Notes
56 | 
57 | - Environment management is tightly coupled with projects in the Coolify API
58 | - Environment variables are managed at the application level during application creation/updates
59 | - Direct environment CRUD operations are not available through dedicated endpoints
60 | - Environment information can be retrieved through the project endpoints
61 | 
```

--------------------------------------------------------------------------------
/docs/features/006-database-management.md:
--------------------------------------------------------------------------------

```markdown
 1 | # ADR 006: Database Management
 2 | 
 3 | ## Context
 4 | 
 5 | Implementation of database management features through MCP resources, allowing users to manage various types of databases (PostgreSQL, MySQL, MariaDB, MongoDB, Redis, etc.).
 6 | 
 7 | ## API Endpoints Used
 8 | 
 9 | - GET `/databases` (List)
10 | 
11 |   - Lists all databases
12 |   - Response: Array of Database objects
13 |   - Auth: Bearer token required
14 | 
15 | - GET `/databases/{uuid}` (Get)
16 | 
17 |   - Get database details
18 |   - Response: Database object
19 |   - Auth: Bearer token required
20 | 
21 | - DELETE `/databases/{uuid}` (Delete)
22 | 
23 |   - Delete database
24 |   - Optional query params:
25 |     - delete_configurations (boolean, default: true)
26 |     - delete_volumes (boolean, default: true)
27 |     - docker_cleanup (boolean, default: true)
28 |     - delete_connected_networks (boolean, default: true)
29 |   - Auth: Bearer token required
30 | 
31 | - PATCH `/databases/{uuid}` (Update)
32 |   - Update database configuration
33 |   - Supports various database types:
34 |     - PostgreSQL
35 |     - MariaDB
36 |     - MySQL
37 |     - MongoDB
38 |     - Redis
39 |     - KeyDB
40 |     - Clickhouse
41 |     - Dragonfly
42 | 
43 | ## Implementation Checklist
44 | 
45 | - [x] Basic Database Management
46 | 
47 |   - [x] List databases resource
48 |   - [x] Get database details
49 |   - [x] Delete database
50 |   - [x] Update database configuration
51 | 
52 | - [x] Database Type Support
53 | 
54 |   - [x] PostgreSQL configuration
55 |   - [x] MariaDB configuration
56 |   - [x] MySQL configuration
57 |   - [x] MongoDB configuration
58 |   - [x] Redis configuration
59 |   - [x] KeyDB configuration
60 |   - [x] Clickhouse configuration
61 |   - [x] Dragonfly configuration
62 | 
63 | - [x] Resource Testing
64 |   - [x] Unit tests for database operations
65 |   - [x] Integration tests with mock data
66 |   - [x] Live test with real Coolify instance
67 | 
68 | ## Dependencies
69 | 
70 | - ADR 001 (Core Server Setup)
71 | - ADR 002 (Server Information Resources)
72 | 
```

--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "name": "@masonator/coolify-mcp",
 3 |   "scope": "@masonator",
 4 |   "version": "0.2.8",
 5 |   "description": "MCP server implementation for Coolify",
 6 |   "type": "module",
 7 |   "main": "./dist/index.js",
 8 |   "types": "./dist/index.d.ts",
 9 |   "exports": {
10 |     ".": {
11 |       "import": "./dist/index.js",
12 |       "require": "./dist/index.cjs",
13 |       "types": "./dist/index.d.ts"
14 |     }
15 |   },
16 |   "bin": {
17 |     "coolify-mcp": "dist/index.js"
18 |   },
19 |   "files": [
20 |     "dist"
21 |   ],
22 |   "scripts": {
23 |     "build": "tsc && shx chmod +x dist/*.js",
24 |     "dev": "tsc --watch",
25 |     "test": "NODE_OPTIONS=--experimental-vm-modules jest",
26 |     "test:watch": "NODE_OPTIONS=--experimental-vm-modules jest --watch",
27 |     "test:coverage": "NODE_OPTIONS=--experimental-vm-modules jest --coverage",
28 |     "lint": "eslint . --ext .ts",
29 |     "lint:fix": "eslint . --ext .ts --fix",
30 |     "format": "prettier --write .",
31 |     "format:check": "prettier --check .",
32 |     "prepare": "husky",
33 |     "prepublishOnly": "npm test && npm run lint",
34 |     "start": "node dist/index.js"
35 |   },
36 |   "keywords": [
37 |     "coolify",
38 |     "mcp",
39 |     "model-context-protocol"
40 |   ],
41 |   "author": "Stuart Mason",
42 |   "license": "MIT",
43 |   "dependencies": {
44 |     "@modelcontextprotocol/sdk": "^1.6.1",
45 |     "reflect-metadata": "^0.2.2",
46 |     "zod": "^3.24.2"
47 |   },
48 |   "devDependencies": {
49 |     "@types/debug": "^4.1.12",
50 |     "@types/jest": "^29.5.14",
51 |     "@types/node": "^20.17.23",
52 |     "@typescript-eslint/eslint-plugin": "^7.18.0",
53 |     "@typescript-eslint/parser": "^7.18.0",
54 |     "eslint": "^8.56.0",
55 |     "eslint-config-prettier": "^9.1.0",
56 |     "husky": "^9.0.11",
57 |     "jest": "^29.7.0",
58 |     "lint-staged": "^15.2.2",
59 |     "markdownlint-cli2": "^0.12.1",
60 |     "prettier": "^3.5.3",
61 |     "shx": "^0.3.4",
62 |     "ts-jest": "^29.2.6",
63 |     "typescript": "^5.8.2"
64 |   },
65 |   "engines": {
66 |     "node": ">=18"
67 |   }
68 | }
69 | 
```

--------------------------------------------------------------------------------
/docs/features/009-mcp-prompts-implementation.md:
--------------------------------------------------------------------------------

```markdown
 1 | # ADR 009: MCP Prompts Implementation
 2 | 
 3 | ## Context
 4 | 
 5 | Create reusable prompt templates for common Coolify workflows to make interactions more efficient.
 6 | 
 7 | ## Implementation Checklist
 8 | 
 9 | - [ ] Deployment Prompts
10 | 
11 |   - [ ] "deploy-application" prompt
12 |     ```typescript
13 |     {
14 |       applicationId: string,
15 |       version?: string,
16 |       environment?: string
17 |     }
18 |     ```
19 |   - [ ] "rollback-deployment" prompt
20 |     ```typescript
21 |     {
22 |       applicationId: string,
23 |       deploymentId: string
24 |     }
25 |     ```
26 | 
27 | - [ ] Configuration Prompts
28 | 
29 |   - [ ] "configure-database" prompt
30 |     ```typescript
31 |     {
32 |       databaseId: string,
33 |       settings: DatabaseSettings
34 |     }
35 |     ```
36 |   - [ ] "configure-environment" prompt
37 |     ```typescript
38 |     {
39 |       environmentId: string,
40 |       variables: Record<string, string>
41 |     }
42 |     ```
43 | 
44 | - [ ] Service Management Prompts
45 | 
46 |   - [ ] "setup-service" prompt
47 |     ```typescript
48 |     {
49 |       environmentId: string,
50 |       serviceType: string,
51 |       configuration: ServiceConfig
52 |     }
53 |     ```
54 |   - [ ] "troubleshoot-service" prompt
55 |     ```typescript
56 |     {
57 |       serviceId: string,
58 |       issueType?: "connectivity" | "performance" | "logs"
59 |     }
60 |     ```
61 | 
62 | - [ ] Resource Management Prompts
63 | 
64 |   - [ ] "optimize-resources" prompt
65 |     ```typescript
66 |     {
67 |       resourceId: string,
68 |       resourceType: "application" | "service" | "database"
69 |     }
70 |     ```
71 |   - [ ] "backup-management" prompt
72 |     ```typescript
73 |     {
74 |       resourceId: string,
75 |       operation: "create" | "restore" | "list"
76 |     }
77 |     ```
78 | 
79 | - [ ] Testing
80 |   - [ ] Prompt validation tests
81 |   - [ ] Response formatting tests
82 |   - [ ] Error handling tests
83 |   - [ ] Integration tests with actual commands
84 | 
85 | ## Dependencies
86 | 
87 | - ADR 001 (Core Server Setup)
88 | - ADR 005 (Application Deployment)
89 | - ADR 006 (Database Management)
90 | - ADR 007 (Service Management)
91 | - ADR 008 (MCP Resources Implementation)
92 | 
```

--------------------------------------------------------------------------------
/src/__tests__/resources/deployment-resources.test.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { DeploymentResources } from '../../resources/deployment-resources.js';
 2 | import { CoolifyClient } from '../../lib/coolify-client.js';
 3 | import { jest } from '@jest/globals';
 4 | 
 5 | jest.mock('../../lib/coolify-client.js');
 6 | 
 7 | describe('DeploymentResources', () => {
 8 |   let mockClient: jest.Mocked<CoolifyClient>;
 9 |   let resources: DeploymentResources;
10 |   const mockDeployment = {
11 |     id: 1,
12 |     uuid: 'test-uuid',
13 |     status: 'running',
14 |     created_at: '2024-01-01',
15 |     updated_at: '2024-01-01',
16 |     application_uuid: 'app-uuid',
17 |     environment_uuid: 'env-uuid',
18 |   };
19 | 
20 |   beforeEach(() => {
21 |     mockClient = {
22 |       deployApplication: jest.fn(),
23 |     } as unknown as jest.Mocked<CoolifyClient>;
24 |     resources = new DeploymentResources(mockClient);
25 |   });
26 | 
27 |   describe('listDeployments', () => {
28 |     it('should throw not implemented error', async () => {
29 |       await expect(resources.listDeployments()).rejects.toThrow('Not implemented');
30 |     });
31 |   });
32 | 
33 |   describe('getDeployment', () => {
34 |     it('should throw not implemented error', async () => {
35 |       await expect(resources.getDeployment('test-id')).rejects.toThrow('Not implemented');
36 |     });
37 |   });
38 | 
39 |   describe('deploy', () => {
40 |     it('should deploy an application', async () => {
41 |       mockClient.deployApplication.mockResolvedValue(mockDeployment);
42 | 
43 |       const result = await resources.deploy({ uuid: 'test-uuid' });
44 | 
45 |       expect(result).toEqual(mockDeployment);
46 |       expect(mockClient.deployApplication).toHaveBeenCalledWith('test-uuid');
47 |     });
48 | 
49 |     it('should handle deployment errors', async () => {
50 |       const error = new Error('Deployment failed');
51 |       mockClient.deployApplication.mockRejectedValue(error);
52 | 
53 |       await expect(resources.deploy({ uuid: 'test-uuid' })).rejects.toThrow('Deployment failed');
54 |       expect(mockClient.deployApplication).toHaveBeenCalledWith('test-uuid');
55 |     });
56 |   });
57 | });
58 | 
```

--------------------------------------------------------------------------------
/docs/features/005-application-deployment.md:
--------------------------------------------------------------------------------

```markdown
 1 | # ADR 005: Application Deployment
 2 | 
 3 | ## Context
 4 | 
 5 | Core application deployment functionality - allows deploying and managing applications within environments.
 6 | 
 7 | ## API Endpoints Used
 8 | 
 9 | - GET `/applications` (Line 10)
10 | 
11 |   - Lists all applications
12 |   - Query params: environment_uuid (optional)
13 |   - Response: Array of Application objects
14 |   - Auth: Bearer token required
15 |   - ✅ Implemented
16 | 
17 | - POST `/applications/public` (Line 31)
18 | 
19 |   - Create new application from public repository
20 |   - Request body: {
21 |     project_uuid: string,
22 |     environment_uuid: string,
23 |     git_repository: string,
24 |     git_branch: string,
25 |     build_pack: "nixpacks" | "static" | "dockerfile" | "dockercompose",
26 |     ports_exposes: string,
27 |     name?: string,
28 |     ...additional configuration
29 |     }
30 |   - Response: Application object
31 |   - Auth: Bearer token required
32 |   - ✅ Implemented
33 | 
34 | - GET `/applications/{uuid}` (Line ~1600)
35 | 
36 |   - Get application details
37 |   - Response: Application object with status
38 |   - Auth: Bearer token required
39 |   - ✅ Implemented
40 | 
41 | - DELETE `/applications/{uuid}` (Line ~1650)
42 | 
43 |   - Delete application
44 |   - Response: 204 No Content
45 |   - Auth: Bearer token required
46 |   - ✅ Implemented
47 | 
48 | - POST `/applications/{uuid}/deploy` (Line ~1700)
49 | 
50 |   - Trigger application deployment
51 |   - Response: Deployment object
52 |   - Auth: Bearer token required
53 |   - ✅ Implemented
54 | 
55 | ## Implementation Checklist
56 | 
57 | - [x] Application List Resource
58 | 
59 |   - [x] resources://coolify/applications/list
60 |   - [x] Filter by environment/project
61 |   - [x] Status information
62 | 
63 | - [x] Application Management Tools
64 | 
65 |   - [x] createApplication tool
66 |   - [x] deployApplication tool
67 |   - [x] configureApplication tool
68 |   - [x] deleteApplication tool
69 | 
70 | - [x] Application Monitoring
71 | 
72 |   - [x] resources://coolify/applications/{id}/status
73 |   - [x] Basic metrics
74 | 
75 | - [x] Testing
76 |   - [x] Deployment workflow tests
77 |   - [x] Configuration management tests
78 | 
79 | ## Dependencies
80 | 
81 | - ADR 001 (Core Server Setup)
82 | - ADR 004 (Environment Management)
83 | 
```

--------------------------------------------------------------------------------
/docs/features/future-adrs.md:
--------------------------------------------------------------------------------

```markdown
 1 | # Future ADR Considerations
 2 | 
 3 | Based on available Coolify API endpoints, here are potential future features to implement:
 4 | 
 5 | ## Deployment Management
 6 | 
 7 | - Webhook integration for GitHub, GitLab, Bitbucket, Gitea
 8 | 
 9 |   - POST `/webhooks/github/{uuid}` (Line ~4000)
10 |   - POST `/webhooks/gitlab/{uuid}` (Line ~4050)
11 |   - POST `/webhooks/bitbucket/{uuid}` (Line ~4100)
12 |   - POST `/webhooks/gitea/{uuid}` (Line ~4150)
13 | 
14 | - Build pack support
15 | 
16 |   - POST `/applications/{uuid}/buildpack` (Line ~4200)
17 |   - GET `/buildpacks/templates` (Line ~4250)
18 | 
19 | - Custom deployment commands
20 |   - POST `/applications/{uuid}/commands/pre-deploy` (Line ~4300)
21 |   - POST `/applications/{uuid}/commands/post-deploy` (Line ~4350)
22 | 
23 | ## Resource Management
24 | 
25 | - Resource limits management
26 | 
27 |   - PUT `/applications/{uuid}/limits` (Line ~4400)
28 |   - PUT `/services/{uuid}/limits` (Line ~4450)
29 |   - GET `/resources/usage` (Line ~4500)
30 | 
31 | - Health check configuration
32 |   - PUT `/applications/{uuid}/health` (Line ~4550)
33 |   - GET `/applications/{uuid}/health/status` (Line ~4600)
34 | 
35 | ## Network Management
36 | 
37 | - Domain management
38 | 
39 |   - POST `/domains` (Line ~4650)
40 |   - GET `/domains/{uuid}/verify` (Line ~4700)
41 |   - PUT `/domains/{uuid}/ssl` (Line ~4750)
42 | 
43 | - SSL/TLS configuration
44 |   - POST `/certificates` (Line ~4800)
45 |   - GET `/certificates/{uuid}/status` (Line ~4850)
46 | 
47 | ## Build Management
48 | 
49 | - Build server configuration
50 |   - POST `/build-servers` (Line ~4900)
51 |   - GET `/build-servers/{uuid}/status` (Line ~4950)
52 |   - PUT `/build-servers/{uuid}/cache` (Line ~5000)
53 | 
54 | ## Team Management
55 | 
56 | - Team member management
57 | 
58 |   - POST `/teams` (Line ~5050)
59 |   - POST `/teams/{uuid}/members` (Line ~5100)
60 |   - PUT `/teams/{uuid}/permissions` (Line ~5150)
61 | 
62 | - API key management
63 |   - POST `/api-keys` (Line ~5200)
64 |   - GET `/api-keys/{uuid}/usage` (Line ~5250)
65 | 
66 | ## Monitoring and Logging
67 | 
68 | - Resource usage monitoring
69 | 
70 |   - GET `/monitoring/resources` (Line ~5300)
71 |   - GET `/monitoring/alerts` (Line ~5350)
72 | 
73 | - Centralized logging
74 |   - GET `/logs/aggregate` (Line ~5400)
75 |   - POST `/logs/search` (Line ~5450)
76 | 
77 | Each of these could be developed into full ADRs once the core functionality is stable. The line numbers reference the OpenAPI specification for implementation details.
78 | 
```

--------------------------------------------------------------------------------
/docs/features/007-service-management.md:
--------------------------------------------------------------------------------

```markdown
  1 | # ADR 007: Service Management
  2 | 
  3 | ## Context
  4 | 
  5 | Implementation of one-click service management features through MCP resources, allowing users to deploy and manage various pre-configured services.
  6 | 
  7 | ## API Endpoints Used
  8 | 
  9 | - GET `/services` (List)
 10 | 
 11 |   - Lists all services
 12 |   - Response: Array of Service objects
 13 |   - Auth: Bearer token required
 14 | 
 15 | - POST `/services` (Create)
 16 | 
 17 |   - Create a one-click service
 18 |   - Required fields:
 19 |     - server_uuid
 20 |     - project_uuid
 21 |     - environment_name/uuid
 22 |     - type (one of many supported service types)
 23 |   - Optional fields:
 24 |     - name
 25 |     - description
 26 |     - destination_uuid
 27 |     - instant_deploy
 28 |   - Auth: Bearer token required
 29 | 
 30 | - GET `/services/{uuid}` (Get)
 31 | 
 32 |   - Get service details
 33 |   - Response: Service object
 34 |   - Auth: Bearer token required
 35 | 
 36 | - DELETE `/services/{uuid}` (Delete)
 37 |   - Delete service
 38 |   - Optional query params:
 39 |     - delete_configurations (boolean, default: true)
 40 |     - delete_volumes (boolean, default: true)
 41 |     - docker_cleanup (boolean, default: true)
 42 |     - delete_connected_networks (boolean, default: true)
 43 |   - Auth: Bearer token required
 44 | 
 45 | ## Supported Service Types
 46 | 
 47 | - Development Tools:
 48 | 
 49 |   - code-server
 50 |   - gitea (with various DB options)
 51 |   - docker-registry
 52 | 
 53 | - CMS & Documentation:
 54 | 
 55 |   - wordpress (with various DB options)
 56 |   - ghost
 57 |   - mediawiki
 58 |   - dokuwiki
 59 | 
 60 | - Monitoring & Analytics:
 61 | 
 62 |   - grafana
 63 |   - umami
 64 |   - glances
 65 |   - uptime-kuma
 66 | 
 67 | - Collaboration & Communication:
 68 | 
 69 |   - rocketchat
 70 |   - chatwoot
 71 |   - nextcloud
 72 | 
 73 | - Database Management:
 74 | 
 75 |   - phpmyadmin
 76 |   - nocodb
 77 |   - directus
 78 | 
 79 | - And many more specialized services
 80 | 
 81 | ## Implementation Checklist
 82 | 
 83 | - [x] Basic Service Management
 84 | 
 85 |   - [x] List services resource
 86 |   - [x] Get service details
 87 |   - [x] Create service
 88 |   - [x] Delete service
 89 | 
 90 | - [x] Service Type Support
 91 | 
 92 |   - [x] Development tools deployment
 93 |   - [x] CMS system deployment
 94 |   - [x] Monitoring tools deployment
 95 |   - [x] Collaboration tools deployment
 96 |   - [x] Database tools deployment
 97 | 
 98 | - [x] Resource Testing
 99 |   - [x] Unit tests for service operations
100 |   - [x] Integration tests with mock data
101 |   - [x] Live test with real Coolify instance
102 | 
103 | ## Dependencies
104 | 
105 | - ADR 001 (Core Server Setup)
106 | - ADR 002 (Server Information Resources)
107 | - ADR 003 (Project Management)
108 | 
```

--------------------------------------------------------------------------------
/docs/features/008-mcp-resources-implementation.md:
--------------------------------------------------------------------------------

```markdown
 1 | # ADR 008: MCP Resources Implementation
 2 | 
 3 | ## Context
 4 | 
 5 | Implement MCP resources for managing Coolify entities through the available API endpoints.
 6 | 
 7 | ## API Endpoints Used
 8 | 
 9 | - Database Management:
10 | 
11 |   - GET `/databases` - List databases
12 |   - GET `/databases/{uuid}` - Get database details
13 |   - POST `/databases/{type}` - Create database
14 |   - PATCH `/databases/{uuid}` - Update database
15 |   - DELETE `/databases/{uuid}` - Delete database
16 | 
17 | - Deployment Management:
18 | 
19 |   - GET `/deployments` - List deployments
20 |   - GET `/deployments/{uuid}` - Get deployment details
21 |   - GET `/deploy` - Deploy by tag or uuid
22 | 
23 | - Application Management:
24 | 
25 |   - GET `/applications` - List applications
26 |   - GET `/applications/{uuid}` - Get application details
27 |   - POST `/applications/public` - Create public application
28 |   - DELETE `/applications/{uuid}` - Delete application
29 | 
30 | - Service Management:
31 |   - GET `/services` - List services
32 |   - GET `/services/{uuid}` - Get service details
33 |   - POST `/services` - Create service
34 |   - DELETE `/services/{uuid}` - Delete service
35 | 
36 | ## Implementation Checklist
37 | 
38 | - [ ] Database Resources
39 | 
40 |   - [ ] resources://coolify/databases/list
41 |   - [ ] resources://coolify/databases/{id}
42 |   - [ ] resources://coolify/databases/create/{type}
43 |   - [ ] resources://coolify/databases/{id}/update
44 |   - [ ] resources://coolify/databases/{id}/delete
45 | 
46 | - [ ] Deployment Resources
47 | 
48 |   - [ ] resources://coolify/deployments/list
49 |   - [ ] resources://coolify/deployments/{id}
50 |   - [ ] resources://coolify/deploy
51 |     - Support for tag-based deployment
52 |     - Support for UUID-based deployment
53 |     - Force rebuild option
54 | 
55 | - [ ] Application Resources
56 | 
57 |   - [ ] resources://coolify/applications/list
58 |   - [ ] resources://coolify/applications/{id}
59 |   - [ ] resources://coolify/applications/create
60 |   - [ ] resources://coolify/applications/{id}/delete
61 | 
62 | - [ ] Service Resources
63 | 
64 |   - [ ] resources://coolify/services/list
65 |   - [ ] resources://coolify/services/{id}
66 |   - [ ] resources://coolify/services/create
67 |   - [ ] resources://coolify/services/{id}/delete
68 | 
69 | - [ ] Testing
70 |   - [ ] Database operation tests
71 |   - [ ] Deployment operation tests
72 |   - [ ] Application operation tests
73 |   - [ ] Service operation tests
74 |   - [ ] Error handling tests
75 |   - [ ] Permission validation tests
76 | 
77 | ## Dependencies
78 | 
79 | - ADR 001 (Core Server Setup)
80 | - ADR 005 (Application Deployment)
81 | - ADR 006 (Database Management)
82 | - ADR 007 (Service Management)
83 | 
```

--------------------------------------------------------------------------------
/src/__tests__/resources/database-resources.test.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { DatabaseResources } from '../../resources/database-resources.js';
 2 | import { CoolifyClient } from '../../lib/coolify-client.js';
 3 | import { PostgresDatabase } from '../../types/coolify.js';
 4 | import { jest } from '@jest/globals';
 5 | 
 6 | jest.mock('../../lib/coolify-client.js');
 7 | 
 8 | describe('DatabaseResources', () => {
 9 |   let mockClient: jest.Mocked<CoolifyClient>;
10 |   let resources: DatabaseResources;
11 |   const mockDatabase: PostgresDatabase = {
12 |     id: 1,
13 |     uuid: 'test-uuid',
14 |     name: 'test-db',
15 |     description: 'test description',
16 |     type: 'postgresql',
17 |     status: 'running',
18 |     created_at: '2024-01-01',
19 |     updated_at: '2024-01-01',
20 |     is_public: false,
21 |     image: 'postgres:latest',
22 |     postgres_user: 'test',
23 |     postgres_password: 'test',
24 |     postgres_db: 'test',
25 |   };
26 | 
27 |   beforeEach(() => {
28 |     mockClient = {
29 |       listDatabases: jest.fn(),
30 |       getDatabase: jest.fn(),
31 |       updateDatabase: jest.fn(),
32 |       deleteDatabase: jest.fn(),
33 |     } as unknown as jest.Mocked<CoolifyClient>;
34 |     resources = new DatabaseResources(mockClient);
35 |   });
36 | 
37 |   describe('listDatabases', () => {
38 |     it('should return a list of databases', async () => {
39 |       mockClient.listDatabases.mockResolvedValue([mockDatabase]);
40 | 
41 |       const result = await resources.listDatabases();
42 | 
43 |       expect(result).toEqual([mockDatabase]);
44 |       expect(mockClient.listDatabases).toHaveBeenCalled();
45 |     });
46 |   });
47 | 
48 |   describe('getDatabase', () => {
49 |     it('should return a database by uuid', async () => {
50 |       mockClient.getDatabase.mockResolvedValue(mockDatabase);
51 | 
52 |       const result = await resources.getDatabase('test-uuid');
53 | 
54 |       expect(result).toEqual(mockDatabase);
55 |       expect(mockClient.getDatabase).toHaveBeenCalledWith('test-uuid');
56 |     });
57 |   });
58 | 
59 |   describe('updateDatabase', () => {
60 |     it('should update a database', async () => {
61 |       const updateData = {
62 |         name: 'updated-db',
63 |         description: 'updated description',
64 |       };
65 | 
66 |       mockClient.updateDatabase.mockResolvedValue({ ...mockDatabase, ...updateData });
67 | 
68 |       const result = await resources.updateDatabase('test-uuid', updateData);
69 | 
70 |       expect(result).toEqual({ ...mockDatabase, ...updateData });
71 |       expect(mockClient.updateDatabase).toHaveBeenCalledWith('test-uuid', updateData);
72 |     });
73 |   });
74 | 
75 |   describe('deleteDatabase', () => {
76 |     it('should delete a database', async () => {
77 |       const mockResponse = { message: 'Database deleted successfully' };
78 |       mockClient.deleteDatabase.mockResolvedValue(mockResponse);
79 | 
80 |       const result = await resources.deleteDatabase('test-uuid', {});
81 | 
82 |       expect(result).toEqual(mockResponse);
83 |       expect(mockClient.deleteDatabase).toHaveBeenCalledWith('test-uuid', {});
84 |     });
85 |   });
86 | });
87 | 
```

--------------------------------------------------------------------------------
/docs/openapi-chunks/untagged-api.yaml:
--------------------------------------------------------------------------------

```yaml
  1 | openapi: 3.1.0
  2 | info:
  3 |   title: Coolify
  4 |   version: '0.1'
  5 | paths:
  6 |   /version:
  7 |     get:
  8 |       summary: Version
  9 |       description: Get Coolify version.
 10 |       operationId: version
 11 |       responses:
 12 |         '200':
 13 |           description: Returns the version of the application
 14 |           content:
 15 |             application/json:
 16 |               schema:
 17 |                 type: string
 18 |               example: v4.0.0
 19 |         '400':
 20 |           $ref: '#/components/responses/400'
 21 |         '401':
 22 |           $ref: '#/components/responses/401'
 23 |       security:
 24 |         - bearerAuth: []
 25 |   /enable:
 26 |     get:
 27 |       summary: Enable API
 28 |       description: Enable API (only with root permissions).
 29 |       operationId: enable-api
 30 |       responses:
 31 |         '200':
 32 |           description: Enable API.
 33 |           content:
 34 |             application/json:
 35 |               schema:
 36 |                 properties:
 37 |                   message:
 38 |                     type: string
 39 |                     example: API enabled.
 40 |                 type: object
 41 |         '400':
 42 |           $ref: '#/components/responses/400'
 43 |         '401':
 44 |           $ref: '#/components/responses/401'
 45 |         '403':
 46 |           description: You are not allowed to enable the API.
 47 |           content:
 48 |             application/json:
 49 |               schema:
 50 |                 properties:
 51 |                   message:
 52 |                     type: string
 53 |                     example: You are not allowed to enable the API.
 54 |                 type: object
 55 |       security:
 56 |         - bearerAuth: []
 57 |   /disable:
 58 |     get:
 59 |       summary: Disable API
 60 |       description: Disable API (only with root permissions).
 61 |       operationId: disable-api
 62 |       responses:
 63 |         '200':
 64 |           description: Disable API.
 65 |           content:
 66 |             application/json:
 67 |               schema:
 68 |                 properties:
 69 |                   message:
 70 |                     type: string
 71 |                     example: API disabled.
 72 |                 type: object
 73 |         '400':
 74 |           $ref: '#/components/responses/400'
 75 |         '401':
 76 |           $ref: '#/components/responses/401'
 77 |         '403':
 78 |           description: You are not allowed to disable the API.
 79 |           content:
 80 |             application/json:
 81 |               schema:
 82 |                 properties:
 83 |                   message:
 84 |                     type: string
 85 |                     example: You are not allowed to disable the API.
 86 |                 type: object
 87 |       security:
 88 |         - bearerAuth: []
 89 |   /health:
 90 |     get:
 91 |       summary: Healthcheck
 92 |       description: Healthcheck endpoint.
 93 |       operationId: healthcheck
 94 |       responses:
 95 |         '200':
 96 |           description: Healthcheck endpoint.
 97 |           content:
 98 |             application/json:
 99 |               schema:
100 |                 type: string
101 |               example: OK
102 |         '400':
103 |           $ref: '#/components/responses/400'
104 |         '401':
105 |           $ref: '#/components/responses/401'
106 | 
```

--------------------------------------------------------------------------------
/src/__tests__/resources/service-resources.test.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { ServiceResources } from '../../resources/service-resources.js';
 2 | import { CoolifyClient } from '../../lib/coolify-client.js';
 3 | import { Service, ServiceType } from '../../types/coolify.js';
 4 | import { jest } from '@jest/globals';
 5 | 
 6 | jest.mock('../../lib/coolify-client.js');
 7 | 
 8 | describe('ServiceResources', () => {
 9 |   let mockClient: jest.Mocked<CoolifyClient>;
10 |   let resources: ServiceResources;
11 |   const mockService: Service = {
12 |     id: 1,
13 |     uuid: 'test-uuid',
14 |     name: 'test-service',
15 |     description: 'test description',
16 |     type: 'code-server',
17 |     status: 'running',
18 |     created_at: '2024-01-01',
19 |     updated_at: '2024-01-01',
20 |     project_uuid: 'project-uuid',
21 |     environment_name: 'test-env',
22 |     environment_uuid: 'env-uuid',
23 |     server_uuid: 'server-uuid',
24 |     domains: ['test.com'],
25 |   };
26 | 
27 |   beforeEach(() => {
28 |     mockClient = {
29 |       listServices: jest.fn(),
30 |       getService: jest.fn(),
31 |       createService: jest.fn(),
32 |       deleteService: jest.fn(),
33 |     } as unknown as jest.Mocked<CoolifyClient>;
34 |     resources = new ServiceResources(mockClient);
35 |   });
36 | 
37 |   describe('listServices', () => {
38 |     it('should return a list of services', async () => {
39 |       mockClient.listServices.mockResolvedValue([mockService]);
40 | 
41 |       const result = await resources.listServices();
42 | 
43 |       expect(result).toEqual([mockService]);
44 |       expect(mockClient.listServices).toHaveBeenCalled();
45 |     });
46 |   });
47 | 
48 |   describe('getService', () => {
49 |     it('should return a service by uuid', async () => {
50 |       mockClient.getService.mockResolvedValue(mockService);
51 | 
52 |       const result = await resources.getService('test-uuid');
53 | 
54 |       expect(result).toEqual(mockService);
55 |       expect(mockClient.getService).toHaveBeenCalledWith('test-uuid');
56 |     });
57 |   });
58 | 
59 |   describe('createService', () => {
60 |     it('should create a new service', async () => {
61 |       const createData = {
62 |         name: 'new-service',
63 |         type: 'code-server' as ServiceType,
64 |         project_uuid: 'project-uuid',
65 |         environment_name: 'test-env',
66 |         environment_uuid: 'env-uuid',
67 |         server_uuid: 'server-uuid',
68 |       };
69 | 
70 |       const mockResponse = {
71 |         uuid: 'new-uuid',
72 |         domains: ['new-service.test.com'],
73 |       };
74 | 
75 |       mockClient.createService.mockResolvedValue(mockResponse);
76 | 
77 |       const result = await resources.createService(createData);
78 | 
79 |       expect(result).toEqual(mockResponse);
80 |       expect(mockClient.createService).toHaveBeenCalledWith(createData);
81 |     });
82 |   });
83 | 
84 |   describe('deleteService', () => {
85 |     it('should delete a service', async () => {
86 |       const mockResponse = { message: 'Service deleted' };
87 |       mockClient.deleteService.mockResolvedValue(mockResponse);
88 | 
89 |       const result = await resources.deleteService('test-uuid');
90 | 
91 |       expect(result).toEqual(mockResponse);
92 |       expect(mockClient.deleteService).toHaveBeenCalledWith('test-uuid', undefined);
93 |     });
94 |   });
95 | });
96 | 
```

--------------------------------------------------------------------------------
/docs/openapi-chunks/deployments-api.yaml:
--------------------------------------------------------------------------------

```yaml
  1 | openapi: 3.1.0
  2 | info:
  3 |   title: Coolify
  4 |   version: '0.1'
  5 | paths:
  6 |   /deployments:
  7 |     get:
  8 |       tags:
  9 |         - Deployments
 10 |       summary: List
 11 |       description: List currently running deployments
 12 |       operationId: list-deployments
 13 |       responses:
 14 |         '200':
 15 |           description: Get all currently running deployments.
 16 |           content:
 17 |             application/json:
 18 |               schema:
 19 |                 type: array
 20 |                 items:
 21 |                   $ref: '#/components/schemas/ApplicationDeploymentQueue'
 22 |         '400':
 23 |           $ref: '#/components/responses/400'
 24 |         '401':
 25 |           $ref: '#/components/responses/401'
 26 |       security:
 27 |         - bearerAuth: []
 28 |   /deployments/{uuid}:
 29 |     get:
 30 |       tags:
 31 |         - Deployments
 32 |       summary: Get
 33 |       description: Get deployment by UUID.
 34 |       operationId: get-deployment-by-uuid
 35 |       parameters:
 36 |         - name: uuid
 37 |           in: path
 38 |           description: Deployment UUID
 39 |           required: true
 40 |           schema:
 41 |             type: string
 42 |       responses:
 43 |         '200':
 44 |           description: Get deployment by UUID.
 45 |           content:
 46 |             application/json:
 47 |               schema:
 48 |                 $ref: '#/components/schemas/ApplicationDeploymentQueue'
 49 |         '400':
 50 |           $ref: '#/components/responses/400'
 51 |         '401':
 52 |           $ref: '#/components/responses/401'
 53 |         '404':
 54 |           $ref: '#/components/responses/404'
 55 |       security:
 56 |         - bearerAuth: []
 57 |   /deploy:
 58 |     get:
 59 |       tags:
 60 |         - Deployments
 61 |       summary: Deploy
 62 |       description: Deploy by tag or uuid. `Post` request also accepted.
 63 |       operationId: deploy-by-tag-or-uuid
 64 |       parameters:
 65 |         - name: tag
 66 |           in: query
 67 |           description: Tag name(s). Comma separated list is also accepted.
 68 |           schema:
 69 |             type: string
 70 |         - name: uuid
 71 |           in: query
 72 |           description: Resource UUID(s). Comma separated list is also accepted.
 73 |           schema:
 74 |             type: string
 75 |         - name: force
 76 |           in: query
 77 |           description: Force rebuild (without cache)
 78 |           schema:
 79 |             type: boolean
 80 |       responses:
 81 |         '200':
 82 |           description: Get deployment(s) UUID's
 83 |           content:
 84 |             application/json:
 85 |               schema:
 86 |                 properties:
 87 |                   deployments:
 88 |                     type: array
 89 |                     items:
 90 |                       properties:
 91 |                         message:
 92 |                           type: string
 93 |                         resource_uuid:
 94 |                           type: string
 95 |                         deployment_uuid:
 96 |                           type: string
 97 |                       type: object
 98 |                 type: object
 99 |         '400':
100 |           $ref: '#/components/responses/400'
101 |         '401':
102 |           $ref: '#/components/responses/401'
103 |       security:
104 |         - bearerAuth: []
105 | 
```

--------------------------------------------------------------------------------
/docs/openapi-chunks/teams-api.yaml:
--------------------------------------------------------------------------------

```yaml
  1 | openapi: 3.1.0
  2 | info:
  3 |   title: Coolify
  4 |   version: '0.1'
  5 | paths:
  6 |   /teams:
  7 |     get:
  8 |       tags:
  9 |         - Teams
 10 |       summary: List
 11 |       description: Get all teams.
 12 |       operationId: list-teams
 13 |       responses:
 14 |         '200':
 15 |           description: List of teams.
 16 |           content:
 17 |             application/json:
 18 |               schema:
 19 |                 type: array
 20 |                 items:
 21 |                   $ref: '#/components/schemas/Team'
 22 |         '400':
 23 |           $ref: '#/components/responses/400'
 24 |         '401':
 25 |           $ref: '#/components/responses/401'
 26 |       security:
 27 |         - bearerAuth: []
 28 |   /teams/{id}:
 29 |     get:
 30 |       tags:
 31 |         - Teams
 32 |       summary: Get
 33 |       description: Get team by TeamId.
 34 |       operationId: get-team-by-id
 35 |       parameters:
 36 |         - name: id
 37 |           in: path
 38 |           description: Team ID
 39 |           required: true
 40 |           schema:
 41 |             type: integer
 42 |       responses:
 43 |         '200':
 44 |           description: List of teams.
 45 |           content:
 46 |             application/json:
 47 |               schema:
 48 |                 $ref: '#/components/schemas/Team'
 49 |         '400':
 50 |           $ref: '#/components/responses/400'
 51 |         '401':
 52 |           $ref: '#/components/responses/401'
 53 |         '404':
 54 |           $ref: '#/components/responses/404'
 55 |       security:
 56 |         - bearerAuth: []
 57 |   /teams/{id}/members:
 58 |     get:
 59 |       tags:
 60 |         - Teams
 61 |       summary: Members
 62 |       description: Get members by TeamId.
 63 |       operationId: get-members-by-team-id
 64 |       parameters:
 65 |         - name: id
 66 |           in: path
 67 |           description: Team ID
 68 |           required: true
 69 |           schema:
 70 |             type: integer
 71 |       responses:
 72 |         '200':
 73 |           description: List of members.
 74 |           content:
 75 |             application/json:
 76 |               schema:
 77 |                 type: array
 78 |                 items:
 79 |                   $ref: '#/components/schemas/User'
 80 |         '400':
 81 |           $ref: '#/components/responses/400'
 82 |         '401':
 83 |           $ref: '#/components/responses/401'
 84 |         '404':
 85 |           $ref: '#/components/responses/404'
 86 |       security:
 87 |         - bearerAuth: []
 88 |   /teams/current:
 89 |     get:
 90 |       tags:
 91 |         - Teams
 92 |       summary: Authenticated Team
 93 |       description: Get currently authenticated team.
 94 |       operationId: get-current-team
 95 |       responses:
 96 |         '200':
 97 |           description: Current Team.
 98 |           content:
 99 |             application/json:
100 |               schema:
101 |                 $ref: '#/components/schemas/Team'
102 |         '400':
103 |           $ref: '#/components/responses/400'
104 |         '401':
105 |           $ref: '#/components/responses/401'
106 |       security:
107 |         - bearerAuth: []
108 |   /teams/current/members:
109 |     get:
110 |       tags:
111 |         - Teams
112 |       summary: Authenticated Team Members
113 |       description: Get currently authenticated team members.
114 |       operationId: get-current-team-members
115 |       responses:
116 |         '200':
117 |           description: Currently authenticated team members.
118 |           content:
119 |             application/json:
120 |               schema:
121 |                 type: array
122 |                 items:
123 |                   $ref: '#/components/schemas/User'
124 |         '400':
125 |           $ref: '#/components/responses/400'
126 |         '401':
127 |           $ref: '#/components/responses/401'
128 |       security:
129 |         - bearerAuth: []
130 | 
```

--------------------------------------------------------------------------------
/docs/openapi-chunks/private-keys-api.yaml:
--------------------------------------------------------------------------------

```yaml
  1 | openapi: 3.1.0
  2 | info:
  3 |   title: Coolify
  4 |   version: '0.1'
  5 | paths:
  6 |   /security/keys:
  7 |     get:
  8 |       tags:
  9 |         - Private Keys
 10 |       summary: List
 11 |       description: List all private keys.
 12 |       operationId: list-private-keys
 13 |       responses:
 14 |         '200':
 15 |           description: Get all private keys.
 16 |           content:
 17 |             application/json:
 18 |               schema:
 19 |                 type: array
 20 |                 items:
 21 |                   $ref: '#/components/schemas/PrivateKey'
 22 |         '400':
 23 |           $ref: '#/components/responses/400'
 24 |         '401':
 25 |           $ref: '#/components/responses/401'
 26 |       security:
 27 |         - bearerAuth: []
 28 |     post:
 29 |       tags:
 30 |         - Private Keys
 31 |       summary: Create
 32 |       description: Create a new private key.
 33 |       operationId: create-private-key
 34 |       requestBody:
 35 |         required: true
 36 |         content:
 37 |           application/json:
 38 |             schema:
 39 |               required:
 40 |                 - private_key
 41 |               properties:
 42 |                 name:
 43 |                   type: string
 44 |                 description:
 45 |                   type: string
 46 |                 private_key:
 47 |                   type: string
 48 |               type: object
 49 |               additionalProperties: false
 50 |       responses:
 51 |         '201':
 52 |           description: The created private key's UUID.
 53 |           content:
 54 |             application/json:
 55 |               schema:
 56 |                 properties:
 57 |                   uuid:
 58 |                     type: string
 59 |                 type: object
 60 |         '400':
 61 |           $ref: '#/components/responses/400'
 62 |         '401':
 63 |           $ref: '#/components/responses/401'
 64 |       security:
 65 |         - bearerAuth: []
 66 |     patch:
 67 |       tags:
 68 |         - Private Keys
 69 |       summary: Update
 70 |       description: Update a private key.
 71 |       operationId: update-private-key
 72 |       requestBody:
 73 |         required: true
 74 |         content:
 75 |           application/json:
 76 |             schema:
 77 |               required:
 78 |                 - private_key
 79 |               properties:
 80 |                 name:
 81 |                   type: string
 82 |                 description:
 83 |                   type: string
 84 |                 private_key:
 85 |                   type: string
 86 |               type: object
 87 |               additionalProperties: false
 88 |       responses:
 89 |         '201':
 90 |           description: The updated private key's UUID.
 91 |           content:
 92 |             application/json:
 93 |               schema:
 94 |                 properties:
 95 |                   uuid:
 96 |                     type: string
 97 |                 type: object
 98 |         '400':
 99 |           $ref: '#/components/responses/400'
100 |         '401':
101 |           $ref: '#/components/responses/401'
102 |       security:
103 |         - bearerAuth: []
104 |   /security/keys/{uuid}:
105 |     get:
106 |       tags:
107 |         - Private Keys
108 |       summary: Get
109 |       description: Get key by UUID.
110 |       operationId: get-private-key-by-uuid
111 |       parameters:
112 |         - name: uuid
113 |           in: path
114 |           description: Private Key UUID
115 |           required: true
116 |           schema:
117 |             type: string
118 |       responses:
119 |         '200':
120 |           description: Get all private keys.
121 |           content:
122 |             application/json:
123 |               schema:
124 |                 $ref: '#/components/schemas/PrivateKey'
125 |         '400':
126 |           $ref: '#/components/responses/400'
127 |         '401':
128 |           $ref: '#/components/responses/401'
129 |         '404':
130 |           description: Private Key not found.
131 |       security:
132 |         - bearerAuth: []
133 |     delete:
134 |       tags:
135 |         - Private Keys
136 |       summary: Delete
137 |       description: Delete a private key.
138 |       operationId: delete-private-key-by-uuid
139 |       parameters:
140 |         - name: uuid
141 |           in: path
142 |           description: Private Key UUID
143 |           required: true
144 |           schema:
145 |             type: string
146 |       responses:
147 |         '200':
148 |           description: Private Key deleted.
149 |           content:
150 |             application/json:
151 |               schema:
152 |                 properties:
153 |                   message:
154 |                     type: string
155 |                     example: Private Key deleted.
156 |                 type: object
157 |         '400':
158 |           $ref: '#/components/responses/400'
159 |         '401':
160 |           $ref: '#/components/responses/401'
161 |         '404':
162 |           description: Private Key not found.
163 |       security:
164 |         - bearerAuth: []
165 | 
```

--------------------------------------------------------------------------------
/docs/openapi-chunks/projects-api.yaml:
--------------------------------------------------------------------------------

```yaml
  1 | openapi: 3.1.0
  2 | info:
  3 |   title: Coolify
  4 |   version: '0.1'
  5 | paths:
  6 |   /projects:
  7 |     get:
  8 |       tags:
  9 |         - Projects
 10 |       summary: List
 11 |       description: List projects.
 12 |       operationId: list-projects
 13 |       responses:
 14 |         '200':
 15 |           description: Get all projects.
 16 |           content:
 17 |             application/json:
 18 |               schema:
 19 |                 type: array
 20 |                 items:
 21 |                   $ref: '#/components/schemas/Project'
 22 |         '400':
 23 |           $ref: '#/components/responses/400'
 24 |         '401':
 25 |           $ref: '#/components/responses/401'
 26 |       security:
 27 |         - bearerAuth: []
 28 |     post:
 29 |       tags:
 30 |         - Projects
 31 |       summary: Create
 32 |       description: Create Project.
 33 |       operationId: create-project
 34 |       requestBody:
 35 |         description: Project created.
 36 |         required: true
 37 |         content:
 38 |           application/json:
 39 |             schema:
 40 |               properties:
 41 |                 name:
 42 |                   type: string
 43 |                   description: The name of the project.
 44 |                 description:
 45 |                   type: string
 46 |                   description: The description of the project.
 47 |               type: object
 48 |       responses:
 49 |         '201':
 50 |           description: Project created.
 51 |           content:
 52 |             application/json:
 53 |               schema:
 54 |                 properties:
 55 |                   uuid:
 56 |                     type: string
 57 |                     example: og888os
 58 |                     description: The UUID of the project.
 59 |                 type: object
 60 |         '400':
 61 |           $ref: '#/components/responses/400'
 62 |         '401':
 63 |           $ref: '#/components/responses/401'
 64 |         '404':
 65 |           $ref: '#/components/responses/404'
 66 |       security:
 67 |         - bearerAuth: []
 68 |   /projects/{uuid}:
 69 |     get:
 70 |       tags:
 71 |         - Projects
 72 |       summary: Get
 73 |       description: Get project by UUID.
 74 |       operationId: get-project-by-uuid
 75 |       parameters:
 76 |         - name: uuid
 77 |           in: path
 78 |           description: Project UUID
 79 |           required: true
 80 |           schema:
 81 |             type: string
 82 |       responses:
 83 |         '200':
 84 |           description: Project details
 85 |           content:
 86 |             application/json:
 87 |               schema:
 88 |                 $ref: '#/components/schemas/Project'
 89 |         '400':
 90 |           $ref: '#/components/responses/400'
 91 |         '401':
 92 |           $ref: '#/components/responses/401'
 93 |         '404':
 94 |           description: Project not found.
 95 |       security:
 96 |         - bearerAuth: []
 97 |     delete:
 98 |       tags:
 99 |         - Projects
100 |       summary: Delete
101 |       description: Delete project by UUID.
102 |       operationId: delete-project-by-uuid
103 |       parameters:
104 |         - name: uuid
105 |           in: path
106 |           description: UUID of the application.
107 |           required: true
108 |           schema:
109 |             type: string
110 |             format: uuid
111 |       responses:
112 |         '200':
113 |           description: Project deleted.
114 |           content:
115 |             application/json:
116 |               schema:
117 |                 properties:
118 |                   message:
119 |                     type: string
120 |                     example: Project deleted.
121 |                 type: object
122 |         '400':
123 |           $ref: '#/components/responses/400'
124 |         '401':
125 |           $ref: '#/components/responses/401'
126 |         '404':
127 |           $ref: '#/components/responses/404'
128 |       security:
129 |         - bearerAuth: []
130 |     patch:
131 |       tags:
132 |         - Projects
133 |       summary: Update
134 |       description: Update Project.
135 |       operationId: update-project-by-uuid
136 |       requestBody:
137 |         description: Project updated.
138 |         required: true
139 |         content:
140 |           application/json:
141 |             schema:
142 |               properties:
143 |                 name:
144 |                   type: string
145 |                   description: The name of the project.
146 |                 description:
147 |                   type: string
148 |                   description: The description of the project.
149 |               type: object
150 |       responses:
151 |         '201':
152 |           description: Project updated.
153 |           content:
154 |             application/json:
155 |               schema:
156 |                 properties:
157 |                   uuid:
158 |                     type: string
159 |                     example: og888os
160 |                   name:
161 |                     type: string
162 |                     example: Project Name
163 |                   description:
164 |                     type: string
165 |                     example: Project Description
166 |                 type: object
167 |         '400':
168 |           $ref: '#/components/responses/400'
169 |         '401':
170 |           $ref: '#/components/responses/401'
171 |         '404':
172 |           $ref: '#/components/responses/404'
173 |       security:
174 |         - bearerAuth: []
175 |   /projects/{uuid}/{environment_name_or_uuid}:
176 |     get:
177 |       tags:
178 |         - Projects
179 |       summary: Environment
180 |       description: Get environment by name or UUID.
181 |       operationId: get-environment-by-name-or-uuid
182 |       parameters:
183 |         - name: uuid
184 |           in: path
185 |           description: Project UUID
186 |           required: true
187 |           schema:
188 |             type: string
189 |         - name: environment_name_or_uuid
190 |           in: path
191 |           description: Environment name or UUID
192 |           required: true
193 |           schema:
194 |             type: string
195 |       responses:
196 |         '200':
197 |           description: Environment details
198 |           content:
199 |             application/json:
200 |               schema:
201 |                 $ref: '#/components/schemas/Environment'
202 |         '400':
203 |           $ref: '#/components/responses/400'
204 |         '401':
205 |           $ref: '#/components/responses/401'
206 |         '404':
207 |           $ref: '#/components/responses/404'
208 |       security:
209 |         - bearerAuth: []
210 | 
```

--------------------------------------------------------------------------------
/src/lib/coolify-client.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import {
  2 |   CoolifyConfig,
  3 |   ErrorResponse,
  4 |   ServerInfo,
  5 |   ServerResources,
  6 |   ServerDomain,
  7 |   ValidationResponse,
  8 |   Project,
  9 |   CreateProjectRequest,
 10 |   UpdateProjectRequest,
 11 |   Environment,
 12 |   Deployment,
 13 |   Database,
 14 |   DatabaseUpdateRequest,
 15 |   Service,
 16 |   CreateServiceRequest,
 17 |   DeleteServiceOptions,
 18 | } from '../types/coolify.js';
 19 | 
 20 | export class CoolifyClient {
 21 |   private baseUrl: string;
 22 |   private accessToken: string;
 23 | 
 24 |   constructor(config: CoolifyConfig) {
 25 |     if (!config.baseUrl) {
 26 |       throw new Error('Coolify base URL is required');
 27 |     }
 28 |     if (!config.accessToken) {
 29 |       throw new Error('Coolify access token is required');
 30 |     }
 31 |     this.baseUrl = config.baseUrl.replace(/\/$/, '');
 32 |     this.accessToken = config.accessToken;
 33 |   }
 34 | 
 35 |   private async request<T>(path: string, options: RequestInit = {}): Promise<T> {
 36 |     try {
 37 |       const url = `${this.baseUrl}/api/v1${path}`;
 38 |       const response = await fetch(url, {
 39 |         headers: {
 40 |           'Content-Type': 'application/json',
 41 |           Authorization: `Bearer ${this.accessToken}`,
 42 |         },
 43 |         ...options,
 44 |       });
 45 | 
 46 |       const data = await response.json();
 47 | 
 48 |       if (!response.ok) {
 49 |         const error = data as ErrorResponse;
 50 |         throw new Error(error.message || `HTTP ${response.status}: ${response.statusText}`);
 51 |       }
 52 | 
 53 |       return data as T;
 54 |     } catch (error) {
 55 |       if (error instanceof TypeError && error.message.includes('fetch')) {
 56 |         throw new Error(
 57 |           `Failed to connect to Coolify server at ${this.baseUrl}. Please check if the server is running and the URL is correct.`,
 58 |         );
 59 |       }
 60 |       throw error;
 61 |     }
 62 |   }
 63 | 
 64 |   async listServers(): Promise<ServerInfo[]> {
 65 |     return this.request<ServerInfo[]>('/servers');
 66 |   }
 67 | 
 68 |   async getServer(uuid: string): Promise<ServerInfo> {
 69 |     return this.request<ServerInfo>(`/servers/${uuid}`);
 70 |   }
 71 | 
 72 |   async getServerResources(uuid: string): Promise<ServerResources> {
 73 |     return this.request<ServerResources>(`/servers/${uuid}/resources`);
 74 |   }
 75 | 
 76 |   async getServerDomains(uuid: string): Promise<ServerDomain[]> {
 77 |     return this.request<ServerDomain[]>(`/servers/${uuid}/domains`);
 78 |   }
 79 | 
 80 |   async validateServer(uuid: string): Promise<ValidationResponse> {
 81 |     return this.request<ValidationResponse>(`/servers/${uuid}/validate`);
 82 |   }
 83 | 
 84 |   async validateConnection(): Promise<void> {
 85 |     try {
 86 |       await this.listServers();
 87 |     } catch (error) {
 88 |       throw new Error(
 89 |         `Failed to connect to Coolify server: ${error instanceof Error ? error.message : 'Unknown error'}`,
 90 |       );
 91 |     }
 92 |   }
 93 | 
 94 |   async listProjects(): Promise<Project[]> {
 95 |     return this.request<Project[]>('/projects');
 96 |   }
 97 | 
 98 |   async getProject(uuid: string): Promise<Project> {
 99 |     return this.request<Project>(`/projects/${uuid}`);
100 |   }
101 | 
102 |   async createProject(project: CreateProjectRequest): Promise<{ uuid: string }> {
103 |     return this.request<{ uuid: string }>('/projects', {
104 |       method: 'POST',
105 |       body: JSON.stringify(project),
106 |     });
107 |   }
108 | 
109 |   async updateProject(uuid: string, project: UpdateProjectRequest): Promise<Project> {
110 |     return this.request<Project>(`/projects/${uuid}`, {
111 |       method: 'PATCH',
112 |       body: JSON.stringify(project),
113 |     });
114 |   }
115 | 
116 |   async deleteProject(uuid: string): Promise<{ message: string }> {
117 |     return this.request<{ message: string }>(`/projects/${uuid}`, {
118 |       method: 'DELETE',
119 |     });
120 |   }
121 | 
122 |   async getProjectEnvironment(
123 |     projectUuid: string,
124 |     environmentNameOrUuid: string,
125 |   ): Promise<Environment> {
126 |     return this.request<Environment>(`/projects/${projectUuid}/${environmentNameOrUuid}`);
127 |   }
128 | 
129 |   async deployApplication(uuid: string): Promise<Deployment> {
130 |     const response = await this.request<Deployment>(`/applications/${uuid}/deploy`, {
131 |       method: 'POST',
132 |     });
133 |     return response;
134 |   }
135 | 
136 |   async listDatabases(): Promise<Database[]> {
137 |     return this.request<Database[]>('/databases');
138 |   }
139 | 
140 |   async getDatabase(uuid: string): Promise<Database> {
141 |     return this.request<Database>(`/databases/${uuid}`);
142 |   }
143 | 
144 |   async updateDatabase(uuid: string, data: DatabaseUpdateRequest): Promise<Database> {
145 |     return this.request<Database>(`/databases/${uuid}`, {
146 |       method: 'PATCH',
147 |       body: JSON.stringify(data),
148 |     });
149 |   }
150 | 
151 |   async deleteDatabase(
152 |     uuid: string,
153 |     options?: {
154 |       deleteConfigurations?: boolean;
155 |       deleteVolumes?: boolean;
156 |       dockerCleanup?: boolean;
157 |       deleteConnectedNetworks?: boolean;
158 |     },
159 |   ): Promise<{ message: string }> {
160 |     const queryParams = new URLSearchParams();
161 |     if (options) {
162 |       if (options.deleteConfigurations !== undefined) {
163 |         queryParams.set('delete_configurations', options.deleteConfigurations.toString());
164 |       }
165 |       if (options.deleteVolumes !== undefined) {
166 |         queryParams.set('delete_volumes', options.deleteVolumes.toString());
167 |       }
168 |       if (options.dockerCleanup !== undefined) {
169 |         queryParams.set('docker_cleanup', options.dockerCleanup.toString());
170 |       }
171 |       if (options.deleteConnectedNetworks !== undefined) {
172 |         queryParams.set('delete_connected_networks', options.deleteConnectedNetworks.toString());
173 |       }
174 |     }
175 | 
176 |     const queryString = queryParams.toString();
177 |     const url = queryString ? `/databases/${uuid}?${queryString}` : `/databases/${uuid}`;
178 | 
179 |     return this.request<{ message: string }>(url, {
180 |       method: 'DELETE',
181 |     });
182 |   }
183 | 
184 |   async listServices(): Promise<Service[]> {
185 |     return this.request<Service[]>('/services');
186 |   }
187 | 
188 |   async getService(uuid: string): Promise<Service> {
189 |     return this.request<Service>(`/services/${uuid}`);
190 |   }
191 | 
192 |   async createService(data: CreateServiceRequest): Promise<{ uuid: string; domains: string[] }> {
193 |     return this.request<{ uuid: string; domains: string[] }>('/services', {
194 |       method: 'POST',
195 |       body: JSON.stringify(data),
196 |     });
197 |   }
198 | 
199 |   async deleteService(uuid: string, options?: DeleteServiceOptions): Promise<{ message: string }> {
200 |     const queryParams = new URLSearchParams();
201 |     if (options) {
202 |       if (options.deleteConfigurations !== undefined) {
203 |         queryParams.set('delete_configurations', options.deleteConfigurations.toString());
204 |       }
205 |       if (options.deleteVolumes !== undefined) {
206 |         queryParams.set('delete_volumes', options.deleteVolumes.toString());
207 |       }
208 |       if (options.dockerCleanup !== undefined) {
209 |         queryParams.set('docker_cleanup', options.dockerCleanup.toString());
210 |       }
211 |       if (options.deleteConnectedNetworks !== undefined) {
212 |         queryParams.set('delete_connected_networks', options.deleteConnectedNetworks.toString());
213 |       }
214 |     }
215 | 
216 |     const queryString = queryParams.toString();
217 |     const url = queryString ? `/services/${uuid}?${queryString}` : `/services/${uuid}`;
218 | 
219 |     return this.request<{ message: string }>(url, {
220 |       method: 'DELETE',
221 |     });
222 |   }
223 | 
224 |   // Add more methods as needed for other endpoints
225 | }
226 | 
```

--------------------------------------------------------------------------------
/src/__tests__/coolify-client.test.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { jest } from '@jest/globals';
  2 | import { CoolifyClient } from '../lib/coolify-client.js';
  3 | import { ServiceType, CreateServiceRequest } from '../types/coolify.js';
  4 | 
  5 | const mockFetch = jest.fn() as any;
  6 | 
  7 | describe('CoolifyClient', () => {
  8 |   let client: CoolifyClient;
  9 | 
 10 |   const mockServers = [
 11 |     {
 12 |       id: 1,
 13 |       uuid: 'test-uuid',
 14 |       name: 'test-server',
 15 |       status: 'running',
 16 |     },
 17 |   ];
 18 | 
 19 |   const mockServerInfo = {
 20 |     id: 1,
 21 |     uuid: 'test-uuid',
 22 |     name: 'test-server',
 23 |     status: 'running',
 24 |   };
 25 | 
 26 |   const mockServerResources = {
 27 |     resources: [
 28 |       {
 29 |         name: 'memory',
 30 |         value: '2GB',
 31 |       },
 32 |       {
 33 |         name: 'disk',
 34 |         value: '20GB',
 35 |       },
 36 |     ],
 37 |   };
 38 | 
 39 |   const mockService = {
 40 |     id: 1,
 41 |     uuid: 'test-uuid',
 42 |     name: 'test-service',
 43 |     type: 'code-server' as ServiceType,
 44 |     status: 'running',
 45 |     created_at: '2024-01-01',
 46 |     updated_at: '2024-01-01',
 47 |   };
 48 | 
 49 |   const errorResponse = {
 50 |     message: 'Resource not found',
 51 |   };
 52 | 
 53 |   beforeEach(() => {
 54 |     mockFetch.mockClear();
 55 |     (global as any).fetch = mockFetch;
 56 |     client = new CoolifyClient({
 57 |       baseUrl: 'http://localhost:3000',
 58 |       accessToken: 'test-api-key',
 59 |     });
 60 |   });
 61 | 
 62 |   describe('listServers', () => {
 63 |     it('should return a list of servers', async () => {
 64 |       mockFetch.mockImplementationOnce(
 65 |         async () =>
 66 |           ({
 67 |             ok: true,
 68 |             json: async () => mockServers,
 69 |           }) as Response,
 70 |       );
 71 | 
 72 |       const servers = await client.listServers();
 73 |       expect(servers).toEqual(mockServers);
 74 |       expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/servers', {
 75 |         headers: {
 76 |           'Content-Type': 'application/json',
 77 |           Authorization: 'Bearer test-api-key',
 78 |         },
 79 |       });
 80 |     });
 81 | 
 82 |     it('should handle errors', async () => {
 83 |       mockFetch.mockImplementationOnce(() =>
 84 |         Promise.resolve({
 85 |           ok: false,
 86 |           json: async () => errorResponse,
 87 |         } as Response),
 88 |       );
 89 | 
 90 |       await expect(client.listServers()).rejects.toThrow('Resource not found');
 91 |     });
 92 |   });
 93 | 
 94 |   describe('getServer', () => {
 95 |     it('should get server info', async () => {
 96 |       mockFetch.mockImplementationOnce(() =>
 97 |         Promise.resolve({
 98 |           ok: true,
 99 |           json: async () => mockServerInfo,
100 |         } as Response),
101 |       );
102 | 
103 |       const result = await client.getServer('test-uuid');
104 | 
105 |       expect(result).toEqual(mockServerInfo);
106 |       expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/servers/test-uuid', {
107 |         headers: {
108 |           'Content-Type': 'application/json',
109 |           Authorization: 'Bearer test-api-key',
110 |         },
111 |       });
112 |     });
113 | 
114 |     it('should handle errors', async () => {
115 |       mockFetch.mockImplementationOnce(() =>
116 |         Promise.resolve({
117 |           ok: false,
118 |           json: async () => errorResponse,
119 |         } as Response),
120 |       );
121 | 
122 |       await expect(client.getServer('test-uuid')).rejects.toThrow('Resource not found');
123 |     });
124 |   });
125 | 
126 |   describe('getServerResources', () => {
127 |     it('should get server resources', async () => {
128 |       mockFetch.mockImplementationOnce(() =>
129 |         Promise.resolve({
130 |           ok: true,
131 |           json: async () => mockServerResources,
132 |         } as Response),
133 |       );
134 | 
135 |       const result = await client.getServerResources('test-uuid');
136 | 
137 |       expect(result).toEqual(mockServerResources);
138 |       expect(mockFetch).toHaveBeenCalledWith(
139 |         'http://localhost:3000/api/v1/servers/test-uuid/resources',
140 |         {
141 |           headers: {
142 |             'Content-Type': 'application/json',
143 |             Authorization: 'Bearer test-api-key',
144 |           },
145 |         },
146 |       );
147 |     });
148 | 
149 |     it('should handle errors', async () => {
150 |       mockFetch.mockImplementationOnce(() =>
151 |         Promise.resolve({
152 |           ok: false,
153 |           json: async () => errorResponse,
154 |         } as Response),
155 |       );
156 | 
157 |       await expect(client.getServerResources('test-uuid')).rejects.toThrow('Resource not found');
158 |     });
159 |   });
160 | 
161 |   describe('listServices', () => {
162 |     it('should list services', async () => {
163 |       mockFetch.mockImplementationOnce(() =>
164 |         Promise.resolve({
165 |           ok: true,
166 |           json: () => Promise.resolve([mockService]),
167 |         } as Response),
168 |       );
169 | 
170 |       const result = await client.listServices();
171 | 
172 |       expect(result).toEqual([mockService]);
173 |       expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/services', {
174 |         headers: {
175 |           'Content-Type': 'application/json',
176 |           Authorization: 'Bearer test-api-key',
177 |         },
178 |       });
179 |     });
180 |   });
181 | 
182 |   describe('getService', () => {
183 |     it('should get service info', async () => {
184 |       mockFetch.mockImplementationOnce(() =>
185 |         Promise.resolve({
186 |           ok: true,
187 |           json: () => Promise.resolve(mockService),
188 |         } as Response),
189 |       );
190 | 
191 |       const result = await client.getService('test-uuid');
192 | 
193 |       expect(result).toEqual(mockService);
194 |       expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/services/test-uuid', {
195 |         headers: {
196 |           'Content-Type': 'application/json',
197 |           Authorization: 'Bearer test-api-key',
198 |         },
199 |       });
200 |     });
201 |   });
202 | 
203 |   describe('createService', () => {
204 |     it('should create a service', async () => {
205 |       mockFetch.mockImplementationOnce(() =>
206 |         Promise.resolve({
207 |           ok: true,
208 |           json: () =>
209 |             Promise.resolve({
210 |               uuid: 'test-uuid',
211 |               domains: ['test.com'],
212 |             }),
213 |         } as Response),
214 |       );
215 | 
216 |       const createData: CreateServiceRequest = {
217 |         name: 'test-service',
218 |         type: 'code-server',
219 |         project_uuid: 'project-uuid',
220 |         environment_uuid: 'env-uuid',
221 |         server_uuid: 'server-uuid',
222 |       };
223 | 
224 |       const result = await client.createService(createData);
225 | 
226 |       expect(result).toEqual({
227 |         uuid: 'test-uuid',
228 |         domains: ['test.com'],
229 |       });
230 |       expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/services', {
231 |         method: 'POST',
232 |         headers: {
233 |           'Content-Type': 'application/json',
234 |           Authorization: 'Bearer test-api-key',
235 |         },
236 |         body: JSON.stringify(createData),
237 |       });
238 |     });
239 |   });
240 | 
241 |   describe('deleteService', () => {
242 |     it('should delete a service', async () => {
243 |       mockFetch.mockImplementationOnce(() =>
244 |         Promise.resolve({
245 |           ok: true,
246 |           json: () => Promise.resolve({ message: 'Service deleted' }),
247 |         } as Response),
248 |       );
249 | 
250 |       const result = await client.deleteService('test-uuid');
251 | 
252 |       expect(result).toEqual({ message: 'Service deleted' });
253 |       expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/services/test-uuid', {
254 |         method: 'DELETE',
255 |         headers: {
256 |           'Content-Type': 'application/json',
257 |           Authorization: 'Bearer test-api-key',
258 |         },
259 |       });
260 |     });
261 |   });
262 | 
263 |   describe('error handling', () => {
264 |     it('should handle network errors', async () => {
265 |       const errorMessage = 'Network error';
266 |       mockFetch.mockImplementationOnce(() => Promise.reject(new Error(errorMessage)));
267 | 
268 |       await expect(client.listServers()).rejects.toThrow(errorMessage);
269 |     });
270 |   });
271 | });
272 | 
```

--------------------------------------------------------------------------------
/src/types/coolify.ts:
--------------------------------------------------------------------------------

```typescript
  1 | export interface CoolifyConfig {
  2 |   baseUrl: string;
  3 |   accessToken: string;
  4 | }
  5 | 
  6 | export interface ServerInfo {
  7 |   uuid: string;
  8 |   name: string;
  9 |   status: 'running' | 'stopped' | 'error';
 10 |   version: string;
 11 |   resources: {
 12 |     cpu: number;
 13 |     memory: number;
 14 |     disk: number;
 15 |   };
 16 | }
 17 | 
 18 | export interface ResourceStatus {
 19 |   id: number;
 20 |   uuid: string;
 21 |   name: string;
 22 |   type: string;
 23 |   created_at: string;
 24 |   updated_at: string;
 25 |   status: string;
 26 | }
 27 | 
 28 | export type ServerResources = ResourceStatus[];
 29 | 
 30 | export interface ErrorResponse {
 31 |   error: string;
 32 |   status: number;
 33 |   message: string;
 34 | }
 35 | 
 36 | export interface ServerDomain {
 37 |   ip: string;
 38 |   domains: string[];
 39 | }
 40 | 
 41 | export interface ValidationResponse {
 42 |   message: string;
 43 | }
 44 | 
 45 | export interface Environment {
 46 |   id: number;
 47 |   uuid: string;
 48 |   name: string;
 49 |   project_uuid: string;
 50 |   variables?: Record<string, string>;
 51 |   created_at: string;
 52 |   updated_at: string;
 53 | }
 54 | 
 55 | export interface Project {
 56 |   id: number;
 57 |   uuid: string;
 58 |   name: string;
 59 |   description?: string;
 60 |   environments?: Environment[];
 61 | }
 62 | 
 63 | export interface CreateProjectRequest {
 64 |   name: string;
 65 |   description?: string;
 66 | }
 67 | 
 68 | export interface UpdateProjectRequest {
 69 |   name?: string;
 70 |   description?: string;
 71 | }
 72 | 
 73 | export interface LogEntry {
 74 |   timestamp: string;
 75 |   level: string;
 76 |   message: string;
 77 | }
 78 | 
 79 | export interface Deployment {
 80 |   id: number;
 81 |   uuid: string;
 82 |   application_uuid: string;
 83 |   status: string;
 84 |   created_at: string;
 85 |   updated_at: string;
 86 | }
 87 | 
 88 | export interface DatabaseBase {
 89 |   id: number;
 90 |   uuid: string;
 91 |   name: string;
 92 |   description?: string;
 93 |   type:
 94 |     | 'postgresql'
 95 |     | 'mysql'
 96 |     | 'mariadb'
 97 |     | 'mongodb'
 98 |     | 'redis'
 99 |     | 'keydb'
100 |     | 'clickhouse'
101 |     | 'dragonfly';
102 |   status: 'running' | 'stopped' | 'error';
103 |   created_at: string;
104 |   updated_at: string;
105 |   is_public: boolean;
106 |   public_port?: number;
107 |   image: string;
108 |   limits?: {
109 |     memory?: string;
110 |     memory_swap?: string;
111 |     memory_swappiness?: number;
112 |     memory_reservation?: string;
113 |     cpus?: string;
114 |     cpuset?: string;
115 |     cpu_shares?: number;
116 |   };
117 | }
118 | 
119 | export interface PostgresDatabase extends DatabaseBase {
120 |   type: 'postgresql';
121 |   postgres_user: string;
122 |   postgres_password: string;
123 |   postgres_db: string;
124 |   postgres_initdb_args?: string;
125 |   postgres_host_auth_method?: string;
126 |   postgres_conf?: string;
127 | }
128 | 
129 | export interface MySQLDatabase extends DatabaseBase {
130 |   type: 'mysql';
131 |   mysql_root_password: string;
132 |   mysql_user?: string;
133 |   mysql_password?: string;
134 |   mysql_database?: string;
135 | }
136 | 
137 | export interface MariaDBDatabase extends DatabaseBase {
138 |   type: 'mariadb';
139 |   mariadb_root_password: string;
140 |   mariadb_user?: string;
141 |   mariadb_password?: string;
142 |   mariadb_database?: string;
143 |   mariadb_conf?: string;
144 | }
145 | 
146 | export interface MongoDBDatabase extends DatabaseBase {
147 |   type: 'mongodb';
148 |   mongo_initdb_root_username: string;
149 |   mongo_initdb_root_password: string;
150 |   mongo_initdb_database?: string;
151 |   mongo_conf?: string;
152 | }
153 | 
154 | export interface RedisDatabase extends DatabaseBase {
155 |   type: 'redis';
156 |   redis_password?: string;
157 |   redis_conf?: string;
158 | }
159 | 
160 | export interface KeyDBDatabase extends DatabaseBase {
161 |   type: 'keydb';
162 |   keydb_password?: string;
163 |   keydb_conf?: string;
164 | }
165 | 
166 | export interface ClickhouseDatabase extends DatabaseBase {
167 |   type: 'clickhouse';
168 |   clickhouse_admin_user: string;
169 |   clickhouse_admin_password: string;
170 | }
171 | 
172 | export interface DragonflyDatabase extends DatabaseBase {
173 |   type: 'dragonfly';
174 |   dragonfly_password: string;
175 | }
176 | 
177 | export type Database =
178 |   | PostgresDatabase
179 |   | MySQLDatabase
180 |   | MariaDBDatabase
181 |   | MongoDBDatabase
182 |   | RedisDatabase
183 |   | KeyDBDatabase
184 |   | ClickhouseDatabase
185 |   | DragonflyDatabase;
186 | 
187 | export interface DatabaseUpdateRequest {
188 |   name?: string;
189 |   description?: string;
190 |   image?: string;
191 |   is_public?: boolean;
192 |   public_port?: number;
193 |   limits_memory?: string;
194 |   limits_memory_swap?: string;
195 |   limits_memory_swappiness?: number;
196 |   limits_memory_reservation?: string;
197 |   limits_cpus?: string;
198 |   limits_cpuset?: string;
199 |   limits_cpu_shares?: number;
200 |   postgres_user?: string;
201 |   postgres_password?: string;
202 |   postgres_db?: string;
203 |   postgres_initdb_args?: string;
204 |   postgres_host_auth_method?: string;
205 |   postgres_conf?: string;
206 |   clickhouse_admin_user?: string;
207 |   clickhouse_admin_password?: string;
208 |   dragonfly_password?: string;
209 |   redis_password?: string;
210 |   redis_conf?: string;
211 |   keydb_password?: string;
212 |   keydb_conf?: string;
213 |   mariadb_conf?: string;
214 |   mariadb_root_password?: string;
215 |   mariadb_user?: string;
216 |   mariadb_password?: string;
217 |   mariadb_database?: string;
218 |   mongo_conf?: string;
219 |   mongo_initdb_root_username?: string;
220 |   mongo_initdb_root_password?: string;
221 |   mongo_initdb_database?: string;
222 |   mysql_root_password?: string;
223 |   mysql_password?: string;
224 |   mysql_user?: string;
225 |   mysql_database?: string;
226 | }
227 | 
228 | export type ServiceType =
229 |   | 'activepieces'
230 |   | 'appsmith'
231 |   | 'appwrite'
232 |   | 'authentik'
233 |   | 'babybuddy'
234 |   | 'budge'
235 |   | 'changedetection'
236 |   | 'chatwoot'
237 |   | 'classicpress-with-mariadb'
238 |   | 'classicpress-with-mysql'
239 |   | 'classicpress-without-database'
240 |   | 'cloudflared'
241 |   | 'code-server'
242 |   | 'dashboard'
243 |   | 'directus'
244 |   | 'directus-with-postgresql'
245 |   | 'docker-registry'
246 |   | 'docuseal'
247 |   | 'docuseal-with-postgres'
248 |   | 'dokuwiki'
249 |   | 'duplicati'
250 |   | 'emby'
251 |   | 'embystat'
252 |   | 'fider'
253 |   | 'filebrowser'
254 |   | 'firefly'
255 |   | 'formbricks'
256 |   | 'ghost'
257 |   | 'gitea'
258 |   | 'gitea-with-mariadb'
259 |   | 'gitea-with-mysql'
260 |   | 'gitea-with-postgresql'
261 |   | 'glance'
262 |   | 'glances'
263 |   | 'glitchtip'
264 |   | 'grafana'
265 |   | 'grafana-with-postgresql'
266 |   | 'grocy'
267 |   | 'heimdall'
268 |   | 'homepage'
269 |   | 'jellyfin'
270 |   | 'kuzzle'
271 |   | 'listmonk'
272 |   | 'logto'
273 |   | 'mediawiki'
274 |   | 'meilisearch'
275 |   | 'metabase'
276 |   | 'metube'
277 |   | 'minio'
278 |   | 'moodle'
279 |   | 'n8n'
280 |   | 'n8n-with-postgresql'
281 |   | 'next-image-transformation'
282 |   | 'nextcloud'
283 |   | 'nocodb'
284 |   | 'odoo'
285 |   | 'openblocks'
286 |   | 'pairdrop'
287 |   | 'penpot'
288 |   | 'phpmyadmin'
289 |   | 'pocketbase'
290 |   | 'posthog'
291 |   | 'reactive-resume'
292 |   | 'rocketchat'
293 |   | 'shlink'
294 |   | 'slash'
295 |   | 'snapdrop'
296 |   | 'statusnook'
297 |   | 'stirling-pdf'
298 |   | 'supabase'
299 |   | 'syncthing'
300 |   | 'tolgee'
301 |   | 'trigger'
302 |   | 'trigger-with-external-database'
303 |   | 'twenty'
304 |   | 'umami'
305 |   | 'unleash-with-postgresql'
306 |   | 'unleash-without-database'
307 |   | 'uptime-kuma'
308 |   | 'vaultwarden'
309 |   | 'vikunja'
310 |   | 'weblate'
311 |   | 'whoogle'
312 |   | 'wordpress-with-mariadb'
313 |   | 'wordpress-with-mysql'
314 |   | 'wordpress-without-database';
315 | 
316 | export interface Service {
317 |   id: number;
318 |   uuid: string;
319 |   name: string;
320 |   description?: string;
321 |   type: ServiceType;
322 |   status: 'running' | 'stopped' | 'error';
323 |   created_at: string;
324 |   updated_at: string;
325 |   project_uuid: string;
326 |   environment_name: string;
327 |   environment_uuid: string;
328 |   server_uuid: string;
329 |   destination_uuid?: string;
330 |   domains?: string[];
331 | }
332 | 
333 | export interface CreateServiceRequest {
334 |   type: ServiceType;
335 |   name?: string;
336 |   description?: string;
337 |   project_uuid: string;
338 |   environment_name?: string;
339 |   environment_uuid?: string;
340 |   server_uuid: string;
341 |   destination_uuid?: string;
342 |   instant_deploy?: boolean;
343 | }
344 | 
345 | export interface DeleteServiceOptions {
346 |   deleteConfigurations?: boolean;
347 |   deleteVolumes?: boolean;
348 |   dockerCleanup?: boolean;
349 |   deleteConnectedNetworks?: boolean;
350 | }
351 | 
352 | export interface Application {
353 |   uuid: string;
354 |   name: string;
355 |   // Add other application properties as needed
356 | }
357 | 
358 | export interface CreateApplicationRequest {
359 |   name: string;
360 |   // Add other required fields for application creation
361 | }
362 | 
```

--------------------------------------------------------------------------------
/docs/openapi-chunks/servers-api.yaml:
--------------------------------------------------------------------------------

```yaml
  1 | openapi: 3.1.0
  2 | info:
  3 |   title: Coolify
  4 |   version: '0.1'
  5 | paths:
  6 |   /servers:
  7 |     get:
  8 |       tags:
  9 |         - Servers
 10 |       summary: List
 11 |       description: List all servers.
 12 |       operationId: list-servers
 13 |       responses:
 14 |         '200':
 15 |           description: Get all servers.
 16 |           content:
 17 |             application/json:
 18 |               schema:
 19 |                 type: array
 20 |                 items:
 21 |                   $ref: '#/components/schemas/Server'
 22 |         '400':
 23 |           $ref: '#/components/responses/400'
 24 |         '401':
 25 |           $ref: '#/components/responses/401'
 26 |       security:
 27 |         - bearerAuth: []
 28 |     post:
 29 |       tags:
 30 |         - Servers
 31 |       summary: Create
 32 |       description: Create Server.
 33 |       operationId: create-server
 34 |       requestBody:
 35 |         description: Server created.
 36 |         required: true
 37 |         content:
 38 |           application/json:
 39 |             schema:
 40 |               properties:
 41 |                 name:
 42 |                   type: string
 43 |                   example: My Server
 44 |                   description: The name of the server.
 45 |                 description:
 46 |                   type: string
 47 |                   example: My Server Description
 48 |                   description: The description of the server.
 49 |                 ip:
 50 |                   type: string
 51 |                   example: 127.0.0.1
 52 |                   description: The IP of the server.
 53 |                 port:
 54 |                   type: integer
 55 |                   example: 22
 56 |                   description: The port of the server.
 57 |                 user:
 58 |                   type: string
 59 |                   example: root
 60 |                   description: The user of the server.
 61 |                 private_key_uuid:
 62 |                   type: string
 63 |                   example: og888os
 64 |                   description: The UUID of the private key.
 65 |                 is_build_server:
 66 |                   type: boolean
 67 |                   example: false
 68 |                   description: Is build server.
 69 |                 instant_validate:
 70 |                   type: boolean
 71 |                   example: false
 72 |                   description: Instant validate.
 73 |                 proxy_type:
 74 |                   type: string
 75 |                   enum:
 76 |                     - traefik
 77 |                     - caddy
 78 |                     - none
 79 |                   example: traefik
 80 |                   description: The proxy type.
 81 |               type: object
 82 |       responses:
 83 |         '201':
 84 |           description: Server created.
 85 |           content:
 86 |             application/json:
 87 |               schema:
 88 |                 properties:
 89 |                   uuid:
 90 |                     type: string
 91 |                     example: og888os
 92 |                     description: The UUID of the server.
 93 |                 type: object
 94 |         '400':
 95 |           $ref: '#/components/responses/400'
 96 |         '401':
 97 |           $ref: '#/components/responses/401'
 98 |         '404':
 99 |           $ref: '#/components/responses/404'
100 |       security:
101 |         - bearerAuth: []
102 |   /servers/{uuid}:
103 |     get:
104 |       tags:
105 |         - Servers
106 |       summary: Get
107 |       description: Get server by UUID.
108 |       operationId: get-server-by-uuid
109 |       parameters:
110 |         - name: uuid
111 |           in: path
112 |           description: Server's UUID
113 |           required: true
114 |           schema:
115 |             type: string
116 |       responses:
117 |         '200':
118 |           description: Get server by UUID
119 |           content:
120 |             application/json:
121 |               schema:
122 |                 $ref: '#/components/schemas/Server'
123 |         '400':
124 |           $ref: '#/components/responses/400'
125 |         '401':
126 |           $ref: '#/components/responses/401'
127 |         '404':
128 |           $ref: '#/components/responses/404'
129 |       security:
130 |         - bearerAuth: []
131 |     delete:
132 |       tags:
133 |         - Servers
134 |       summary: Delete
135 |       description: Delete server by UUID.
136 |       operationId: delete-server-by-uuid
137 |       parameters:
138 |         - name: uuid
139 |           in: path
140 |           description: UUID of the server.
141 |           required: true
142 |           schema:
143 |             type: string
144 |             format: uuid
145 |       responses:
146 |         '200':
147 |           description: Server deleted.
148 |           content:
149 |             application/json:
150 |               schema:
151 |                 properties:
152 |                   message:
153 |                     type: string
154 |                     example: Server deleted.
155 |                 type: object
156 |         '400':
157 |           $ref: '#/components/responses/400'
158 |         '401':
159 |           $ref: '#/components/responses/401'
160 |         '404':
161 |           $ref: '#/components/responses/404'
162 |       security:
163 |         - bearerAuth: []
164 |     patch:
165 |       tags:
166 |         - Servers
167 |       summary: Update
168 |       description: Update Server.
169 |       operationId: update-server-by-uuid
170 |       parameters:
171 |         - name: uuid
172 |           in: path
173 |           description: Server UUID
174 |           required: true
175 |           schema:
176 |             type: string
177 |       requestBody:
178 |         description: Server updated.
179 |         required: true
180 |         content:
181 |           application/json:
182 |             schema:
183 |               properties:
184 |                 name:
185 |                   type: string
186 |                   description: The name of the server.
187 |                 description:
188 |                   type: string
189 |                   description: The description of the server.
190 |                 ip:
191 |                   type: string
192 |                   description: The IP of the server.
193 |                 port:
194 |                   type: integer
195 |                   description: The port of the server.
196 |                 user:
197 |                   type: string
198 |                   description: The user of the server.
199 |                 private_key_uuid:
200 |                   type: string
201 |                   description: The UUID of the private key.
202 |                 is_build_server:
203 |                   type: boolean
204 |                   description: Is build server.
205 |                 instant_validate:
206 |                   type: boolean
207 |                   description: Instant validate.
208 |                 proxy_type:
209 |                   type: string
210 |                   enum:
211 |                     - traefik
212 |                     - caddy
213 |                     - none
214 |                   description: The proxy type.
215 |               type: object
216 |       responses:
217 |         '201':
218 |           description: Server updated.
219 |           content:
220 |             application/json:
221 |               schema:
222 |                 $ref: '#/components/schemas/Server'
223 |         '400':
224 |           $ref: '#/components/responses/400'
225 |         '401':
226 |           $ref: '#/components/responses/401'
227 |         '404':
228 |           $ref: '#/components/responses/404'
229 |       security:
230 |         - bearerAuth: []
231 |   /servers/{uuid}/resources:
232 |     get:
233 |       tags:
234 |         - Servers
235 |       summary: Resources
236 |       description: Get resources by server.
237 |       operationId: get-resources-by-server-uuid
238 |       parameters:
239 |         - name: uuid
240 |           in: path
241 |           description: Server's UUID
242 |           required: true
243 |           schema:
244 |             type: string
245 |       responses:
246 |         '200':
247 |           description: Get resources by server
248 |           content:
249 |             application/json:
250 |               schema:
251 |                 type: array
252 |                 items:
253 |                   properties:
254 |                     id:
255 |                       type: integer
256 |                     uuid:
257 |                       type: string
258 |                     name:
259 |                       type: string
260 |                     type:
261 |                       type: string
262 |                     created_at:
263 |                       type: string
264 |                     updated_at:
265 |                       type: string
266 |                     status:
267 |                       type: string
268 |                   type: object
269 |         '400':
270 |           $ref: '#/components/responses/400'
271 |         '401':
272 |           $ref: '#/components/responses/401'
273 |       security:
274 |         - bearerAuth: []
275 |   /servers/{uuid}/domains:
276 |     get:
277 |       tags:
278 |         - Servers
279 |       summary: Domains
280 |       description: Get domains by server.
281 |       operationId: get-domains-by-server-uuid
282 |       parameters:
283 |         - name: uuid
284 |           in: path
285 |           description: Server's UUID
286 |           required: true
287 |           schema:
288 |             type: string
289 |       responses:
290 |         '200':
291 |           description: Get domains by server
292 |           content:
293 |             application/json:
294 |               schema:
295 |                 type: array
296 |                 items:
297 |                   properties:
298 |                     ip:
299 |                       type: string
300 |                     domains:
301 |                       type: array
302 |                       items:
303 |                         type: string
304 |                   type: object
305 |         '400':
306 |           $ref: '#/components/responses/400'
307 |         '401':
308 |           $ref: '#/components/responses/401'
309 |       security:
310 |         - bearerAuth: []
311 |   /servers/{uuid}/validate:
312 |     get:
313 |       tags:
314 |         - Servers
315 |       summary: Validate
316 |       description: Validate server by UUID.
317 |       operationId: validate-server-by-uuid
318 |       parameters:
319 |         - name: uuid
320 |           in: path
321 |           description: Server UUID
322 |           required: true
323 |           schema:
324 |             type: string
325 |       responses:
326 |         '201':
327 |           description: Server validation started.
328 |           content:
329 |             application/json:
330 |               schema:
331 |                 properties:
332 |                   message:
333 |                     type: string
334 |                     example: Validation started.
335 |                 type: object
336 |         '400':
337 |           $ref: '#/components/responses/400'
338 |         '401':
339 |           $ref: '#/components/responses/401'
340 |         '404':
341 |           $ref: '#/components/responses/404'
342 |       security:
343 |         - bearerAuth: []
344 | 
```

--------------------------------------------------------------------------------
/docs/features/013-npx-config-fix.md:
--------------------------------------------------------------------------------

```markdown
  1 | # npx config fix
  2 | 
  3 | I want to be able to have the following config:
  4 | 
  5 | ```json
  6 | {
  7 |   "mcpServers": {
  8 |     "coolify": {
  9 |       "command": "npx",
 10 |       "args": ["-y", "@masonator/coolify-mcp"],
 11 |       "env": {
 12 |         "COOLIFY_ACCESS_TOKEN": "token",
 13 |         "COOLIFY_BASE_URL": "https://url"
 14 |       }
 15 |     },
 16 |     "github": {
 17 |       "command": "npx",
 18 |       "args": ["-y", "@modelcontextprotocol/server-github"],
 19 |       "env": {
 20 |         "GITHUB_PERSONAL_ACCESS_TOKEN": "pat"
 21 |       }
 22 |     }
 23 |   }
 24 | }
 25 | ```
 26 | 
 27 | The github config is correct, but the coolify config currently does not work.
 28 | 
 29 | I get the following error:
 30 | 
 31 | ```
 32 | 2025-03-07T09:43:34.691Z [coolify] [info] Initializing server...
 33 | 2025-03-07T09:43:34.783Z [coolify] [info] Server started and connected successfully
 34 | 2025-03-07T09:43:35.882Z [coolify] [info] Message from client: {"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"claude-ai","version":"0.1.0"}},"jsonrpc":"2.0","id":0}
 35 | node:internal/modules/cjs/loader:603
 36 |       throw e;
 37 |       ^
 38 | 
 39 | Error: Cannot find module '/Users/stumason/.npm/_npx/4e3adc8df0812880/node_modules/@modelcontextprotocol/sdk/dist/cjs/server/mcp'
 40 |     at createEsmNotFoundErr (node:internal/modules/cjs/loader:1186:15)
 41 |     at finalizeEsmResolution (node:internal/modules/cjs/loader:1174:15)
 42 |     at resolveExports (node:internal/modules/cjs/loader:596:14)
 43 |     at Module._findPath (node:internal/modules/cjs/loader:673:31)
 44 |     at Module._resolveFilename (node:internal/modules/cjs/loader:1135:27)
 45 |     at Module._load (node:internal/modules/cjs/loader:990:27)
 46 |     at Module.require (node:internal/modules/cjs/loader:1237:19)
 47 |     at require (node:internal/modules/helpers:176:18)
 48 |     at Object.<anonymous> (/Users/stumason/.npm/_npx/4e3adc8df0812880/node_modules/@masonator/coolify-mcp/dist/lib/mcp-server.js:4:15)
 49 |     at Module._compile (node:internal/modules/cjs/loader:1378:14) {
 50 |   code: 'MODULE_NOT_FOUND',
 51 |   path: '/Users/stumason/.npm/_npx/4e3adc8df0812880/node_modules/@modelcontextprotocol/sdk/package.json'
 52 | }
 53 | node:internal/modules/cjs/loader:603
 54 |       throw e;
 55 |       ^
 56 | 
 57 | Error: Cannot find module '/Users/stumason/.npm/_npx/4e3adc8df0812880/node_modules/@modelcontextprotocol/sdk/dist/cjs/server/mcp'
 58 |     at createEsmNotFoundErr (node:internal/modules/cjs/loader:1186:15)
 59 |     at finalizeEsmResolution (node:internal/modules/cjs/loader:1174:15)
 60 |     at resolveExports (node:internal/modules/cjs/loader:596:14)
 61 |     at Module._findPath (node:internal/modules/cjs/loader:673:31)
 62 |     at Module._resolveFilename (node:internal/modules/cjs/loader:1135:27)
 63 |     at Module._load (node:internal/modules/cjs/loader:990:27)
 64 |     at Module.require (node:internal/modules/cjs/loader:1237:19)
 65 |     at require (node:internal/modules/helpers:176:18)
 66 |     at Object.<anonymous> (/Users/stumason/.npm/_npx/4e3adc8df0812880/node_modules/@masonator/coolify-mcp/dist/lib/mcp-server.js:4:15)
 67 |     at Module._compile (node:internal/modules/cjs/loader:1378:14) {
 68 |   code: 'MODULE_NOT_FOUND',
 69 |   path: '/Users/stumason/.npm/_npx/4e3adc8df0812880/node_modules/@modelcontextprotocol/sdk/package.json'
 70 | }
 71 | 
 72 | Node.js v21.6.0
 73 | 
 74 | Node.js v21.6.0
 75 | 2025-03-07T09:43:36.909Z [coolify] [info] Server transport closed
 76 | 2025-03-07T09:43:36.910Z [coolify] [info] Client transport closed
 77 | 2025-03-07T09:43:36.910Z [coolify] [info] Server transport closed unexpectedly, this is likely due to the process exiting early. If you are developing this MCP server you can add output to stderr (i.e. `console.error('...')` in JavaScript, `print('...', file=sys.stderr)` in python) and it will appear in this log.
 78 | 2025-03-07T09:43:36.910Z [coolify] [error] Server disconnected. For troubleshooting guidance, please visit our [debugging documentation](https://modelcontextprotocol.io/docs/tools/debugging) {"context":"connection"}
 79 | 2025-03-07T09:43:36.911Z [coolify] [info] Client transport closed
 80 | 2025-03-07T09:43:36.912Z [coolify] [info] Server transport closed
 81 | 2025-03-07T09:43:36.912Z [coolify] [info] Client transport closed
 82 | 2025-03-07T09:43:36.912Z [coolify] [info] Server transport closed unexpectedly, this is likely due to the process exiting early. If you are developing this MCP server you can add output to stderr (i.e. `console.error('...')` in JavaScript, `print('...', file=sys.stderr)` in python) and it will appear in this log.
 83 | 2025-03-07T09:43:36.912Z [coolify] [error] Server disconnected. For troubleshooting guidance, please visit our [debugging documentation](https://modelcontextprotocol.io/docs/tools/debugging) {"context":"connection"}
 84 | 2025-03-07T09:43:36.913Z [coolify] [info] Client transport closed
 85 | 2025-03-07T09:51:22.595Z [coolify] [info] Initializing server...
 86 | 2025-03-07T09:51:22.618Z [coolify] [info] Server started and connected successfully
 87 | 2025-03-07T09:51:22.621Z [coolify] [info] Message from client: {"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"claude-ai","version":"0.1.0"}},"jsonrpc":"2.0","id":0}
 88 | 2025-03-07T09:51:23.837Z [coolify] [info] Initializing server...
 89 | 2025-03-07T09:51:23.853Z [coolify] [info] Server started and connected successfully
 90 | 2025-03-07T09:51:23.948Z [coolify] [info] Message from client: {"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"claude-ai","version":"0.1.0"}},"jsonrpc":"2.0","id":0}
 91 | node:internal/modules/cjs/loader:603
 92 |       throw e;
 93 |       ^
 94 | 
 95 | Error: Cannot find module '/Users/stumason/.npm/_npx/4e3adc8df0812880/node_modules/@modelcontextprotocol/sdk/dist/cjs/server/mcp'
 96 |     at createEsmNotFoundErr (node:internal/modules/cjs/loader:1186:15)
 97 |     at finalizeEsmResolution (node:internal/modules/cjs/loader:1174:15)
 98 |     at resolveExports (node:internal/modules/cjs/loader:596:14)
 99 |     at Module._findPath (node:internal/modules/cjs/loader:673:31)
100 |     at Module._resolveFilename (node:internal/modules/cjs/loader:1135:27)
101 |     at Module._load (node:internal/modules/cjs/loader:990:27)
102 |     at Module.require (node:internal/modules/cjs/loader:1237:19)
103 |     at require (node:internal/modules/helpers:176:18)
104 |     at Object.<anonymous> (/Users/stumason/.npm/_npx/4e3adc8df0812880/node_modules/@masonator/coolify-mcp/dist/lib/mcp-server.js:4:15)
105 |     at Module._compile (node:internal/modules/cjs/loader:1378:14) {
106 |   code: 'MODULE_NOT_FOUND',
107 |   path: '/Users/stumason/.npm/_npx/4e3adc8df0812880/node_modules/@modelcontextprotocol/sdk/package.json'
108 | }
109 | 
110 | Node.js v21.6.0
111 | 2025-03-07T09:51:25.767Z [coolify] [info] Server transport closed
112 | 2025-03-07T09:51:25.768Z [coolify] [info] Client transport closed
113 | 2025-03-07T09:51:25.768Z [coolify] [info] Server transport closed unexpectedly, this is likely due to the process exiting early. If you are developing this MCP server you can add output to stderr (i.e. `console.error('...')` in JavaScript, `print('...', file=sys.stderr)` in python) and it will appear in this log.
114 | 2025-03-07T09:51:25.768Z [coolify] [error] Server disconnected. For troubleshooting guidance, please visit our [debugging documentation](https://modelcontextprotocol.io/docs/tools/debugging) {"context":"connection"}
115 | 2025-03-07T09:51:25.769Z [coolify] [info] Client transport closed
116 | node:internal/modules/cjs/loader:603
117 |       throw e;
118 |       ^
119 | 
120 | Error: Cannot find module '/Users/stumason/.npm/_npx/4e3adc8df0812880/node_modules/@modelcontextprotocol/sdk/dist/cjs/server/mcp'
121 |     at createEsmNotFoundErr (node:internal/modules/cjs/loader:1186:15)
122 |     at finalizeEsmResolution (node:internal/modules/cjs/loader:1174:15)
123 |     at resolveExports (node:internal/modules/cjs/loader:596:14)
124 |     at Module._findPath (node:internal/modules/cjs/loader:673:31)
125 |     at Module._resolveFilename (node:internal/modules/cjs/loader:1135:27)
126 |     at Module._load (node:internal/modules/cjs/loader:990:27)
127 |     at Module.require (node:internal/modules/cjs/loader:1237:19)
128 |     at require (node:internal/modules/helpers:176:18)
129 |     at Object.<anonymous> (/Users/stumason/.npm/_npx/4e3adc8df0812880/node_modules/@masonator/coolify-mcp/dist/lib/mcp-server.js:4:15)
130 |     at Module._compile (node:internal/modules/cjs/loader:1378:14) {
131 |   code: 'MODULE_NOT_FOUND',
132 |   path: '/Users/stumason/.npm/_npx/4e3adc8df0812880/node_modules/@modelcontextprotocol/sdk/package.json'
133 | }
134 | 
135 | Node.js v21.6.0
136 | 2025-03-07T09:51:25.851Z [coolify] [info] Server transport closed
137 | 2025-03-07T09:51:25.851Z [coolify] [info] Client transport closed
138 | 2025-03-07T09:51:25.852Z [coolify] [info] Server transport closed unexpectedly, this is likely due to the process exiting early. If you are developing this MCP server you can add output to stderr (i.e. `console.error('...')` in JavaScript, `print('...', file=sys.stderr)` in python) and it will appear in this log.
139 | 2025-03-07T09:51:25.852Z [coolify] [error] Server disconnected. For troubleshooting guidance, please visit our [debugging documentation](https://modelcontextprotocol.io/docs/tools/debugging) {"context":"connection"}
140 | 2025-03-07T09:51:25.852Z [coolify] [info] Client transport closed
141 | ```
142 | 
143 | For reference, the github package.json looks like this:
144 | 
145 | ```json
146 | {
147 |   "name": "@modelcontextprotocol/server-github",
148 |   "version": "0.6.2",
149 |   "description": "MCP server for using the GitHub API",
150 |   "license": "MIT",
151 |   "author": "Anthropic, PBC (https://anthropic.com)",
152 |   "homepage": "https://modelcontextprotocol.io",
153 |   "bugs": "https://github.com/modelcontextprotocol/servers/issues",
154 |   "type": "module",
155 |   "bin": {
156 |     "mcp-server-github": "dist/index.js"
157 |   },
158 |   "files": ["dist"],
159 |   "scripts": {
160 |     "build": "tsc && shx chmod +x dist/*.js",
161 |     "prepare": "npm run build",
162 |     "watch": "tsc --watch"
163 |   },
164 |   "dependencies": {
165 |     "@modelcontextprotocol/sdk": "1.0.1",
166 |     "@types/node": "^22",
167 |     "@types/node-fetch": "^2.6.12",
168 |     "node-fetch": "^3.3.2",
169 |     "universal-user-agent": "^7.0.2",
170 |     "zod": "^3.22.4",
171 |     "zod-to-json-schema": "^3.23.5"
172 |   },
173 |   "devDependencies": {
174 |     "shx": "^0.3.4",
175 |     "typescript": "^5.6.2"
176 |   }
177 | }
178 | ```
179 | 
```

--------------------------------------------------------------------------------
/docs/mcp-js-readme.md:
--------------------------------------------------------------------------------

```markdown
  1 | # MCP TypeScript SDK ![NPM Version](https://img.shields.io/npm/v/%40modelcontextprotocol%2Fsdk) ![MIT licensed](https://img.shields.io/npm/l/%40modelcontextprotocol%2Fsdk)
  2 | 
  3 | ## Table of Contents
  4 | 
  5 | - [Overview](#overview)
  6 | - [Installation](#installation)
  7 | - [Quickstart](#quickstart)
  8 | - [What is MCP?](#what-is-mcp)
  9 | - [Core Concepts](#core-concepts)
 10 |   - [Server](#server)
 11 |   - [Resources](#resources)
 12 |   - [Tools](#tools)
 13 |   - [Prompts](#prompts)
 14 | - [Running Your Server](#running-your-server)
 15 |   - [stdio](#stdio)
 16 |   - [HTTP with SSE](#http-with-sse)
 17 |   - [Testing and Debugging](#testing-and-debugging)
 18 | - [Examples](#examples)
 19 |   - [Echo Server](#echo-server)
 20 |   - [SQLite Explorer](#sqlite-explorer)
 21 | - [Advanced Usage](#advanced-usage)
 22 |   - [Low-Level Server](#low-level-server)
 23 |   - [Writing MCP Clients](#writing-mcp-clients)
 24 |   - [Server Capabilities](#server-capabilities)
 25 | 
 26 | ## Overview
 27 | 
 28 | The Model Context Protocol allows applications to provide context for LLMs in a standardized way, separating the concerns of providing context from the actual LLM interaction. This TypeScript SDK implements the full MCP specification, making it easy to:
 29 | 
 30 | - Build MCP clients that can connect to any MCP server
 31 | - Create MCP servers that expose resources, prompts and tools
 32 | - Use standard transports like stdio and SSE
 33 | - Handle all MCP protocol messages and lifecycle events
 34 | 
 35 | ## Installation
 36 | 
 37 | ```bash
 38 | npm install @modelcontextprotocol/sdk
 39 | ```
 40 | 
 41 | ## Quick Start
 42 | 
 43 | Let's create a simple MCP server that exposes a calculator tool and some data:
 44 | 
 45 | ```typescript
 46 | import { McpServer, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
 47 | import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
 48 | import { z } from 'zod';
 49 | 
 50 | // Create an MCP server
 51 | const server = new McpServer({
 52 |   name: 'Demo',
 53 |   version: '1.0.0',
 54 | });
 55 | 
 56 | // Add an addition tool
 57 | server.tool('add', { a: z.number(), b: z.number() }, async ({ a, b }) => ({
 58 |   content: [{ type: 'text', text: String(a + b) }],
 59 | }));
 60 | 
 61 | // Add a dynamic greeting resource
 62 | server.resource(
 63 |   'greeting',
 64 |   new ResourceTemplate('greeting://{name}', { list: undefined }),
 65 |   async (uri, { name }) => ({
 66 |     contents: [
 67 |       {
 68 |         uri: uri.href,
 69 |         text: `Hello, ${name}!`,
 70 |       },
 71 |     ],
 72 |   }),
 73 | );
 74 | 
 75 | // Start receiving messages on stdin and sending messages on stdout
 76 | const transport = new StdioServerTransport();
 77 | await server.connect(transport);
 78 | ```
 79 | 
 80 | ## What is MCP?
 81 | 
 82 | The [Model Context Protocol (MCP)](https://modelcontextprotocol.io) lets you build servers that expose data and functionality to LLM applications in a secure, standardized way. Think of it like a web API, but specifically designed for LLM interactions. MCP servers can:
 83 | 
 84 | - Expose data through **Resources** (think of these sort of like GET endpoints; they are used to load information into the LLM's context)
 85 | - Provide functionality through **Tools** (sort of like POST endpoints; they are used to execute code or otherwise produce a side effect)
 86 | - Define interaction patterns through **Prompts** (reusable templates for LLM interactions)
 87 | - And more!
 88 | 
 89 | ## Core Concepts
 90 | 
 91 | ### Server
 92 | 
 93 | The McpServer is your core interface to the MCP protocol. It handles connection management, protocol compliance, and message routing:
 94 | 
 95 | ```typescript
 96 | const server = new McpServer({
 97 |   name: 'My App',
 98 |   version: '1.0.0',
 99 | });
100 | ```
101 | 
102 | ### Resources
103 | 
104 | Resources are how you expose data to LLMs. They're similar to GET endpoints in a REST API - they provide data but shouldn't perform significant computation or have side effects:
105 | 
106 | ```typescript
107 | // Static resource
108 | server.resource('config', 'config://app', async (uri) => ({
109 |   contents: [
110 |     {
111 |       uri: uri.href,
112 |       text: 'App configuration here',
113 |     },
114 |   ],
115 | }));
116 | 
117 | // Dynamic resource with parameters
118 | server.resource(
119 |   'user-profile',
120 |   new ResourceTemplate('users://{userId}/profile', { list: undefined }),
121 |   async (uri, { userId }) => ({
122 |     contents: [
123 |       {
124 |         uri: uri.href,
125 |         text: `Profile data for user ${userId}`,
126 |       },
127 |     ],
128 |   }),
129 | );
130 | ```
131 | 
132 | ### Tools
133 | 
134 | Tools let LLMs take actions through your server. Unlike resources, tools are expected to perform computation and have side effects:
135 | 
136 | ```typescript
137 | // Simple tool with parameters
138 | server.tool(
139 |   'calculate-bmi',
140 |   {
141 |     weightKg: z.number(),
142 |     heightM: z.number(),
143 |   },
144 |   async ({ weightKg, heightM }) => ({
145 |     content: [
146 |       {
147 |         type: 'text',
148 |         text: String(weightKg / (heightM * heightM)),
149 |       },
150 |     ],
151 |   }),
152 | );
153 | 
154 | // Async tool with external API call
155 | server.tool('fetch-weather', { city: z.string() }, async ({ city }) => {
156 |   const response = await fetch(`https://api.weather.com/${city}`);
157 |   const data = await response.text();
158 |   return {
159 |     content: [{ type: 'text', text: data }],
160 |   };
161 | });
162 | ```
163 | 
164 | ### Prompts
165 | 
166 | Prompts are reusable templates that help LLMs interact with your server effectively:
167 | 
168 | ```typescript
169 | server.prompt('review-code', { code: z.string() }, ({ code }) => ({
170 |   messages: [
171 |     {
172 |       role: 'user',
173 |       content: {
174 |         type: 'text',
175 |         text: `Please review this code:\n\n${code}`,
176 |       },
177 |     },
178 |   ],
179 | }));
180 | ```
181 | 
182 | ## Running Your Server
183 | 
184 | MCP servers in TypeScript need to be connected to a transport to communicate with clients. How you start the server depends on the choice of transport:
185 | 
186 | ### stdio
187 | 
188 | For command-line tools and direct integrations:
189 | 
190 | ```typescript
191 | import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
192 | import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
193 | 
194 | const server = new McpServer({
195 |   name: 'example-server',
196 |   version: '1.0.0',
197 | });
198 | 
199 | // ... set up server resources, tools, and prompts ...
200 | 
201 | const transport = new StdioServerTransport();
202 | await server.connect(transport);
203 | ```
204 | 
205 | ### HTTP with SSE
206 | 
207 | For remote servers, start a web server with a Server-Sent Events (SSE) endpoint, and a separate endpoint for the client to send its messages to:
208 | 
209 | ```typescript
210 | import express from 'express';
211 | import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
212 | import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
213 | 
214 | const server = new McpServer({
215 |   name: 'example-server',
216 |   version: '1.0.0',
217 | });
218 | 
219 | // ... set up server resources, tools, and prompts ...
220 | 
221 | const app = express();
222 | 
223 | app.get('/sse', async (req, res) => {
224 |   const transport = new SSEServerTransport('/messages', res);
225 |   await server.connect(transport);
226 | });
227 | 
228 | app.post('/messages', async (req, res) => {
229 |   // Note: to support multiple simultaneous connections, these messages will
230 |   // need to be routed to a specific matching transport. (This logic isn't
231 |   // implemented here, for simplicity.)
232 |   await transport.handlePostMessage(req, res);
233 | });
234 | 
235 | app.listen(3001);
236 | ```
237 | 
238 | ### Testing and Debugging
239 | 
240 | To test your server, you can use the [MCP Inspector](https://github.com/modelcontextprotocol/inspector). See its README for more information.
241 | 
242 | ## Examples
243 | 
244 | ### Echo Server
245 | 
246 | A simple server demonstrating resources, tools, and prompts:
247 | 
248 | ```typescript
249 | import { McpServer, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
250 | import { z } from 'zod';
251 | 
252 | const server = new McpServer({
253 |   name: 'Echo',
254 |   version: '1.0.0',
255 | });
256 | 
257 | server.resource(
258 |   'echo',
259 |   new ResourceTemplate('echo://{message}', { list: undefined }),
260 |   async (uri, { message }) => ({
261 |     contents: [
262 |       {
263 |         uri: uri.href,
264 |         text: `Resource echo: ${message}`,
265 |       },
266 |     ],
267 |   }),
268 | );
269 | 
270 | server.tool('echo', { message: z.string() }, async ({ message }) => ({
271 |   content: [{ type: 'text', text: `Tool echo: ${message}` }],
272 | }));
273 | 
274 | server.prompt('echo', { message: z.string() }, ({ message }) => ({
275 |   messages: [
276 |     {
277 |       role: 'user',
278 |       content: {
279 |         type: 'text',
280 |         text: `Please process this message: ${message}`,
281 |       },
282 |     },
283 |   ],
284 | }));
285 | ```
286 | 
287 | ### SQLite Explorer
288 | 
289 | A more complex example showing database integration:
290 | 
291 | ```typescript
292 | import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
293 | import sqlite3 from 'sqlite3';
294 | import { promisify } from 'util';
295 | import { z } from 'zod';
296 | 
297 | const server = new McpServer({
298 |   name: 'SQLite Explorer',
299 |   version: '1.0.0',
300 | });
301 | 
302 | // Helper to create DB connection
303 | const getDb = () => {
304 |   const db = new sqlite3.Database('database.db');
305 |   return {
306 |     all: promisify<string, any[]>(db.all.bind(db)),
307 |     close: promisify(db.close.bind(db)),
308 |   };
309 | };
310 | 
311 | server.resource('schema', 'schema://main', async (uri) => {
312 |   const db = getDb();
313 |   try {
314 |     const tables = await db.all("SELECT sql FROM sqlite_master WHERE type='table'");
315 |     return {
316 |       contents: [
317 |         {
318 |           uri: uri.href,
319 |           text: tables.map((t: { sql: string }) => t.sql).join('\n'),
320 |         },
321 |       ],
322 |     };
323 |   } finally {
324 |     await db.close();
325 |   }
326 | });
327 | 
328 | server.tool('query', { sql: z.string() }, async ({ sql }) => {
329 |   const db = getDb();
330 |   try {
331 |     const results = await db.all(sql);
332 |     return {
333 |       content: [
334 |         {
335 |           type: 'text',
336 |           text: JSON.stringify(results, null, 2),
337 |         },
338 |       ],
339 |     };
340 |   } catch (err: unknown) {
341 |     const error = err as Error;
342 |     return {
343 |       content: [
344 |         {
345 |           type: 'text',
346 |           text: `Error: ${error.message}`,
347 |         },
348 |       ],
349 |       isError: true,
350 |     };
351 |   } finally {
352 |     await db.close();
353 |   }
354 | });
355 | ```
356 | 
357 | ## Advanced Usage
358 | 
359 | ### Low-Level Server
360 | 
361 | For more control, you can use the low-level Server class directly:
362 | 
363 | ```typescript
364 | import { Server } from '@modelcontextprotocol/sdk/server/index.js';
365 | import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
366 | import {
367 |   ListPromptsRequestSchema,
368 |   GetPromptRequestSchema,
369 | } from '@modelcontextprotocol/sdk/types.js';
370 | 
371 | const server = new Server(
372 |   {
373 |     name: 'example-server',
374 |     version: '1.0.0',
375 |   },
376 |   {
377 |     capabilities: {
378 |       prompts: {},
379 |     },
380 |   },
381 | );
382 | 
383 | server.setRequestHandler(ListPromptsRequestSchema, async () => {
384 |   return {
385 |     prompts: [
386 |       {
387 |         name: 'example-prompt',
388 |         description: 'An example prompt template',
389 |         arguments: [
390 |           {
391 |             name: 'arg1',
392 |             description: 'Example argument',
393 |             required: true,
394 |           },
395 |         ],
396 |       },
397 |     ],
398 |   };
399 | });
400 | 
401 | server.setRequestHandler(GetPromptRequestSchema, async (request) => {
402 |   if (request.params.name !== 'example-prompt') {
403 |     throw new Error('Unknown prompt');
404 |   }
405 |   return {
406 |     description: 'Example prompt',
407 |     messages: [
408 |       {
409 |         role: 'user',
410 |         content: {
411 |           type: 'text',
412 |           text: 'Example prompt text',
413 |         },
414 |       },
415 |     ],
416 |   };
417 | });
418 | 
419 | const transport = new StdioServerTransport();
420 | await server.connect(transport);
421 | ```
422 | 
423 | ### Writing MCP Clients
424 | 
425 | The SDK provides a high-level client interface:
426 | 
427 | ```typescript
428 | import { Client } from '@modelcontextprotocol/sdk/client/index.js';
429 | import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
430 | 
431 | const transport = new StdioClientTransport({
432 |   command: 'node',
433 |   args: ['server.js'],
434 | });
435 | 
436 | const client = new Client(
437 |   {
438 |     name: 'example-client',
439 |     version: '1.0.0',
440 |   },
441 |   {
442 |     capabilities: {
443 |       prompts: {},
444 |       resources: {},
445 |       tools: {},
446 |     },
447 |   },
448 | );
449 | 
450 | await client.connect(transport);
451 | 
452 | // List prompts
453 | const prompts = await client.listPrompts();
454 | 
455 | // Get a prompt
456 | const prompt = await client.getPrompt('example-prompt', {
457 |   arg1: 'value',
458 | });
459 | 
460 | // List resources
461 | const resources = await client.listResources();
462 | 
463 | // Read a resource
464 | const resource = await client.readResource('file:///example.txt');
465 | 
466 | // Call a tool
467 | const result = await client.callTool({
468 |   name: 'example-tool',
469 |   arguments: {
470 |     arg1: 'value',
471 |   },
472 | });
473 | ```
474 | 
475 | ## Documentation
476 | 
477 | - [Model Context Protocol documentation](https://modelcontextprotocol.io)
478 | - [MCP Specification](https://spec.modelcontextprotocol.io)
479 | - [Example Servers](https://github.com/modelcontextprotocol/servers)
480 | 
481 | ## Contributing
482 | 
483 | Issues and pull requests are welcome on GitHub at https://github.com/modelcontextprotocol/typescript-sdk.
484 | 
485 | ## License
486 | 
487 | This project is licensed under the MIT License—see the [LICENSE](LICENSE) file for details.
488 | 
```

--------------------------------------------------------------------------------
/src/lib/mcp-server.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
  2 | import { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
  3 | import { CoolifyClient } from './coolify-client.js';
  4 | import debug from 'debug';
  5 | import { z } from 'zod';
  6 | import type {
  7 |   ServerInfo,
  8 |   ServerResources,
  9 |   ServerDomain,
 10 |   ValidationResponse,
 11 |   Project,
 12 |   CreateProjectRequest,
 13 |   UpdateProjectRequest,
 14 |   Environment,
 15 |   Deployment,
 16 |   Database,
 17 |   DatabaseUpdateRequest,
 18 |   Service,
 19 |   CreateServiceRequest,
 20 |   DeleteServiceOptions,
 21 | } from '../types/coolify.js';
 22 | 
 23 | const log = debug('coolify:mcp');
 24 | 
 25 | // Define valid service types
 26 | const serviceTypes = [
 27 |   'activepieces',
 28 |   'appsmith',
 29 |   'appwrite',
 30 |   'authentik',
 31 |   'babybuddy',
 32 |   'budge',
 33 |   'changedetection',
 34 |   'chatwoot',
 35 |   'classicpress-with-mariadb',
 36 |   'classicpress-with-mysql',
 37 |   'classicpress-without-database',
 38 |   'cloudflared',
 39 |   'code-server',
 40 |   'dashboard',
 41 |   'directus',
 42 |   'directus-with-postgresql',
 43 |   'docker-registry',
 44 |   'docuseal',
 45 |   'docuseal-with-postgres',
 46 |   'dokuwiki',
 47 |   'duplicati',
 48 |   'emby',
 49 |   'embystat',
 50 |   'fider',
 51 |   'filebrowser',
 52 |   'firefly',
 53 |   'formbricks',
 54 |   'ghost',
 55 |   'gitea',
 56 |   'gitea-with-mariadb',
 57 |   'gitea-with-mysql',
 58 |   'gitea-with-postgresql',
 59 |   'glance',
 60 |   'glances',
 61 |   'glitchtip',
 62 |   'grafana',
 63 |   'grafana-with-postgresql',
 64 |   'grocy',
 65 |   'heimdall',
 66 |   'homepage',
 67 |   'jellyfin',
 68 |   'kuzzle',
 69 |   'listmonk',
 70 |   'logto',
 71 |   'mediawiki',
 72 |   'meilisearch',
 73 |   'metabase',
 74 |   'metube',
 75 |   'minio',
 76 |   'moodle',
 77 |   'n8n',
 78 |   'n8n-with-postgresql',
 79 |   'next-image-transformation',
 80 |   'nextcloud',
 81 |   'nocodb',
 82 |   'odoo',
 83 |   'openblocks',
 84 |   'pairdrop',
 85 |   'penpot',
 86 |   'phpmyadmin',
 87 |   'pocketbase',
 88 |   'posthog',
 89 |   'reactive-resume',
 90 |   'rocketchat',
 91 |   'shlink',
 92 |   'slash',
 93 |   'snapdrop',
 94 |   'statusnook',
 95 |   'stirling-pdf',
 96 |   'supabase',
 97 |   'syncthing',
 98 |   'tolgee',
 99 |   'trigger',
100 |   'trigger-with-external-database',
101 |   'twenty',
102 |   'umami',
103 |   'unleash-with-postgresql',
104 |   'unleash-without-database',
105 |   'uptime-kuma',
106 |   'vaultwarden',
107 |   'vikunja',
108 |   'weblate',
109 |   'whoogle',
110 |   'wordpress-with-mariadb',
111 |   'wordpress-with-mysql',
112 |   'wordpress-without-database'
113 | ] as const;
114 | 
115 | export class CoolifyMcpServer extends McpServer {
116 |   private client: CoolifyClient;
117 | 
118 |   constructor(config: { baseUrl: string; accessToken: string }) {
119 |     super({
120 |       name: 'coolify',
121 |       version: '0.1.18'
122 |     });
123 |     
124 |     log('Initializing server with config: %o', config);
125 |     this.client = new CoolifyClient(config);
126 |   }
127 | 
128 |   async initialize(): Promise<void> {
129 |     // Register capabilities first
130 |     await this.server.registerCapabilities({
131 |       tools: {}
132 |     });
133 | 
134 |     // Then register all tools
135 |     this.tool('list_servers', 'List all Coolify servers', {}, async () => {
136 |       const servers = await this.client.listServers();
137 |       return {
138 |         content: [{ type: 'text', text: JSON.stringify(servers, null, 2) }]
139 |       };
140 |     });
141 | 
142 |     this.tool('get_server', 'Get details about a specific Coolify server', {
143 |       uuid: z.string().describe('UUID of the server to get details for')
144 |     }, async (args) => {
145 |       const server = await this.client.getServer(args.uuid);
146 |       return {
147 |         content: [{ type: 'text', text: JSON.stringify(server, null, 2) }]
148 |       };
149 |     });
150 | 
151 |     this.tool('get_server_resources', 'Get the current resources running on a specific Coolify server', {
152 |       uuid: z.string()
153 |     }, async (args, _extra) => {
154 |       const resources = await this.client.getServerResources(args.uuid);
155 |       return {
156 |         content: [{ type: 'text', text: JSON.stringify(resources, null, 2) }]
157 |       };
158 |     });
159 | 
160 |     this.tool('get_server_domains', 'Get domains for a specific Coolify server', {
161 |       uuid: z.string()
162 |     }, async (args, _extra) => {
163 |       const domains = await this.client.getServerDomains(args.uuid);
164 |       return {
165 |         content: [{ type: 'text', text: JSON.stringify(domains, null, 2) }]
166 |       };
167 |     });
168 | 
169 |     this.tool('validate_server', 'Validate a specific Coolify server', {
170 |       uuid: z.string()
171 |     }, async (args, _extra) => {
172 |       const validation = await this.client.validateServer(args.uuid);
173 |       return {
174 |         content: [{ type: 'text', text: JSON.stringify(validation, null, 2) }]
175 |       };
176 |     });
177 | 
178 |     this.tool('list_projects', 'List all Coolify projects', {}, async (_args, _extra) => {
179 |       const projects = await this.client.listProjects();
180 |       return {
181 |         content: [{ type: 'text', text: JSON.stringify(projects, null, 2) }]
182 |       };
183 |     });
184 | 
185 |     this.tool('get_project', 'Get details about a specific Coolify project', {
186 |       uuid: z.string()
187 |     }, async (args, _extra) => {
188 |       const project = await this.client.getProject(args.uuid);
189 |       return {
190 |         content: [{ type: 'text', text: JSON.stringify(project, null, 2) }]
191 |       };
192 |     });
193 | 
194 |     this.tool('create_project', 'Create a new Coolify project', {
195 |       name: z.string(),
196 |       description: z.string().optional()
197 |     }, async (args, _extra) => {
198 |       const result = await this.client.createProject({
199 |         name: args.name,
200 |         description: args.description
201 |       });
202 |       return {
203 |         content: [{ type: 'text', text: JSON.stringify(result, null, 2) }]
204 |       };
205 |     });
206 | 
207 |     this.tool('update_project', 'Update an existing Coolify project', {
208 |       uuid: z.string(),
209 |       name: z.string(),
210 |       description: z.string().optional()
211 |     }, async (args, _extra) => {
212 |       const { uuid, ...updateData } = args;
213 |       const result = await this.client.updateProject(uuid, updateData);
214 |       return {
215 |         content: [{ type: 'text', text: JSON.stringify(result, null, 2) }]
216 |       };
217 |     });
218 | 
219 |     this.tool('delete_project', 'Delete a Coolify project', {
220 |       uuid: z.string()
221 |     }, async (args, _extra) => {
222 |       const result = await this.client.deleteProject(args.uuid);
223 |       return {
224 |         content: [{ type: 'text', text: JSON.stringify(result, null, 2) }]
225 |       };
226 |     });
227 | 
228 |     this.tool('get_project_environment', 'Get environment details for a Coolify project', {
229 |       project_uuid: z.string(),
230 |       environment_name_or_uuid: z.string()
231 |     }, async (args, _extra) => {
232 |       const environment = await this.client.getProjectEnvironment(args.project_uuid, args.environment_name_or_uuid);
233 |       return {
234 |         content: [{ type: 'text', text: JSON.stringify(environment, null, 2) }]
235 |       };
236 |     });
237 | 
238 |     this.tool('list_databases', 'List all Coolify databases', {}, async (_args, _extra) => {
239 |       const databases = await this.client.listDatabases();
240 |       return {
241 |         content: [{ type: 'text', text: JSON.stringify(databases, null, 2) }]
242 |       };
243 |     });
244 | 
245 |     this.tool('get_database', 'Get details about a specific Coolify database', {
246 |       uuid: z.string()
247 |     }, async (args, _extra) => {
248 |       const database = await this.client.getDatabase(args.uuid);
249 |       return {
250 |         content: [{ type: 'text', text: JSON.stringify(database, null, 2) }]
251 |       };
252 |     });
253 | 
254 |     this.tool('update_database', 'Update a Coolify database', {
255 |       uuid: z.string(),
256 |       data: z.record(z.unknown())
257 |     }, async (args, _extra) => {
258 |       const result = await this.client.updateDatabase(args.uuid, args.data);
259 |       return {
260 |         content: [{ type: 'text', text: JSON.stringify(result, null, 2) }]
261 |       };
262 |     });
263 | 
264 |     const deleteOptionsSchema = {
265 |       deleteConfigurations: z.boolean().optional(),
266 |       deleteVolumes: z.boolean().optional(),
267 |       dockerCleanup: z.boolean().optional(),
268 |       deleteConnectedNetworks: z.boolean().optional()
269 |     };
270 | 
271 |     this.tool('delete_database', 'Delete a Coolify database', {
272 |       uuid: z.string(),
273 |       options: z.object(deleteOptionsSchema).optional()
274 |     }, async (args, _extra) => {
275 |       const result = await this.client.deleteDatabase(args.uuid, args.options);
276 |       return {
277 |         content: [{ type: 'text', text: JSON.stringify(result, null, 2) }]
278 |       };
279 |     });
280 | 
281 |     this.tool('deploy_application', 'Deploy a Coolify application', {
282 |       uuid: z.string()
283 |     }, async (args, _extra) => {
284 |       const result = await this.client.deployApplication(args.uuid);
285 |       return {
286 |         content: [{ type: 'text', text: JSON.stringify(result, null, 2) }]
287 |       };
288 |     });
289 | 
290 |     this.tool('list_services', 'List all Coolify services', {}, async (_args, _extra) => {
291 |       const services = await this.client.listServices();
292 |       return {
293 |         content: [{ type: 'text', text: JSON.stringify(services, null, 2) }]
294 |       };
295 |     });
296 | 
297 |     this.tool('get_service', 'Get details about a specific Coolify service', {
298 |       uuid: z.string()
299 |     }, async (args, _extra) => {
300 |       const service = await this.client.getService(args.uuid);
301 |       return {
302 |         content: [{ type: 'text', text: JSON.stringify(service, null, 2) }]
303 |       };
304 |     });
305 | 
306 |     this.tool('create_service', 'Create a new Coolify service', {
307 |       type: z.enum(serviceTypes),
308 |       project_uuid: z.string(),
309 |       server_uuid: z.string(),
310 |       name: z.string().optional(),
311 |       description: z.string().optional(),
312 |       environment_name: z.string().optional(),
313 |       environment_uuid: z.string().optional(),
314 |       destination_uuid: z.string().optional(),
315 |       instant_deploy: z.boolean().optional()
316 |     }, async (args, _extra) => {
317 |       const result = await this.client.createService(args);
318 |       return {
319 |         content: [{ type: 'text', text: JSON.stringify(result, null, 2) }]
320 |       };
321 |     });
322 | 
323 |     this.tool('delete_service', 'Delete a Coolify service', {
324 |       uuid: z.string(),
325 |       options: z.object(deleteOptionsSchema).optional()
326 |     }, async (args, _extra) => {
327 |       const result = await this.client.deleteService(args.uuid, args.options);
328 |       return {
329 |         content: [{ type: 'text', text: JSON.stringify(result, null, 2) }]
330 |       };
331 |     });
332 |   }
333 | 
334 |   async connect(transport: Transport): Promise<void> {
335 |     log('Starting server...');
336 |     log('Validating connection...');
337 |     await this.client.validateConnection();
338 |     await this.initialize();
339 |     await super.connect(transport);
340 |     log('Server started successfully');
341 |   }
342 | 
343 |   async list_servers(): Promise<ServerInfo[]> {
344 |     return this.client.listServers();
345 |   }
346 | 
347 |   async get_server(uuid: string): Promise<ServerInfo> {
348 |     return this.client.getServer(uuid);
349 |   }
350 | 
351 |   async get_server_resources(uuid: string): Promise<ServerResources> {
352 |     return this.client.getServerResources(uuid);
353 |   }
354 | 
355 |   async get_server_domains(uuid: string): Promise<ServerDomain[]> {
356 |     return this.client.getServerDomains(uuid);
357 |   }
358 | 
359 |   async validate_server(uuid: string): Promise<ValidationResponse> {
360 |     return this.client.validateServer(uuid);
361 |   }
362 | 
363 |   async list_projects(): Promise<Project[]> {
364 |     return this.client.listProjects();
365 |   }
366 | 
367 |   async get_project(uuid: string): Promise<Project> {
368 |     return this.client.getProject(uuid);
369 |   }
370 | 
371 |   async create_project(project: CreateProjectRequest): Promise<{ uuid: string }> {
372 |     return this.client.createProject(project);
373 |   }
374 | 
375 |   async update_project(uuid: string, project: UpdateProjectRequest): Promise<Project> {
376 |     return this.client.updateProject(uuid, project);
377 |   }
378 | 
379 |   async delete_project(uuid: string): Promise<{ message: string }> {
380 |     return this.client.deleteProject(uuid);
381 |   }
382 | 
383 |   async get_project_environment(
384 |     projectUuid: string,
385 |     environmentNameOrUuid: string,
386 |   ): Promise<Environment> {
387 |     return this.client.getProjectEnvironment(projectUuid, environmentNameOrUuid);
388 |   }
389 | 
390 |   async deploy_application(params: { uuid: string }): Promise<Deployment> {
391 |     return this.client.deployApplication(params.uuid);
392 |   }
393 | 
394 |   async list_databases(): Promise<Database[]> {
395 |     return this.client.listDatabases();
396 |   }
397 | 
398 |   async get_database(uuid: string): Promise<Database> {
399 |     return this.client.getDatabase(uuid);
400 |   }
401 | 
402 |   async update_database(uuid: string, data: DatabaseUpdateRequest): Promise<Database> {
403 |     return this.client.updateDatabase(uuid, data);
404 |   }
405 | 
406 |   async delete_database(
407 |     uuid: string,
408 |     options?: {
409 |       deleteConfigurations?: boolean;
410 |       deleteVolumes?: boolean;
411 |       dockerCleanup?: boolean;
412 |       deleteConnectedNetworks?: boolean;
413 |     },
414 |   ): Promise<{ message: string }> {
415 |     return this.client.deleteDatabase(uuid, options);
416 |   }
417 | 
418 |   async list_services(): Promise<Service[]> {
419 |     return this.client.listServices();
420 |   }
421 | 
422 |   async get_service(uuid: string): Promise<Service> {
423 |     return this.client.getService(uuid);
424 |   }
425 | 
426 |   async create_service(data: CreateServiceRequest): Promise<{ uuid: string; domains: string[] }> {
427 |     return this.client.createService(data);
428 |   }
429 | 
430 |   async delete_service(uuid: string, options?: DeleteServiceOptions): Promise<{ message: string }> {
431 |     return this.client.deleteService(uuid, options);
432 |   }
433 | }
434 | 
```
Page 1/5FirstPrevNextLast