Skip to content

ClusterControl MCP

The ClusterControl MCP Server is a bridge between your AI assistant and your databases. You type what you need in plain English or other languages, and it figures out the rest. No dashboards to click through, no commands to memorize. It works with AI tools like Claude Desktop, Claude Code, and OpenAI Codex, and offers 89 tools, 21 resources and 13 guided procedures, covering backups, cluster management, user accounts, performance monitoring, advisors that report findings, a digest of what changed across your fleet, guided recovery, and an account of what an agent asked to do. It works with MySQL, MariaDB, PostgreSQL, MongoDB, SQL Server, and TimescaleDB.

How It Works

The diagram below shows how everything connects. Your AI tool sits at the top. When you type a request, it goes through the MCP Server, which translates it into the right action and passes it to the CMON Controller. The controller then talks to whichever database cluster you are working with.

ClusterControl MCP Topology

Before you get started, make sure you have the following in place:

  • A running ClusterControl 2.3.1 or later installation with CMON Controller accessible over the network (CMON controller reachable on port 9501). See Controller Version
  • A CMON user account with access to the clusters you want to query
  • An MCP-compatible AI client such as Claude Desktop, Claude Code, or OpenAI Codex

Once you have confirmed all the requirements above are in place, you are ready to install and configure the ClusterControl MCP Server. Packages are published to the Severalnines repository alongside other ClusterControl components.

sudo yum install clustercontrol-mcp
sudo apt-get install clustercontrol-mcp

The binary installs to /usr/bin/cmon-mcp. A systemd service unit and default configuration file are also installed:

  • /etc/systemd/system/cmon-mcp.service : service unit
  • /etc/default/cmon-mcp : environment file for credentials and transport options
  • /etc/cmon-mcp/governed.env : written by a fresh install; turns the audit log on and names the digest key
  • /etc/cmon-mcp/digest.key : the digest key a fresh install generates (mode 0600)
  • /usr/share/cmon-mcp/profiles/ : the two deployment profiles
  • /usr/share/cmon-mcp/docs : the documentation bundle search_docs reads
  • /etc/logrotate.d/cmon-mcp : rotation for the audit log

Packages are built for x86_64 only.

Tool Annotations

Every tool the MCP Server exposes advertises the standard MCP annotations readOnlyHint and destructiveHint alongside its name and description. Of the 89 tools, 60 are pure reads, 26 change state on the CMON Controller, and 3 write files on the local filesystem of the MCP Server host; 22 tools carry destructiveHint: true. The annotations are generated at tool registration from a committed, human-reviewed manifest, so the hints a client sees cannot drift from what was reviewed.

MCP clients can use these annotations to auto-approve read-only tools and to require explicit confirmation before state-changing or destructive ones. Keep in mind that annotations are advisory metadata for the client: the server does not enforce them. On its own, the dry-run preview for write operations is a client-side safeguard too, but the server can enforce it: with the execution guard on, a destructive call runs only against a confirmation minted on a preview of the same call, and the approval tier can require an approval as well. See Confirmation and Approval.

If you need the server itself to refuse an action rather than rely on the client, see Access Control below.

Configuration

The ClusterControl MCP Server is configured through the /etc/default/cmon-mcp file on your CMON host. Open the file and edit the values to match your environment:

sudo vi /etc/default/cmon-mcp

Where Settings Come From

Every setting can be given as a command-line flag or as an environment variable. On a server host, the environment comes from two files that systemd loads in order.

Source Used when Notes
Command-line flags, for example -endpoint or -read-only An AI client starts the server itself (stdio), for example claude mcp add clustercontrol -- cmon-mcp … A flag wins over the same setting in the environment. A password passed as a flag is briefly visible in the process list, so prefer CMON_PASSWORD or, better, CMON_KEY_FILE.
Environment variables in the client's configuration An AI client starts the server itself, for example the env block in claude_desktop_config.json The same names as on a server host.
/etc/default/cmon-mcp The cmon-mcp systemd service Your file. systemd loads it second, so a setting here wins over the same setting in governed.env. An upgrade does not overwrite it.
/etc/cmon-mcp/governed.env The cmon-mcp systemd service Written by a fresh install: it turns the audit log on and names the generated digest key. Loaded first, so /etc/default/cmon-mcp can override it, for example with MCP_AUDIT_LOG=off.
A shipped deployment profile MCP_PROFILE or -profile names one Read in place from /usr/share/cmon-mcp/profiles/, beneath every source above: a flag wins, then the environment, then the profile, then the built-in default.
Files a setting points to When you name them The identity map, the approver map, the digest key, an operator lifecycle table and the documentation bundle.

The audit log, the execution guard, the approval mode, the approval window and the approver settings have no flag: set them in the environment.

Two Ways to Run It

On the client (stdio) As a service (HTTP)
Who starts the server Each AI client starts its own process systemd, once, on a host that can reach the CMON Controller
Where settings live Flags or the client's env block /etc/default/cmon-mcp
Turned on by MCP_BIND_ADDRESS unset MCP_BIND_ADDRESS set, for example 127.0.0.1:3000
ClusterControl identity The one user in its settings One user, or one per caller with an identity map
Needed for A single user on their own machine Team access, per-agent identities, approvals attributed to a user, the agentic-operate profile, independent approval

Configuration Parameters

CMON Controller Connection

