This is page 2 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 -------------------------------------------------------------------------------- /docs/openapi-chunks/services-api.yaml: -------------------------------------------------------------------------------- ```yaml 1 | openapi: 3.1.0 2 | info: 3 | title: Coolify 4 | version: '0.1' 5 | paths: 6 | /services: 7 | get: 8 | tags: 9 | - Services 10 | summary: List 11 | description: List all services. 12 | operationId: list-services 13 | responses: 14 | '200': 15 | description: Get all services 16 | content: 17 | application/json: 18 | schema: 19 | type: array 20 | items: 21 | $ref: '#/components/schemas/Service' 22 | '400': 23 | $ref: '#/components/responses/400' 24 | '401': 25 | $ref: '#/components/responses/401' 26 | security: 27 | - bearerAuth: [] 28 | post: 29 | tags: 30 | - Services 31 | summary: Create 32 | description: Create a one-click service 33 | operationId: create-service 34 | requestBody: 35 | required: true 36 | content: 37 | application/json: 38 | schema: 39 | required: 40 | - server_uuid 41 | - project_uuid 42 | - environment_name 43 | - environment_uuid 44 | - type 45 | properties: 46 | type: 47 | description: The one-click service type 48 | type: string 49 | enum: 50 | - activepieces 51 | - appsmith 52 | - appwrite 53 | - authentik 54 | - babybuddy 55 | - budge 56 | - changedetection 57 | - chatwoot 58 | - classicpress-with-mariadb 59 | - classicpress-with-mysql 60 | - classicpress-without-database 61 | - cloudflared 62 | - code-server 63 | - dashboard 64 | - directus 65 | - directus-with-postgresql 66 | - docker-registry 67 | - docuseal 68 | - docuseal-with-postgres 69 | - dokuwiki 70 | - duplicati 71 | - emby 72 | - embystat 73 | - fider 74 | - filebrowser 75 | - firefly 76 | - formbricks 77 | - ghost 78 | - gitea 79 | - gitea-with-mariadb 80 | - gitea-with-mysql 81 | - gitea-with-postgresql 82 | - glance 83 | - glances 84 | - glitchtip 85 | - grafana 86 | - grafana-with-postgresql 87 | - grocy 88 | - heimdall 89 | - homepage 90 | - jellyfin 91 | - kuzzle 92 | - listmonk 93 | - logto 94 | - mediawiki 95 | - meilisearch 96 | - metabase 97 | - metube 98 | - minio 99 | - moodle 100 | - n8n 101 | - n8n-with-postgresql 102 | - next-image-transformation 103 | - nextcloud 104 | - nocodb 105 | - odoo 106 | - openblocks 107 | - pairdrop 108 | - penpot 109 | - phpmyadmin 110 | - pocketbase 111 | - posthog 112 | - reactive-resume 113 | - rocketchat 114 | - shlink 115 | - slash 116 | - snapdrop 117 | - statusnook 118 | - stirling-pdf 119 | - supabase 120 | - syncthing 121 | - tolgee 122 | - trigger 123 | - trigger-with-external-database 124 | - twenty 125 | - umami 126 | - unleash-with-postgresql 127 | - unleash-without-database 128 | - uptime-kuma 129 | - vaultwarden 130 | - vikunja 131 | - weblate 132 | - whoogle 133 | - wordpress-with-mariadb 134 | - wordpress-with-mysql 135 | - wordpress-without-database 136 | name: 137 | type: string 138 | maxLength: 255 139 | description: Name of the service. 140 | description: 141 | type: string 142 | nullable: true 143 | description: Description of the service. 144 | project_uuid: 145 | type: string 146 | description: Project UUID. 147 | environment_name: 148 | type: string 149 | description: Environment name. You need to provide at least one of environment_name or environment_uuid. 150 | environment_uuid: 151 | type: string 152 | description: Environment UUID. You need to provide at least one of environment_name or environment_uuid. 153 | server_uuid: 154 | type: string 155 | description: Server UUID. 156 | destination_uuid: 157 | type: string 158 | description: Destination UUID. Required if server has multiple destinations. 159 | instant_deploy: 160 | type: boolean 161 | default: false 162 | description: Start the service immediately after creation. 163 | type: object 164 | responses: 165 | '201': 166 | description: Create a service. 167 | content: 168 | application/json: 169 | schema: 170 | properties: 171 | uuid: 172 | type: string 173 | description: Service UUID. 174 | domains: 175 | type: array 176 | items: 177 | type: string 178 | description: Service domains. 179 | type: object 180 | '400': 181 | $ref: '#/components/responses/400' 182 | '401': 183 | $ref: '#/components/responses/401' 184 | security: 185 | - bearerAuth: [] 186 | /services/{uuid}: 187 | get: 188 | tags: 189 | - Services 190 | summary: Get 191 | description: Get service by UUID. 192 | operationId: get-service-by-uuid 193 | parameters: 194 | - name: uuid 195 | in: path 196 | description: Service UUID 197 | required: true 198 | schema: 199 | type: string 200 | responses: 201 | '200': 202 | description: Get a service by UUID. 203 | content: 204 | application/json: 205 | schema: 206 | $ref: '#/components/schemas/Service' 207 | '400': 208 | $ref: '#/components/responses/400' 209 | '401': 210 | $ref: '#/components/responses/401' 211 | '404': 212 | $ref: '#/components/responses/404' 213 | security: 214 | - bearerAuth: [] 215 | delete: 216 | tags: 217 | - Services 218 | summary: Delete 219 | description: Delete service by UUID. 220 | operationId: delete-service-by-uuid 221 | parameters: 222 | - name: uuid 223 | in: path 224 | description: Service UUID 225 | required: true 226 | schema: 227 | type: string 228 | - name: delete_configurations 229 | in: query 230 | description: Delete configurations. 231 | required: false 232 | schema: 233 | type: boolean 234 | default: true 235 | - name: delete_volumes 236 | in: query 237 | description: Delete volumes. 238 | required: false 239 | schema: 240 | type: boolean 241 | default: true 242 | - name: docker_cleanup 243 | in: query 244 | description: Run docker cleanup. 245 | required: false 246 | schema: 247 | type: boolean 248 | default: true 249 | - name: delete_connected_networks 250 | in: query 251 | description: Delete connected networks. 252 | required: false 253 | schema: 254 | type: boolean 255 | default: true 256 | responses: 257 | '200': 258 | description: Delete a service by UUID 259 | content: 260 | application/json: 261 | schema: 262 | properties: 263 | message: 264 | type: string 265 | example: Service deletion request queued. 266 | type: object 267 | '400': 268 | $ref: '#/components/responses/400' 269 | '401': 270 | $ref: '#/components/responses/401' 271 | '404': 272 | $ref: '#/components/responses/404' 273 | security: 274 | - bearerAuth: [] 275 | /services/{uuid}/envs: 276 | get: 277 | tags: 278 | - Services 279 | summary: List Envs 280 | description: List all envs by service UUID. 281 | operationId: list-envs-by-service-uuid 282 | parameters: 283 | - name: uuid 284 | in: path 285 | description: UUID of the service. 286 | required: true 287 | schema: 288 | type: string 289 | format: uuid 290 | responses: 291 | '200': 292 | description: All environment variables by service UUID. 293 | content: 294 | application/json: 295 | schema: 296 | type: array 297 | items: 298 | $ref: '#/components/schemas/EnvironmentVariable' 299 | '400': 300 | $ref: '#/components/responses/400' 301 | '401': 302 | $ref: '#/components/responses/401' 303 | '404': 304 | $ref: '#/components/responses/404' 305 | security: 306 | - bearerAuth: [] 307 | post: 308 | tags: 309 | - Services 310 | summary: Create Env 311 | description: Create env by service UUID. 312 | operationId: create-env-by-service-uuid 313 | parameters: 314 | - name: uuid 315 | in: path 316 | description: UUID of the service. 317 | required: true 318 | schema: 319 | type: string 320 | format: uuid 321 | requestBody: 322 | description: Env created. 323 | required: true 324 | content: 325 | application/json: 326 | schema: 327 | properties: 328 | key: 329 | type: string 330 | description: The key of the environment variable. 331 | value: 332 | type: string 333 | description: The value of the environment variable. 334 | is_preview: 335 | type: boolean 336 | description: The flag to indicate if the environment variable is used in preview deployments. 337 | is_build_time: 338 | type: boolean 339 | description: The flag to indicate if the environment variable is used in build time. 340 | is_literal: 341 | type: boolean 342 | description: The flag to indicate if the environment variable is a literal, nothing espaced. 343 | is_multiline: 344 | type: boolean 345 | description: The flag to indicate if the environment variable is multiline. 346 | is_shown_once: 347 | type: boolean 348 | description: The flag to indicate if the environment variable's value is shown on the UI. 349 | type: object 350 | responses: 351 | '201': 352 | description: Environment variable created. 353 | content: 354 | application/json: 355 | schema: 356 | properties: 357 | uuid: 358 | type: string 359 | example: nc0k04gk8g0cgsk440g0koko 360 | type: object 361 | '400': 362 | $ref: '#/components/responses/400' 363 | '401': 364 | $ref: '#/components/responses/401' 365 | '404': 366 | $ref: '#/components/responses/404' 367 | security: 368 | - bearerAuth: [] 369 | patch: 370 | tags: 371 | - Services 372 | summary: Update Env 373 | description: Update env by service UUID. 374 | operationId: update-env-by-service-uuid 375 | parameters: 376 | - name: uuid 377 | in: path 378 | description: UUID of the service. 379 | required: true 380 | schema: 381 | type: string 382 | format: uuid 383 | requestBody: 384 | description: Env updated. 385 | required: true 386 | content: 387 | application/json: 388 | schema: 389 | required: 390 | - key 391 | - value 392 | properties: 393 | key: 394 | type: string 395 | description: The key of the environment variable. 396 | value: 397 | type: string 398 | description: The value of the environment variable. 399 | is_preview: 400 | type: boolean 401 | description: The flag to indicate if the environment variable is used in preview deployments. 402 | is_build_time: 403 | type: boolean 404 | description: The flag to indicate if the environment variable is used in build time. 405 | is_literal: 406 | type: boolean 407 | description: The flag to indicate if the environment variable is a literal, nothing espaced. 408 | is_multiline: 409 | type: boolean 410 | description: The flag to indicate if the environment variable is multiline. 411 | is_shown_once: 412 | type: boolean 413 | description: The flag to indicate if the environment variable's value is shown on the UI. 414 | type: object 415 | responses: 416 | '201': 417 | description: Environment variable updated. 418 | content: 419 | application/json: 420 | schema: 421 | properties: 422 | message: 423 | type: string 424 | example: Environment variable updated. 425 | type: object 426 | '400': 427 | $ref: '#/components/responses/400' 428 | '401': 429 | $ref: '#/components/responses/401' 430 | '404': 431 | $ref: '#/components/responses/404' 432 | security: 433 | - bearerAuth: [] 434 | /services/{uuid}/envs/bulk: 435 | patch: 436 | tags: 437 | - Services 438 | summary: Update Envs (Bulk) 439 | description: Update multiple envs by service UUID. 440 | operationId: update-envs-by-service-uuid 441 | parameters: 442 | - name: uuid 443 | in: path 444 | description: UUID of the service. 445 | required: true 446 | schema: 447 | type: string 448 | format: uuid 449 | requestBody: 450 | description: Bulk envs updated. 451 | required: true 452 | content: 453 | application/json: 454 | schema: 455 | required: 456 | - data 457 | properties: 458 | data: 459 | type: array 460 | items: 461 | properties: 462 | key: 463 | type: string 464 | description: The key of the environment variable. 465 | value: 466 | type: string 467 | description: The value of the environment variable. 468 | is_preview: 469 | type: boolean 470 | description: The flag to indicate if the environment variable is used in preview deployments. 471 | is_build_time: 472 | type: boolean 473 | description: The flag to indicate if the environment variable is used in build time. 474 | is_literal: 475 | type: boolean 476 | description: The flag to indicate if the environment variable is a literal, nothing espaced. 477 | is_multiline: 478 | type: boolean 479 | description: The flag to indicate if the environment variable is multiline. 480 | is_shown_once: 481 | type: boolean 482 | description: The flag to indicate if the environment variable's value is shown on the UI. 483 | type: object 484 | type: object 485 | responses: 486 | '201': 487 | description: Environment variables updated. 488 | content: 489 | application/json: 490 | schema: 491 | properties: 492 | message: 493 | type: string 494 | example: Environment variables updated. 495 | type: object 496 | '400': 497 | $ref: '#/components/responses/400' 498 | '401': 499 | $ref: '#/components/responses/401' 500 | '404': 501 | $ref: '#/components/responses/404' 502 | security: 503 | - bearerAuth: [] 504 | /services/{uuid}/envs/{env_uuid}: 505 | delete: 506 | tags: 507 | - Services 508 | summary: Delete Env 509 | description: Delete env by UUID. 510 | operationId: delete-env-by-service-uuid 511 | parameters: 512 | - name: uuid 513 | in: path 514 | description: UUID of the service. 515 | required: true 516 | schema: 517 | type: string 518 | format: uuid 519 | - name: env_uuid 520 | in: path 521 | description: UUID of the environment variable. 522 | required: true 523 | schema: 524 | type: string 525 | format: uuid 526 | responses: 527 | '200': 528 | description: Environment variable deleted. 529 | content: 530 | application/json: 531 | schema: 532 | properties: 533 | message: 534 | type: string 535 | example: Environment variable deleted. 536 | type: object 537 | '400': 538 | $ref: '#/components/responses/400' 539 | '401': 540 | $ref: '#/components/responses/401' 541 | '404': 542 | $ref: '#/components/responses/404' 543 | security: 544 | - bearerAuth: [] 545 | /services/{uuid}/start: 546 | get: 547 | tags: 548 | - Services 549 | summary: Start 550 | description: Start service. `Post` request is also accepted. 551 | operationId: start-service-by-uuid 552 | parameters: 553 | - name: uuid 554 | in: path 555 | description: UUID of the service. 556 | required: true 557 | schema: 558 | type: string 559 | format: uuid 560 | responses: 561 | '200': 562 | description: Start service. 563 | content: 564 | application/json: 565 | schema: 566 | properties: 567 | message: 568 | type: string 569 | example: Service starting request queued. 570 | type: object 571 | '400': 572 | $ref: '#/components/responses/400' 573 | '401': 574 | $ref: '#/components/responses/401' 575 | '404': 576 | $ref: '#/components/responses/404' 577 | security: 578 | - bearerAuth: [] 579 | /services/{uuid}/stop: 580 | get: 581 | tags: 582 | - Services 583 | summary: Stop 584 | description: Stop service. `Post` request is also accepted. 585 | operationId: stop-service-by-uuid 586 | parameters: 587 | - name: uuid 588 | in: path 589 | description: UUID of the service. 590 | required: true 591 | schema: 592 | type: string 593 | format: uuid 594 | responses: 595 | '200': 596 | description: Stop service. 597 | content: 598 | application/json: 599 | schema: 600 | properties: 601 | message: 602 | type: string 603 | example: Service stopping request queued. 604 | type: object 605 | '400': 606 | $ref: '#/components/responses/400' 607 | '401': 608 | $ref: '#/components/responses/401' 609 | '404': 610 | $ref: '#/components/responses/404' 611 | security: 612 | - bearerAuth: [] 613 | /services/{uuid}/restart: 614 | get: 615 | tags: 616 | - Services 617 | summary: Restart 618 | description: Restart service. `Post` request is also accepted. 619 | operationId: restart-service-by-uuid 620 | parameters: 621 | - name: uuid 622 | in: path 623 | description: UUID of the service. 624 | required: true 625 | schema: 626 | type: string 627 | format: uuid 628 | responses: 629 | '200': 630 | description: Restart service. 631 | content: 632 | application/json: 633 | schema: 634 | properties: 635 | message: 636 | type: string 637 | example: Service restaring request queued. 638 | type: object 639 | '400': 640 | $ref: '#/components/responses/400' 641 | '401': 642 | $ref: '#/components/responses/401' 643 | '404': 644 | $ref: '#/components/responses/404' 645 | security: 646 | - bearerAuth: [] 647 | ``` -------------------------------------------------------------------------------- /docs/openapi-chunks/schemas.yaml: -------------------------------------------------------------------------------- ```yaml 1 | openapi: 3.1.0 2 | info: 3 | title: Coolify 4 | version: '0.1' 5 | paths: {} 6 | components: 7 | schemas: 8 | Application: 9 | description: Application model 10 | properties: 11 | id: 12 | type: integer 13 | description: The application identifier in the database. 14 | description: 15 | type: string 16 | nullable: true 17 | description: The application description. 18 | repository_project_id: 19 | type: integer 20 | nullable: true 21 | description: The repository project identifier. 22 | uuid: 23 | type: string 24 | description: The application UUID. 25 | name: 26 | type: string 27 | description: The application name. 28 | fqdn: 29 | type: string 30 | nullable: true 31 | description: The application domains. 32 | config_hash: 33 | type: string 34 | description: Configuration hash. 35 | git_repository: 36 | type: string 37 | description: Git repository URL. 38 | git_branch: 39 | type: string 40 | description: Git branch. 41 | git_commit_sha: 42 | type: string 43 | description: Git commit SHA. 44 | git_full_url: 45 | type: string 46 | nullable: true 47 | description: Git full URL. 48 | docker_registry_image_name: 49 | type: string 50 | nullable: true 51 | description: Docker registry image name. 52 | docker_registry_image_tag: 53 | type: string 54 | nullable: true 55 | description: Docker registry image tag. 56 | build_pack: 57 | type: string 58 | description: Build pack. 59 | enum: 60 | - nixpacks 61 | - static 62 | - dockerfile 63 | - dockercompose 64 | static_image: 65 | type: string 66 | description: Static image used when static site is deployed. 67 | install_command: 68 | type: string 69 | description: Install command. 70 | build_command: 71 | type: string 72 | description: Build command. 73 | start_command: 74 | type: string 75 | description: Start command. 76 | ports_exposes: 77 | type: string 78 | description: Ports exposes. 79 | ports_mappings: 80 | type: string 81 | nullable: true 82 | description: Ports mappings. 83 | base_directory: 84 | type: string 85 | description: Base directory for all commands. 86 | publish_directory: 87 | type: string 88 | description: Publish directory. 89 | health_check_enabled: 90 | type: boolean 91 | description: Health check enabled. 92 | health_check_path: 93 | type: string 94 | description: Health check path. 95 | health_check_port: 96 | type: string 97 | nullable: true 98 | description: Health check port. 99 | health_check_host: 100 | type: string 101 | nullable: true 102 | description: Health check host. 103 | health_check_method: 104 | type: string 105 | description: Health check method. 106 | health_check_return_code: 107 | type: integer 108 | description: Health check return code. 109 | health_check_scheme: 110 | type: string 111 | description: Health check scheme. 112 | health_check_response_text: 113 | type: string 114 | nullable: true 115 | description: Health check response text. 116 | health_check_interval: 117 | type: integer 118 | description: Health check interval in seconds. 119 | health_check_timeout: 120 | type: integer 121 | description: Health check timeout in seconds. 122 | health_check_retries: 123 | type: integer 124 | description: Health check retries count. 125 | health_check_start_period: 126 | type: integer 127 | description: Health check start period in seconds. 128 | limits_memory: 129 | type: string 130 | description: Memory limit. 131 | limits_memory_swap: 132 | type: string 133 | description: Memory swap limit. 134 | limits_memory_swappiness: 135 | type: integer 136 | description: Memory swappiness. 137 | limits_memory_reservation: 138 | type: string 139 | description: Memory reservation. 140 | limits_cpus: 141 | type: string 142 | description: CPU limit. 143 | limits_cpuset: 144 | type: string 145 | nullable: true 146 | description: CPU set. 147 | limits_cpu_shares: 148 | type: integer 149 | description: CPU shares. 150 | status: 151 | type: string 152 | description: Application status. 153 | preview_url_template: 154 | type: string 155 | description: Preview URL template. 156 | destination_type: 157 | type: string 158 | description: Destination type. 159 | destination_id: 160 | type: integer 161 | description: Destination identifier. 162 | source_id: 163 | type: integer 164 | nullable: true 165 | description: Source identifier. 166 | private_key_id: 167 | type: integer 168 | nullable: true 169 | description: Private key identifier. 170 | environment_id: 171 | type: integer 172 | description: Environment identifier. 173 | dockerfile: 174 | type: string 175 | nullable: true 176 | description: Dockerfile content. Used for dockerfile build pack. 177 | dockerfile_location: 178 | type: string 179 | description: Dockerfile location. 180 | custom_labels: 181 | type: string 182 | nullable: true 183 | description: Custom labels. 184 | dockerfile_target_build: 185 | type: string 186 | nullable: true 187 | description: Dockerfile target build. 188 | manual_webhook_secret_github: 189 | type: string 190 | nullable: true 191 | description: Manual webhook secret for GitHub. 192 | manual_webhook_secret_gitlab: 193 | type: string 194 | nullable: true 195 | description: Manual webhook secret for GitLab. 196 | manual_webhook_secret_bitbucket: 197 | type: string 198 | nullable: true 199 | description: Manual webhook secret for Bitbucket. 200 | manual_webhook_secret_gitea: 201 | type: string 202 | nullable: true 203 | description: Manual webhook secret for Gitea. 204 | docker_compose_location: 205 | type: string 206 | description: Docker compose location. 207 | docker_compose: 208 | type: string 209 | nullable: true 210 | description: Docker compose content. Used for docker compose build pack. 211 | docker_compose_raw: 212 | type: string 213 | nullable: true 214 | description: Docker compose raw content. 215 | docker_compose_domains: 216 | type: string 217 | nullable: true 218 | description: Docker compose domains. 219 | docker_compose_custom_start_command: 220 | type: string 221 | nullable: true 222 | description: Docker compose custom start command. 223 | docker_compose_custom_build_command: 224 | type: string 225 | nullable: true 226 | description: Docker compose custom build command. 227 | swarm_replicas: 228 | type: integer 229 | nullable: true 230 | description: Swarm replicas. Only used for swarm deployments. 231 | swarm_placement_constraints: 232 | type: string 233 | nullable: true 234 | description: Swarm placement constraints. Only used for swarm deployments. 235 | custom_docker_run_options: 236 | type: string 237 | nullable: true 238 | description: Custom docker run options. 239 | post_deployment_command: 240 | type: string 241 | nullable: true 242 | description: Post deployment command. 243 | post_deployment_command_container: 244 | type: string 245 | nullable: true 246 | description: Post deployment command container. 247 | pre_deployment_command: 248 | type: string 249 | nullable: true 250 | description: Pre deployment command. 251 | pre_deployment_command_container: 252 | type: string 253 | nullable: true 254 | description: Pre deployment command container. 255 | watch_paths: 256 | type: string 257 | nullable: true 258 | description: Watch paths. 259 | custom_healthcheck_found: 260 | type: boolean 261 | description: Custom healthcheck found. 262 | redirect: 263 | type: string 264 | nullable: true 265 | description: How to set redirect with Traefik / Caddy. www<->non-www. 266 | enum: 267 | - www 268 | - non-www 269 | - both 270 | created_at: 271 | type: string 272 | format: date-time 273 | description: The date and time when the application was created. 274 | updated_at: 275 | type: string 276 | format: date-time 277 | description: The date and time when the application was last updated. 278 | deleted_at: 279 | type: string 280 | format: date-time 281 | nullable: true 282 | description: The date and time when the application was deleted. 283 | compose_parsing_version: 284 | type: string 285 | description: How Coolify parse the compose file. 286 | custom_nginx_configuration: 287 | type: string 288 | nullable: true 289 | description: Custom Nginx configuration base64 encoded. 290 | type: object 291 | ApplicationDeploymentQueue: 292 | description: Project model 293 | properties: 294 | id: 295 | type: integer 296 | application_id: 297 | type: string 298 | deployment_uuid: 299 | type: string 300 | pull_request_id: 301 | type: integer 302 | force_rebuild: 303 | type: boolean 304 | commit: 305 | type: string 306 | status: 307 | type: string 308 | is_webhook: 309 | type: boolean 310 | is_api: 311 | type: boolean 312 | created_at: 313 | type: string 314 | updated_at: 315 | type: string 316 | logs: 317 | type: string 318 | current_process_id: 319 | type: string 320 | restart_only: 321 | type: boolean 322 | git_type: 323 | type: string 324 | server_id: 325 | type: integer 326 | application_name: 327 | type: string 328 | server_name: 329 | type: string 330 | deployment_url: 331 | type: string 332 | destination_id: 333 | type: string 334 | only_this_server: 335 | type: boolean 336 | rollback: 337 | type: boolean 338 | commit_message: 339 | type: string 340 | type: object 341 | Environment: 342 | description: Environment model 343 | properties: 344 | id: 345 | type: integer 346 | name: 347 | type: string 348 | project_id: 349 | type: integer 350 | created_at: 351 | type: string 352 | updated_at: 353 | type: string 354 | description: 355 | type: string 356 | type: object 357 | EnvironmentVariable: 358 | description: Environment Variable model 359 | properties: 360 | id: 361 | type: integer 362 | uuid: 363 | type: string 364 | resourceable_type: 365 | type: string 366 | resourceable_id: 367 | type: integer 368 | is_build_time: 369 | type: boolean 370 | is_literal: 371 | type: boolean 372 | is_multiline: 373 | type: boolean 374 | is_preview: 375 | type: boolean 376 | is_shared: 377 | type: boolean 378 | is_shown_once: 379 | type: boolean 380 | key: 381 | type: string 382 | value: 383 | type: string 384 | real_value: 385 | type: string 386 | version: 387 | type: string 388 | created_at: 389 | type: string 390 | updated_at: 391 | type: string 392 | type: object 393 | PrivateKey: 394 | description: Private Key model 395 | properties: 396 | id: 397 | type: integer 398 | uuid: 399 | type: string 400 | name: 401 | type: string 402 | description: 403 | type: string 404 | private_key: 405 | type: string 406 | format: private-key 407 | is_git_related: 408 | type: boolean 409 | team_id: 410 | type: integer 411 | created_at: 412 | type: string 413 | updated_at: 414 | type: string 415 | type: object 416 | Project: 417 | description: Project model 418 | properties: 419 | id: 420 | type: integer 421 | uuid: 422 | type: string 423 | name: 424 | type: string 425 | description: 426 | type: string 427 | environments: 428 | description: The environments of the project. 429 | type: array 430 | items: 431 | $ref: '#/components/schemas/Environment' 432 | type: object 433 | Server: 434 | description: Server model 435 | properties: 436 | id: 437 | type: integer 438 | description: The server ID. 439 | uuid: 440 | type: string 441 | description: The server UUID. 442 | name: 443 | type: string 444 | description: The server name. 445 | description: 446 | type: string 447 | description: The server description. 448 | ip: 449 | type: string 450 | description: The IP address. 451 | user: 452 | type: string 453 | description: The user. 454 | port: 455 | type: integer 456 | description: The port number. 457 | proxy: 458 | type: object 459 | description: The proxy configuration. 460 | proxy_type: 461 | type: string 462 | enum: 463 | - traefik 464 | - caddy 465 | - none 466 | description: The proxy type. 467 | high_disk_usage_notification_sent: 468 | type: boolean 469 | description: The flag to indicate if the high disk usage notification has been sent. 470 | unreachable_notification_sent: 471 | type: boolean 472 | description: The flag to indicate if the unreachable notification has been sent. 473 | unreachable_count: 474 | type: integer 475 | description: The unreachable count for your server. 476 | validation_logs: 477 | type: string 478 | description: The validation logs. 479 | log_drain_notification_sent: 480 | type: boolean 481 | description: The flag to indicate if the log drain notification has been sent. 482 | swarm_cluster: 483 | type: string 484 | description: The swarm cluster configuration. 485 | settings: 486 | $ref: '#/components/schemas/ServerSetting' 487 | type: object 488 | ServerSetting: 489 | description: Server Settings model 490 | properties: 491 | id: 492 | type: integer 493 | concurrent_builds: 494 | type: integer 495 | dynamic_timeout: 496 | type: integer 497 | force_disabled: 498 | type: boolean 499 | force_server_cleanup: 500 | type: boolean 501 | is_build_server: 502 | type: boolean 503 | is_cloudflare_tunnel: 504 | type: boolean 505 | is_jump_server: 506 | type: boolean 507 | is_logdrain_axiom_enabled: 508 | type: boolean 509 | is_logdrain_custom_enabled: 510 | type: boolean 511 | is_logdrain_highlight_enabled: 512 | type: boolean 513 | is_logdrain_newrelic_enabled: 514 | type: boolean 515 | is_metrics_enabled: 516 | type: boolean 517 | is_reachable: 518 | type: boolean 519 | is_sentinel_enabled: 520 | type: boolean 521 | is_swarm_manager: 522 | type: boolean 523 | is_swarm_worker: 524 | type: boolean 525 | is_usable: 526 | type: boolean 527 | logdrain_axiom_api_key: 528 | type: string 529 | logdrain_axiom_dataset_name: 530 | type: string 531 | logdrain_custom_config: 532 | type: string 533 | logdrain_custom_config_parser: 534 | type: string 535 | logdrain_highlight_project_id: 536 | type: string 537 | logdrain_newrelic_base_uri: 538 | type: string 539 | logdrain_newrelic_license_key: 540 | type: string 541 | sentinel_metrics_history_days: 542 | type: integer 543 | sentinel_metrics_refresh_rate_seconds: 544 | type: integer 545 | sentinel_token: 546 | type: string 547 | docker_cleanup_frequency: 548 | type: string 549 | docker_cleanup_threshold: 550 | type: integer 551 | server_id: 552 | type: integer 553 | wildcard_domain: 554 | type: string 555 | created_at: 556 | type: string 557 | updated_at: 558 | type: string 559 | delete_unused_volumes: 560 | type: boolean 561 | description: The flag to indicate if the unused volumes should be deleted. 562 | delete_unused_networks: 563 | type: boolean 564 | description: The flag to indicate if the unused networks should be deleted. 565 | type: object 566 | Service: 567 | description: Service model 568 | properties: 569 | id: 570 | type: integer 571 | description: The unique identifier of the service. Only used for database identification. 572 | uuid: 573 | type: string 574 | description: The unique identifier of the service. 575 | name: 576 | type: string 577 | description: The name of the service. 578 | environment_id: 579 | type: integer 580 | description: The unique identifier of the environment where the service is attached to. 581 | server_id: 582 | type: integer 583 | description: The unique identifier of the server where the service is running. 584 | description: 585 | type: string 586 | description: The description of the service. 587 | docker_compose_raw: 588 | type: string 589 | description: The raw docker-compose.yml file of the service. 590 | docker_compose: 591 | type: string 592 | description: The docker-compose.yml file that is parsed and modified by Coolify. 593 | destination_type: 594 | type: string 595 | description: Destination type. 596 | destination_id: 597 | type: integer 598 | description: The unique identifier of the destination where the service is running. 599 | connect_to_docker_network: 600 | type: boolean 601 | description: The flag to connect the service to the predefined Docker network. 602 | is_container_label_escape_enabled: 603 | type: boolean 604 | description: The flag to enable the container label escape. 605 | is_container_label_readonly_enabled: 606 | type: boolean 607 | description: The flag to enable the container label readonly. 608 | config_hash: 609 | type: string 610 | description: The hash of the service configuration. 611 | service_type: 612 | type: string 613 | description: The type of the service. 614 | created_at: 615 | type: string 616 | description: The date and time when the service was created. 617 | updated_at: 618 | type: string 619 | description: The date and time when the service was last updated. 620 | deleted_at: 621 | type: string 622 | description: The date and time when the service was deleted. 623 | type: object 624 | Team: 625 | description: Team model 626 | properties: 627 | id: 628 | type: integer 629 | description: The unique identifier of the team. 630 | name: 631 | type: string 632 | description: The name of the team. 633 | description: 634 | type: string 635 | description: The description of the team. 636 | personal_team: 637 | type: boolean 638 | description: Whether the team is personal or not. 639 | created_at: 640 | type: string 641 | description: The date and time the team was created. 642 | updated_at: 643 | type: string 644 | description: The date and time the team was last updated. 645 | show_boarding: 646 | type: boolean 647 | description: Whether to show the boarding screen or not. 648 | custom_server_limit: 649 | type: string 650 | description: The custom server limit. 651 | members: 652 | description: The members of the team. 653 | type: array 654 | items: 655 | $ref: '#/components/schemas/User' 656 | type: object 657 | User: 658 | description: User model 659 | properties: 660 | id: 661 | type: integer 662 | description: The user identifier in the database. 663 | name: 664 | type: string 665 | description: The user name. 666 | email: 667 | type: string 668 | description: The user email. 669 | email_verified_at: 670 | type: string 671 | description: The date when the user email was verified. 672 | created_at: 673 | type: string 674 | description: The date when the user was created. 675 | updated_at: 676 | type: string 677 | description: The date when the user was updated. 678 | two_factor_confirmed_at: 679 | type: string 680 | description: The date when the user two factor was confirmed. 681 | force_password_reset: 682 | type: boolean 683 | description: The flag to force the user to reset the password. 684 | marketing_emails: 685 | type: boolean 686 | description: The flag to receive marketing emails. 687 | type: object 688 | ``` -------------------------------------------------------------------------------- /docs/openapi-chunks/databases-api.yaml: -------------------------------------------------------------------------------- ```yaml 1 | openapi: 3.1.0 2 | info: 3 | title: Coolify 4 | version: '0.1' 5 | paths: 6 | /databases: 7 | get: 8 | tags: 9 | - Databases 10 | summary: List 11 | description: List all databases. 12 | operationId: list-databases 13 | responses: 14 | '200': 15 | description: Get all databases 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 | /databases/{uuid}: 28 | get: 29 | tags: 30 | - Databases 31 | summary: Get 32 | description: Get database by UUID. 33 | operationId: get-database-by-uuid 34 | parameters: 35 | - name: uuid 36 | in: path 37 | description: UUID of the database. 38 | required: true 39 | schema: 40 | type: string 41 | format: uuid 42 | responses: 43 | '200': 44 | description: Get all databases 45 | content: 46 | application/json: 47 | schema: 48 | type: string 49 | example: Content is very complex. Will be implemented later. 50 | '400': 51 | $ref: '#/components/responses/400' 52 | '401': 53 | $ref: '#/components/responses/401' 54 | '404': 55 | $ref: '#/components/responses/404' 56 | security: 57 | - bearerAuth: [] 58 | delete: 59 | tags: 60 | - Databases 61 | summary: Delete 62 | description: Delete database by UUID. 63 | operationId: delete-database-by-uuid 64 | parameters: 65 | - name: uuid 66 | in: path 67 | description: UUID of the database. 68 | required: true 69 | schema: 70 | type: string 71 | format: uuid 72 | - name: delete_configurations 73 | in: query 74 | description: Delete configurations. 75 | required: false 76 | schema: 77 | type: boolean 78 | default: true 79 | - name: delete_volumes 80 | in: query 81 | description: Delete volumes. 82 | required: false 83 | schema: 84 | type: boolean 85 | default: true 86 | - name: docker_cleanup 87 | in: query 88 | description: Run docker cleanup. 89 | required: false 90 | schema: 91 | type: boolean 92 | default: true 93 | - name: delete_connected_networks 94 | in: query 95 | description: Delete connected networks. 96 | required: false 97 | schema: 98 | type: boolean 99 | default: true 100 | responses: 101 | '200': 102 | description: Database deleted. 103 | content: 104 | application/json: 105 | schema: 106 | properties: 107 | message: 108 | type: string 109 | example: Database deleted. 110 | type: object 111 | '400': 112 | $ref: '#/components/responses/400' 113 | '401': 114 | $ref: '#/components/responses/401' 115 | '404': 116 | $ref: '#/components/responses/404' 117 | security: 118 | - bearerAuth: [] 119 | patch: 120 | tags: 121 | - Databases 122 | summary: Update 123 | description: Update database by UUID. 124 | operationId: update-database-by-uuid 125 | parameters: 126 | - name: uuid 127 | in: path 128 | description: UUID of the database. 129 | required: true 130 | schema: 131 | type: string 132 | format: uuid 133 | requestBody: 134 | description: Database data 135 | required: true 136 | content: 137 | application/json: 138 | schema: 139 | properties: 140 | name: 141 | type: string 142 | description: Name of the database 143 | description: 144 | type: string 145 | description: Description of the database 146 | image: 147 | type: string 148 | description: Docker Image of the database 149 | is_public: 150 | type: boolean 151 | description: Is the database public? 152 | public_port: 153 | type: integer 154 | description: Public port of the database 155 | limits_memory: 156 | type: string 157 | description: Memory limit of the database 158 | limits_memory_swap: 159 | type: string 160 | description: Memory swap limit of the database 161 | limits_memory_swappiness: 162 | type: integer 163 | description: Memory swappiness of the database 164 | limits_memory_reservation: 165 | type: string 166 | description: Memory reservation of the database 167 | limits_cpus: 168 | type: string 169 | description: CPU limit of the database 170 | limits_cpuset: 171 | type: string 172 | description: CPU set of the database 173 | limits_cpu_shares: 174 | type: integer 175 | description: CPU shares of the database 176 | postgres_user: 177 | type: string 178 | description: PostgreSQL user 179 | postgres_password: 180 | type: string 181 | description: PostgreSQL password 182 | postgres_db: 183 | type: string 184 | description: PostgreSQL database 185 | postgres_initdb_args: 186 | type: string 187 | description: PostgreSQL initdb args 188 | postgres_host_auth_method: 189 | type: string 190 | description: PostgreSQL host auth method 191 | postgres_conf: 192 | type: string 193 | description: PostgreSQL conf 194 | clickhouse_admin_user: 195 | type: string 196 | description: Clickhouse admin user 197 | clickhouse_admin_password: 198 | type: string 199 | description: Clickhouse admin password 200 | dragonfly_password: 201 | type: string 202 | description: DragonFly password 203 | redis_password: 204 | type: string 205 | description: Redis password 206 | redis_conf: 207 | type: string 208 | description: Redis conf 209 | keydb_password: 210 | type: string 211 | description: KeyDB password 212 | keydb_conf: 213 | type: string 214 | description: KeyDB conf 215 | mariadb_conf: 216 | type: string 217 | description: MariaDB conf 218 | mariadb_root_password: 219 | type: string 220 | description: MariaDB root password 221 | mariadb_user: 222 | type: string 223 | description: MariaDB user 224 | mariadb_password: 225 | type: string 226 | description: MariaDB password 227 | mariadb_database: 228 | type: string 229 | description: MariaDB database 230 | mongo_conf: 231 | type: string 232 | description: Mongo conf 233 | mongo_initdb_root_username: 234 | type: string 235 | description: Mongo initdb root username 236 | mongo_initdb_root_password: 237 | type: string 238 | description: Mongo initdb root password 239 | mongo_initdb_database: 240 | type: string 241 | description: Mongo initdb init database 242 | mysql_root_password: 243 | type: string 244 | description: MySQL root password 245 | mysql_password: 246 | type: string 247 | description: MySQL password 248 | mysql_user: 249 | type: string 250 | description: MySQL user 251 | mysql_database: 252 | type: string 253 | description: MySQL database 254 | mysql_conf: 255 | type: string 256 | description: MySQL conf 257 | type: object 258 | responses: 259 | '200': 260 | description: Database updated 261 | '400': 262 | $ref: '#/components/responses/400' 263 | '401': 264 | $ref: '#/components/responses/401' 265 | '404': 266 | $ref: '#/components/responses/404' 267 | security: 268 | - bearerAuth: [] 269 | /databases/postgresql: 270 | post: 271 | tags: 272 | - Databases 273 | summary: Create (PostgreSQL) 274 | description: Create a new PostgreSQL database. 275 | operationId: create-database-postgresql 276 | requestBody: 277 | description: Database data 278 | required: true 279 | content: 280 | application/json: 281 | schema: 282 | required: 283 | - server_uuid 284 | - project_uuid 285 | - environment_name 286 | - environment_uuid 287 | properties: 288 | server_uuid: 289 | type: string 290 | description: UUID of the server 291 | project_uuid: 292 | type: string 293 | description: UUID of the project 294 | environment_name: 295 | type: string 296 | description: Name of the environment. You need to provide at least one of environment_name or environment_uuid. 297 | environment_uuid: 298 | type: string 299 | description: UUID of the environment. You need to provide at least one of environment_name or environment_uuid. 300 | postgres_user: 301 | type: string 302 | description: PostgreSQL user 303 | postgres_password: 304 | type: string 305 | description: PostgreSQL password 306 | postgres_db: 307 | type: string 308 | description: PostgreSQL database 309 | postgres_initdb_args: 310 | type: string 311 | description: PostgreSQL initdb args 312 | postgres_host_auth_method: 313 | type: string 314 | description: PostgreSQL host auth method 315 | postgres_conf: 316 | type: string 317 | description: PostgreSQL conf 318 | destination_uuid: 319 | type: string 320 | description: UUID of the destination if the server has multiple destinations 321 | name: 322 | type: string 323 | description: Name of the database 324 | description: 325 | type: string 326 | description: Description of the database 327 | image: 328 | type: string 329 | description: Docker Image of the database 330 | is_public: 331 | type: boolean 332 | description: Is the database public? 333 | public_port: 334 | type: integer 335 | description: Public port of the database 336 | limits_memory: 337 | type: string 338 | description: Memory limit of the database 339 | limits_memory_swap: 340 | type: string 341 | description: Memory swap limit of the database 342 | limits_memory_swappiness: 343 | type: integer 344 | description: Memory swappiness of the database 345 | limits_memory_reservation: 346 | type: string 347 | description: Memory reservation of the database 348 | limits_cpus: 349 | type: string 350 | description: CPU limit of the database 351 | limits_cpuset: 352 | type: string 353 | description: CPU set of the database 354 | limits_cpu_shares: 355 | type: integer 356 | description: CPU shares of the database 357 | instant_deploy: 358 | type: boolean 359 | description: Instant deploy the database 360 | type: object 361 | responses: 362 | '200': 363 | description: Database updated 364 | '400': 365 | $ref: '#/components/responses/400' 366 | '401': 367 | $ref: '#/components/responses/401' 368 | security: 369 | - bearerAuth: [] 370 | /databases/clickhouse: 371 | post: 372 | tags: 373 | - Databases 374 | summary: Create (Clickhouse) 375 | description: Create a new Clickhouse database. 376 | operationId: create-database-clickhouse 377 | requestBody: 378 | description: Database data 379 | required: true 380 | content: 381 | application/json: 382 | schema: 383 | required: 384 | - server_uuid 385 | - project_uuid 386 | - environment_name 387 | - environment_uuid 388 | properties: 389 | server_uuid: 390 | type: string 391 | description: UUID of the server 392 | project_uuid: 393 | type: string 394 | description: UUID of the project 395 | environment_name: 396 | type: string 397 | description: Name of the environment. You need to provide at least one of environment_name or environment_uuid. 398 | environment_uuid: 399 | type: string 400 | description: UUID of the environment. You need to provide at least one of environment_name or environment_uuid. 401 | destination_uuid: 402 | type: string 403 | description: UUID of the destination if the server has multiple destinations 404 | clickhouse_admin_user: 405 | type: string 406 | description: Clickhouse admin user 407 | clickhouse_admin_password: 408 | type: string 409 | description: Clickhouse admin password 410 | name: 411 | type: string 412 | description: Name of the database 413 | description: 414 | type: string 415 | description: Description of the database 416 | image: 417 | type: string 418 | description: Docker Image of the database 419 | is_public: 420 | type: boolean 421 | description: Is the database public? 422 | public_port: 423 | type: integer 424 | description: Public port of the database 425 | limits_memory: 426 | type: string 427 | description: Memory limit of the database 428 | limits_memory_swap: 429 | type: string 430 | description: Memory swap limit of the database 431 | limits_memory_swappiness: 432 | type: integer 433 | description: Memory swappiness of the database 434 | limits_memory_reservation: 435 | type: string 436 | description: Memory reservation of the database 437 | limits_cpus: 438 | type: string 439 | description: CPU limit of the database 440 | limits_cpuset: 441 | type: string 442 | description: CPU set of the database 443 | limits_cpu_shares: 444 | type: integer 445 | description: CPU shares of the database 446 | instant_deploy: 447 | type: boolean 448 | description: Instant deploy the database 449 | type: object 450 | responses: 451 | '200': 452 | description: Database updated 453 | '400': 454 | $ref: '#/components/responses/400' 455 | '401': 456 | $ref: '#/components/responses/401' 457 | security: 458 | - bearerAuth: [] 459 | /databases/dragonfly: 460 | post: 461 | tags: 462 | - Databases 463 | summary: Create (DragonFly) 464 | description: Create a new DragonFly database. 465 | operationId: create-database-dragonfly 466 | requestBody: 467 | description: Database data 468 | required: true 469 | content: 470 | application/json: 471 | schema: 472 | required: 473 | - server_uuid 474 | - project_uuid 475 | - environment_name 476 | - environment_uuid 477 | properties: 478 | server_uuid: 479 | type: string 480 | description: UUID of the server 481 | project_uuid: 482 | type: string 483 | description: UUID of the project 484 | environment_name: 485 | type: string 486 | description: Name of the environment. You need to provide at least one of environment_name or environment_uuid. 487 | environment_uuid: 488 | type: string 489 | description: UUID of the environment. You need to provide at least one of environment_name or environment_uuid. 490 | destination_uuid: 491 | type: string 492 | description: UUID of the destination if the server has multiple destinations 493 | dragonfly_password: 494 | type: string 495 | description: DragonFly password 496 | name: 497 | type: string 498 | description: Name of the database 499 | description: 500 | type: string 501 | description: Description of the database 502 | image: 503 | type: string 504 | description: Docker Image of the database 505 | is_public: 506 | type: boolean 507 | description: Is the database public? 508 | public_port: 509 | type: integer 510 | description: Public port of the database 511 | limits_memory: 512 | type: string 513 | description: Memory limit of the database 514 | limits_memory_swap: 515 | type: string 516 | description: Memory swap limit of the database 517 | limits_memory_swappiness: 518 | type: integer 519 | description: Memory swappiness of the database 520 | limits_memory_reservation: 521 | type: string 522 | description: Memory reservation of the database 523 | limits_cpus: 524 | type: string 525 | description: CPU limit of the database 526 | limits_cpuset: 527 | type: string 528 | description: CPU set of the database 529 | limits_cpu_shares: 530 | type: integer 531 | description: CPU shares of the database 532 | instant_deploy: 533 | type: boolean 534 | description: Instant deploy the database 535 | type: object 536 | responses: 537 | '200': 538 | description: Database updated 539 | '400': 540 | $ref: '#/components/responses/400' 541 | '401': 542 | $ref: '#/components/responses/401' 543 | security: 544 | - bearerAuth: [] 545 | /databases/redis: 546 | post: 547 | tags: 548 | - Databases 549 | summary: Create (Redis) 550 | description: Create a new Redis database. 551 | operationId: create-database-redis 552 | requestBody: 553 | description: Database data 554 | required: true 555 | content: 556 | application/json: 557 | schema: 558 | required: 559 | - server_uuid 560 | - project_uuid 561 | - environment_name 562 | - environment_uuid 563 | properties: 564 | server_uuid: 565 | type: string 566 | description: UUID of the server 567 | project_uuid: 568 | type: string 569 | description: UUID of the project 570 | environment_name: 571 | type: string 572 | description: Name of the environment. You need to provide at least one of environment_name or environment_uuid. 573 | environment_uuid: 574 | type: string 575 | description: UUID of the environment. You need to provide at least one of environment_name or environment_uuid. 576 | destination_uuid: 577 | type: string 578 | description: UUID of the destination if the server has multiple destinations 579 | redis_password: 580 | type: string 581 | description: Redis password 582 | redis_conf: 583 | type: string 584 | description: Redis conf 585 | name: 586 | type: string 587 | description: Name of the database 588 | description: 589 | type: string 590 | description: Description of the database 591 | image: 592 | type: string 593 | description: Docker Image of the database 594 | is_public: 595 | type: boolean 596 | description: Is the database public? 597 | public_port: 598 | type: integer 599 | description: Public port of the database 600 | limits_memory: 601 | type: string 602 | description: Memory limit of the database 603 | limits_memory_swap: 604 | type: string 605 | description: Memory swap limit of the database 606 | limits_memory_swappiness: 607 | type: integer 608 | description: Memory swappiness of the database 609 | limits_memory_reservation: 610 | type: string 611 | description: Memory reservation of the database 612 | limits_cpus: 613 | type: string 614 | description: CPU limit of the database 615 | limits_cpuset: 616 | type: string 617 | description: CPU set of the database 618 | limits_cpu_shares: 619 | type: integer 620 | description: CPU shares of the database 621 | instant_deploy: 622 | type: boolean 623 | description: Instant deploy the database 624 | type: object 625 | responses: 626 | '200': 627 | description: Database updated 628 | '400': 629 | $ref: '#/components/responses/400' 630 | '401': 631 | $ref: '#/components/responses/401' 632 | security: 633 | - bearerAuth: [] 634 | /databases/keydb: 635 | post: 636 | tags: 637 | - Databases 638 | summary: Create (KeyDB) 639 | description: Create a new KeyDB database. 640 | operationId: create-database-keydb 641 | requestBody: 642 | description: Database data 643 | required: true 644 | content: 645 | application/json: 646 | schema: 647 | required: 648 | - server_uuid 649 | - project_uuid 650 | - environment_name 651 | - environment_uuid 652 | properties: 653 | server_uuid: 654 | type: string 655 | description: UUID of the server 656 | project_uuid: 657 | type: string 658 | description: UUID of the project 659 | environment_name: 660 | type: string 661 | description: Name of the environment. You need to provide at least one of environment_name or environment_uuid. 662 | environment_uuid: 663 | type: string 664 | description: UUID of the environment. You need to provide at least one of environment_name or environment_uuid. 665 | destination_uuid: 666 | type: string 667 | description: UUID of the destination if the server has multiple destinations 668 | keydb_password: 669 | type: string 670 | description: KeyDB password 671 | keydb_conf: 672 | type: string 673 | description: KeyDB conf 674 | name: 675 | type: string 676 | description: Name of the database 677 | description: 678 | type: string 679 | description: Description of the database 680 | image: 681 | type: string 682 | description: Docker Image of the database 683 | is_public: 684 | type: boolean 685 | description: Is the database public? 686 | public_port: 687 | type: integer 688 | description: Public port of the database 689 | limits_memory: 690 | type: string 691 | description: Memory limit of the database 692 | limits_memory_swap: 693 | type: string 694 | description: Memory swap limit of the database 695 | limits_memory_swappiness: 696 | type: integer 697 | description: Memory swappiness of the database 698 | limits_memory_reservation: 699 | type: string 700 | description: Memory reservation of the database 701 | limits_cpus: 702 | type: string 703 | description: CPU limit of the database 704 | limits_cpuset: 705 | type: string 706 | description: CPU set of the database 707 | limits_cpu_shares: 708 | type: integer 709 | description: CPU shares of the database 710 | instant_deploy: 711 | type: boolean 712 | description: Instant deploy the database 713 | type: object 714 | responses: 715 | '200': 716 | description: Database updated 717 | '400': 718 | $ref: '#/components/responses/400' 719 | '401': 720 | $ref: '#/components/responses/401' 721 | security: 722 | - bearerAuth: [] 723 | /databases/mariadb: 724 | post: 725 | tags: 726 | - Databases 727 | summary: Create (MariaDB) 728 | description: Create a new MariaDB database. 729 | operationId: create-database-mariadb 730 | requestBody: 731 | description: Database data 732 | required: true 733 | content: 734 | application/json: 735 | schema: 736 | required: 737 | - server_uuid 738 | - project_uuid 739 | - environment_name 740 | - environment_uuid 741 | properties: 742 | server_uuid: 743 | type: string 744 | description: UUID of the server 745 | project_uuid: 746 | type: string 747 | description: UUID of the project 748 | environment_name: 749 | type: string 750 | description: Name of the environment. You need to provide at least one of environment_name or environment_uuid. 751 | environment_uuid: 752 | type: string 753 | description: UUID of the environment. You need to provide at least one of environment_name or environment_uuid. 754 | destination_uuid: 755 | type: string 756 | description: UUID of the destination if the server has multiple destinations 757 | mariadb_conf: 758 | type: string 759 | description: MariaDB conf 760 | mariadb_root_password: 761 | type: string 762 | description: MariaDB root password 763 | mariadb_user: 764 | type: string 765 | description: MariaDB user 766 | mariadb_password: 767 | type: string 768 | description: MariaDB password 769 | mariadb_database: 770 | type: string 771 | description: MariaDB database 772 | name: 773 | type: string 774 | description: Name of the database 775 | description: 776 | type: string 777 | description: Description of the database 778 | image: 779 | type: string 780 | description: Docker Image of the database 781 | is_public: 782 | type: boolean 783 | description: Is the database public? 784 | public_port: 785 | type: integer 786 | description: Public port of the database 787 | limits_memory: 788 | type: string 789 | description: Memory limit of the database 790 | limits_memory_swap: 791 | type: string 792 | description: Memory swap limit of the database 793 | limits_memory_swappiness: 794 | type: integer 795 | description: Memory swappiness of the database 796 | limits_memory_reservation: 797 | type: string 798 | description: Memory reservation of the database 799 | limits_cpus: 800 | type: string 801 | description: CPU limit of the database 802 | limits_cpuset: 803 | type: string 804 | description: CPU set of the database 805 | limits_cpu_shares: 806 | type: integer 807 | description: CPU shares of the database 808 | instant_deploy: 809 | type: boolean 810 | description: Instant deploy the database 811 | type: object 812 | responses: 813 | '200': 814 | description: Database updated 815 | '400': 816 | $ref: '#/components/responses/400' 817 | '401': 818 | $ref: '#/components/responses/401' 819 | security: 820 | - bearerAuth: [] 821 | /databases/mysql: 822 | post: 823 | tags: 824 | - Databases 825 | summary: Create (MySQL) 826 | description: Create a new MySQL database. 827 | operationId: create-database-mysql 828 | requestBody: 829 | description: Database data 830 | required: true 831 | content: 832 | application/json: 833 | schema: 834 | required: 835 | - server_uuid 836 | - project_uuid 837 | - environment_name 838 | - environment_uuid 839 | properties: 840 | server_uuid: 841 | type: string 842 | description: UUID of the server 843 | project_uuid: 844 | type: string 845 | description: UUID of the project 846 | environment_name: 847 | type: string 848 | description: Name of the environment. You need to provide at least one of environment_name or environment_uuid. 849 | environment_uuid: 850 | type: string 851 | description: UUID of the environment. You need to provide at least one of environment_name or environment_uuid. 852 | destination_uuid: 853 | type: string 854 | description: UUID of the destination if the server has multiple destinations 855 | mysql_root_password: 856 | type: string 857 | description: MySQL root password 858 | mysql_password: 859 | type: string 860 | description: MySQL password 861 | mysql_user: 862 | type: string 863 | description: MySQL user 864 | mysql_database: 865 | type: string 866 | description: MySQL database 867 | mysql_conf: 868 | type: string 869 | description: MySQL conf 870 | name: 871 | type: string 872 | description: Name of the database 873 | description: 874 | type: string 875 | description: Description of the database 876 | image: 877 | type: string 878 | description: Docker Image of the database 879 | is_public: 880 | type: boolean 881 | description: Is the database public? 882 | public_port: 883 | type: integer 884 | description: Public port of the database 885 | limits_memory: 886 | type: string 887 | description: Memory limit of the database 888 | limits_memory_swap: 889 | type: string 890 | description: Memory swap limit of the database 891 | limits_memory_swappiness: 892 | type: integer 893 | description: Memory swappiness of the database 894 | limits_memory_reservation: 895 | type: string 896 | description: Memory reservation of the database 897 | limits_cpus: 898 | type: string 899 | description: CPU limit of the database 900 | limits_cpuset: 901 | type: string 902 | description: CPU set of the database 903 | limits_cpu_shares: 904 | type: integer 905 | description: CPU shares of the database 906 | instant_deploy: 907 | type: boolean 908 | description: Instant deploy the database 909 | type: object 910 | responses: 911 | '200': 912 | description: Database updated 913 | '400': 914 | $ref: '#/components/responses/400' 915 | '401': 916 | $ref: '#/components/responses/401' 917 | security: 918 | - bearerAuth: [] 919 | /databases/mongodb: 920 | post: 921 | tags: 922 | - Databases 923 | summary: Create (MongoDB) 924 | description: Create a new MongoDB database. 925 | operationId: create-database-mongodb 926 | requestBody: 927 | description: Database data 928 | required: true 929 | content: 930 | application/json: 931 | schema: 932 | required: 933 | - server_uuid 934 | - project_uuid 935 | - environment_name 936 | - environment_uuid 937 | properties: 938 | server_uuid: 939 | type: string 940 | description: UUID of the server 941 | project_uuid: 942 | type: string 943 | description: UUID of the project 944 | environment_name: 945 | type: string 946 | description: Name of the environment. You need to provide at least one of environment_name or environment_uuid. 947 | environment_uuid: 948 | type: string 949 | description: UUID of the environment. You need to provide at least one of environment_name or environment_uuid. 950 | destination_uuid: 951 | type: string 952 | description: UUID of the destination if the server has multiple destinations 953 | mongo_conf: 954 | type: string 955 | description: MongoDB conf 956 | mongo_initdb_root_username: 957 | type: string 958 | description: MongoDB initdb root username 959 | name: 960 | type: string 961 | description: Name of the database 962 | description: 963 | type: string 964 | description: Description of the database 965 | image: 966 | type: string 967 | description: Docker Image of the database 968 | is_public: 969 | type: boolean 970 | description: Is the database public? 971 | public_port: 972 | type: integer 973 | description: Public port of the database 974 | limits_memory: 975 | type: string 976 | description: Memory limit of the database 977 | limits_memory_swap: 978 | type: string 979 | description: Memory swap limit of the database 980 | limits_memory_swappiness: 981 | type: integer 982 | description: Memory swappiness of the database 983 | limits_memory_reservation: 984 | type: string 985 | description: Memory reservation of the database 986 | limits_cpus: 987 | type: string 988 | description: CPU limit of the database 989 | limits_cpuset: 990 | type: string 991 | description: CPU set of the database 992 | limits_cpu_shares: 993 | type: integer 994 | description: CPU shares of the database 995 | instant_deploy: 996 | type: boolean 997 | description: Instant deploy the database 998 | type: object 999 | responses: 1000 | '200': 1001 | description: Database updated 1002 | '400': 1003 | $ref: '#/components/responses/400' 1004 | '401': 1005 | $ref: '#/components/responses/401' 1006 | security: 1007 | - bearerAuth: [] 1008 | /databases/{uuid}/start: 1009 | get: 1010 | tags: 1011 | - Databases 1012 | summary: Start 1013 | description: Start database. `Post` request is also accepted. 1014 | operationId: start-database-by-uuid 1015 | parameters: 1016 | - name: uuid 1017 | in: path 1018 | description: UUID of the database. 1019 | required: true 1020 | schema: 1021 | type: string 1022 | format: uuid 1023 | responses: 1024 | '200': 1025 | description: Start database. 1026 | content: 1027 | application/json: 1028 | schema: 1029 | properties: 1030 | message: 1031 | type: string 1032 | example: Database starting request queued. 1033 | type: object 1034 | '400': 1035 | $ref: '#/components/responses/400' 1036 | '401': 1037 | $ref: '#/components/responses/401' 1038 | '404': 1039 | $ref: '#/components/responses/404' 1040 | security: 1041 | - bearerAuth: [] 1042 | /databases/{uuid}/stop: 1043 | get: 1044 | tags: 1045 | - Databases 1046 | summary: Stop 1047 | description: Stop database. `Post` request is also accepted. 1048 | operationId: stop-database-by-uuid 1049 | parameters: 1050 | - name: uuid 1051 | in: path 1052 | description: UUID of the database. 1053 | required: true 1054 | schema: 1055 | type: string 1056 | format: uuid 1057 | responses: 1058 | '200': 1059 | description: Stop database. 1060 | content: 1061 | application/json: 1062 | schema: 1063 | properties: 1064 | message: 1065 | type: string 1066 | example: Database stopping request queued. 1067 | type: object 1068 | '400': 1069 | $ref: '#/components/responses/400' 1070 | '401': 1071 | $ref: '#/components/responses/401' 1072 | '404': 1073 | $ref: '#/components/responses/404' 1074 | security: 1075 | - bearerAuth: [] 1076 | /databases/{uuid}/restart: 1077 | get: 1078 | tags: 1079 | - Databases 1080 | summary: Restart 1081 | description: Restart database. `Post` request is also accepted. 1082 | operationId: restart-database-by-uuid 1083 | parameters: 1084 | - name: uuid 1085 | in: path 1086 | description: UUID of the database. 1087 | required: true 1088 | schema: 1089 | type: string 1090 | format: uuid 1091 | responses: 1092 | '200': 1093 | description: Restart database. 1094 | content: 1095 | application/json: 1096 | schema: 1097 | properties: 1098 | message: 1099 | type: string 1100 | example: Database restaring request queued. 1101 | type: object 1102 | '400': 1103 | $ref: '#/components/responses/400' 1104 | '401': 1105 | $ref: '#/components/responses/401' 1106 | '404': 1107 | $ref: '#/components/responses/404' 1108 | security: 1109 | - bearerAuth: [] 1110 | ``` -------------------------------------------------------------------------------- /repomix-output.xml: -------------------------------------------------------------------------------- ``` 1 | This file is a merged representation of the entire codebase, combining all repository files into a single document. 2 | Generated by Repomix on: 2025-03-08T15:17:09.667Z 3 | 4 | <file_summary> 5 | This section contains a summary of this file. 6 | 7 | <purpose> 8 | This file contains a packed representation of the entire repository's contents. 9 | It is designed to be easily consumable by AI systems for analysis, code review, 10 | or other automated processes. 11 | </purpose> 12 | 13 | <file_format> 14 | The content is organized as follows: 15 | 1. This summary section 16 | 2. Repository information 17 | 3. Repository structure 18 | 4. Repository files, each consisting of: 19 | - File path as an attribute 20 | - Full contents of the file 21 | </file_format> 22 | 23 | <usage_guidelines> 24 | - This file should be treated as read-only. Any changes should be made to the 25 | original repository files, not this packed version. 26 | - When processing this file, use the file path to distinguish 27 | between different files in the repository. 28 | - Be aware that this file may contain sensitive information. Handle it with 29 | the same level of security as you would the original repository. 30 | </usage_guidelines> 31 | 32 | <notes> 33 | - Some files may have been excluded based on .gitignore rules and Repomix's 34 | configuration. 35 | - Binary files are not included in this packed representation. Please refer to 36 | the Repository Structure section for a complete list of file paths, including 37 | binary files. 38 | </notes> 39 | 40 | <additional_info> 41 | 42 | For more information about Repomix, visit: https://github.com/yamadashy/repomix 43 | </additional_info> 44 | 45 | </file_summary> 46 | 47 | <repository_structure> 48 | src/ 49 | __tests__/ 50 | resources/ 51 | application-resources.test.ts 52 | database-resources.test.ts 53 | deployment-resources.test.ts 54 | service-resources.test.ts 55 | coolify-client.test.ts 56 | lib/ 57 | coolify-client.ts 58 | mcp-server.ts 59 | resource.ts 60 | resources/ 61 | application-resources.ts 62 | database-resources.ts 63 | deployment-resources.ts 64 | index.ts 65 | service-resources.ts 66 | types/ 67 | coolify.ts 68 | index.ts 69 | .eslintrc.json 70 | .gitignore 71 | .lintstagedrc.json 72 | .markdownlint-cli2.jsonc 73 | .prettierrc 74 | .repomixignore 75 | debug.js 76 | jest.config.js 77 | package.json 78 | README.md 79 | tsconfig.json 80 | </repository_structure> 81 | 82 | <repository_files> 83 | This section contains the contents of the repository's files. 84 | 85 | <file path="src/__tests__/resources/application-resources.test.ts"> 86 | import { ApplicationResources } from '../../resources/application-resources.js'; 87 | import { CoolifyClient } from '../../lib/coolify-client.js'; 88 | import { jest } from '@jest/globals'; 89 | 90 | jest.mock('../../lib/coolify-client.js'); 91 | 92 | describe('ApplicationResources', () => { 93 | let resources: ApplicationResources; 94 | let mockClient: jest.Mocked<CoolifyClient>; 95 | 96 | beforeEach(() => { 97 | mockClient = { 98 | deployApplication: jest.fn(), 99 | } as unknown as jest.Mocked<CoolifyClient>; 100 | 101 | resources = new ApplicationResources(mockClient); 102 | }); 103 | 104 | describe('listApplications', () => { 105 | it('should throw not implemented error', async () => { 106 | await expect(resources.listApplications()).rejects.toThrow('Not implemented'); 107 | }); 108 | }); 109 | 110 | describe('getApplication', () => { 111 | it('should throw not implemented error', async () => { 112 | await expect(resources.getApplication('test-id')).rejects.toThrow('Not implemented'); 113 | }); 114 | }); 115 | 116 | describe('createApplication', () => { 117 | it('should throw not implemented error', async () => { 118 | await expect(resources.createApplication({ name: 'test-app' })).rejects.toThrow( 119 | 'Not implemented', 120 | ); 121 | }); 122 | }); 123 | 124 | describe('deleteApplication', () => { 125 | it('should throw not implemented error', async () => { 126 | await expect(resources.deleteApplication('test-id')).rejects.toThrow('Not implemented'); 127 | }); 128 | }); 129 | }); 130 | </file> 131 | 132 | <file path="src/__tests__/resources/database-resources.test.ts"> 133 | import { DatabaseResources } from '../../resources/database-resources.js'; 134 | import { CoolifyClient } from '../../lib/coolify-client.js'; 135 | import { PostgresDatabase } from '../../types/coolify.js'; 136 | import { jest } from '@jest/globals'; 137 | 138 | jest.mock('../../lib/coolify-client.js'); 139 | 140 | describe('DatabaseResources', () => { 141 | let mockClient: jest.Mocked<CoolifyClient>; 142 | let resources: DatabaseResources; 143 | const mockDatabase: PostgresDatabase = { 144 | id: 1, 145 | uuid: 'test-uuid', 146 | name: 'test-db', 147 | description: 'test description', 148 | type: 'postgresql', 149 | status: 'running', 150 | created_at: '2024-01-01', 151 | updated_at: '2024-01-01', 152 | is_public: false, 153 | image: 'postgres:latest', 154 | postgres_user: 'test', 155 | postgres_password: 'test', 156 | postgres_db: 'test', 157 | }; 158 | 159 | beforeEach(() => { 160 | mockClient = { 161 | listDatabases: jest.fn(), 162 | getDatabase: jest.fn(), 163 | updateDatabase: jest.fn(), 164 | deleteDatabase: jest.fn(), 165 | } as unknown as jest.Mocked<CoolifyClient>; 166 | resources = new DatabaseResources(mockClient); 167 | }); 168 | 169 | describe('listDatabases', () => { 170 | it('should return a list of databases', async () => { 171 | mockClient.listDatabases.mockResolvedValue([mockDatabase]); 172 | 173 | const result = await resources.listDatabases(); 174 | 175 | expect(result).toEqual([mockDatabase]); 176 | expect(mockClient.listDatabases).toHaveBeenCalled(); 177 | }); 178 | }); 179 | 180 | describe('getDatabase', () => { 181 | it('should return a database by uuid', async () => { 182 | mockClient.getDatabase.mockResolvedValue(mockDatabase); 183 | 184 | const result = await resources.getDatabase('test-uuid'); 185 | 186 | expect(result).toEqual(mockDatabase); 187 | expect(mockClient.getDatabase).toHaveBeenCalledWith('test-uuid'); 188 | }); 189 | }); 190 | 191 | describe('updateDatabase', () => { 192 | it('should update a database', async () => { 193 | const updateData = { 194 | name: 'updated-db', 195 | description: 'updated description', 196 | }; 197 | 198 | mockClient.updateDatabase.mockResolvedValue({ ...mockDatabase, ...updateData }); 199 | 200 | const result = await resources.updateDatabase('test-uuid', updateData); 201 | 202 | expect(result).toEqual({ ...mockDatabase, ...updateData }); 203 | expect(mockClient.updateDatabase).toHaveBeenCalledWith('test-uuid', updateData); 204 | }); 205 | }); 206 | 207 | describe('deleteDatabase', () => { 208 | it('should delete a database', async () => { 209 | const mockResponse = { message: 'Database deleted successfully' }; 210 | mockClient.deleteDatabase.mockResolvedValue(mockResponse); 211 | 212 | const result = await resources.deleteDatabase('test-uuid', {}); 213 | 214 | expect(result).toEqual(mockResponse); 215 | expect(mockClient.deleteDatabase).toHaveBeenCalledWith('test-uuid', {}); 216 | }); 217 | }); 218 | }); 219 | </file> 220 | 221 | <file path="src/__tests__/resources/deployment-resources.test.ts"> 222 | import { DeploymentResources } from '../../resources/deployment-resources.js'; 223 | import { CoolifyClient } from '../../lib/coolify-client.js'; 224 | import { jest } from '@jest/globals'; 225 | 226 | jest.mock('../../lib/coolify-client.js'); 227 | 228 | describe('DeploymentResources', () => { 229 | let mockClient: jest.Mocked<CoolifyClient>; 230 | let resources: DeploymentResources; 231 | const mockDeployment = { 232 | id: 1, 233 | uuid: 'test-uuid', 234 | status: 'running', 235 | created_at: '2024-01-01', 236 | updated_at: '2024-01-01', 237 | application_uuid: 'app-uuid', 238 | environment_uuid: 'env-uuid', 239 | }; 240 | 241 | beforeEach(() => { 242 | mockClient = { 243 | deployApplication: jest.fn(), 244 | } as unknown as jest.Mocked<CoolifyClient>; 245 | resources = new DeploymentResources(mockClient); 246 | }); 247 | 248 | describe('listDeployments', () => { 249 | it('should throw not implemented error', async () => { 250 | await expect(resources.listDeployments()).rejects.toThrow('Not implemented'); 251 | }); 252 | }); 253 | 254 | describe('getDeployment', () => { 255 | it('should throw not implemented error', async () => { 256 | await expect(resources.getDeployment('test-id')).rejects.toThrow('Not implemented'); 257 | }); 258 | }); 259 | 260 | describe('deploy', () => { 261 | it('should deploy an application', async () => { 262 | mockClient.deployApplication.mockResolvedValue(mockDeployment); 263 | 264 | const result = await resources.deploy({ uuid: 'test-uuid' }); 265 | 266 | expect(result).toEqual(mockDeployment); 267 | expect(mockClient.deployApplication).toHaveBeenCalledWith('test-uuid'); 268 | }); 269 | 270 | it('should handle deployment errors', async () => { 271 | const error = new Error('Deployment failed'); 272 | mockClient.deployApplication.mockRejectedValue(error); 273 | 274 | await expect(resources.deploy({ uuid: 'test-uuid' })).rejects.toThrow('Deployment failed'); 275 | expect(mockClient.deployApplication).toHaveBeenCalledWith('test-uuid'); 276 | }); 277 | }); 278 | }); 279 | </file> 280 | 281 | <file path="src/__tests__/resources/service-resources.test.ts"> 282 | import { ServiceResources } from '../../resources/service-resources.js'; 283 | import { CoolifyClient } from '../../lib/coolify-client.js'; 284 | import { Service, ServiceType } from '../../types/coolify.js'; 285 | import { jest } from '@jest/globals'; 286 | 287 | jest.mock('../../lib/coolify-client.js'); 288 | 289 | describe('ServiceResources', () => { 290 | let mockClient: jest.Mocked<CoolifyClient>; 291 | let resources: ServiceResources; 292 | const mockService: Service = { 293 | id: 1, 294 | uuid: 'test-uuid', 295 | name: 'test-service', 296 | description: 'test description', 297 | type: 'code-server', 298 | status: 'running', 299 | created_at: '2024-01-01', 300 | updated_at: '2024-01-01', 301 | project_uuid: 'project-uuid', 302 | environment_name: 'test-env', 303 | environment_uuid: 'env-uuid', 304 | server_uuid: 'server-uuid', 305 | domains: ['test.com'], 306 | }; 307 | 308 | beforeEach(() => { 309 | mockClient = { 310 | listServices: jest.fn(), 311 | getService: jest.fn(), 312 | createService: jest.fn(), 313 | deleteService: jest.fn(), 314 | } as unknown as jest.Mocked<CoolifyClient>; 315 | resources = new ServiceResources(mockClient); 316 | }); 317 | 318 | describe('listServices', () => { 319 | it('should return a list of services', async () => { 320 | mockClient.listServices.mockResolvedValue([mockService]); 321 | 322 | const result = await resources.listServices(); 323 | 324 | expect(result).toEqual([mockService]); 325 | expect(mockClient.listServices).toHaveBeenCalled(); 326 | }); 327 | }); 328 | 329 | describe('getService', () => { 330 | it('should return a service by uuid', async () => { 331 | mockClient.getService.mockResolvedValue(mockService); 332 | 333 | const result = await resources.getService('test-uuid'); 334 | 335 | expect(result).toEqual(mockService); 336 | expect(mockClient.getService).toHaveBeenCalledWith('test-uuid'); 337 | }); 338 | }); 339 | 340 | describe('createService', () => { 341 | it('should create a new service', async () => { 342 | const createData = { 343 | name: 'new-service', 344 | type: 'code-server' as ServiceType, 345 | project_uuid: 'project-uuid', 346 | environment_name: 'test-env', 347 | environment_uuid: 'env-uuid', 348 | server_uuid: 'server-uuid', 349 | }; 350 | 351 | const mockResponse = { 352 | uuid: 'new-uuid', 353 | domains: ['new-service.test.com'], 354 | }; 355 | 356 | mockClient.createService.mockResolvedValue(mockResponse); 357 | 358 | const result = await resources.createService(createData); 359 | 360 | expect(result).toEqual(mockResponse); 361 | expect(mockClient.createService).toHaveBeenCalledWith(createData); 362 | }); 363 | }); 364 | 365 | describe('deleteService', () => { 366 | it('should delete a service', async () => { 367 | const mockResponse = { message: 'Service deleted' }; 368 | mockClient.deleteService.mockResolvedValue(mockResponse); 369 | 370 | const result = await resources.deleteService('test-uuid'); 371 | 372 | expect(result).toEqual(mockResponse); 373 | expect(mockClient.deleteService).toHaveBeenCalledWith('test-uuid', undefined); 374 | }); 375 | }); 376 | }); 377 | </file> 378 | 379 | <file path="src/__tests__/coolify-client.test.ts"> 380 | import { jest } from '@jest/globals'; 381 | import { CoolifyClient } from '../lib/coolify-client.js'; 382 | import { ServiceType, CreateServiceRequest } from '../types/coolify.js'; 383 | 384 | const mockFetch = jest.fn() as any; 385 | 386 | describe('CoolifyClient', () => { 387 | let client: CoolifyClient; 388 | 389 | const mockServers = [ 390 | { 391 | id: 1, 392 | uuid: 'test-uuid', 393 | name: 'test-server', 394 | status: 'running', 395 | }, 396 | ]; 397 | 398 | const mockServerInfo = { 399 | id: 1, 400 | uuid: 'test-uuid', 401 | name: 'test-server', 402 | status: 'running', 403 | }; 404 | 405 | const mockServerResources = { 406 | resources: [ 407 | { 408 | name: 'memory', 409 | value: '2GB', 410 | }, 411 | { 412 | name: 'disk', 413 | value: '20GB', 414 | }, 415 | ], 416 | }; 417 | 418 | const mockService = { 419 | id: 1, 420 | uuid: 'test-uuid', 421 | name: 'test-service', 422 | type: 'code-server' as ServiceType, 423 | status: 'running', 424 | created_at: '2024-01-01', 425 | updated_at: '2024-01-01', 426 | }; 427 | 428 | const errorResponse = { 429 | message: 'Resource not found', 430 | }; 431 | 432 | beforeEach(() => { 433 | mockFetch.mockClear(); 434 | (global as any).fetch = mockFetch; 435 | client = new CoolifyClient({ 436 | baseUrl: 'http://localhost:3000', 437 | accessToken: 'test-api-key', 438 | }); 439 | }); 440 | 441 | describe('listServers', () => { 442 | it('should return a list of servers', async () => { 443 | mockFetch.mockImplementationOnce( 444 | async () => 445 | ({ 446 | ok: true, 447 | json: async () => mockServers, 448 | }) as Response, 449 | ); 450 | 451 | const servers = await client.listServers(); 452 | expect(servers).toEqual(mockServers); 453 | expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/servers', { 454 | headers: { 455 | 'Content-Type': 'application/json', 456 | Authorization: 'Bearer test-api-key', 457 | }, 458 | }); 459 | }); 460 | 461 | it('should handle errors', async () => { 462 | mockFetch.mockImplementationOnce(() => 463 | Promise.resolve({ 464 | ok: false, 465 | json: async () => errorResponse, 466 | } as Response), 467 | ); 468 | 469 | await expect(client.listServers()).rejects.toThrow('Resource not found'); 470 | }); 471 | }); 472 | 473 | describe('getServer', () => { 474 | it('should get server info', async () => { 475 | mockFetch.mockImplementationOnce(() => 476 | Promise.resolve({ 477 | ok: true, 478 | json: async () => mockServerInfo, 479 | } as Response), 480 | ); 481 | 482 | const result = await client.getServer('test-uuid'); 483 | 484 | expect(result).toEqual(mockServerInfo); 485 | expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/servers/test-uuid', { 486 | headers: { 487 | 'Content-Type': 'application/json', 488 | Authorization: 'Bearer test-api-key', 489 | }, 490 | }); 491 | }); 492 | 493 | it('should handle errors', async () => { 494 | mockFetch.mockImplementationOnce(() => 495 | Promise.resolve({ 496 | ok: false, 497 | json: async () => errorResponse, 498 | } as Response), 499 | ); 500 | 501 | await expect(client.getServer('test-uuid')).rejects.toThrow('Resource not found'); 502 | }); 503 | }); 504 | 505 | describe('getServerResources', () => { 506 | it('should get server resources', async () => { 507 | mockFetch.mockImplementationOnce(() => 508 | Promise.resolve({ 509 | ok: true, 510 | json: async () => mockServerResources, 511 | } as Response), 512 | ); 513 | 514 | const result = await client.getServerResources('test-uuid'); 515 | 516 | expect(result).toEqual(mockServerResources); 517 | expect(mockFetch).toHaveBeenCalledWith( 518 | 'http://localhost:3000/api/v1/servers/test-uuid/resources', 519 | { 520 | headers: { 521 | 'Content-Type': 'application/json', 522 | Authorization: 'Bearer test-api-key', 523 | }, 524 | }, 525 | ); 526 | }); 527 | 528 | it('should handle errors', async () => { 529 | mockFetch.mockImplementationOnce(() => 530 | Promise.resolve({ 531 | ok: false, 532 | json: async () => errorResponse, 533 | } as Response), 534 | ); 535 | 536 | await expect(client.getServerResources('test-uuid')).rejects.toThrow('Resource not found'); 537 | }); 538 | }); 539 | 540 | describe('listServices', () => { 541 | it('should list services', async () => { 542 | mockFetch.mockImplementationOnce(() => 543 | Promise.resolve({ 544 | ok: true, 545 | json: () => Promise.resolve([mockService]), 546 | } as Response), 547 | ); 548 | 549 | const result = await client.listServices(); 550 | 551 | expect(result).toEqual([mockService]); 552 | expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/services', { 553 | headers: { 554 | 'Content-Type': 'application/json', 555 | Authorization: 'Bearer test-api-key', 556 | }, 557 | }); 558 | }); 559 | }); 560 | 561 | describe('getService', () => { 562 | it('should get service info', async () => { 563 | mockFetch.mockImplementationOnce(() => 564 | Promise.resolve({ 565 | ok: true, 566 | json: () => Promise.resolve(mockService), 567 | } as Response), 568 | ); 569 | 570 | const result = await client.getService('test-uuid'); 571 | 572 | expect(result).toEqual(mockService); 573 | expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/services/test-uuid', { 574 | headers: { 575 | 'Content-Type': 'application/json', 576 | Authorization: 'Bearer test-api-key', 577 | }, 578 | }); 579 | }); 580 | }); 581 | 582 | describe('createService', () => { 583 | it('should create a service', async () => { 584 | mockFetch.mockImplementationOnce(() => 585 | Promise.resolve({ 586 | ok: true, 587 | json: () => 588 | Promise.resolve({ 589 | uuid: 'test-uuid', 590 | domains: ['test.com'], 591 | }), 592 | } as Response), 593 | ); 594 | 595 | const createData: CreateServiceRequest = { 596 | name: 'test-service', 597 | type: 'code-server', 598 | project_uuid: 'project-uuid', 599 | environment_uuid: 'env-uuid', 600 | server_uuid: 'server-uuid', 601 | }; 602 | 603 | const result = await client.createService(createData); 604 | 605 | expect(result).toEqual({ 606 | uuid: 'test-uuid', 607 | domains: ['test.com'], 608 | }); 609 | expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/services', { 610 | method: 'POST', 611 | headers: { 612 | 'Content-Type': 'application/json', 613 | Authorization: 'Bearer test-api-key', 614 | }, 615 | body: JSON.stringify(createData), 616 | }); 617 | }); 618 | }); 619 | 620 | describe('deleteService', () => { 621 | it('should delete a service', async () => { 622 | mockFetch.mockImplementationOnce(() => 623 | Promise.resolve({ 624 | ok: true, 625 | json: () => Promise.resolve({ message: 'Service deleted' }), 626 | } as Response), 627 | ); 628 | 629 | const result = await client.deleteService('test-uuid'); 630 | 631 | expect(result).toEqual({ message: 'Service deleted' }); 632 | expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/v1/services/test-uuid', { 633 | method: 'DELETE', 634 | headers: { 635 | 'Content-Type': 'application/json', 636 | Authorization: 'Bearer test-api-key', 637 | }, 638 | }); 639 | }); 640 | }); 641 | 642 | describe('error handling', () => { 643 | it('should handle network errors', async () => { 644 | const errorMessage = 'Network error'; 645 | mockFetch.mockImplementationOnce(() => Promise.reject(new Error(errorMessage))); 646 | 647 | await expect(client.listServers()).rejects.toThrow(errorMessage); 648 | }); 649 | }); 650 | }); 651 | </file> 652 | 653 | <file path="src/lib/coolify-client.ts"> 654 | import { 655 | CoolifyConfig, 656 | ErrorResponse, 657 | ServerInfo, 658 | ServerResources, 659 | ServerDomain, 660 | ValidationResponse, 661 | Project, 662 | CreateProjectRequest, 663 | UpdateProjectRequest, 664 | Environment, 665 | Deployment, 666 | Database, 667 | DatabaseUpdateRequest, 668 | Service, 669 | CreateServiceRequest, 670 | DeleteServiceOptions, 671 | } from '../types/coolify.js'; 672 | 673 | export class CoolifyClient { 674 | private baseUrl: string; 675 | private accessToken: string; 676 | 677 | constructor(config: CoolifyConfig) { 678 | if (!config.baseUrl) { 679 | throw new Error('Coolify base URL is required'); 680 | } 681 | if (!config.accessToken) { 682 | throw new Error('Coolify access token is required'); 683 | } 684 | this.baseUrl = config.baseUrl.replace(/\/$/, ''); 685 | this.accessToken = config.accessToken; 686 | } 687 | 688 | private async request<T>(path: string, options: RequestInit = {}): Promise<T> { 689 | try { 690 | const url = `${this.baseUrl}/api/v1${path}`; 691 | const response = await fetch(url, { 692 | headers: { 693 | 'Content-Type': 'application/json', 694 | Authorization: `Bearer ${this.accessToken}`, 695 | }, 696 | ...options, 697 | }); 698 | 699 | const data = await response.json(); 700 | 701 | if (!response.ok) { 702 | const error = data as ErrorResponse; 703 | throw new Error(error.message || `HTTP ${response.status}: ${response.statusText}`); 704 | } 705 | 706 | return data as T; 707 | } catch (error) { 708 | if (error instanceof TypeError && error.message.includes('fetch')) { 709 | throw new Error( 710 | `Failed to connect to Coolify server at ${this.baseUrl}. Please check if the server is running and the URL is correct.`, 711 | ); 712 | } 713 | throw error; 714 | } 715 | } 716 | 717 | async listServers(): Promise<ServerInfo[]> { 718 | return this.request<ServerInfo[]>('/servers'); 719 | } 720 | 721 | async getServer(uuid: string): Promise<ServerInfo> { 722 | return this.request<ServerInfo>(`/servers/${uuid}`); 723 | } 724 | 725 | async getServerResources(uuid: string): Promise<ServerResources> { 726 | return this.request<ServerResources>(`/servers/${uuid}/resources`); 727 | } 728 | 729 | async getServerDomains(uuid: string): Promise<ServerDomain[]> { 730 | return this.request<ServerDomain[]>(`/servers/${uuid}/domains`); 731 | } 732 | 733 | async validateServer(uuid: string): Promise<ValidationResponse> { 734 | return this.request<ValidationResponse>(`/servers/${uuid}/validate`); 735 | } 736 | 737 | async validateConnection(): Promise<void> { 738 | try { 739 | await this.listServers(); 740 | } catch (error) { 741 | throw new Error( 742 | `Failed to connect to Coolify server: ${error instanceof Error ? error.message : 'Unknown error'}`, 743 | ); 744 | } 745 | } 746 | 747 | async listProjects(): Promise<Project[]> { 748 | return this.request<Project[]>('/projects'); 749 | } 750 | 751 | async getProject(uuid: string): Promise<Project> { 752 | return this.request<Project>(`/projects/${uuid}`); 753 | } 754 | 755 | async createProject(project: CreateProjectRequest): Promise<{ uuid: string }> { 756 | return this.request<{ uuid: string }>('/projects', { 757 | method: 'POST', 758 | body: JSON.stringify(project), 759 | }); 760 | } 761 | 762 | async updateProject(uuid: string, project: UpdateProjectRequest): Promise<Project> { 763 | return this.request<Project>(`/projects/${uuid}`, { 764 | method: 'PATCH', 765 | body: JSON.stringify(project), 766 | }); 767 | } 768 | 769 | async deleteProject(uuid: string): Promise<{ message: string }> { 770 | return this.request<{ message: string }>(`/projects/${uuid}`, { 771 | method: 'DELETE', 772 | }); 773 | } 774 | 775 | async getProjectEnvironment( 776 | projectUuid: string, 777 | environmentNameOrUuid: string, 778 | ): Promise<Environment> { 779 | return this.request<Environment>(`/projects/${projectUuid}/${environmentNameOrUuid}`); 780 | } 781 | 782 | async deployApplication(uuid: string): Promise<Deployment> { 783 | const response = await this.request<Deployment>(`/applications/${uuid}/deploy`, { 784 | method: 'POST', 785 | }); 786 | return response; 787 | } 788 | 789 | async listDatabases(): Promise<Database[]> { 790 | return this.request<Database[]>('/databases'); 791 | } 792 | 793 | async getDatabase(uuid: string): Promise<Database> { 794 | return this.request<Database>(`/databases/${uuid}`); 795 | } 796 | 797 | async updateDatabase(uuid: string, data: DatabaseUpdateRequest): Promise<Database> { 798 | return this.request<Database>(`/databases/${uuid}`, { 799 | method: 'PATCH', 800 | body: JSON.stringify(data), 801 | }); 802 | } 803 | 804 | async deleteDatabase( 805 | uuid: string, 806 | options?: { 807 | deleteConfigurations?: boolean; 808 | deleteVolumes?: boolean; 809 | dockerCleanup?: boolean; 810 | deleteConnectedNetworks?: boolean; 811 | }, 812 | ): Promise<{ message: string }> { 813 | const queryParams = new URLSearchParams(); 814 | if (options) { 815 | if (options.deleteConfigurations !== undefined) { 816 | queryParams.set('delete_configurations', options.deleteConfigurations.toString()); 817 | } 818 | if (options.deleteVolumes !== undefined) { 819 | queryParams.set('delete_volumes', options.deleteVolumes.toString()); 820 | } 821 | if (options.dockerCleanup !== undefined) { 822 | queryParams.set('docker_cleanup', options.dockerCleanup.toString()); 823 | } 824 | if (options.deleteConnectedNetworks !== undefined) { 825 | queryParams.set('delete_connected_networks', options.deleteConnectedNetworks.toString()); 826 | } 827 | } 828 | 829 | const queryString = queryParams.toString(); 830 | const url = queryString ? `/databases/${uuid}?${queryString}` : `/databases/${uuid}`; 831 | 832 | return this.request<{ message: string }>(url, { 833 | method: 'DELETE', 834 | }); 835 | } 836 | 837 | async listServices(): Promise<Service[]> { 838 | return this.request<Service[]>('/services'); 839 | } 840 | 841 | async getService(uuid: string): Promise<Service> { 842 | return this.request<Service>(`/services/${uuid}`); 843 | } 844 | 845 | async createService(data: CreateServiceRequest): Promise<{ uuid: string; domains: string[] }> { 846 | return this.request<{ uuid: string; domains: string[] }>('/services', { 847 | method: 'POST', 848 | body: JSON.stringify(data), 849 | }); 850 | } 851 | 852 | async deleteService(uuid: string, options?: DeleteServiceOptions): Promise<{ message: string }> { 853 | const queryParams = new URLSearchParams(); 854 | if (options) { 855 | if (options.deleteConfigurations !== undefined) { 856 | queryParams.set('delete_configurations', options.deleteConfigurations.toString()); 857 | } 858 | if (options.deleteVolumes !== undefined) { 859 | queryParams.set('delete_volumes', options.deleteVolumes.toString()); 860 | } 861 | if (options.dockerCleanup !== undefined) { 862 | queryParams.set('docker_cleanup', options.dockerCleanup.toString()); 863 | } 864 | if (options.deleteConnectedNetworks !== undefined) { 865 | queryParams.set('delete_connected_networks', options.deleteConnectedNetworks.toString()); 866 | } 867 | } 868 | 869 | const queryString = queryParams.toString(); 870 | const url = queryString ? `/services/${uuid}?${queryString}` : `/services/${uuid}`; 871 | 872 | return this.request<{ message: string }>(url, { 873 | method: 'DELETE', 874 | }); 875 | } 876 | 877 | // Add more methods as needed for other endpoints 878 | } 879 | </file> 880 | 881 | <file path="src/lib/mcp-server.ts"> 882 | import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; 883 | import { Transport } from '@modelcontextprotocol/sdk/shared/transport.js'; 884 | import { CoolifyClient } from './coolify-client.js'; 885 | import debug from 'debug'; 886 | import { z } from 'zod'; 887 | import type { 888 | ServerInfo, 889 | ServerResources, 890 | ServerDomain, 891 | ValidationResponse, 892 | Project, 893 | CreateProjectRequest, 894 | UpdateProjectRequest, 895 | Environment, 896 | Deployment, 897 | Database, 898 | DatabaseUpdateRequest, 899 | Service, 900 | CreateServiceRequest, 901 | DeleteServiceOptions, 902 | } from '../types/coolify.js'; 903 | 904 | const log = debug('coolify:mcp'); 905 | 906 | // Define valid service types 907 | const serviceTypes = [ 908 | 'activepieces', 909 | 'appsmith', 910 | 'appwrite', 911 | 'authentik', 912 | 'babybuddy', 913 | 'budge', 914 | 'changedetection', 915 | 'chatwoot', 916 | 'classicpress-with-mariadb', 917 | 'classicpress-with-mysql', 918 | 'classicpress-without-database', 919 | 'cloudflared', 920 | 'code-server', 921 | 'dashboard', 922 | 'directus', 923 | 'directus-with-postgresql', 924 | 'docker-registry', 925 | 'docuseal', 926 | 'docuseal-with-postgres', 927 | 'dokuwiki', 928 | 'duplicati', 929 | 'emby', 930 | 'embystat', 931 | 'fider', 932 | 'filebrowser', 933 | 'firefly', 934 | 'formbricks', 935 | 'ghost', 936 | 'gitea', 937 | 'gitea-with-mariadb', 938 | 'gitea-with-mysql', 939 | 'gitea-with-postgresql', 940 | 'glance', 941 | 'glances', 942 | 'glitchtip', 943 | 'grafana', 944 | 'grafana-with-postgresql', 945 | 'grocy', 946 | 'heimdall', 947 | 'homepage', 948 | 'jellyfin', 949 | 'kuzzle', 950 | 'listmonk', 951 | 'logto', 952 | 'mediawiki', 953 | 'meilisearch', 954 | 'metabase', 955 | 'metube', 956 | 'minio', 957 | 'moodle', 958 | 'n8n', 959 | 'n8n-with-postgresql', 960 | 'next-image-transformation', 961 | 'nextcloud', 962 | 'nocodb', 963 | 'odoo', 964 | 'openblocks', 965 | 'pairdrop', 966 | 'penpot', 967 | 'phpmyadmin', 968 | 'pocketbase', 969 | 'posthog', 970 | 'reactive-resume', 971 | 'rocketchat', 972 | 'shlink', 973 | 'slash', 974 | 'snapdrop', 975 | 'statusnook', 976 | 'stirling-pdf', 977 | 'supabase', 978 | 'syncthing', 979 | 'tolgee', 980 | 'trigger', 981 | 'trigger-with-external-database', 982 | 'twenty', 983 | 'umami', 984 | 'unleash-with-postgresql', 985 | 'unleash-without-database', 986 | 'uptime-kuma', 987 | 'vaultwarden', 988 | 'vikunja', 989 | 'weblate', 990 | 'whoogle', 991 | 'wordpress-with-mariadb', 992 | 'wordpress-with-mysql', 993 | 'wordpress-without-database' 994 | ] as const; 995 | 996 | export class CoolifyMcpServer extends McpServer { 997 | private client: CoolifyClient; 998 | 999 | constructor(config: { baseUrl: string; accessToken: string }) { 1000 | super({ 1001 | name: 'coolify', 1002 | version: '0.1.18', 1003 | capabilities: { 1004 | tools: true 1005 | } 1006 | }); 1007 | log('Initializing server with config: %o', config); 1008 | this.client = new CoolifyClient(config); 1009 | this.setupTools(); 1010 | } 1011 | 1012 | private setupTools(): void { 1013 | log('Setting up tools'); 1014 | 1015 | this.tool('list_servers', 'List all Coolify servers', {}, async (_args, _extra) => { 1016 | const servers = await this.client.listServers(); 1017 | return { 1018 | content: [{ type: 'text', text: JSON.stringify(servers, null, 2) }] 1019 | }; 1020 | }); 1021 | 1022 | this.tool('get_server', 'Get details about a specific Coolify server', { 1023 | uuid: z.string() 1024 | }, async (args, _extra) => { 1025 | const server = await this.client.getServer(args.uuid); 1026 | return { 1027 | content: [{ type: 'text', text: JSON.stringify(server, null, 2) }] 1028 | }; 1029 | }); 1030 | 1031 | this.tool('get_server_resources', 'Get the current resources running on a specific Coolify server', { 1032 | uuid: z.string() 1033 | }, async (args, _extra) => { 1034 | const resources = await this.client.getServerResources(args.uuid); 1035 | return { 1036 | content: [{ type: 'text', text: JSON.stringify(resources, null, 2) }] 1037 | }; 1038 | }); 1039 | 1040 | this.tool('get_server_domains', 'Get domains for a specific Coolify server', { 1041 | uuid: z.string() 1042 | }, async (args, _extra) => { 1043 | const domains = await this.client.getServerDomains(args.uuid); 1044 | return { 1045 | content: [{ type: 'text', text: JSON.stringify(domains, null, 2) }] 1046 | }; 1047 | }); 1048 | 1049 | this.tool('validate_server', 'Validate a specific Coolify server', { 1050 | uuid: z.string() 1051 | }, async (args, _extra) => { 1052 | const validation = await this.client.validateServer(args.uuid); 1053 | return { 1054 | content: [{ type: 'text', text: JSON.stringify(validation, null, 2) }] 1055 | }; 1056 | }); 1057 | 1058 | this.tool('list_projects', 'List all Coolify projects', {}, async (_args, _extra) => { 1059 | const projects = await this.client.listProjects(); 1060 | return { 1061 | content: [{ type: 'text', text: JSON.stringify(projects, null, 2) }] 1062 | }; 1063 | }); 1064 | 1065 | this.tool('get_project', 'Get details about a specific Coolify project', { 1066 | uuid: z.string() 1067 | }, async (args, _extra) => { 1068 | const project = await this.client.getProject(args.uuid); 1069 | return { 1070 | content: [{ type: 'text', text: JSON.stringify(project, null, 2) }] 1071 | }; 1072 | }); 1073 | 1074 | this.tool('create_project', 'Create a new Coolify project', { 1075 | name: z.string(), 1076 | description: z.string().optional() 1077 | }, async (args, _extra) => { 1078 | const result = await this.client.createProject({ 1079 | name: args.name, 1080 | description: args.description 1081 | }); 1082 | return { 1083 | content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] 1084 | }; 1085 | }); 1086 | 1087 | this.tool('update_project', 'Update an existing Coolify project', { 1088 | uuid: z.string(), 1089 | name: z.string(), 1090 | description: z.string().optional() 1091 | }, async (args, _extra) => { 1092 | const { uuid, ...updateData } = args; 1093 | const result = await this.client.updateProject(uuid, updateData); 1094 | return { 1095 | content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] 1096 | }; 1097 | }); 1098 | 1099 | this.tool('delete_project', 'Delete a Coolify project', { 1100 | uuid: z.string() 1101 | }, async (args, _extra) => { 1102 | const result = await this.client.deleteProject(args.uuid); 1103 | return { 1104 | content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] 1105 | }; 1106 | }); 1107 | 1108 | this.tool('get_project_environment', 'Get environment details for a Coolify project', { 1109 | project_uuid: z.string(), 1110 | environment_name_or_uuid: z.string() 1111 | }, async (args, _extra) => { 1112 | const environment = await this.client.getProjectEnvironment(args.project_uuid, args.environment_name_or_uuid); 1113 | return { 1114 | content: [{ type: 'text', text: JSON.stringify(environment, null, 2) }] 1115 | }; 1116 | }); 1117 | 1118 | this.tool('list_databases', 'List all Coolify databases', {}, async (_args, _extra) => { 1119 | const databases = await this.client.listDatabases(); 1120 | return { 1121 | content: [{ type: 'text', text: JSON.stringify(databases, null, 2) }] 1122 | }; 1123 | }); 1124 | 1125 | this.tool('get_database', 'Get details about a specific Coolify database', { 1126 | uuid: z.string() 1127 | }, async (args, _extra) => { 1128 | const database = await this.client.getDatabase(args.uuid); 1129 | return { 1130 | content: [{ type: 'text', text: JSON.stringify(database, null, 2) }] 1131 | }; 1132 | }); 1133 | 1134 | this.tool('update_database', 'Update a Coolify database', { 1135 | uuid: z.string(), 1136 | data: z.record(z.unknown()) 1137 | }, async (args, _extra) => { 1138 | const result = await this.client.updateDatabase(args.uuid, args.data); 1139 | return { 1140 | content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] 1141 | }; 1142 | }); 1143 | 1144 | const deleteOptionsSchema = { 1145 | deleteConfigurations: z.boolean().optional(), 1146 | deleteVolumes: z.boolean().optional(), 1147 | dockerCleanup: z.boolean().optional(), 1148 | deleteConnectedNetworks: z.boolean().optional() 1149 | }; 1150 | 1151 | this.tool('delete_database', 'Delete a Coolify database', { 1152 | uuid: z.string(), 1153 | options: z.object(deleteOptionsSchema).optional() 1154 | }, async (args, _extra) => { 1155 | const result = await this.client.deleteDatabase(args.uuid, args.options); 1156 | return { 1157 | content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] 1158 | }; 1159 | }); 1160 | 1161 | this.tool('deploy_application', 'Deploy a Coolify application', { 1162 | uuid: z.string() 1163 | }, async (args, _extra) => { 1164 | const result = await this.client.deployApplication(args.uuid); 1165 | return { 1166 | content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] 1167 | }; 1168 | }); 1169 | 1170 | this.tool('list_services', 'List all Coolify services', {}, async (_args, _extra) => { 1171 | const services = await this.client.listServices(); 1172 | return { 1173 | content: [{ type: 'text', text: JSON.stringify(services, null, 2) }] 1174 | }; 1175 | }); 1176 | 1177 | this.tool('get_service', 'Get details about a specific Coolify service', { 1178 | uuid: z.string() 1179 | }, async (args, _extra) => { 1180 | const service = await this.client.getService(args.uuid); 1181 | return { 1182 | content: [{ type: 'text', text: JSON.stringify(service, null, 2) }] 1183 | }; 1184 | }); 1185 | 1186 | this.tool('create_service', 'Create a new Coolify service', { 1187 | type: z.enum(serviceTypes), 1188 | project_uuid: z.string(), 1189 | server_uuid: z.string(), 1190 | name: z.string().optional(), 1191 | description: z.string().optional(), 1192 | environment_name: z.string().optional(), 1193 | environment_uuid: z.string().optional(), 1194 | destination_uuid: z.string().optional(), 1195 | instant_deploy: z.boolean().optional() 1196 | }, async (args, _extra) => { 1197 | const result = await this.client.createService(args); 1198 | return { 1199 | content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] 1200 | }; 1201 | }); 1202 | 1203 | this.tool('delete_service', 'Delete a Coolify service', { 1204 | uuid: z.string(), 1205 | options: z.object(deleteOptionsSchema).optional() 1206 | }, async (args, _extra) => { 1207 | const result = await this.client.deleteService(args.uuid, args.options); 1208 | return { 1209 | content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] 1210 | }; 1211 | }); 1212 | } 1213 | 1214 | async connect(transport: Transport): Promise<void> { 1215 | log('Starting server...'); 1216 | log('Validating connection...'); 1217 | await this.client.validateConnection(); 1218 | await super.connect(transport); 1219 | log('Server started successfully'); 1220 | } 1221 | 1222 | async list_servers(): Promise<ServerInfo[]> { 1223 | return this.client.listServers(); 1224 | } 1225 | 1226 | async get_server(uuid: string): Promise<ServerInfo> { 1227 | return this.client.getServer(uuid); 1228 | } 1229 | 1230 | async get_server_resources(uuid: string): Promise<ServerResources> { 1231 | return this.client.getServerResources(uuid); 1232 | } 1233 | 1234 | async get_server_domains(uuid: string): Promise<ServerDomain[]> { 1235 | return this.client.getServerDomains(uuid); 1236 | } 1237 | 1238 | async validate_server(uuid: string): Promise<ValidationResponse> { 1239 | return this.client.validateServer(uuid); 1240 | } 1241 | 1242 | async list_projects(): Promise<Project[]> { 1243 | return this.client.listProjects(); 1244 | } 1245 | 1246 | async get_project(uuid: string): Promise<Project> { 1247 | return this.client.getProject(uuid); 1248 | } 1249 | 1250 | async create_project(project: CreateProjectRequest): Promise<{ uuid: string }> { 1251 | return this.client.createProject(project); 1252 | } 1253 | 1254 | async update_project(uuid: string, project: UpdateProjectRequest): Promise<Project> { 1255 | return this.client.updateProject(uuid, project); 1256 | } 1257 | 1258 | async delete_project(uuid: string): Promise<{ message: string }> { 1259 | return this.client.deleteProject(uuid); 1260 | } 1261 | 1262 | async get_project_environment( 1263 | projectUuid: string, 1264 | environmentNameOrUuid: string, 1265 | ): Promise<Environment> { 1266 | return this.client.getProjectEnvironment(projectUuid, environmentNameOrUuid); 1267 | } 1268 | 1269 | async deploy_application(params: { uuid: string }): Promise<Deployment> { 1270 | return this.client.deployApplication(params.uuid); 1271 | } 1272 | 1273 | async list_databases(): Promise<Database[]> { 1274 | return this.client.listDatabases(); 1275 | } 1276 | 1277 | async get_database(uuid: string): Promise<Database> { 1278 | return this.client.getDatabase(uuid); 1279 | } 1280 | 1281 | async update_database(uuid: string, data: DatabaseUpdateRequest): Promise<Database> { 1282 | return this.client.updateDatabase(uuid, data); 1283 | } 1284 | 1285 | async delete_database( 1286 | uuid: string, 1287 | options?: { 1288 | deleteConfigurations?: boolean; 1289 | deleteVolumes?: boolean; 1290 | dockerCleanup?: boolean; 1291 | deleteConnectedNetworks?: boolean; 1292 | }, 1293 | ): Promise<{ message: string }> { 1294 | return this.client.deleteDatabase(uuid, options); 1295 | } 1296 | 1297 | async list_services(): Promise<Service[]> { 1298 | return this.client.listServices(); 1299 | } 1300 | 1301 | async get_service(uuid: string): Promise<Service> { 1302 | return this.client.getService(uuid); 1303 | } 1304 | 1305 | async create_service(data: CreateServiceRequest): Promise<{ uuid: string; domains: string[] }> { 1306 | return this.client.createService(data); 1307 | } 1308 | 1309 | async delete_service(uuid: string, options?: DeleteServiceOptions): Promise<{ message: string }> { 1310 | return this.client.deleteService(uuid, options); 1311 | } 1312 | } 1313 | </file> 1314 | 1315 | <file path="src/lib/resource.ts"> 1316 | import 'reflect-metadata'; 1317 | 1318 | /** 1319 | * Metadata key for storing the resource URI 1320 | */ 1321 | const RESOURCE_URI_KEY = Symbol('resourceUri'); 1322 | 1323 | /** 1324 | * Decorator for marking methods as MCP resources. 1325 | * @param uri The URI pattern for the resource 1326 | */ 1327 | export function Resource(uri: string): MethodDecorator { 1328 | return function ( 1329 | target: object, 1330 | propertyKey: string | symbol, 1331 | descriptor: PropertyDescriptor, 1332 | ): PropertyDescriptor { 1333 | // Store the URI pattern in the method's metadata 1334 | Reflect.defineMetadata(RESOURCE_URI_KEY, uri, target, propertyKey); 1335 | return descriptor; 1336 | }; 1337 | } 1338 | 1339 | /** 1340 | * Get the resource URI for a decorated method 1341 | * @param target The class instance or constructor 1342 | * @param propertyKey The method name 1343 | * @returns The resource URI or undefined if not a resource 1344 | */ 1345 | export function getResourceUri(target: object, propertyKey: string | symbol): string | undefined { 1346 | return Reflect.getMetadata(RESOURCE_URI_KEY, target, propertyKey); 1347 | } 1348 | </file> 1349 | 1350 | <file path="src/resources/application-resources.ts"> 1351 | import { Resource } from '../lib/resource.js'; 1352 | import { CoolifyClient } from '../lib/coolify-client.js'; 1353 | import { Application, CreateApplicationRequest } from '../types/coolify.js'; 1354 | 1355 | export class ApplicationResources { 1356 | private client: CoolifyClient; 1357 | 1358 | constructor(client: CoolifyClient) { 1359 | this.client = client; 1360 | } 1361 | 1362 | @Resource('coolify/applications/list') 1363 | async listApplications(): Promise<Application[]> { 1364 | // TODO: Implement listApplications in CoolifyClient 1365 | throw new Error('Not implemented'); 1366 | } 1367 | 1368 | @Resource('coolify/applications/{id}') 1369 | async getApplication(_id: string): Promise<Application> { 1370 | // TODO: Implement getApplication in CoolifyClient 1371 | throw new Error('Not implemented'); 1372 | } 1373 | 1374 | @Resource('coolify/applications/create') 1375 | async createApplication(_data: CreateApplicationRequest): Promise<{ uuid: string }> { 1376 | // TODO: Implement createApplication in CoolifyClient 1377 | throw new Error('Not implemented'); 1378 | } 1379 | 1380 | @Resource('coolify/applications/{id}/delete') 1381 | async deleteApplication(_id: string): Promise<{ message: string }> { 1382 | // TODO: Implement deleteApplication in CoolifyClient 1383 | throw new Error('Not implemented'); 1384 | } 1385 | } 1386 | </file> 1387 | 1388 | <file path="src/resources/database-resources.ts"> 1389 | import { Resource } from '../lib/resource.js'; 1390 | import { CoolifyClient } from '../lib/coolify-client.js'; 1391 | import { Database, DatabaseUpdateRequest } from '../types/coolify.js'; 1392 | 1393 | export class DatabaseResources { 1394 | private client: CoolifyClient; 1395 | 1396 | constructor(client: CoolifyClient) { 1397 | this.client = client; 1398 | } 1399 | 1400 | @Resource('coolify/databases/list') 1401 | async listDatabases(): Promise<Database[]> { 1402 | return this.client.listDatabases(); 1403 | } 1404 | 1405 | @Resource('coolify/databases/{id}') 1406 | async getDatabase(id: string): Promise<Database> { 1407 | return this.client.getDatabase(id); 1408 | } 1409 | 1410 | @Resource('coolify/databases/{id}/update') 1411 | async updateDatabase(id: string, data: DatabaseUpdateRequest): Promise<Database> { 1412 | return this.client.updateDatabase(id, data); 1413 | } 1414 | 1415 | @Resource('coolify/databases/{id}/delete') 1416 | async deleteDatabase( 1417 | id: string, 1418 | options?: { 1419 | deleteConfigurations?: boolean; 1420 | deleteVolumes?: boolean; 1421 | dockerCleanup?: boolean; 1422 | deleteConnectedNetworks?: boolean; 1423 | }, 1424 | ): Promise<{ message: string }> { 1425 | return this.client.deleteDatabase(id, options); 1426 | } 1427 | } 1428 | </file> 1429 | 1430 | <file path="src/resources/deployment-resources.ts"> 1431 | import { Resource } from '../lib/resource.js'; 1432 | import { CoolifyClient } from '../lib/coolify-client.js'; 1433 | import { Deployment } from '../types/coolify.js'; 1434 | 1435 | export class DeploymentResources { 1436 | private client: CoolifyClient; 1437 | 1438 | constructor(client: CoolifyClient) { 1439 | this.client = client; 1440 | } 1441 | 1442 | @Resource('coolify/deployments/list') 1443 | async listDeployments(): Promise<Deployment[]> { 1444 | // TODO: Implement listDeployments in CoolifyClient 1445 | throw new Error('Not implemented'); 1446 | } 1447 | 1448 | @Resource('coolify/deployments/{id}') 1449 | async getDeployment(_id: string): Promise<Deployment> { 1450 | // TODO: Implement getDeployment in CoolifyClient 1451 | throw new Error('Not implemented'); 1452 | } 1453 | 1454 | @Resource('coolify/deploy') 1455 | async deploy(params: { uuid: string; forceRebuild?: boolean }): Promise<Deployment> { 1456 | return this.client.deployApplication(params.uuid); 1457 | } 1458 | } 1459 | </file> 1460 | 1461 | <file path="src/resources/index.ts"> 1462 | export * from './database-resources.js'; 1463 | export * from './deployment-resources.js'; 1464 | export * from './application-resources.js'; 1465 | export * from './service-resources.js'; 1466 | </file> 1467 | 1468 | <file path="src/resources/service-resources.ts"> 1469 | import { Resource } from '../lib/resource.js'; 1470 | import { CoolifyClient } from '../lib/coolify-client.js'; 1471 | import { Service, CreateServiceRequest, DeleteServiceOptions } from '../types/coolify.js'; 1472 | 1473 | export class ServiceResources { 1474 | private client: CoolifyClient; 1475 | 1476 | constructor(client: CoolifyClient) { 1477 | this.client = client; 1478 | } 1479 | 1480 | @Resource('coolify/services/list') 1481 | async listServices(): Promise<Service[]> { 1482 | return this.client.listServices(); 1483 | } 1484 | 1485 | @Resource('coolify/services/{id}') 1486 | async getService(id: string): Promise<Service> { 1487 | return this.client.getService(id); 1488 | } 1489 | 1490 | @Resource('coolify/services/create') 1491 | async createService(data: CreateServiceRequest): Promise<{ uuid: string; domains: string[] }> { 1492 | return this.client.createService(data); 1493 | } 1494 | 1495 | @Resource('coolify/services/{id}/delete') 1496 | async deleteService(id: string, options?: DeleteServiceOptions): Promise<{ message: string }> { 1497 | return this.client.deleteService(id, options); 1498 | } 1499 | } 1500 | </file> 1501 | 1502 | <file path="src/types/coolify.ts"> 1503 | export interface CoolifyConfig { 1504 | baseUrl: string; 1505 | accessToken: string; 1506 | } 1507 | 1508 | export interface ServerInfo { 1509 | uuid: string; 1510 | name: string; 1511 | status: 'running' | 'stopped' | 'error'; 1512 | version: string; 1513 | resources: { 1514 | cpu: number; 1515 | memory: number; 1516 | disk: number; 1517 | }; 1518 | } 1519 | 1520 | export interface ResourceStatus { 1521 | id: number; 1522 | uuid: string; 1523 | name: string; 1524 | type: string; 1525 | created_at: string; 1526 | updated_at: string; 1527 | status: string; 1528 | } 1529 | 1530 | export type ServerResources = ResourceStatus[]; 1531 | 1532 | export interface ErrorResponse { 1533 | error: string; 1534 | status: number; 1535 | message: string; 1536 | } 1537 | 1538 | export interface ServerDomain { 1539 | ip: string; 1540 | domains: string[]; 1541 | } 1542 | 1543 | export interface ValidationResponse { 1544 | message: string; 1545 | } 1546 | 1547 | export interface Environment { 1548 | id: number; 1549 | uuid: string; 1550 | name: string; 1551 | project_uuid: string; 1552 | variables?: Record<string, string>; 1553 | created_at: string; 1554 | updated_at: string; 1555 | } 1556 | 1557 | export interface Project { 1558 | id: number; 1559 | uuid: string; 1560 | name: string; 1561 | description?: string; 1562 | environments?: Environment[]; 1563 | } 1564 | 1565 | export interface CreateProjectRequest { 1566 | name: string; 1567 | description?: string; 1568 | } 1569 | 1570 | export interface UpdateProjectRequest { 1571 | name?: string; 1572 | description?: string; 1573 | } 1574 | 1575 | export interface LogEntry { 1576 | timestamp: string; 1577 | level: string; 1578 | message: string; 1579 | } 1580 | 1581 | export interface Deployment { 1582 | id: number; 1583 | uuid: string; 1584 | application_uuid: string; 1585 | status: string; 1586 | created_at: string; 1587 | updated_at: string; 1588 | } 1589 | 1590 | export interface DatabaseBase { 1591 | id: number; 1592 | uuid: string; 1593 | name: string; 1594 | description?: string; 1595 | type: 1596 | | 'postgresql' 1597 | | 'mysql' 1598 | | 'mariadb' 1599 | | 'mongodb' 1600 | | 'redis' 1601 | | 'keydb' 1602 | | 'clickhouse' 1603 | | 'dragonfly'; 1604 | status: 'running' | 'stopped' | 'error'; 1605 | created_at: string; 1606 | updated_at: string; 1607 | is_public: boolean; 1608 | public_port?: number; 1609 | image: string; 1610 | limits?: { 1611 | memory?: string; 1612 | memory_swap?: string; 1613 | memory_swappiness?: number; 1614 | memory_reservation?: string; 1615 | cpus?: string; 1616 | cpuset?: string; 1617 | cpu_shares?: number; 1618 | }; 1619 | } 1620 | 1621 | export interface PostgresDatabase extends DatabaseBase { 1622 | type: 'postgresql'; 1623 | postgres_user: string; 1624 | postgres_password: string; 1625 | postgres_db: string; 1626 | postgres_initdb_args?: string; 1627 | postgres_host_auth_method?: string; 1628 | postgres_conf?: string; 1629 | } 1630 | 1631 | export interface MySQLDatabase extends DatabaseBase { 1632 | type: 'mysql'; 1633 | mysql_root_password: string; 1634 | mysql_user?: string; 1635 | mysql_password?: string; 1636 | mysql_database?: string; 1637 | } 1638 | 1639 | export interface MariaDBDatabase extends DatabaseBase { 1640 | type: 'mariadb'; 1641 | mariadb_root_password: string; 1642 | mariadb_user?: string; 1643 | mariadb_password?: string; 1644 | mariadb_database?: string; 1645 | mariadb_conf?: string; 1646 | } 1647 | 1648 | export interface MongoDBDatabase extends DatabaseBase { 1649 | type: 'mongodb'; 1650 | mongo_initdb_root_username: string; 1651 | mongo_initdb_root_password: string; 1652 | mongo_initdb_database?: string; 1653 | mongo_conf?: string; 1654 | } 1655 | 1656 | export interface RedisDatabase extends DatabaseBase { 1657 | type: 'redis'; 1658 | redis_password?: string; 1659 | redis_conf?: string; 1660 | } 1661 | 1662 | export interface KeyDBDatabase extends DatabaseBase { 1663 | type: 'keydb'; 1664 | keydb_password?: string; 1665 | keydb_conf?: string; 1666 | } 1667 | 1668 | export interface ClickhouseDatabase extends DatabaseBase { 1669 | type: 'clickhouse'; 1670 | clickhouse_admin_user: string; 1671 | clickhouse_admin_password: string; 1672 | } 1673 | 1674 | export interface DragonflyDatabase extends DatabaseBase { 1675 | type: 'dragonfly'; 1676 | dragonfly_password: string; 1677 | } 1678 | 1679 | export type Database = 1680 | | PostgresDatabase 1681 | | MySQLDatabase 1682 | | MariaDBDatabase 1683 | | MongoDBDatabase 1684 | | RedisDatabase 1685 | | KeyDBDatabase 1686 | | ClickhouseDatabase 1687 | | DragonflyDatabase; 1688 | 1689 | export interface DatabaseUpdateRequest { 1690 | name?: string; 1691 | description?: string; 1692 | image?: string; 1693 | is_public?: boolean; 1694 | public_port?: number; 1695 | limits_memory?: string; 1696 | limits_memory_swap?: string; 1697 | limits_memory_swappiness?: number; 1698 | limits_memory_reservation?: string; 1699 | limits_cpus?: string; 1700 | limits_cpuset?: string; 1701 | limits_cpu_shares?: number; 1702 | postgres_user?: string; 1703 | postgres_password?: string; 1704 | postgres_db?: string; 1705 | postgres_initdb_args?: string; 1706 | postgres_host_auth_method?: string; 1707 | postgres_conf?: string; 1708 | clickhouse_admin_user?: string; 1709 | clickhouse_admin_password?: string; 1710 | dragonfly_password?: string; 1711 | redis_password?: string; 1712 | redis_conf?: string; 1713 | keydb_password?: string; 1714 | keydb_conf?: string; 1715 | mariadb_conf?: string; 1716 | mariadb_root_password?: string; 1717 | mariadb_user?: string; 1718 | mariadb_password?: string; 1719 | mariadb_database?: string; 1720 | mongo_conf?: string; 1721 | mongo_initdb_root_username?: string; 1722 | mongo_initdb_root_password?: string; 1723 | mongo_initdb_database?: string; 1724 | mysql_root_password?: string; 1725 | mysql_password?: string; 1726 | mysql_user?: string; 1727 | mysql_database?: string; 1728 | } 1729 | 1730 | export type ServiceType = 1731 | | 'activepieces' 1732 | | 'appsmith' 1733 | | 'appwrite' 1734 | | 'authentik' 1735 | | 'babybuddy' 1736 | | 'budge' 1737 | | 'changedetection' 1738 | | 'chatwoot' 1739 | | 'classicpress-with-mariadb' 1740 | | 'classicpress-with-mysql' 1741 | | 'classicpress-without-database' 1742 | | 'cloudflared' 1743 | | 'code-server' 1744 | | 'dashboard' 1745 | | 'directus' 1746 | | 'directus-with-postgresql' 1747 | | 'docker-registry' 1748 | | 'docuseal' 1749 | | 'docuseal-with-postgres' 1750 | | 'dokuwiki' 1751 | | 'duplicati' 1752 | | 'emby' 1753 | | 'embystat' 1754 | | 'fider' 1755 | | 'filebrowser' 1756 | | 'firefly' 1757 | | 'formbricks' 1758 | | 'ghost' 1759 | | 'gitea' 1760 | | 'gitea-with-mariadb' 1761 | | 'gitea-with-mysql' 1762 | | 'gitea-with-postgresql' 1763 | | 'glance' 1764 | | 'glances' 1765 | | 'glitchtip' 1766 | | 'grafana' 1767 | | 'grafana-with-postgresql' 1768 | | 'grocy' 1769 | | 'heimdall' 1770 | | 'homepage' 1771 | | 'jellyfin' 1772 | | 'kuzzle' 1773 | | 'listmonk' 1774 | | 'logto' 1775 | | 'mediawiki' 1776 | | 'meilisearch' 1777 | | 'metabase' 1778 | | 'metube' 1779 | | 'minio' 1780 | | 'moodle' 1781 | | 'n8n' 1782 | | 'n8n-with-postgresql' 1783 | | 'next-image-transformation' 1784 | | 'nextcloud' 1785 | | 'nocodb' 1786 | | 'odoo' 1787 | | 'openblocks' 1788 | | 'pairdrop' 1789 | | 'penpot' 1790 | | 'phpmyadmin' 1791 | | 'pocketbase' 1792 | | 'posthog' 1793 | | 'reactive-resume' 1794 | | 'rocketchat' 1795 | | 'shlink' 1796 | | 'slash' 1797 | | 'snapdrop' 1798 | | 'statusnook' 1799 | | 'stirling-pdf' 1800 | | 'supabase' 1801 | | 'syncthing' 1802 | | 'tolgee' 1803 | | 'trigger' 1804 | | 'trigger-with-external-database' 1805 | | 'twenty' 1806 | | 'umami' 1807 | | 'unleash-with-postgresql' 1808 | | 'unleash-without-database' 1809 | | 'uptime-kuma' 1810 | | 'vaultwarden' 1811 | | 'vikunja' 1812 | | 'weblate' 1813 | | 'whoogle' 1814 | | 'wordpress-with-mariadb' 1815 | | 'wordpress-with-mysql' 1816 | | 'wordpress-without-database'; 1817 | 1818 | export interface Service { 1819 | id: number; 1820 | uuid: string; 1821 | name: string; 1822 | description?: string; 1823 | type: ServiceType; 1824 | status: 'running' | 'stopped' | 'error'; 1825 | created_at: string; 1826 | updated_at: string; 1827 | project_uuid: string; 1828 | environment_name: string; 1829 | environment_uuid: string; 1830 | server_uuid: string; 1831 | destination_uuid?: string; 1832 | domains?: string[]; 1833 | } 1834 | 1835 | export interface CreateServiceRequest { 1836 | type: ServiceType; 1837 | name?: string; 1838 | description?: string; 1839 | project_uuid: string; 1840 | environment_name?: string; 1841 | environment_uuid?: string; 1842 | server_uuid: string; 1843 | destination_uuid?: string; 1844 | instant_deploy?: boolean; 1845 | } 1846 | 1847 | export interface DeleteServiceOptions { 1848 | deleteConfigurations?: boolean; 1849 | deleteVolumes?: boolean; 1850 | dockerCleanup?: boolean; 1851 | deleteConnectedNetworks?: boolean; 1852 | } 1853 | 1854 | export interface Application { 1855 | uuid: string; 1856 | name: string; 1857 | // Add other application properties as needed 1858 | } 1859 | 1860 | export interface CreateApplicationRequest { 1861 | name: string; 1862 | // Add other required fields for application creation 1863 | } 1864 | </file> 1865 | 1866 | <file path="src/index.ts"> 1867 | #!/usr/bin/env node 1868 | 1869 | import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; 1870 | import { CoolifyMcpServer } from './lib/mcp-server.js'; 1871 | import { CoolifyConfig } from './types/coolify.js'; 1872 | 1873 | declare const process: NodeJS.Process; 1874 | 1875 | async function main(): Promise<void> { 1876 | const config: CoolifyConfig = { 1877 | baseUrl: process.env.COOLIFY_BASE_URL || 'http://localhost:3000', 1878 | accessToken: process.env.COOLIFY_ACCESS_TOKEN || '', 1879 | }; 1880 | 1881 | if (!config.accessToken) { 1882 | throw new Error('COOLIFY_ACCESS_TOKEN environment variable is required'); 1883 | } 1884 | 1885 | const server = new CoolifyMcpServer(config); 1886 | const transport = new StdioServerTransport(); 1887 | 1888 | await server.connect(transport); 1889 | } 1890 | 1891 | main().catch((error) => { 1892 | console.error('Fatal error:', error); 1893 | process.exit(1); 1894 | }); 1895 | </file> 1896 | 1897 | <file path=".eslintrc.json"> 1898 | { 1899 | "env": { 1900 | "node": true, 1901 | "es2021": true 1902 | }, 1903 | "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended", "prettier"], 1904 | "parser": "@typescript-eslint/parser", 1905 | "parserOptions": { 1906 | "ecmaVersion": "latest", 1907 | "sourceType": "module" 1908 | }, 1909 | "plugins": ["@typescript-eslint"], 1910 | "rules": { 1911 | "@typescript-eslint/explicit-function-return-type": "warn", 1912 | "@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }], 1913 | "@typescript-eslint/no-explicit-any": "warn" 1914 | } 1915 | } 1916 | </file> 1917 | 1918 | <file path=".gitignore"> 1919 | # Dependencies 1920 | node_modules/ 1921 | 1922 | # Build output 1923 | dist/ 1924 | build/ 1925 | /lib/ 1926 | coverage/ 1927 | 1928 | # IDE and editor files 1929 | .idea/ 1930 | .vscode/ 1931 | *.swp 1932 | *.swo 1933 | .DS_Store 1934 | Thumbs.db 1935 | 1936 | # Environment variables 1937 | .env 1938 | .env.local 1939 | .env.*.local 1940 | 1941 | # Logs 1942 | logs/ 1943 | *.log 1944 | npm-debug.log* 1945 | yarn-debug.log* 1946 | yarn-error.log* 1947 | 1948 | # Test coverage 1949 | coverage/ 1950 | .nyc_output/ 1951 | 1952 | # Optional npm cache directory 1953 | .npm 1954 | 1955 | # Optional eslint cache 1956 | .eslintcache 1957 | 1958 | # Optional REPL history 1959 | .node_repl_history 1960 | 1961 | # Output of 'npm pack' 1962 | *.tgz 1963 | 1964 | # Yarn Integrity file 1965 | .yarn-integrity 1966 | </file> 1967 | 1968 | <file path=".lintstagedrc.json"> 1969 | { 1970 | "src/**/*.ts": ["eslint --fix", "prettier --write"], 1971 | "*.json": ["prettier --write"], 1972 | "*.md": ["prettier --write", "markdownlint-cli2"] 1973 | } 1974 | </file> 1975 | 1976 | <file path=".markdownlint-cli2.jsonc"> 1977 | { 1978 | "config": { 1979 | "line-length": false, 1980 | "no-duplicate-heading": false, 1981 | "no-inline-html": false, 1982 | }, 1983 | "ignores": ["node_modules", "dist"], 1984 | } 1985 | </file> 1986 | 1987 | <file path=".prettierrc"> 1988 | { 1989 | "semi": true, 1990 | "trailingComma": "all", 1991 | "singleQuote": true, 1992 | "printWidth": 100, 1993 | "tabWidth": 2 1994 | } 1995 | </file> 1996 | 1997 | <file path=".repomixignore"> 1998 | .cursor/ 1999 | .github/ 2000 | .husky/ 2001 | docs/ 2002 | package-lock.json 2003 | </file> 2004 | 2005 | <file path="debug.js"> 2006 | import { CoolifyMcpServer } from './dist/lib/mcp-server.js'; 2007 | import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; 2008 | 2009 | // Enable debug logging 2010 | process.env.DEBUG = '*'; 2011 | 2012 | const server = new CoolifyMcpServer({ 2013 | baseUrl: 'https://coolify.dev', // Replace with your actual Coolify URL 2014 | accessToken: 'your-actual-token' // Replace with your actual Coolify token 2015 | }); 2016 | 2017 | const transport = new StdioServerTransport(); 2018 | await server.connect(transport); 2019 | </file> 2020 | 2021 | <file path="jest.config.js"> 2022 | /** @type {import('ts-jest').JestConfigWithTsJest} */ 2023 | export default { 2024 | preset: 'ts-jest', 2025 | testEnvironment: 'node', 2026 | moduleNameMapper: { 2027 | '^(\\.{1,2}/.*)\\.js$': '$1', 2028 | }, 2029 | transform: { 2030 | '^.+\\.tsx?$': [ 2031 | 'ts-jest', 2032 | { 2033 | useESM: true, 2034 | }, 2035 | ], 2036 | }, 2037 | extensionsToTreatAsEsm: ['.ts'], 2038 | testPathIgnorePatterns: ['/node_modules/', '/dist/', '\\.d\\.ts$'], 2039 | }; 2040 | </file> 2041 | 2042 | <file path="package.json"> 2043 | { 2044 | "name": "@masonator/coolify-mcp", 2045 | "scope": "@masonator", 2046 | "version": "0.2.2", 2047 | "description": "MCP server implementation for Coolify", 2048 | "type": "module", 2049 | "main": "./dist/index.js", 2050 | "types": "./dist/index.d.ts", 2051 | "exports": { 2052 | ".": { 2053 | "import": "./dist/index.js", 2054 | "require": "./dist/index.cjs", 2055 | "types": "./dist/index.d.ts" 2056 | } 2057 | }, 2058 | "bin": { 2059 | "coolify-mcp": "dist/index.js" 2060 | }, 2061 | "files": [ 2062 | "dist" 2063 | ], 2064 | "scripts": { 2065 | "build": "tsc && shx chmod +x dist/*.js", 2066 | "dev": "tsc --watch", 2067 | "test": "NODE_OPTIONS=--experimental-vm-modules jest", 2068 | "test:watch": "NODE_OPTIONS=--experimental-vm-modules jest --watch", 2069 | "test:coverage": "NODE_OPTIONS=--experimental-vm-modules jest --coverage", 2070 | "lint": "eslint . --ext .ts", 2071 | "lint:fix": "eslint . --ext .ts --fix", 2072 | "format": "prettier --write .", 2073 | "format:check": "prettier --check .", 2074 | "prepare": "husky", 2075 | "prepublishOnly": "npm test && npm run lint", 2076 | "start": "node dist/index.js" 2077 | }, 2078 | "keywords": [ 2079 | "coolify", 2080 | "mcp", 2081 | "model-context-protocol" 2082 | ], 2083 | "author": "Stuart Mason", 2084 | "license": "MIT", 2085 | "dependencies": { 2086 | "@modelcontextprotocol/sdk": "^1.6.1", 2087 | "@modelcontextprotocol/server-github": "^2025.1.23", 2088 | "reflect-metadata": "^0.2.2", 2089 | "zod": "^3.24.2" 2090 | }, 2091 | "devDependencies": { 2092 | "@types/debug": "^4.1.12", 2093 | "@types/jest": "^29.5.14", 2094 | "@types/node": "^20.17.23", 2095 | "@typescript-eslint/eslint-plugin": "^7.18.0", 2096 | "@typescript-eslint/parser": "^7.18.0", 2097 | "eslint": "^8.56.0", 2098 | "eslint-config-prettier": "^9.1.0", 2099 | "husky": "^9.0.11", 2100 | "jest": "^29.7.0", 2101 | "lint-staged": "^15.2.2", 2102 | "markdownlint-cli2": "^0.12.1", 2103 | "prettier": "^3.5.3", 2104 | "shx": "^0.3.4", 2105 | "ts-jest": "^29.2.6", 2106 | "typescript": "^5.8.2" 2107 | }, 2108 | "engines": { 2109 | "node": ">=18" 2110 | } 2111 | } 2112 | </file> 2113 | 2114 | <file path="README.md"> 2115 | # Coolify MCP Server 2116 | 2117 | A Model Context Protocol (MCP) server implementation for [Coolify](https://coolify.io/), enabling AI assistants to interact with your Coolify instances through natural language. 2118 | 2119 | ## Example Prompts 2120 | 2121 | Here are example prompts you can use with MCP-compatible AI assistants to interact with your Coolify instance: 2122 | 2123 | ### Server Management 2124 | 2125 | ``` 2126 | # List and Inspect Servers 2127 | - Show me all Coolify servers in my instance 2128 | - What's the status of server {uuid}? 2129 | - Show me the resources running on server {uuid} 2130 | - What domains are configured for server {uuid}? 2131 | - Can you validate the connection to server {uuid}? 2132 | 2133 | # Resource Monitoring 2134 | - How much CPU and memory is server {uuid} using? 2135 | - List all resources running on server {uuid} 2136 | - Show me the current status of all servers 2137 | ``` 2138 | 2139 | ### Project Management 2140 | 2141 | ``` 2142 | # Project Operations 2143 | - List all my Coolify projects 2144 | - Create a new project called "my-webapp" with description "My web application" 2145 | - Show me the details of project {uuid} 2146 | - Update project {uuid} to change its name to "new-name" 2147 | - Delete project {uuid} 2148 | 2149 | # Environment Management 2150 | - Show me the environments in project {uuid} 2151 | - Get details of the production environment in project {uuid} 2152 | - What variables are set in the staging environment of project {uuid}? 2153 | ``` 2154 | 2155 | ### Application and Service Management 2156 | 2157 | ``` 2158 | # Application Management 2159 | - List all applications 2160 | - Show me details of application {uuid} 2161 | - Create a new application called "my-nodejs-app" 2162 | - Delete application {uuid} 2163 | 2164 | # Service Operations 2165 | - Show me all running services 2166 | - Create a new WordPress service: 2167 | - Name: my-blog 2168 | - Project UUID: {project_uuid} 2169 | - Server UUID: {server_uuid} 2170 | - Type: wordpress-with-mysql 2171 | - What's the status of service {uuid}? 2172 | - Delete service {uuid} and clean up its resources 2173 | ``` 2174 | 2175 | ### Database Management 2176 | 2177 | ``` 2178 | # Database Operations 2179 | - List all databases 2180 | - Show me the configuration of database {uuid} 2181 | - Update database {uuid}: 2182 | - Increase memory limit to 1GB 2183 | - Change public port to 5432 2184 | - Update password 2185 | - Delete database {uuid} and clean up volumes 2186 | 2187 | # Database Types 2188 | - Create a PostgreSQL database 2189 | - Set up a Redis instance 2190 | - Configure a MongoDB database 2191 | - Initialize a MySQL database 2192 | ``` 2193 | 2194 | ### Deployment Management 2195 | 2196 | ``` 2197 | # Deployment Operations 2198 | - Show me all active deployments 2199 | - What's the status of deployment {uuid}? 2200 | - Deploy application {uuid} 2201 | - Force rebuild and deploy application {uuid} 2202 | - List recent deployments for application {uuid} 2203 | ``` 2204 | 2205 | ## Installation 2206 | 2207 | ### Prerequisites 2208 | 2209 | - Node.js >= 18 2210 | - A running Coolify instance 2211 | - Coolify API access token 2212 | 2213 | ### Setup in AI Tools 2214 | 2215 | #### Claude Desktop 2216 | 2217 | ```json 2218 | "coolify": { 2219 | "command": "npx", 2220 | "args": [ 2221 | "-y", "@masonator/coolify-mcp" 2222 | ], 2223 | "env": { 2224 | "COOLIFY_ACCESS_TOKEN": "0|your-secret-token", 2225 | "COOLIFY_BASE_URL": "https://your-coolify-instance.com" 2226 | } 2227 | } 2228 | ``` 2229 | 2230 | #### Cursor 2231 | 2232 | ```bash 2233 | env COOLIFY_ACCESS_TOKEN:0|your-secret-token COOLIFY_BASE_URL:https://your-coolify-instance.com npx -y @stumason/coolify-mcp 2234 | ``` 2235 | 2236 | ## Development 2237 | 2238 | ### Local Setup 2239 | 2240 | ```bash 2241 | # Clone the repository 2242 | git clone https://github.com/stumason/coolify-mcp.git 2243 | cd coolify-mcp 2244 | 2245 | # Install dependencies 2246 | npm install 2247 | 2248 | # Build the project 2249 | npm run build 2250 | 2251 | # Run tests 2252 | npm test 2253 | ``` 2254 | 2255 | ### Environment Variables 2256 | 2257 | ```bash 2258 | # Required 2259 | COOLIFY_ACCESS_TOKEN=your_access_token_here 2260 | 2261 | # Optional (defaults to http://localhost:3000) 2262 | COOLIFY_BASE_URL=https://your.coolify.instance 2263 | ``` 2264 | 2265 | ## API Reference 2266 | 2267 | ### Resource Types 2268 | 2269 | #### Application 2270 | 2271 | ```typescript 2272 | interface Application { 2273 | uuid: string; 2274 | name: string; 2275 | // Additional properties based on your Coolify instance 2276 | } 2277 | ``` 2278 | 2279 | #### Service 2280 | 2281 | ```typescript 2282 | interface Service { 2283 | id: number; 2284 | uuid: string; 2285 | name: string; 2286 | type: ServiceType; // Various types like 'wordpress', 'mysql', etc. 2287 | status: 'running' | 'stopped' | 'error'; 2288 | project_uuid: string; 2289 | environment_uuid: string; 2290 | server_uuid: string; 2291 | domains?: string[]; 2292 | } 2293 | ``` 2294 | 2295 | #### Database 2296 | 2297 | ```typescript 2298 | interface Database { 2299 | id: number; 2300 | uuid: string; 2301 | name: string; 2302 | type: 'postgresql' | 'mysql' | 'mongodb' | 'redis' | /* other types */; 2303 | status: 'running' | 'stopped' | 'error'; 2304 | is_public: boolean; 2305 | public_port?: number; 2306 | // Additional configuration based on database type 2307 | } 2308 | ``` 2309 | 2310 | #### Deployment 2311 | 2312 | ```typescript 2313 | interface Deployment { 2314 | id: number; 2315 | uuid: string; 2316 | application_uuid: string; 2317 | status: string; 2318 | created_at: string; 2319 | updated_at: string; 2320 | } 2321 | ``` 2322 | 2323 | ## Contributing 2324 | 2325 | 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. 2326 | 2327 | ## License 2328 | 2329 | MIT 2330 | 2331 | ## Support 2332 | 2333 | For support, please: 2334 | 2335 | 1. Check the [issues](https://github.com/stumason/coolify-mcp/issues) page 2336 | 2. Create a new issue if needed 2337 | 3. Join the Coolify community 2338 | </file> 2339 | 2340 | <file path="tsconfig.json"> 2341 | { 2342 | "compilerOptions": { 2343 | "target": "ES2020", 2344 | "module": "NodeNext", 2345 | "moduleResolution": "NodeNext", 2346 | "declaration": true, 2347 | "outDir": "./dist", 2348 | "strict": true, 2349 | "esModuleInterop": true, 2350 | "skipLibCheck": true, 2351 | "forceConsistentCasingInFileNames": true, 2352 | "experimentalDecorators": true, 2353 | "emitDecoratorMetadata": true, 2354 | "allowJs": true, 2355 | "resolveJsonModule": true 2356 | }, 2357 | "include": ["src"], 2358 | "exclude": ["node_modules", "dist", "tests"] 2359 | } 2360 | </file> 2361 | 2362 | </repository_files> 2363 | ```