Skip to content

Releases

v3.10.1

Choose a tag to compare

@github-actions github-actions released this 12 Nov 15:42
efce1d2

What's Changed

Fixed

  • storages can now create a storage on a service (#478). Coolify's POST /services/{uuid}/storages requires resource_uuid, the service application or database the mount belongs to, and the tool had no way to send it. storages takes resource_uuid and refuses a service create without it, pointing at get_service to find it. Thanks @meditatingsurgeon for the report.

📦 View on npm

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

v3.10.0

Choose a tag to compare

@github-actions github-actions released this 12 Nov 14:57
948222e

What's Changed

Added

  • COOLIFY_MCP_REQUIRE_HUMAN=on makes the stdio server fail closed like HTTP mode (#471). Over stdio, a client without elicitation (including an MCP proxy or agent harness that does not forward it) runs destructive operations on the model's own confirm: true. With the opt-in they are refused unless a human can be asked; read-only tools are unaffected. Unset, stdio behaves exactly as before. The README and docs/security.md now say plainly what the stdio fallback is.

Security

  • Client registration in HTTP mode is bounded and can be restricted. client_name is capped at 200 characters, and a client may register at most 10 redirect URIs of up to 2048 characters each. Before this, one unauthenticated request could persist megabytes. Registration has a limit of 30 an hour across all callers on top of the per-IP one, so a forged X-Forwarded-For no longer gets round it. A registered client nobody approves within a day is removed. Clients from state files written by earlier versions are kept.
  • MCP_REDIRECT_HOSTS restricts non-loopback redirect URIs to the listed hostnames, at registration, on client metadata documents, and on every authorization request, so clients registered before it was set obey it too. Unset, registration stays open.
  • The consent page says where approving sends access (the redirect host, or "an app on this computer" for loopback), not only the name the client gave itself.
  • MCP_CLIENT_IP_HEADER names a header such as cf-connecting-ip for the per-IP limits to key on, for deployments where the first X-Forwarded-For hop is forgeable or is the proxy's own address.
  • Identifiers can no longer change which endpoint a call reaches. Tools passed uuids and names into Coolify request paths as given, so a crafted value could point a call, including one from a read-only tool, at a different endpoint. Every uuid argument is now checked to be a Coolify uuid (letters, digits, -, _), and the client encodes every identifier it puts in a path and refuses one that would still be read as . or ... Nothing changes on tools/list.

📦 View on npm

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

v3.9.0

Choose a tag to compare

@github-actions github-actions released this 02 Oct 15:25
596e645

What's Changed

Added

  • Tool errors point at doctor when it can diagnose them (#384). A failed connection to Coolify and a 401 or 403 now end with "To diagnose, run npx @masonator/coolify-mcp doctor with this server's environment." The wording names the server's environment because in HTTP mode the person reading the error is not the one who configured it. get_version, which reads the plain-text /version outside the usual request path, gets the same pointer on the same two failures. Other errors are unchanged. The unused CoolifyClient#validateConnection, which built its own copy of the connection message, is removed.

Changed

  • Coolify calls no longer follow redirects (#453). An http:// base URL that redirects to https:// used to work and now fails with "Use https://… as the base URL"; any other redirect fails naming its host. doctor fails a redirect to match (it used to warn on ones that were not Cloudflare Access).

Fixed

  • A Cloudflare Access login page is no longer returned as a successful result (#453). Coolify calls followed redirects, so when Access intercepted one, fetch landed on the login page (200, HTML) and the client handed it back as the endpoint's data. Calls now refuse redirects and fail with an error naming what answered: Cloudflare Access (with the service-token fix), an http:// to https:// redirect (with the address to use), or any other host. Access is recognised on its *.cloudflareaccess.com team domain or a custom domain (the /cdn-cgi/access/ path), by one check shared with doctor. Not following redirects also stops our custom headers, the Access service token among them, from going on to whatever host a redirect names, since fetch only strips Authorization; HTTP mode's token check on the authorize page no longer follows them either.
  • list_instances cannot hang on one instance (#454). It fanned out get_version with no deadline, so an instance that accepted the connection and never answered held it for fetch's 300 s headers timeout. It now shares the 10 s listing deadline from #393, and an instance that misses it is reported as "no answer within 10 s", which resources/list now logs too.
  • resources/list no longer waits out a hung instance (#393). Nothing bounded a Coolify call, so in a fleet with one instance that accepts connections but never answers, every listing waited for fetch's own timeout (300 s for a missing response, 10 s for an unroutable host) before returning the others. The listing now gives each instance ten seconds (the same as HTTP mode's token probe), aborts the request at that point, and leaves the instance out as it already did for one that refused the connection; stderr names each instance left out and why. The list* client methods take an optional signal for this. The listing stays uncached on purpose: it is meant to be current.
  • Log lines is a whole number from 1 to 10000 on logs and application_logs (#386). It took any number, and Coolify reads -1 as "every line" from the release that added all, so one call could pull an entire container log into context. Coolify caps the count at 10000 itself, silently, since 4.3.0; the limit is now in the schema where the model can see it, and on 4.0–4.2, which had no cap, it is a deliberate limit of our own. deployment's lines (our own truncation of stored build logs) becomes a positive whole number too: 0 showed an empty window and offered a next page forever. all is deliberately not offered: before the release that added it, Coolify answers it with the default 100 lines and no error. About 30 tokens on the tool list.
  • COOLIFY_INSTANCES entries get the startup checks (#383). The single-instance variables were checked at startup for a doubled /api/v1, an unusable URL, leading whitespace or a line break in the token, line breaks in header values, an unexpanded ${VAR}, and an internal URL with no dashboard URL. A fleet entry was only checked for a scheme and a non-empty token, so the rest failed at that instance's first call. Each entry now gets the same checks, labelled by index and name (COOLIFY_INSTANCES[1] ("staging") url ends with /api/v1), all reported at once. doctor reports them on a fleet-config line of their own, so a bad entry no longer fails the default instance's config check and skips its token probe. Header names that are not valid HTTP header names are caught too; custom header values may contain ${, as a --header flag may.

📦 View on npm

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

v3.8.0

Choose a tag to compare

@github-actions github-actions released this 02 Oct 09:30
efa4c05

What's Changed

Added

  • Rollback (#442, Coolify 4.3+). deployment gains rollback_images (the image tags Coolify can roll an application back to) and deploy gains rollback_to (queue a rollback to one of them; wait: true follows it like any deploy). A tag that is not in a non-empty images list is refused locally with the tags that are; an empty list, or the check itself failing, means Coolify could not inspect the server, so it does not block (only a 404, meaning no rollback on this Coolify, stops it). rollback_images returns at most 20 images and counts the rest. Like deploy, it asks for no confirmation: a rollback is a deploy of an earlier image and is undone by deploying again. On an older Coolify, the 404 now says rollback needs 4.3.
  • coolify_url: dashboard links built, not guessed (#342). Takes a resource type (application, database, service, deployment, server, project, environment, private key) and a uuid, and returns the page in the Coolify dashboard, using the web routes (stable since v4.0.0). Application, database, service and deployment pages need the project and environment uuids, which the API does not put on the resource, so they are looked up once per instance from the project list and cached; a miss rebuilds the index once. get_application offers it as an "Open in Coolify" action. New optional COOLIFY_UI_URL (or ui_url on a COOLIFY_INSTANCES entry) sets the dashboard address when COOLIFY_BASE_URL is internal, as on a server in the Coolify docker network. When the base URL is internal (a single-label host such as coolify) and COOLIFY_UI_URL is unset, the link says it will not open in a browser and the startup check (and so doctor) warns; a COOLIFY_UI_URL that is not a URL fails the startup check. One project the token cannot read no longer breaks every link: its resources miss, and the error names it. 46 tools; the tool list goes from about 8,700 to about 8,900 tokens.

Changed

  • Vendored Coolify OpenAPI spec re-synced with upstream main (#416): 275 → 287 operations. 18 are new (preview update and logs, database imports, SQLite databases, per-server registries, application secret managers, email settings, integration tokens, PATCH /team) and 6 are gone (the server transfer routes). Every route the client calls is still present, and none of them gained a required field.
  • storages host_path is documented as ending at Coolify 4.3.21. Upstream removed host paths in 4.3.22, and the storage endpoints now reject the field with 422. It is kept for older instances; the field's description and docs/tools.md say where it stops.
  • Cache hints on 2026-07-28 list results (#337). Without them the SDK sent ttlMs: 0, telling a client to re-list on every use. The tool, prompt and template lists are fixed for the life of the process and now say five minutes (the revision is stateless, so the TTL is the only bound on a stale list after an upgrade); resources/list names live applications and says a minute; resources/read stays 0. All private. 2025-era responses are unchanged. The interop test now asserts both eras against one HTTP server, including that a pinned 2026-07-28 client gets through server/discover, and a new test pins the tool order as stable.
  • MCP SDK 2.2.0 (#448). Bug fixes only for this server: no stray unhandled rejection when a notification is sent on a closed connection, and a subscriptions/listen stream that honours none of the requested types now ends after its acknowledgement instead of staying open.

📦 View on npm

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

v3.7.0

Choose a tag to compare

@github-actions github-actions released this 30 Sep 11:56
38c44f1

What's Changed

Added

  • deploy accepts pr (#425) to redeploy an existing pull-request preview, the other half of preview support after #434. It needs an application uuid; a tag or name with pr is refused before Coolify is called, with a pointer to list_applications. wait: true follows the preview deployment. Coolify only redeploys a preview its GitHub webhook already created: with none for that PR it answers "Pull request N not found for this resource." and queues nothing, and the tool passes that answer through.

Changed

  • Unknown argument keys are dropped out loud (#438). Every tool used to strip a key its schema did not declare, silently. That is how #434 happened: an application update carrying only an unsupported flag became an empty body ("Invalid request"), and one carrying it next to a known field returned 200 with nothing changed. The schemas are now loose, so the key survives parsing; it is dropped before any handler can forward it to Coolify, and the result gains a note naming it ("Note: ignored foo, which application does not accept, so it was not sent."). Item keys in arrays of objects are checked too, so a typo such as is_build_time in an env_vars data entry is named rather than lost (upstream's bulk endpoint ignores it and answers 201). An application update with nothing left to send now answers "Error: nothing to update" instead of making the empty PATCH, matching database and service. The audit log records the call as sent. Wire-visible: every tool's inputSchema now carries "additionalProperties": {}, which costs about 290 tokens on tools/list, so the published figure moves from ~8,300 to ~8,700.

📦 View on npm

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

v3.6.0

Choose a tag to compare

@github-actions github-actions released this 30 Sep 10:27
9bebf82

What's Changed

Added

  • Desktop extension icon. manifest.json now declares icon.png (512 x 512, the site's ferry mark drawn as paths) so the .mcpb shows an icon in Claude Desktop and meets the Connectors Directory submission form (#406). The bundle also stops shipping evals/, which .mcpbignore never excluded.
  • application accepts Coolify's preview settings (#434, reported by @KasperHonore): is_preview_deployments_enabled, is_pr_deployments_public_enabled and preview_url_template, on update and on every create_* action. Coolify creates each app with previews off, and until now no tool could turn them on. The schema stripped the unknown keys, so an update carrying only the flag sent Coolify an empty body ("Invalid request"), and one carrying the flag alongside a known field returned 200 with previews still off. The first two fields need Coolify 4.2, preview_url_template 4.3.

Security

  • Lockfile bumped past three brace-expansion advisories (#439). npm audit fix moves the transitive brace-expansion from 5.0.9 to 5.0.12 (high: CPU and stack-exhaustion DoS, GHSA-q2hr-2g5m-vwhr, GHSA-qhr7-859c-m2p7, GHSA-6j4f-fj2g-mc7p). Dev tooling only; the server never expands untrusted brace patterns.

📦 View on npm

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

v3.5.1

Choose a tag to compare

@github-actions github-actions released this 16 Sep 16:29
a32b0a1

What's Changed

Added

  • Outcome-scored task evals (evals/src/tasks, npm run evals:tasks). The tool-selection score counts a hit when an expected tool name appears anywhere in the transcript, so a model that calls many tools scores well: a 3B model reached 15/15 while restarting services on a read request (evals/FINDINGS.md #7). The new suite runs 16 multi-step requests and passes one only when the exact request landed or the exact arguments were sent, the answer carries the fact that was asked for, and nothing else was written, including after a declined confirmation. An ambiguous request ("restart my app") must be answered with a question, and reaching for a bulk restart fails it even when the declined confirmation stops the call. It also counts schema-invalid arguments and invented ids, repeats cases with EVALS_TRIALS, and is not part of npm run evals, so CI cost is unchanged.

Fixed

  • HTTP mode outside the container no longer dies on the first client registration (#417, reported by @artgas1). The OAuth state file defaults to /data/oauth-state.json, a directory that only exists in the image. Run coolify-mcp-http on a workstation without MCP_OAUTH_STATE_FILE and the server started, answered /healthz, accepted the first POST /register with a 201, then exited 1 about 250ms later when the debounced state write threw from a timer. A plain SIGTERM did the same through the shutdown flush. The server now checks at startup that the state file's directory exists or can be created and is writable, and lists the failure with the other reasons it cannot start, naming the path and how to set one; the probe is a real write of the temp file the provider uses, so a stale .tmp left by another user is caught too. A write that fails later, say a volume that goes read-only under a running server, logs one line on the way in and one on the way out, reports "persistence": "degraded" on /healthz in between, and the server keeps serving from memory instead of ending.
  • The eval fixture reported no deployments for any app. The client pages /deployments/applications/{uuid} and reads a { count, deployments } envelope; the fixture matched only the bare path and returned an array, so deployment list_for_app came back empty in every eval.

Upgrading from 3.5

  • Container deployments are unaffected. The image's /data volume passes the new check.
  • One exception: a bind mount the daemon created. ./data:/data on a
    host where Docker made the directory root-owned never worked either, but it
    used to start, go green and die at the first registration. It now refuses to
    start and names the directory. Fix the ownership (the image runs as node,
    uid 1000) rather than the config.
  • A write that fails after boot is visible in two places: one stderr line
    on the transition, and /healthz gains "persistence": "degraded" until a
    write succeeds again. Anything that restarts on health can now see it before
    the restart drops every registration and token.
  • Running coolify-mcp-http outside a container? Set MCP_OAUTH_STATE_FILE
    to a path your user can write, e.g. ./oauth-state.json. Until now that
    omission crashed the server after its first registration; it is now a
    startup error that says so.

📦 View on npm

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

v3.5.0

Choose a tag to compare

@github-actions github-actions released this 14 Sep 16:18

What's Changed

Added

  • MCP_HOST sets the HTTP listen address (#418, @artgas1). HTTP mode has always listened on every interface. That is right in a container and wrong on a workstation, where the server answered on whatever network the machine was attached to. Unset keeps today's behaviour, so existing deployments are untouched; MCP_HOST=127.0.0.1 keeps it on loopback. When set, the startup line names the bound address, and an unexpanded ${VAR} in it is a startup error like the other HTTP variables.

Changed

  • search_docs covers the restructured Coolify docs (#420). Coolify reorganised its documentation, and the bundled index grew from 271 pages to 845, including the full API endpoint reference and the CLI. Results now link to current paths rather than to URLs that only redirect.

Upgrading from 3.4

  • Drop-in. No configuration changes. HTTP mode still listens on every
    interface unless you set MCP_HOST, so container deployments behave exactly
    as before.
  • Running coolify-mcp-http outside a container? Set MCP_HOST=127.0.0.1
    so the server stops answering on every network your machine joins.

📦 View on npm

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

v3.4.0

Choose a tag to compare

@github-actions github-actions released this 11 Sep 11:32
c593eae

What's Changed

Added

  • Destructive confirmation works on protocol revision 2026-07-28 (#341). That revision removed server-initiated requests mid-call, so the elicitation prompt this server has always used simply stopped working: every guarded operation on a current claude.ai client aborted with could not confirm with the user, and no setting recovered it. Confirmation is now a two round-trip exchange on that era, unchanged on the 2025 era, chosen per call from the request itself. The summary you were shown is digested into signed state that the client echoes back, so an approval only authorises the blast radius it described — an emergency stop confirmed at 12 applications is refused rather than widened if 14 are running by the time you answer. Ten-minute expiry, fails closed, MCP_REQUEST_STATE_KEY to survive restarts. Blast-radius summaries now render in a stable order, so a reordering by Coolify cannot read as a changed blast radius and refuse an approval that was legitimately given.

Fixed

  • The audit log no longer reports a failed confirmation as a human decline (#408). The refusal category was recovered by matching a substring of the error text, so a timeout, a cancelled call or a client that could not be asked all recorded as declined. On the current protocol revision that was every destructive refusal, meaning the log answered "did someone approve this?" wrongly in exactly the case it exists for. The branch that knows why now says so, and cancelled, unavailable and stale_confirmation are distinguishable from a person saying no.
  • A raised confirmation is no longer audited as a successful destructive call (#341). On the two round-trip era one guarded operation writes two lines, and the first is a question. It recorded as ok, so anything counting successful destructive operations doubled every one. It now records as awaiting_confirmation, which is worth counting in its own right.
  • HTTP audit lines carry the OAuth client id they always claimed to (#408). It was read from authInfo on the handler context when the SDK hangs it off http.authInfo, so the field was silently absent from every line this server has ever written in HTTP mode.

Upgrading from 3.3

  • Fixes a total outage of destructive operations on current clients. If
    your client speaks protocol revision 2026-07-28 — claude.ai does today —
    every guarded operation on the remote server was aborting with could not confirm with the user, and no setting recovered it. This release is the fix.
    Nothing changes for clients on the 2025 revisions, including the local stdio
    server, which was never affected.
  • Drop-in. No required configuration. MCP_REQUEST_STATE_KEY is optional
    and only matters if you run more than one replica, or would rather a redeploy
    did not interrupt someone mid-confirmation.
  • The audit log gained an outcome. awaiting_confirmation marks the first
    of the two lines a guarded operation now writes on that revision. Anything
    counting successful destructive calls should skip it, and anything counting
    refusals should know that declined now means a person actually said no.
  • Blast-radius summaries are sorted. The same resources always render in
    the same order. If you diff these prompts, expect a one-off reshuffle.

📦 View on npm

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

v3.3.0

Choose a tag to compare

@github-actions github-actions released this 11 Sep 10:13
25586b3

What's Changed

Added

  • Move a resource between environments (#299). application, database and service take a move action, taking the target environment_uuid. "Promote this app from staging to production" now has an answer that is one call instead of recreating the resource and copying env vars across. Requires Coolify v4.2+; an older instance is told so by name rather than returning a bare 404. The confirmation says what a move actually does: containers keep running, and from the next deployment the resource uses the target environment's shared environment variables.
  • Scheduled volume backups (#305). storages gains backup_set, backup_delete and backup_run for application, database and service volumes (Coolify v4.2+). backup_delete removes the archives as well as the schedule, so it asks first, and points at backup_set with enabled: false for the "stop backing up but keep what I have" case. Note two upstream limits: Coolify has no endpoint to read a schedule back, so find_issues still cannot flag a volume with no backup; and backup_set replaces the whole schedule rather than merging, so omitted fields revert to their defaults.
  • COOLIFY_ACCESS_TOKEN_FILE: rotate the Coolify token without restarting (#398). A stdio server is spawned once per client session and a subprocess never sees a later change to its parent's environment, so rotating COOLIFY_ACCESS_TOKEN could not reach a running server. Point the new variable at a file and it is re-read whenever the file changes, taking effect on the next tool call. A 401 triggers exactly one retry, and only when a re-read actually produced a different token, so a rotation landing mid-call recovers while a genuinely bad token still fails on the first attempt. doctor reports the source, and for a file its path and age, never the value. Raised by a user who retired this server partly for this reason.
  • Audit log for every tool call and every refusal (#370). One JSON line per call carrying the tool, action, resource uuids, outcome, duration, and in HTTP mode the OAuth client id. On by default in HTTP mode, off over stdio, COOLIFY_MCP_AUDIT overrides either way. Refusals are distinguished from errors and carry a reason category, so "a human said no" reads differently from "the client could not be asked". Arguments and responses are never logged: identifiers come from a closed allowlist of key names, so a future argument carrying a secret cannot reach a log line without a deliberate edit. See the audit section in docs/http-mode.md.
  • Privacy policy (PRIVACY.md). Written against the code, stating what the server collects (nothing), where the token goes (your Coolify instance and nowhere else), the one other outbound request it makes, and what the audit log does and does not record. Linked from the README and declared in manifest.json, both of which the Connectors Directory requires of a local connector.
  • A display title on every tool (#406). Tools now carry the spec's top-level title alongside name, so a client can show a person "Emergency stop" where the model reads stop_all_apps. Titles live in a table typed Record<ToolName, string>, so a new tool without one is a compile error rather than a blank label. The Connectors Directory requires a title on every submitted tool.

Changed

  • HTTP mode's audit line now reports what happened rather than what was asked. It previously peeked at the request body before dispatch, which could only name the tool; it is now written after the call completes and carries the outcome and duration.
  • The Client ID Metadata Document fetch is bounded at 3 seconds (#340), rather than inheriting the 10 second default. Claude allows 10 seconds for the whole of discovery, registration and token exchange, so one hop cannot be allowed to spend all of it. The client_id is a URL the caller chooses, so a host that connects and then stalls is entirely under their control.
  • The published tool-list token figure is corrected from ~6,600 to ~8,300 (#406). The surface grew through 3.x and the README did not follow, because the budget gate sat ~1,400 tokens above the number the docs advertised and so never tripped. The gate no longer holds a number of its own: it reads the figure out of the README and derives its ceiling from it, bounded in both directions so neither the payload nor the published claim can drift away from the other. Titles account for ~300 of the increase; the rest was already there and unreported.

Fixed

  • A loopback client can pick its own callback port (#340). redirect_uri was matched by exact string equality, so a native client that registers http://127.0.0.1/callback and then calls back on the ephemeral port it actually bound was rejected mid-redirect with a generic invalid_request. RFC 8252 requires the port to be free at request time for loopback redirects, and it now is. The relaxation applies only to loopback: a remote https callback differing by port is still a different endpoint and is still refused.

  • README answers "why not Coolify's own MCP server?" directly, with a comparison against the built-in /mcp and the official CLI, and recommends the built-in outright for single-instance read-only use.

  • Test fixtures, docs examples and one README example prompt now use IANA-reserved example.com names instead of live third-party domains, and the blast-radius example reads the same way in the changelog as it does in docs/security.md. No behaviour change.

Upgrading from 3.2

  • Drop-in for stdio users. No config changes and no new required
    variables. The tool count is unchanged at 45; three existing tools gained
    actions rather than new tools appearing.
  • HTTP mode now writes an audit line per tool call by default. It is one
    JSON line on stderr carrying the outcome, and it replaces the older
    pre-dispatch line that could only name the tool. Anything parsing on the
    audit key still matches, but the timing and the field set changed. Set
    COOLIFY_MCP_AUDIT=false to turn it off.
  • move and the volume backup actions need Coolify v4.2+. On an older
    instance they name the version requirement rather than returning a bare 404. Everything else works from v4.0.
  • Tools now carry a display title. Clients that show one will start
    showing it; clients that do not are unaffected. This is why the published
    tool-list figure moved.

📦 View on npm

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