Parameter Description Example
CMON_ENDPOINT The URL of your CMON Controller API https://127.0.0.1:9501
CMON_USERNAME The ClusterControl username admin
CMON_PASSWORD Password for the user (leave empty if using key auth) severalNINES999
CMON_KEY_FILE Path to RSA private key (alternative to password) /etc/clustercontrol/id_rsa

You can authenticate using either a password or an RSA key file, not both at the same time. If CMON_PASSWORD is set, leave CMON_KEY_FILE empty, and vice versa.

The CMON user you specify here is the identity the MCP Server uses for every action it performs. We recommend creating a dedicated CMON user with only the privileges your team actually needs and please avoid reusing your main admin account.

MCP Server

Parameter Description Example
MCP_BIND_ADDRESS Address and port the MCP Server listens on 127.0.0.1:3000
MCP_BASE_URL URL advertised to SSE clients, when they connect through a proxy https://cc.example.com
MCP_AUTH_TOKEN Bearer token to secure all incoming connections your-strong-random-token
MCP_TLS_BIND_ADDRESS Address and port of the optional TLS listener (1.0.0-246 or later) 0.0.0.0:3443
MCP_TLS_CERT_FILE PEM certificate (chain) for the TLS listener /etc/cmon-mcp/tls.crt
MCP_TLS_KEY_FILE PEM private key for the TLS listener; not readable by group or others /etc/cmon-mcp/tls.key

The listener on MCP_BIND_ADDRESS speaks plain HTTP. Keep it at 127.0.0.1:3000 and let remote AI clients reach the server through an SSH tunnel, the optional TLS listener, or a TLS reverse proxy, as Reaching the Server Securely describes. 0.0.0.0:3000 is only for a network you trust end to end.

The three MCP_TLS_* settings are set together or not at all, and only with MCP_BIND_ADDRESS: the TLS listener is added beside the plain one, which keeps serving unchanged. Because the TLS listener is meant to be reached from other hosts, it also needs authentication: MCP_AUTH_TOKEN or an identity map. The server refuses to start (exit code 78, naming the setting) when only some are set, when neither a token nor an identity map is configured, when a file is missing or unreadable, when the certificate and key don't match, when the key file is readable by group or others or not owned by the service's user, or when MCP_TLS_BIND_ADDRESS overlaps MCP_BIND_ADDRESS or MCP_APPROVAL_BIND_ADDRESS. TLS 1.2 is the minimum. To rotate the certificate, replace the files and restart the service.

If you are moving an existing server to 127.0.0.1:3000 and its configuration still sets MCP_BASE_URL to the host's address, remove that line, unless you set it for a TLS reverse proxy. Otherwise SSE clients are sent a /message address outside the SSH tunnel and cannot finish connecting. Unset, MCP_BASE_URL defaults to http:// plus MCP_BIND_ADDRESS, which is the tunnel address.

MCP Transport Endpoints

The MCP Server exposes two transports on the same port: /mcp is the Streamable HTTP transport, used by Claude Code and OpenAI Codex. /sse and /message are the older SSE transport, used by Claude Desktop, and are not served when an identity map is active.

Per-Caller Identity

Parameter Description Example
MCP_IDENTITY_MAP_FILE Maps each caller's bearer token to its own ClusterControl user (also -identity-map-file); HTTP only /etc/cmon-mcp/identity-map.yaml

By default every session acts as the one CMON user configured above, so the audit log cannot tell two callers apart. An identity map binds each token to its own ClusterControl user. The file must be mode 0600, and an unmapped token is refused before anything runs; it never falls back to the configured user.

identities:
  - token_label: agent-a            # names this entry in errors and logs
    token_hash: sha256:<64 hex>     # or token: <a strong random token>
    cmon_username: agent-a          # what every audit record names
    cmon_password: <password>       # or cmon_key_file: /path/to/key

An identity map changes the HTTP endpoints

With a map active, MCP_AUTH_TOKEN is ignored (each token authenticates on its own) and /sse and /message are not served. Clients that can only use SSE cannot be used with an identity map; connect them to /mcp instead.

Access Control

These settings are enforced by the server, not by the AI client. Unlike tool annotations and dry_run previews, a refusal here cannot be overridden by the client.

Parameter Description Example
MCP_READ_ONLY Refuses all 29 state-changing tools for the lifetime of the process 1
MCP_TOOL_ALLOW Comma-separated list of the only tools to expose list_clusters,list_alarms
MCP_TOOL_DENY Comma-separated list of tools to refuse; wins over MCP_TOOL_ALLOW create_job,restore_backup

Read-only mode both hides the write tools from the client's tool list and refuses a direct call on one, so a client that calls a tool it was never shown is still refused. Scoping works the same way, and the resource surfaces follow their tools automatically — a surface is served only when every tool it maps to is in scope.

A name that matches no tool stops the server

An unknown entry in either list is a startup error naming every offender, and the service exits non-zero. This is deliberate: a silently ignored deny entry would leave a tool you meant to remove fully exposed.

Prefer MCP_TOOL_ALLOW when the requirement is only these

A deny list names what to remove, so a tool added in a later release is exposed by default. An allow list names what to keep, so a new tool is out of scope until you add it.

Read the startup warnings. Scoping is by tool name, and a tool name is not a data class. Several tools read the same data from the CMON Controller, so denying one can leave a sibling serving the same rows. The server names every such case at startup as tool scope: WARNING: …. If you denied a tool for a security reason, that line tells you whether you actually removed the data.

