coolify-mcp
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@coolify-mcpsearch all instances for api-gateway"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
coolify-mcp
Fleet-level MCP server for Coolify — multiple instances and teams from one connection, full REST API coverage, MIT.
One MCP server that talks to every Coolify instance and every Coolify team you have, from one connection. Search across the fleet in a single call, read logs and environment variables, deploy, and reach all 189 catalogued REST operations through a generic door.
Quickstart (60 seconds)
Two environment variables and one command.
export COOLIFY_BASE_URL=https://coolify.example.com
export COOLIFY_API_TOKEN='13|xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
npx coolify-mcp installPowerShell:
$env:COOLIFY_BASE_URL = "https://coolify.example.com"
$env:COOLIFY_API_TOKEN = "13|xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
npx coolify-mcp installGet the token from Coolify → Keys & Tokens → API tokens. install detects
the MCP clients on the machine, shows you a unified diff of every file it would
touch, and writes only after you approve.
Then verify:
npx coolify-mcp check # is the instance reachable, and does the token work?
npx coolify-mcp doctor # config health, and a scan for credentials at restOne thing to know before you start
The installer writes pointer config only — never a credential. The entry it
writes into your client config is npx -y coolify-mcp@latest and, at most, a
connection name. It never writes COOLIFY_BASE_URL or COOLIFY_API_TOKEN
into a file.
That means the two variables have to reach the server some other way:
Client kind | How the token arrives |
CLI clients (Claude Code, Codex, Kimi, OpenCode) | They inherit your shell environment. Export the two variables in |
Editors (Cursor, Zed) | Inherit the environment of the process that launched them — which on macOS is often not your shell. Add the variables to the entry's |
Claude Desktop | Does not inherit the shell environment at all. Use a registry file with |
npx coolify-mcp doctor tells you which of these applies to your machine and
whether the token actually resolves.
Related MCP server: coolify-mcp
Why this exists
There are already two good ways to reach Coolify from an MCP client. This is an honest account of both, and of the one axis where neither competes.
Coolify's built-in |
| coolify-mcp (this project) | |
Where it runs | Inside your Coolify instance | Local process | Local process |
Tools | 10 | ~42, hand-curated | 15 by default (16 with destructive enabled) + 189-operation catalog |
Writes | No — all 10 are read-only | Yes | Yes, behind flags |
Instances per process | 1 | 1 | N |
Teams reachable | The token's team | The token's team | M — one connection per (instance, team) |
Cross-instance search | No | No | Yes — |
API coverage | Curated subset | Curated subset | Full published surface via |
Install | Nothing to install | npm | npm, with an installer for 8 clients |
License | Coolify's | MIT | MIT |
Coolify's own /mcp endpoint is the lowest-friction option by a mile:
nothing to install, nothing to configure, and it lives where the data is. It is
read-only and scoped to the token's team. Upstream
PR #11000 would expand it
considerably; if it lands and you run one instance and one team, you may not
need this project at all.
@masonator/coolify-mcp is the established third-party server and genuinely good — around 42 curated, hand-shaped tools, popular for the right reasons. Its model is one instance per process. Running three instances means running three servers, and the model still cannot search across them in one call.
Our axis is FLEET. N instances × M teams from one connection, cross-instance
search, full catalog coverage. If you run one Coolify with one team, the
built-in endpoint or Mason's server may serve you better and with less setup.
If you are looking at four boxes and cannot remember which one api-gateway
lives on, that is what this is for.
We are not competing on tool count. See docs/tools.md for why the default surface is 15 tools and not 42.
Install per client
Every snippet below is the byte-exact output of npx coolify-mcp install into an
empty config file. Nothing here is paraphrased, and keeping it that way is a
required check for anyone adding a client adapter — see
CONTRIBUTING.
Prefer the installer. It merges into existing configs without disturbing other
MCP servers, preserves comments in JSONC files, refuses to write into a config
it cannot parse, and can be reversed with npx coolify-mcp uninstall.
Claude Code — ~/.claude.json or <project>/.mcp.json
npx coolify-mcp install --client claude-code{
"mcpServers": {
"coolify": {
"type": "stdio",
"command": "npx",
"args": [
"-y",
"coolify-mcp@latest"
],
"env": {}
}
}
}Claude Code expands ${VAR} and ${VAR:-default} inside env, so you can add
"COOLIFY_API_TOKEN": "${COOLIFY_API_TOKEN}" yourself.
Details →
Cursor — ~/.cursor/mcp.json or <project>/.cursor/mcp.json
npx coolify-mcp install --client cursor{
"mcpServers": {
"coolify": {
"command": "npx",
"args": [
"-y",
"coolify-mcp@latest"
],
"env": {}
}
}
}Cursor's reference syntax is ${env:NAME}, not ${NAME}. A Claude-style
reference is stored verbatim and reaches the server as literal text.
Details →
Codex CLI — ~/.codex/config.toml
npx coolify-mcp install --client codex[mcp_servers.coolify]
command = "npx"
args = [ "-y", "coolify-mcp@latest" ]
startup_timeout_sec = 60Always stdio, never a url key — older Codex versions drop the entire
config.toml on an unknown key, taking every other MCP server in it with them.
Details →
Kimi CLI — ~/.kimi/mcp.json
npx coolify-mcp install --client kimi{
"mcpServers": {
"coolify": {
"command": "npx",
"args": [
"-y",
"coolify-mcp@latest"
],
"env": {}
}
}
}Zed — settings.json
npx coolify-mcp install --client zed{
"context_servers": {
"coolify": {
"command": "npx",
"args": [
"-y",
"coolify-mcp@latest"
],
"env": {}
}
}
}The key is context_servers, not mcpServers and not agent_servers. The file
is JSONC — the installer edits it through a JSONC writer so your comments
survive. Details →
OpenCode — opencode.json
npx coolify-mcp install --client opencode --scope project{
"mcp": {
"coolify": {
"type": "local",
"command": [
"npx",
"-y",
"coolify-mcp@latest"
],
"enabled": true,
"environment": {}
}
}
}Every field is spelled differently here: command is an array that includes the
executable, and the env key is environment.
Details →
Claude Desktop — claude_desktop_config.json
npx coolify-mcp install --client claude-desktop{
"mcpServers": {
"coolify": {
"command": "npx",
"args": [
"-y",
"coolify-mcp@latest"
],
"env": {}
}
}
}On Windows the installer emits "command": "cmd" with
"args": ["/c", "npx", "-y", "coolify-mcp@latest"], because Claude Desktop
spawns without a shell and npx.cmd cannot be executed directly. Claude Desktop
also does not inherit your shell environment — see
details →.
MiniMax CLI — ~/.minimax/config.yaml — UNVERIFIED, print only
npx coolify-mcp install --client minimax --printThe key MiniMax uses for MCP servers is not known, and coolify-mcp will not
guess it: a wrong top-level key in that file would corrupt your model
configuration. The adapter is marked confidence: 'unverified', which makes
apply() refuse to write. --print shows both plausible shapes plus the
procedure for settling which is correct.
Details →
Multiple instances and teams
A connection is (baseUrl, token). That is the whole model.
A Coolify API token is bound in the database to the team that was active when it
was created, and the REST API has no team-switch parameter — no X-Team-Id
header, no ?team= query. Another team's UUIDs simply return 404. So a second
team is a second token, which is a second connection, pointing at the same
baseUrl. N connections covers N instances × M teams uniformly, and no separate
"team" concept is needed anywhere in the server.
COOLIFY_BASE_URL_PROD=https://coolify.example.com
COOLIFY_API_TOKEN_PROD=13|...
COOLIFY_BASE_URL_ACME=https://coolify.acme.dev
COOLIFY_API_TOKEN_ACME=41|... # acme, team "platform"
COOLIFY_BASE_URL_ACME_OPS=https://coolify.acme.dev
COOLIFY_API_TOKEN_ACME_OPS=42|... # same instance, team "ops"
COOLIFY_CONNECTION=prod # the default for read toolsWith more than one connection every tool grows an instance parameter, typed as
an enum over the configured names. Reads may default to the designated
connection; writes and destructive operations require an explicit instance
with no default — guessing wrong on a read costs one wasted call, guessing
wrong on a deploy costs a production incident.
find_resources alone accepts instance: "*", which queries every connection in
parallel and tags each row with the instance it came from. One unreachable box
does not take the answer down: failures land in meta.errors[] beside the rows
that did come back.
Configuration
Three layers, in increasing order of ceremony. Full reference: docs/connections.md.
Layer 0 — two variables, no file. The 90% case.
COOLIFY_BASE_URL=https://coolify.example.com
COOLIFY_API_TOKEN=13|...A single connection named default is born. The instance parameter is absent
from every tool schema.
Layer 1 — named connections, still no file.
COOLIFY_BASE_URL_<NAME> defines a connection called <name>
COOLIFY_API_TOKEN_<NAME> supplies its token by convention
COOLIFY_CONNECTION picks the defaultCOOLIFY_BASE_URL_ACME_OPS defines the connection acme-ops: the suffix is
lowercased and _ is read as -.
Layer 2 — a registry file. First hit wins, wholesale; there is no merging across locations.
$COOLIFY_MCP_CONFIG(an explicit path that does not exist is an error, not a fallthrough)the nearest
.coolify-mcp.json, walking up from the working directory and stopping at a repo root or$HOME$XDG_CONFIG_HOME/coolify-mcp/config.json(on Windows,%APPDATA%\coolify-mcp\config.json)~/.coolify-mcp/config.json
{
"$schema": "https://raw.githubusercontent.com/devrim-1283/coolify-mcp/main/schema/config.v1.json",
"version": 1,
"defaultConnection": "prod",
"connections": {
"prod": { "baseUrl": "https://coolify.example.com", "readOnly": true },
"acme": { "baseUrl": "https://coolify.acme.dev",
"tokenCommand": ["op", "read", "op://Infra/coolify-acme/credential"] },
"acme-ops": { "baseUrl": "https://coolify.acme.dev",
"tokenKeychain": { "service": "coolify-mcp", "account": "acme-ops" } }
}
}acme and acme-ops are the same instance and two different teams. That is all
multi-team is.
Precedence
Setting | Rule |
A connection name defined in both env and file | Env replaces the file entry WHOLE. No field-level merge. |
| Env wins. |
| Tightens only. It can close a connection the file left open; it can never re-open one the file closed. |
| The file value wins for that connection when present, otherwise the env default applies. Both must admit the call for the destructive tool to be registered at all. |
| Exactly one level. A name in both replaces the base entry whole. |
No default and several connections | There is no default. Every tool requires an explicit |
Other variables: COOLIFY_TIMEOUT_MS (1000–120000, default 30000),
COOLIFY_INSECURE_TLS, COOLIFY_LOG_LEVEL (error/warn/info/debug).
Secrets
Do this.
Keep the token in an environment variable, a secret manager, or the OS keychain.
Use
tokenCommandforop/pass/vault/gopass— it is a shell-free argv array, so any command that prints the token works.Create the Coolify token with the narrowest ability set that does the job.
read+read:sensitivecovers everything read-only.Prefer
readOnly: trueon connections you do not need to write to.Run
npx coolify-mcp doctoron any machine that has ever had an MCP client on it.
Never do this.
Never paste a token into a client config file.
~/.claude.json,mcp.jsonandconfig.tomlare synced, backed up, screen-shared and pasted into bug reports.Never put a token in a registry file. The schema has no
tokenproperty andadditionalProperties: false, so it is a validation error, not a discouraged option — and the error message names the three supported sources.Never embed credentials in
baseUrl(https://user:pass@hostis rejected).Never commit a
.coolify-mcp.jsoncontaining anything but pointers.
Run doctor.
npx coolify-mcp doctor # your entry only
npx coolify-mcp doctor --all-servers # every MCP server in every config
npx coolify-mcp doctor --json # for CIDoctor scans every client config for the Laravel Sanctum token shape
(<id>|<40 chars>), literal Bearer headers, credential-shaped env literals and
POSIX modes that let other users read the file. Exit code 2 means a credential
is sitting at rest. --fix is deliberately narrow: it rewrites a literal to
${VAR} only when $VAR already holds exactly that value and the client is
known to expand the reference, it never prints the value, and it always tells
you to rotate anyway. Full details: docs/secrets.md.
Tools
15 tools by default. 11 when the server is read-only, 16 when destructive
operations are enabled. Everything else in Coolify's API is behind
search_operations → describe_operation → execute_*.
Tool | Class | Coolify ability |
| read |
|
| read |
|
| read |
|
| read |
|
| read |
|
| read |
|
| read |
|
| read |
|
| read | none — local catalog |
| read | none — local catalog |
| read |
|
| write |
|
| write |
|
| write |
|
| write |
|
| destructive |
|
control_resource carries destructiveHint: false on purpose: stopping a
resource causes downtime but destroys no data and is undone by the start in the
same enum. The destructive flag is reserved for operations that cannot be undone
through the API. The reasoning is spelled out in
docs/tools.md, along with every parameter of every tool.
Enabling destructive operations
execute_destructive_operation is not registered unless
COOLIFY_ALLOW_DESTRUCTIVE=true — it does not appear in tools/list at all,
rather than appearing and refusing. With it off, no tool on the server carries
destructiveHint: true, so a host that auto-approves non-destructive tools is
auto-approving something genuinely non-destructive.
The gate is enforced three times: at registration (what exists), at dispatch (what each door admits) and in the HTTP client before the socket opens (what may leave the process). The last one cannot be routed around by any tool.
Enterprise
Version pinning. By default the installer writes coolify-mcp@latest, so
every client spawn resolves the newest published version and may execute code
that did not exist when you installed it. Convenient for one developer,
out of policy at most companies:
npx coolify-mcp install --all-detected --pin # pin the version you are running now
npx coolify-mcp install --all-detected --pin=1.4.2 # pin an exact versionA pinned install upgrades only when you run install again. doctor reports
unpinned-version when it finds @latest, and pinned-version-mismatch when
two clients on one machine disagree about which version to run.
Read-only connections. readOnly: true on a connection — or
COOLIFY_READ_ONLY=true process-wide — refuses every non-GET at the HTTP client,
regardless of what the token is scoped to. When every connection is read-only,
the write and destructive tools are never registered, so the model is not holding
tools it cannot use. Read-only is a ceiling: no per-connection setting can widen
it.
npm provenance. Releases are published from GitHub Actions with
npm publish --provenance over OIDC, gated behind a manual approval environment,
from a tag matching ^v[0-9]+\.[0-9]+\.[0-9]+$. Every workflow action is pinned
to a full commit SHA. The provenance attestation is verifiable on npm.
What doctor --json gives a platform team. A stable, machine-readable
report with kebab-case finding codes suitable for matching in a pipeline:
{
"runtime": { "node": "v22.11.0", "packageVersion": "0.1.0", "platform": "darwin", "cwd": "…" },
"connections": [{ "name": "prod", "baseUrl": "…", "tokenSource": "$COOLIFY_API_TOKEN_PROD (by convention, set)",
"resolved": true, "wellFormed": true, "readOnly": true,
"allowDestructive": false, "shadowsFile": false }],
"clients": [{ "adapterId": "claude-code-user", "path": "…", "configExists": true,
"entryPresent": true, "packageSpec": "coolify-mcp@1.4.2",
"confidence": "verified", "unscannedEntries": 3 }],
"findings": [{ "code": "plaintext-credential", "severity": "critical",
"file": "…", "keyPath": ["mcpServers","other","env","API_TOKEN"],
"remediation": "ROTATE THIS TOKEN NOW. …" }]
}Exit codes: 0 clean, 1 warnings, 2 a credential was found at rest. Findings
never contain a secret value — not the value, not a prefix, not a length — and
everything that leaves the module passes through redact(). Run
coolify-mcp doctor --all-servers --json fleet-wide and alert on
severity == "critical".
Security posture we recommend as the default: a read-scoped Coolify token
(read + read:sensitive) plus readOnly: true, and a second, explicitly named
connection for the writes you actually need. See SECURITY.md
for the threat model.
Limitations
This section is here so you find out now rather than at 2am.
Pagination is ours, and it is not stable under concurrent mutation. Coolify
does not paginate: /applications, /databases, /services, /resources and
/deployments each return the complete array (on an instance with 80
applications, /applications exceeds 1 MB). So we fetch the whole list, filter,
sort and slice it in-process. meta.next_cursor is an opaque offset plus a hash
of the filters it was produced under — replaying a cursor against different
filters is refused, but a resource created or deleted between page 1 and page 2
will shift the rows. This is offset pagination over a re-fetched list, not a
snapshot. The one exception is
GET /deployments/applications/{uuid}?skip&take, which upstream really does
paginate, and which list_deployments uses when given an application_uuid.
Things Coolify's API cannot do, so neither can we. No command execution
(Coolify's web terminal is a websocket, not REST). No notification API. No S3
storage CRUD. No healthcheck endpoint — healthchecks are roughly fifteen fields
on PATCH /applications/{uuid}. No shared environment variables. No team writes.
We will not ship a tool that pretends otherwise.
The catalog is generated from a pinned spec. It is built at release time from
Coolify 4.2.0 (spec sha 150e9f3c…), covering 189 operations — 74 read,
86 write, 29 destructive, 3 flagged as provisioning billable cloud
infrastructure. An instance newer than the catalog may expose operations we do
not list; search_operations says so explicitly when nothing matches, naming the
spec version and date. CI fails the build when npm run codegen produces a diff,
so upstream drift is loud rather than silent.
Generic body validation is deliberately permissive. Coolify's OpenAPI
document is generated from PHP attributes and is wrong in places
(upstream #7702). A generic
executor that hard-validated against it would reject valid calls with no way to
override. So execute_write_operation passes body through unchanged and lets
Coolify's own per-field validation errors come back verbatim. Path parameters
are validated strictly — those come from the URL template, which cannot be
wrong.
The rate-limit bucket is per Coolify user, not per token. Five tokens
belonging to one account share one allowance (documented default 200/min, read
from X-RateLimit-Limit because self-hosters change it). Fleet fan-out across
connections owned by the same user draws on the same bucket. The client retries a
429 exactly once, and only when Retry-After is under 30 seconds.
Coolify silently drops around 22 fields on PATCH. removeUnnecessaryFields
strips them server-side. That is a no-op, not an error, and there is nothing we
can do about it other than tell you.
deploy(wait: true) polls, and the queue response shape is not fully
verified. POST /v1/deploy's body has not been confirmed against a live
instance; the handler reads the documented { deployments: [...] } shape,
tolerates the containers Coolify uses elsewhere, and falls back to matching on
the application's deployment history when it cannot find a deployment_uuid.
The fallback is timestamp-based and therefore racy under concurrent deploys.
insecureTLS needs an optional package. Node's fetch has no per-request
TLS switch; the only hook is undici's dispatcher, and undici is not a dependency
(no native modules, fast cold start). Without undici present in your tree the
flag warns on stderr and requests stay verified. Prefer
NODE_EXTRA_CA_CERTS=/path/to/ca.pem, which fixes the trust chain instead of
switching verification off.
Client paths that are not fully confirmed. Zed on Windows (derived from
%APPDATA%), the OpenCode global config location, and the Claude Desktop Linux
path (there is no official Linux build). Each is flagged by doctor with a
*-path-unconfirmed finding when the file is absent. MiniMax is unverified
end-to-end and is print-only. See docs/clients/.
Commands
npx coolify-mcp install [--client a,b] [--scope user|project] [--connection NAME]
[--pin[=VERSION]] [--transport stdio|http] [--no-native-cli]
[--update] [--dry-run] [--print] [--yes] [--json]
npx coolify-mcp doctor [--all-servers] [--fix] [--json]
npx coolify-mcp uninstall [--client a,b] [--all] [--scope user|project] [--force]
[--dry-run] [--yes] [--json]
npx coolify-mcp connections [--json]
npx coolify-mcp check [--connection NAME] [--json]coolify-mcp <command> --help for per-command options.
Documentation
Connections, instances, teams, the registry file, precedence | |
Token sources, doctor, rotation | |
Every tool, every parameter, the danger gate, response shaping | |
One page per client: exact file, exact snippet, what to verify | |
Threat model and disclosure | |
Dev loop, and how to add a client adapter |
Requirements
Node.js ≥ 20.10. No native dependencies, ever — that is what keeps
npx coolify-mcp working everywhere. Runtime dependencies are
@modelcontextprotocol/sdk, zod, smol-toml, jsonc-parser and yaml.
License
MIT © Devrim Tuncer. Not affiliated with or endorsed by Coollabs or the Coolify project.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/devrim-1283/coolify-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server