Skip to content

v2.16.0

Choose a tag to compare

@github-actions github-actions released this 29 Jul 14:48
· 39 commits to main since this release
v2.16.0
c9a7462

What's Changed

Upgrade from 2.15.0 if you run Coolify 4.1 or older. 2.15.0 broke system enable_api / disable_api and validate_server on every pre-4.2 instance; see the first entry under Fixed.

Added

  • tags tool: list, attach and detach tags on applications, databases and services (#298) — Coolify v4.2 exposed tag management over the API (coollabsio/coolify#9275). deploy already accepted a tag name and triggered a deployment for everything carrying it, so the server could deploy by a tag but not create or assign one — tags only worked if a human had set them up in the UI first. That loop is now closed: tag several resources, then deploy them together with a single deploy call.

    One consolidated tool with an action param, matching env_vars and control. action=list with no resource returns the current team's tags (tokens are team-scoped), which is how you discover a name; with resource+uuid it returns that resource's tags. attach creates tags that do not exist yet. Upstream accepts either tag_name or tag_names, and this client always sends the array form so there is one shape to reason about. Tool count 43 → 44.

  • Integration suite for v4.2 method compatibilityv42-compat.integration.test.ts asserts the pre-4.2 behaviour on an older instance and the v4.2 behaviour on a newer one, so the suite is meaningful either way. Every assertion is side-effect free: a rejected method executes nothing, and /deploy with a tag matching no resource proves the method was accepted without deploying anything. /disable is never called, since it would cut off the API the client depends on.

  • Version gating for the log integration tests — database and service log endpoints are v4.2-only, so on an older instance they now report as genuinely skipped rather than failed, with the version printed. Resolved at module scope so jest can skip the suites outright: an early return inside a test still shows a green tick, which is the false confidence these tests exist to prevent. dotenv also runs with override: true, because an empty COOLIFY_URL in the ambient environment otherwise wins over .env and silently skips everything.

  • Tool annotations on all 43 tools, and migration off the deprecated tool() API (#260) — every tool now declares readOnlyHint / destructiveHint / idempotentHint / openWorldHint. These are spec-stable since 2025-03-26 and already change client behaviour: Claude Code parallel-dispatches tools marked read-only, and VS Code Copilot stops raising confirmation dialogs for reads. 21 tools are read-only, 20 destructive, 2 neither (hetzner provisions but only ever adds; validate_server is idempotent).

    Registration moved from the SDK's deprecated tool() overloads to registerTool(), which is also the shape SDK v2 keeps, so this is prep for #259. All 43 call sites go through one defineTool wrapper that looks annotations up from a single table — a tool missing from it throws at construction rather than silently shipping unannotated. The classification lives in one auditable place rather than scattered across 43 call sites, and tests assert the table and the registered tools stay exactly in step.

    Correction to the issue's premise: #260 said annotations were zero-cost because they ride the existing tools/list response. They ride it, but they are not free — spelling out every hint measured at ~751 tokens against a ~6,600 token budget. Only non-default hints are emitted now (per spec: readOnlyHint=false, destructiveHint=true, idempotentHint=false, openWorldHint=true), which halves it to ~415 tokens, with a test that fails if the defaults creep back in.

  • logs tool: container logs for databases and services, not just applications (#300) — diagnose_app could explain an unhealthy application by reading its logs, but the same question about a database or a service had no answer and sent you to the Coolify UI, which is exactly the moment you want logs.

    Added as one consolidated logs tool with a resource parameter (application / database / service), matching the env_vars and control pattern, rather than three or four sibling tools — the token budget is the point of the v2.0.0 design. application_logs still works and is marked superseded in its description; it will be removed in v3. Also adds show_timestamps, which upstream has always supported and this client never sent.

    A service is a multi-container stack, so Coolify requires sub_service_name on /services/{uuid}/logs and "the service logs" has no single answer. The service tool gains a list_containers action returning the applications and databases inside a service, and logs refuses a service request without a container, pointing at that action rather than silently picking one.

Fixed

  • The v4.2 GET-to-POST fallback never fired on pre-4.2 instances (#296 follow-up) — a regression shipped in 2.15.0 that broke system enable_api / disable_api and validate_server on exactly the Coolify versions the fallback existed to support.

    postWithLegacyGetFallback retried with GET only on a 405. Coolify ends routes/api.php with a catch-all — Route::any('/{any}', ...) returning 404 "Not found." — which swallows an unmatched method+path before Laravel can raise a 405. So a pre-4.2 instance answers POST /enable with 404, the fallback never triggered, and the call failed outright where it had worked before #296.

    Found by running the new compatibility suite against a real Coolify 4.1.2, not by re-reading the spec: the route archaeology behind #296 was correct about which methods each version registers, and wrong about what a rejected method actually returns.

    The fallback now treats a 405, or a 404 carrying the catch-all's signature, as a method rejection. Matching on the body shape rather than on status alone matters: the catch-all returns {message: "Not found.", docs: ...} and no controller 404 carries a docs key, so a controller's genuine "resource not found" is recognised as a real answer and never enters the retry path at all. Both cases mean no controller ran, so nothing executed. A 500 still propagates untouched, since it may mean the action partially ran.

    When the GET retry also fails, the error reported is the one from whichever request actually reached a controller — the POST is a routing miss by construction and says nothing about the request, so surfacing its bare "Not found." (complete with an irrelevant uuid hint) would bury the real "Server not found.".

  • Log endpoints returned an object behind a string type (#300) — getApplicationLogs was declared Promise<string> but handed back Coolify's { logs: "..." } envelope unchanged, so any caller doing string work on it would have failed at runtime. Confirmed against a live Coolify instance; the unit tests had mocked a bare string, which is why it went unnoticed. Responses are now unwrapped to a plain string, with bare strings still accepted for older instances.

  • Corrected the documented behaviour of search_docssite/concepts/how-it-works.md described it as "a local MiniSearch index over docs/openapi-chunks/". It actually fetches Coolify's published llms-full.txt at runtime and indexes that; it has never read the chunks.

Changed

  • Re-vendored docs/coolify-openapi.yaml from upstream, now covering Coolify v4.2 (#302) — the bundled spec is ground truth for "does Coolify support X" and predated v4.2, so it was missing 42 paths (nothing was removed). The additions line up with the open v4.2 issues: tags on applications/databases/services (#298), /move between environments (#299), database, service and per-container logs (#300), service application and database management (#301), and destinations (#302). Also new and not previously tracked: volume backup schedules on storages, and DigitalOcean/Vultr server provisioning alongside Hetzner firewalls and networks. Five new schemas: VolumeBackupScheduleRequest, VolumeBackupScheduleResponse, ApplicationSetting, Destination, Tag.

  • docs/openapi-chunks/ is now generated, not hand-maintained (#302) — the chunks are a developer reference that CLAUDE.md and the contributing guide both point at, but they were curated by hand and silently went stale the moment the spec was re-vendored, which made the reference a trap. npm run build:chunks regenerates them from the bundled spec by first path segment, and npm run check:chunk-drift fails CI if they diverge. New destinations-api, tags-api and cloud-providers-api chunks; every path and schema in the spec is carried exactly once, asserted by tests.

  • The vendored spec is byte-identical to upstream again — Prettier had reformatted it at some point, so git diff against a fresh upstream download showed whitespace churn rather than real API changes. docs/coolify-openapi.yaml and the generated chunks are now in .prettierignore, which also means re-vendoring is a clean overwrite.


📦 View on npm

🖱️ One-click Claude Desktop install: download coolify-mcp.mcpb below and drag it into Settings → Extensions.