One case deserves singling out: create_job takes a free-form command, which reaches CMON job commands that have no tool of their own and that no tool deny list can name — including drop_cluster, remove_node and failover. Limit them with the job command policy below. Without one, if create_job is in scope, treat the assistant as able to run any CMON job.

Job Command Policy

Parameter Description Example
MCP_JOB_COMMAND_ALLOW Comma-separated CMON job commands that create_job and the dedicated job tools may submit; unset permits every one verify_backup,stage_replication_slave
MCP_JOB_COMMAND_DENY Comma-separated job commands to refuse; wins over the allow list remove_cluster

A command the policy does not permit is refused with JOB_COMMAND_NOT_PERMITTED before any preview, confirmation, approval request or controller request, and is checked again just before submission. A tool that submits one fixed command (delete_backup, restore_backup, generate_error_report, create_backup_schedule, update_backup_schedule) is hidden when its command is not permitted. A name that is not a known job command stops the server at startup. The policy is logged at every start as job command policy: …, and get_session_info reports it.

The policy governs job submission, not effects

set_controller_config, set_node_config, unset_node_config and add_maintenance_window reach some of the same effects as job commands through other requests. Scope those tools with MCP_TOOL_ALLOW or MCP_TOOL_DENY as well; the server names each such case at startup.

Independently of the policy, create_job checks a job before anyone is asked to approve it: the identity needs read and execute access on the cluster, job_data must be one JSON object with unique keys, and replica rebuilds, upgrades, PostgreSQL promotions and point-in-time recovery are held to the shapes the release notes describe.

Confirmation and Approval

Parameter Description Example
MCP_EXECUTION_GUARD enforced: a destructive call runs only against a single-use confirmation minted on a preview of the same call. Unset or off: off enforced
MCP_HUMAN_APPROVAL elicitation: every destructive execution is put to the calling session for approval. out_of_band: a separately authenticated approver decides. Unset or off: off elicitation
MCP_APPROVAL_BIND_ADDRESS For out_of_band: the separate loopback address approvers answer on 127.0.0.1:8089
MCP_APPROVER_MAP_FILE For out_of_band: the operator-owned approver list, with cmon_username and token_sha256 entries /etc/cmon-mcp/approvers.yaml
MCP_APPROVAL_TTL The approval window for both modes, a duration from 1m to 30m. Unset: 2m. Anything else stops the server at startup 10m
MCP_GALERA_BOOTSTRAP_REQUIRE_OOB true: a Galera bootstrap is refused under any approval mode but out_of_band, even with the guard off (also -galera-bootstrap-require-oob). Unset: false true

With the guard on, a refused call gets CONFIRMATION_REQUIRED; preview it first and present the confirmation it returns. Confirmations are single-use and held in memory, so a restart revokes every outstanding one. A confirmation that admitted a call stays valid for as long as the call can wait for its approval.

MCP_HUMAN_APPROVAL accepts on, enforced, 1 and true as older spellings of elicitation. Any other value stops the server at startup.

  • elicitation sends the approval request to the calling session. That is a person's decision only if the client shows the request to a person the agent cannot answer for; so far the official MCP Python SDK with an elicitation_callback is the only client shown approving. It needs an identity map: without per-session identity a destructive call is refused with HUMAN_APPROVAL_UNAVAILABLE.
  • out_of_band waits for an approver whose ClusterControl user is not the requester's. Approver tokens must differ from every caller's, and a request expires when the approval window, MCP_APPROVAL_TTL, runs out. The window is measured from the request, so a late decision gets only what is left. It requires the identity map, the digest key and the audit log, and the server does not start without them. Approvers decide with cmon-mcp approve.

Point-in-time recovery always needs an approval model: with MCP_HUMAN_APPROVAL off it is refused with PITR_APPROVAL_MODEL_REQUIRED. Either mode proves who answered, not that a person read the request.

A Galera bootstrap, which sop_galera_recovery submits, also needs an approval model, and its approval asks the approver to confirm, for each node by name, that mysqld is shut down on that host. Under elicitation the requester's own client answers those confirmations, so the server cannot tell whether a person checked any host. Set MCP_GALERA_BOOTSTRAP_REQUIRE_OOB=true to require out_of_band for it.

Governed mode needs a digest key

Turning on the audit log, the execution guard or the approval tier puts the server in a governed mode, and a governed server needs the digest key. A fresh install creates /etc/cmon-mcp/digest.key; on a host that was upgraded rather than installed, write a key and set MCP_DIGEST_HMAC_KEY_FILE before you restart.

Approving from the Command Line

Under out_of_band, an approver decides with the cmon-mcp binary itself:

cmon-mcp approve list             # one row per request pending for you
cmon-mcp approve show <id>        # that request's whole envelope
cmon-mcp approve approve <id>     # approve it
cmon-mcp approve decline <id>     # decline it

list shows each request with an 8-character id, and the other commands take 4 or more characters of it. Read a request with show before you decide: list shows only the first line of its summary, and the lines the server adds, such as the primary a promotion stops and the standby it promotes, appear only in show. A Galera bootstrap's approval takes --confirm-shutdown <host:port> once for each node the request names; give each one only after checking that host yourself.

The command reads three settings and nothing else, and opens no listener:

Parameter Description
MCP_APPROVAL_BIND_ADDRESS The server's loopback approval listener, for example 127.0.0.1:8089
MCP_APPROVER_TOKEN_FILE A file holding the approver's token: a regular file owned by the user running the command, mode 0600
MCP_APPROVER_TOKEN The token itself. Set this or the file, not both; there is no token flag, so the token never appears in a command line

