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 supports 72 actions and counting, covering backups, cluster management, user accounts, and performance monitoring. 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 installation with CMON Controller accessible over the network (CMON controller reachable on port 9501)
  • 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

Tool Annotations

Every tool the MCP Server exposes advertises the standard MCP annotations readOnlyHint and destructiveHint alongside its name and description. Of the 72 tools, 43 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, and the dry-run preview for write operations is likewise a client-side safeguard, not server-side enforcement.

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

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 0.0.0.0:3000
MCP_BASE_URL Public URL that AI clients use to connect http://your-cc-host:3000
MCP_AUTH_TOKEN Bearer token to secure all incoming connections your-strong-random-token

For MCP_BIND_ADDRESS, use 127.0.0.1:3000 if you only need local access, or 0.0.0.0:3000 to allow remote AI clients to connect.

MCP Transport Endpoints

The MCP Server exposes two transports on the same port: /sse and /message is SSE transport, used by Claude Desktop and Claude Code. /mcp is Streamable HTTP transport, used by OpenAI Codex

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 deny list can name — including drop_cluster, remove_node and failover. If create_job is in scope, treat the assistant as able to run any CMON job.

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.

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.

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
MCP_BIND_ADDRESS=0.0.0.0:3000
MCP_BASE_URL=http://your-cc-host:3000
MCP_AUTH_TOKEN=

# 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

Start read-only

MCP_READ_ONLY=1 is the setting to reach for first when pointing an assistant at a production controller. All 43 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

It is strongly recommended to protect your MCP Server with a bearer token, especially when MCP_BIND_ADDRESS is set to 0.0.0.0. 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.

Connecting to AI Clients

Once the MCP Server is running, you need to register it with your AI client. The steps differ slightly depending on which client you use.

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://your-cc-host: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://your-cc-host:3000/sse",
        "--allow-http",
        "--header",
        "Authorization: Bearer your-auth-token"
      ]
    }
  }
}

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

Run the following command in your terminal:

claude mcp add clustercontrol -- cmon-mcp \
  -endpoint https://your-cc-host:9501 \
  -username admin \
  -password your-password

To verify it was added:

claude mcp list

OpenAI Codex

Add the MCP Server to your Codex tool configuration using the /mcp endpoint:

{
  "tools": [
    {
      "type": "mcp",
      "server_label": "clustercontrol",
      "server_url": "http://your-cc-host:3000/mcp",
      "headers": {
        "Authorization": "Bearer your-auth-token"
      },
      "require_approval": "never"
    }
  ]
}

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