Give each approver their own token, never one an MCP caller holds, and add its SHA-256 to the approver map. Restart the server after adding an entry:

umask 077
openssl rand -hex 32 > "$HOME/.cmon-mcp-approver.token"
printf %s "$(cat "$HOME/.cmon-mcp-approver.token")" | sha256sum   # the token_sha256 for the map
version: 1
approvers:
- cmon_username: bob          # the approver's ClusterControl username
  token_sha256: <the 64 hex characters printed above>

From another machine, keep the listener on loopback and forward a port to it over SSH:

ssh -N -L 127.0.0.1:18089:127.0.0.1:8089 operator@mcp-host &
MCP_APPROVAL_BIND_ADDRESS=127.0.0.1:18089 \
  MCP_APPROVER_TOKEN_FILE="$HOME/.cmon-mcp-approver.token" cmon-mcp approve list

A decision records the approval; it does not run the job. The server still checks the call again, and spends the approval once, before anything reaches the controller.

Client Timeouts

Under out_of_band, the agent's tool call stays open until a decision arrives or MCP_APPROVAL_TTL runs out, and it sends no progress messages while it waits. If the client gives up first, the call is cancelled, the request is withdrawn, and a later approve is refused. So the client's own timeout for this server must be longer than the approval window:

  • Claude Code times out a request to an HTTP or SSE server after 60 seconds by default. Set the server's timeout in .mcp.json, in milliseconds, above MCP_APPROVAL_TTL, or MCP_TOOL_TIMEOUT for every server. A server's own timeout overrides MCP_TOOL_TIMEOUT. The Claude Code plugin sets timeout to 1860000, above the longest window the server accepts (30 minutes), so it needs no tuning.
  • Codex CLI: set tool_timeout_sec in [mcp_servers.<name>] above MCP_APPROVAL_TTL; the default is 60. The Codex plugin sets 1860.
  • Other clients often default to 60 seconds per request. Where the timeout cannot be raised, keep MCP_APPROVAL_TTL within it and have approvers ready before the call is made.
  • A reverse proxy in front of /mcp needs a read timeout above MCP_APPROVAL_TTL too.

Auditing

Parameter Description Example
MCP_AUDIT_LOG Path to the JSONL audit log; off disables auditing /var/log/cmon-mcp/audit.jsonl
MCP_DIGEST_HMAC_KEY_FILE Key file the argument digests are computed under /etc/cmon-mcp/digest.key

Every tool call and resource read is recorded twice — an intent record before the action runs and a completion record after, correlated by call_id. Background tasks add a third, acceptance, correlated by task_id. Two records rather than one, because a single record written at the end cannot be told apart from an action that never happened, and the actions that matter most are the ones that did not return.

Raw arguments are never written. What is recorded is a keyed digest of them, so that someone who can read the log cannot work backwards to a low-entropy argument such as a password.

A fresh install is audited by default; an upgrade changes nothing

A first install provisions /etc/cmon-mcp/digest.key and /etc/cmon-mcp/governed.env, and that second file switches auditing on — so a newly installed host records from its first call. An upgrade enables nothing, because a host that has never been audited has no key, and a server that is told to audit without one refuses to start. To turn auditing off on a fresh install, set MCP_AUDIT_LOG=off in /etc/default/cmon-mcp, which is read after the package's file and wins.

Auditing is fail-closed

If the intent record cannot be written, the call is refused with AUDIT_UNAVAILABLE and the action never runs. This is the intended behaviour: an unauditable server does not act.

It also means the log's permissions matter. The server refuses any log file whose mode is wider than 0600, on every record, so a rotation policy that recreated it world-readable would refuse every later call. The shipped /etc/logrotate.d/cmon-mcp already handles this correctly — do not loosen its create 0600 root root line.

To find failed actions you need both fields, because they mean different things:

jq 'select(.outcome=="error" or .tool_is_error==true)' /var/log/cmon-mcp/audit.jsonl

outcome covers transport and task-level failure; tool_is_error covers a tool that ran and returned an error. The absence of tool_is_error is not evidence that the tool succeeded.

Agent Activity

Parameter Description Example
MCP_ACTIVITY_OPERATOR_IDENTITIES Comma-separated ClusterControl users who may summarize another identity's activity, or everyone's (also -activity-operator-identities). Unset: nobody alice,bob

summarize_agent_activity answers "what did the agent ask to do?" from the audit log above, with no controller request. Given an interval, it counts unique calls, reads, previews and execution requests, their outcomes, the refusals recorded, and the independent approval decisions. An execution request includes refused attempts and does not show that the controller changed anything.

  • Each identity sees only its own records. Only the users named in MCP_ACTIVITY_OPERATOR_IDENTITIES can ask for another identity or all, and no ClusterControl admin role implies it. Name them exactly; wildcards, duplicates and surrounding spaces stop the server at startup.
  • It never decodes arguments, digests, keys or tokens, and it reads only the configured log, never a path a caller supplies. With auditing off it answers ACTIVITY_UNAVAILABLE.
  • It reads the first 16 MiB or 100,000 records of the log, and always marks the summary partial: nothing proves that earlier rotations were kept, so its counts are never claimed as a complete day.

Neither deployment profile allows summarize_agent_activity; add it to your allow list deliberately.

File Exports

Parameter Description Example
MCP_EXPORT_DIR The one directory local file writes are confined to; off refuses them all /srv/cc-exports

Three tools write to the MCP Server host's own filesystem: download_error_report, export_cluster_log and unpack_error_report. Each takes a destination path from the client, and the service runs as root, so without a boundary, granting an assistant export_cluster_log alone is not meaningfully different from granting it root write access to the host. Every path the client supplies is resolved inside this directory and may not leave it.

The default is /var/lib/cmon-mcp/exports, created at mode 0700 on start.

This can change behaviour on an existing host

The boundary is on by default, so a server whose configuration you never touched will begin refusing paths such as output_path=/tmp/report.tar.gz that previously worked. Point MCP_EXPORT_DIR at the directory those callers already use to keep their existing paths valid.

The boundary is only as strong as the directory's own permissions. If it is a symlink, group- or world-writable, or owned by another user, the server says so at startup as export dir: WARNING: … rather than refusing — a link onto a larger volume is a legitimate thing to want.

Parameter Description Example
MCP_DOCS_DIR Installed ClusterControl documentation bundle used by search_docs /usr/share/cmon-mcp/docs

The package ships a documentation bundle and the search_docs tool answers from it locally, with no network calls, so it works in an air-gapped installation. The bundle is read once at startup; if it cannot be loaded, only search_docs is disabled and the reason is written to the journal, leaving every other tool working. Restart the service after repairing or upgrading it.

Advisors

Parameter Description Example
MCP_SUPPORT_ENTITLEMENT Your support entitlement, which check_version_eol judges end of life against: mysql= premier, extended or sustaining; mariadb= community, enterprise or extended; and cluster:<id>= overrides. Unset: each engine's free community stream mysql=extended
MCP_EOL_TABLE_FILE Your own release lifecycle table instead of the bundled one, in the same format, for example to get newer end-of-life dates on an air-gapped host between packages /etc/cmon-mcp/eol.yaml

The bundled lifecycle table is never fetched from the network. A table you name that cannot be read or parsed stops the server at startup, and the refusal never quotes a value from the file.

Complete Configuration Example

The following shows the ClusterControl MCP Server example configuration after it has been set up through the /etc/default/cmon-mcp file on your CMON host.

# CMON Controller connection
CMON_ENDPOINT=https://127.0.0.1:9501
CMON_USERNAME=admin
CMON_PASSWORD='severalNINES999'
# RSA key auth alternative (leave CMON_PASSWORD empty if using this):
# CMON_KEY_FILE=/etc/clustercontrol/id_rsa

# MCP Server: loopback only; clients connect through an SSH tunnel or a TLS reverse proxy
MCP_BIND_ADDRESS=127.0.0.1:3000
MCP_AUTH_TOKEN=
# Only for SSE clients (Claude Desktop) behind a TLS reverse proxy:
# MCP_BASE_URL=https://cc.example.com
# Optional TLS listener beside the plain one (all three or none; key mode 0600;
# needs MCP_AUTH_TOKEN or an identity map):
# MCP_TLS_BIND_ADDRESS=0.0.0.0:3443
# MCP_TLS_CERT_FILE=/etc/cmon-mcp/tls.crt
# MCP_TLS_KEY_FILE=/etc/cmon-mcp/tls.key

# Or start an agent from a shipped deployment profile, read in place:
# MCP_PROFILE=agentic-observe

# Access control — enforced by the server, not the AI client.
# Start here if you are connecting an assistant to a production controller.
MCP_READ_ONLY=1
# MCP_TOOL_ALLOW=list_clusters,get_cluster,list_alarms,search_docs
# MCP_TOOL_DENY=create_job

# Auditing — set automatically on a fresh install; shown here for reference.
# MCP_AUDIT_LOG=/var/log/cmon-mcp/audit.jsonl
# MCP_DIGEST_HMAC_KEY_FILE=/etc/cmon-mcp/digest.key

# File exports — defaults to /var/lib/cmon-mcp/exports. Set this if callers
# already rely on a different directory.
# MCP_EXPORT_DIR=/srv/cc-exports

# Per-caller identity (HTTP only) — one ClusterControl user per token.
# MCP_IDENTITY_MAP_FILE=/etc/cmon-mcp/identity-map.yaml

# Job command policy — the job commands create_job may submit.
# MCP_JOB_COMMAND_ALLOW=verify_backup,stage_replication_slave

# Confirmation and approval for destructive calls.
# MCP_EXECUTION_GUARD=enforced
# MCP_HUMAN_APPROVAL=elicitation
# Or independent approval:
# MCP_HUMAN_APPROVAL=out_of_band
# MCP_APPROVAL_BIND_ADDRESS=127.0.0.1:8089
# MCP_APPROVER_MAP_FILE=/etc/cmon-mcp/approvers.yaml
# The approval window, 1m to 30m (default 2m); raise client timeouts above it.
# MCP_APPROVAL_TTL=10m

Start read-only

MCP_READ_ONLY=1 is the setting to reach for first when pointing an assistant at a production controller. All 60 read tools keep working, so the assistant can still investigate, explain and report — it simply cannot change anything. Turn it off deliberately, once you know which write actions you actually want to allow.

Generating a Secure Token

Protect your MCP Server with a bearer token whenever AI clients connect to it from another machine. The Claude Code and Codex plugins require one. Generate one with:

openssl rand -hex 32

Copy the output and set it as the value of MCP_AUTH_TOKEN.

Starting the Service

Save the configuration file, then restart the service:

systemctl restart cmon-mcp

Confirm it started successfully:

journalctl -u cmon-mcp -n 20

You should see output confirming the MCP Server is running and listening on the address defined in MCP_BIND_ADDRESS.

A malformed value in any setting stops the server at startup with exit code 78 and names the setting, and systemd does not keep restarting it, so nothing runs half-configured. Each setting logs its effective value at startup, so the journal shows what took effect. From a client, get_session_info shows the session's transport, its ClusterControl identity, the job command policy, the deployment profile the server started from, the controller's version, and whether an approval request can reach the client.

Controller Version

Every tool works against ClusterControl 2.3.1 and later. The server reads the controller's version at startup with one unauthenticated ping, which needs no login, and again every 15 minutes, or every minute while the version is unknown. The check takes at most 5 seconds and never delays or stops startup. The journal gets one line when the version or its status changes:

controller version 2.5.0.25177: at or above the floor 2.3.1
controller version 2.3.0.13840 is BELOW the floor 2.3.1: these tools will fail: list_publications (needs 2.3.1: /v2/logical_replication getpublications), list_subscriptions (needs 2.3.1: /v2/logical_replication getsubscriptions)
controller version unknown (<reason>): the floor 2.3.1 was not checked

A controller below the floor is a warning, never a refusal: the server starts, and a tool the controller cannot serve fails when it is called. get_session_info reports the last answer as controller_version.

Common Setups

You want Configure
One person on their own machine stdio: the connection settings in the client's configuration. Add MCP_READ_ONLY=1 if it should only read.
A team sharing one server The service with MCP_BIND_ADDRESS and an identity map, so each person acts as their own ClusterControl user.
An agent that only diagnoses and advises MCP_PROFILE=agentic-observe, the read-only deployment profile.
An agent that fixes what a finding names, under approval MCP_PROFILE=agentic-operate, and MCP_HUMAN_APPROVAL=out_of_band with cmon-mcp approve if a second person must approve.
An account of what an agent asked to do Auditing on (the default on a fresh install), summarize_agent_activity in scope, and MCP_ACTIVITY_OPERATOR_IDENTITIES for whoever reviews other identities.
A host without internet access Nothing extra: the lifecycle table and documentation bundle ship in the package. Use MCP_EOL_TABLE_FILE for newer end-of-life dates.

Deployment Profiles

The package ships two ready-made configurations for running the server under an AI agent. Each is a short list of settings that switches on the controls an agent should run with and limits it to the tools its job needs. Start from one rather than writing the configuration yourself.

What a Profile Is

  • Two files in /usr/share/cmon-mcp/profiles/: agentic-observe.env and agentic-operate.env. The package owns them: they are readable by everyone (0644), hold no credentials, and are replaced on every upgrade.
  • Name one with MCP_PROFILE=agentic-observe or MCP_PROFILE=agentic-operate in /etc/default/cmon-mcp (or -profile), and the server reads it in place at every start. An upgrade then brings the release's tool list and job commands with no edit. Your own settings win over the profile's: a flag, then the environment, then the profile, then the built-in default. A key you set wins even when you set it empty, so MCP_READ_ONLY= turns agentic-observe's read-only off.
  • Or copy one: copy its assignments into /etc/default/cmon-mcp and leave MCP_PROFILE unset. The copy is yours, and an upgrade never changes it.
  • Any other MCP_PROFILE value, a path or file name included, stops the server at startup. Do not include a profile file through a systemd drop-in with EnvironmentFile=: it would be read after /etc/default/cmon-mcp and silently override what you later change there.

Which One to Use

Profile The agent can The agent cannot
agentic-observe Run every advisor, the fleet view and the fleet digest, follow a finding's next action to the read it names, and make every read a guided procedure needs Change anything: every write tool is hidden and refused with READ_ONLY_MODE, so a procedure stops at its submit step
agentic-operate Everything agentic-observe can, plus run a guided procedure to its submit step and submit the job through create_job or restore_backup, after a confirmation and an approval Submit a job command its procedures do not use, such as remove_cluster, or use any other write tool

Use agentic-observe for an assistant that diagnoses and advises, with nothing to approve. Use agentic-operate for one that can fix what a finding names, under approval.

What Each Profile Sets

Setting agentic-observe agentic-operate
MCP_READ_ONLY 1 unset
MCP_AUDIT_LOG /var/log/cmon-mcp/audit.jsonl /var/log/cmon-mcp/audit.jsonl
MCP_EXECUTION_GUARD unset (nothing to confirm) enforced
MCP_HUMAN_APPROVAL unset (nothing to approve) elicitation
MCP_EXPORT_DIR unset off
MCP_BIND_ADDRESS, MCP_IDENTITY_MAP_FILE not needed required; the file carries them as commented placeholders
MCP_TOOL_ALLOW 28 tools: every advisor, get_fleet_posture, get_fleet_digest, get_session_info, and the read tools that findings and procedures name the same 28 plus create_job and restore_backup
MCP_JOB_COMMAND_ALLOW unset (create_job is refused anyway) bootstrap_cluster, find_most_advanced, promote_replication_slave, restore_backup, stage_replication_slave, upgrade_cluster, verify_backup

Before You Start

  • Both profiles turn the audit log on, which requires the digest key. A fresh install provisions one; on an upgraded host, write a key and set MCP_DIGEST_HMAC_KEY_FILE before you restart.
  • agentic-operate needs the HTTP transport and an identity map. Without them the server starts but refuses every create_job and restore_backup execution with HUMAN_APPROVAL_UNAVAILABLE (a dry_run preview still works), and says so at startup.
  • Bring an approver client: under elicitation the approval request goes to the agent's own session. For an approval from a separate person, use out_of_band (see Confirmation and Approval).
  • Moving an agent from agentic-observe to agentic-operate: on a host that names the profile, change MCP_PROFILE. On a host that copied it, remove the copied MCP_READ_ONLY line too, or every create_job and restore_backup is refused with READ_ONLY_MODE.

Setting It Up

  1. In /etc/default/cmon-mcp, set MCP_PROFILE=agentic-observe or MCP_PROFILE=agentic-operate, or copy the profile's assignments there instead. For agentic-operate, keep the MCP_BIND_ADDRESS line already in that file and set MCP_IDENTITY_MAP_FILE to your identity map.
  2. Restart: systemctl restart cmon-mcp.
  3. Read the startup lines: journalctl -u cmon-mcp. A named profile logs profile: <name> (shipped with <version>) and, only when you set one of its keys to another value, profile overrides: naming those keys, never their values. The job command policy: line lists the job commands allowed, and each session's get_session_info reports the profile and the same list.

Each profile prints some WARNING: lines at every start, and they are expected. The tool scope: lines name data that an allowed tool still reads for a tool the profile leaves out. Under agentic-observe, the sop prompts: line says no approval is enforced, which is true and harmless: nothing can be submitted there. A warning of any other kind is the one to read.

What You Can Change

Set the key in /etc/default/cmon-mcp, where it wins over a named profile, or change your copy there. Never edit the profile file: the next upgrade replaces it.

Change Effect
Add a tool to MCP_TOOL_ALLOW The agent can call it. A non-destructive write tool you add is gated by neither the execution guard nor the approval tier, so prefer read tools.
Remove a tool The agent can no longer call it. Other allowed tools may still read the same data; the tool scope: warnings at startup name them.
Add a job command to MCP_JOB_COMMAND_ALLOW The agent can submit it through create_job, after its own preview and an approval.
Have a separate person approve Set MCP_HUMAN_APPROVAL=out_of_band with MCP_APPROVAL_BIND_ADDRESS and MCP_APPROVER_MAP_FILE.
Switch the guard or the approval off under agentic-operate Not recommended: destructive calls then run without a confirmation or an approval, and point-in-time recovery is refused until an approval model is set.

After an Upgrade

A host that names its profile with MCP_PROFILE gets the new release's lists at the restart that follows the upgrade, and a key you set yourself still wins.

A copy in /etc/default/cmon-mcp is never overwritten. Compare it with the new profile files after each upgrade: a tool or job command that a release adds stays refused under your copy until you add it, or until you replace the copy with MCP_PROFILE. In 1.0.0-213 both profiles add advise_workload and list_available_upgrades, and agentic-operate adds the restore_backup tool and job command. In 1.0.0-241 both profiles add advise_galera_health, get_fleet_digest and list_alarms, and agentic-operate adds the bootstrap_cluster and find_most_advanced job commands.

diff <(grep -E '^MCP_' /usr/share/cmon-mcp/profiles/agentic-operate.env | sort) \
     <(grep -E '^MCP_' /etc/default/cmon-mcp | sort)

Limits

  • A profile limits tools, not data. Under agentic-observe, advise_workload reads the database user and database names of long-running and idle-in-transaction sessions, and the database names and normalised statement patterns of busy or full-scanning queries. get_fleet_posture and get_fleet_digest run the same advisor inside themselves, and leaving advise_workload out does not filter their findings. If an observing agent should not see this data, set your own MCP_TOOL_ALLOW without all three.
  • A profile governs calls made through the MCP Server only; the ClusterControl GUI, the s9s CLI and other clients are not affected.

Connecting to AI Clients

Once the MCP Server is running, you need to register it with your AI client. For Claude Code and Codex, install the Severalnines plugin; for other clients, add the server to the client's configuration by hand.

Reaching the Server Securely

The listener on MCP_BIND_ADDRESS speaks plain HTTP, so a bearer token sent to it over a network crosses that network unencrypted. Keep it listening on 127.0.0.1:3000 and reach the server in one of three ways:

  • SSH tunnel. On the machine that runs your AI client, open a tunnel to the CMON host and keep it open while you work:

    ssh -N -L 3000:127.0.0.1:3000 <user>@your-cc-host
    

    The client then connects to http://127.0.0.1:3000, and the token travels inside SSH. The examples on this page use this address.

  • Native TLS listener (1.0.0-246 or later). Set MCP_TLS_BIND_ADDRESS, MCP_TLS_CERT_FILE and MCP_TLS_KEY_FILE (see MCP Server), together with MCP_AUTH_TOKEN or an identity map, which the TLS listener requires. The server then also serves /mcp over TLS on that address, with the same authentication, and /sse too unless an identity map is active (SSE is never served with a map), while the plain listener keeps serving SSH-tunnel clients. Clients connect to https://your-cc-host:3443/mcp. A client must trust the certificate: for a private CA, start Claude Code with NODE_EXTRA_CA_CERTS=/path/to/ca.pem, and add the CA to the operating system's trust store for other clients.

  • TLS reverse proxy. Put a proxy with a certificate, such as nginx, on the CMON host in front of 127.0.0.1:3000, and connect to its https:// address instead. Turn response buffering off for /mcp and /sse, and set the proxy's read timeout above MCP_APPROVAL_TTL. For Claude Desktop, also set MCP_BASE_URL to the proxy's address.

Claude Code and Codex Plugins

Severalnines publishes plugins for Claude Code and Codex in the severalnines/ai-plugins marketplace. They connect to the MCP Server running on the CMON host over the /mcp endpoint, so nothing is installed on your machine, and they set a client timeout longer than the longest approval window the server allows (see Client Timeouts).

The plugins do not switch approvals on. Approvals are configured on the server, and they need an identity map, so each person connects with their own token as their own ClusterControl user, plus the settings in Confirmation and Approval. With only the shared MCP_AUTH_TOKEN, the server refuses destructive calls when approvals are on.

In Claude Code, add the marketplace and install the plugin:

/plugin marketplace add severalnines/ai-plugins
/plugin install clustercontrol-mcp@severalnines

Claude Code asks for the MCP URL and the bearer token. The URL defaults to the SSH tunnel address, http://127.0.0.1:3000/mcp; with the native TLS listener or a TLS reverse proxy, enter its https:// address ending in /mcp. The token is kept in your system's credential store, not in a settings file. To change either later, run /plugin configure clustercontrol-mcp@severalnines.

Run /mcp to check that plugin:clustercontrol-mcp:clustercontrol is connected.

Put the bearer token in your environment, for example in your shell profile, then add the marketplace and install the plugin:

export CLUSTERCONTROL_MCP_TOKEN=your-auth-token
codex plugin marketplace add severalnines/ai-plugins
codex plugin add clustercontrol-mcp@severalnines

A Codex plugin cannot ask for a URL, so this one connects to the SSH tunnel address, http://127.0.0.1:3000/mcp. With the native TLS listener or a TLS reverse proxy, define the server in ~/.codex/config.toml as in OpenAI Codex below; an entry named clustercontrol replaces the plugin's.

Claude Desktop

Open your Claude Desktop configuration file:

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows: %APPDATA%\Claude\claude_desktop_config.json

Add the following entry under mcpServers so that it looks like this :

{
  "mcpServers": {
    "clustercontrol": {
      "type": "sse",
      "url": "http://127.0.0.1:3000/sse",
      "headers": {
        "Authorization": "Bearer your-auth-token"
      }
    }
  }
}

Alternatively, you can use mcp-remote with Node.js installed on your environment with the following JSON configuration:

{
  "mcpServers": {
    "clustercontrol": {
      "command": "npx",
      "args": [
        "mcp-remote",
        "http://127.0.0.1:3000/sse",
        "--allow-http",
        "--header",
        "Authorization: Bearer your-auth-token"
      ]
    }
  }
}

With an identity map

When an identity map is active, /sse and /message are not served. Point the mcp-remote form at http://127.0.0.1:3000/mcp with the agent's own token instead.

Save the file and restart Claude Desktop. You should see clustercontrol appear in the File → Settings → developer → Local MCP servers panel like this after restart Claude Desktop.

ClusterControl MCP Claude Desktop

Claude Code

Use the Claude Code plugin. To connect to the MCP Server by hand instead, add it to a project's .mcp.json, using the streamable HTTP endpoint:

{
  "mcpServers": {
    "clustercontrol": {
      "type": "http",
      "url": "http://127.0.0.1:3000/mcp",
      "headers": {
        "Authorization": "Bearer ${CLUSTERCONTROL_MCP_TOKEN}"
      },
      "timeout": 1860000
    }
  }
}

Claude Code expands ${CLUSTERCONTROL_MCP_TOKEN} from your environment, so the token is not written to the file. With an identity map, use the agent's own token. timeout is in milliseconds and must exceed MCP_APPROVAL_TTL under out_of_band approval; see Client Timeouts.

To run the MCP Server on your own machine instead (stdio, with cmon-mcp installed there), let Claude Code start it. Pass the credentials in the environment rather than as flags, which are briefly visible in the process list:

claude mcp add \
  --env CMON_ENDPOINT=https://your-cc-host:9501 \
  --env CMON_USERNAME=admin \
  --env CMON_KEY_FILE=/path/to/id_rsa \
  clustercontrol -- cmon-mcp

To verify it was added:

claude mcp list

OpenAI Codex

Use the Codex plugin. To connect to the MCP Server by hand instead, or to point the plugin at a TLS reverse proxy, add the server to ~/.codex/config.toml, using the /mcp endpoint:

[mcp_servers.clustercontrol]
url = "http://127.0.0.1:3000/mcp"
bearer_token_env_var = "CLUSTERCONTROL_MCP_TOKEN"
tool_timeout_sec = 1860

Codex reads the token from the CLUSTERCONTROL_MCP_TOKEN environment variable. An entry named clustercontrol replaces the plugin's definition as a whole, so give every setting, the timeout included.

Approving tool calls

Whether Codex asks you before it calls a tool is a Codex setting. If you let it call tools without asking, use MCP_READ_ONLY=1 or the agentic-observe profile, or switch on the server's execution guard and approval tier so that destructive calls cannot run unconfirmed.

Verifying the Connection

Once connected, ask your AI assistant the following to confirm everything is working:

List all my database clusters

If the setup is correct, it will return the list of clusters managed by your ClusterControl instance.

ClusterControl MCP Claude Desktop Response