Proof-of-concept MCP servers for Zabbix have been around for a while, and most of them wrap a handful of API methods. This one wraps the whole API - every group, plus a set of extension tools that do the correlation work for you. That is the reason to care about it, and also the reason it needs thinking about before you point it at production. Here is what it does, what it costs in tokens, and how to scope it so an assistant cannot do more than you intended.
Whose project this is. The Zabbix MCP server is built and maintained by initMAX and the community, not by me. It is free and open source under AGPL-3.0 and lives at github.com/initMAX/zabbix-mcp-server. I have installed it, broken it, and made two videos about it - this page is the written version of that, with the numbers that matter pulled to the front.
MCP (Model Context Protocol) is an open standard for letting an AI assistant call external tools. The Zabbix MCP server is a standalone HTTP service that exposes the Zabbix API as those tools, so any MCP-capable client - Claude, Codex, VS Code Copilot, JetBrains AI, Cursor, n8n, a self-hosted model - can talk to Zabbix without you writing a single API call by hand.
| Property | Value |
|---|---|
| Coverage | All 58 Zabbix API groups - 223 API tools plus 14 extension tools, 237 in total |
| Zabbix versions | 7.0 LTS, 7.2 and 7.4 fully covered; 6.x works with gaps; 5.x core only; 8.0 experimental behind skip_version_check |
| Runtime | Python 3.10+ on Linux, systemd service or Docker |
| Licence | AGPL-3.0, no paid tier |
| Default ports | 8080 for the MCP endpoint, 9090 for the admin portal |
The 14 extension tools are the part that is easy to skim past and shouldn't be. Five of
them are pre-correlated views - problem_active_get,
host_status_get, hostgroup_overview_get,
infrastructure_summary_get, item_history_summary_get - which
fold three to five raw API calls into one round trip. That matters more than it sounds:
every extra round trip is another chunk of JSON going through the model, and the model
pays for all of it. The rest are things the API has no method for at all:
graph_render (PNG export), anomaly_detect (z-score),
capacity_forecast (linear regression), item_threshold_search,
report_generate, and zabbix_raw_api_call as an escape hatch for
anything not wrapped.
Two kinds of work get faster. The first is asking questions you would otherwise answer by clicking through the frontend or writing a throwaway API script. The second is configuration, where the assistant chains several calls together to do something you would normally do across four screens.
| What you ask | What happens underneath |
|---|---|
| "List every host in my Zabbix" | host_get |
| "Which hosts are down right now?" | problem_get or host_status_get |
| "Create host db-01 in group Windows servers, IP 10.0.0.5, with the Windows agent template" | hostgroup_get then template_get then host_create |
| "Summarise CPU on the Zabbix server host - is anything wrong?" | host_get, item_get, history_get, plus problem_get in parallel |
| "Put db-01 in maintenance for two hours" | maintenance_create |
| "Find the outliers in CPU usage across this host group" | anomaly_detect over trend data |
The chaining is the interesting bit. Ask for a host in "Windows hosts" when your group is actually called "Windows servers" and a decent client will search, fail to find an exact match, search more broadly, pick the closest, do the work, and then tell you it substituted. That is genuinely useful and it is also exactly why the write path deserves a moment of thought - it is a system that resolves your ambiguity by guessing well.
It does not have to run on the Zabbix server. Any Linux host that can reach the Zabbix frontend will do, because everything goes through the frontend API.
git clone https://github.com/initMAX/zabbix-mcp-server.git
cd zabbix-mcp-server
sudo ./deploy/install.sh
sudo nano /etc/zabbix-mcp/config.toml
sudo systemctl enable --now zabbix-mcp-server
The installer creates a zabbix-mcp system user with no login shell, builds a
virtualenv under /opt/zabbix-mcp, drops a systemd unit and a logrotate rule,
and copies the example config into place. There is a deploy/install-user.sh
variant that needs no root and registers a LaunchAgent on macOS or a systemd user unit on
Linux, which is the right choice for a laptop.
The minimum you have to fill in:
[server]
transport = "http"
host = "127.0.0.1" # 0.0.0.0 only with auth configured - see below
port = 8080
[zabbix.production]
url = "https://zabbix.example.com" # frontend root, nothing deeper
api_token = "your-api-token"
read_only = true
verify_ssl = true
sudo ./deploy/install.sh test-config validates the file and checks that
Zabbix is reachable without restarting anything, which is a better first move than
restarting and reading logs. Then confirm the service is alive before you go anywhere
near a client:
curl http://127.0.0.1:8080/health should return a status of OK. The MCP
endpoint itself is /mcp, and hitting that in a browser will not give you
anything readable - a protocol-level response is the correct answer there.
Two things that will eat your first evening.
The service starts and immediately dies with a permission error on its log
file. Read the journal rather than guessing - the fix is ownership of
/var/log/zabbix-mcp, and it is worth doing properly with
chown zabbix-mcp:zabbix-mcp instead of the chmod 777 that
makes the symptom disappear.
The MCP server calls the Zabbix API, which runs inside PHP on the
frontend. So the request timeouts that actually bind are PHP's, not the ones
in config.toml. If a long query keeps dying at the same two-minute mark
no matter how high you push the MCP timeout, stop tuning the MCP server and go look
at max_execution_time.
This is the step that stops most first installs, and it is a client restriction rather
than a bug in the server. Claude Desktop only accepts a remote MCP connector over
HTTPS, and it rejects self-signed certificates. A perfectly working server on
http://192.168.x.x:8080/mcp simply cannot be added, and neither can the same
server with a certificate you generated yourself.
The reason is worth knowing, because it tells you which workarounds can possibly work. A remote connector is brokered through Anthropic's infrastructure: the request arrives at your MCP server from their servers, not from your laptop. So the certificate has to be verifiable by a public certificate authority - there is no local trust store to add an exception to. That also explains why a tunnel fixes it and why a local CLI client never had the problem in the first place.
| Client | Self-signed certificate | Publicly trusted certificate |
|---|---|---|
| Local CLI and IDE clients (Claude Code, Cursor, VS Code, JetBrains) | Works | Works |
| Remote connectors (Claude Desktop, web clients) | Rejected | Required |
Three ways out, in the order I would try them:
/etc/zabbix-mcp/tls/,
writes tls_cert_file and tls_key_file into the config, and
installs a renewal hook that reloads the service:
sudo ./deploy/install.sh request-tls \
--hostname mcp.example.com --email [email protected]
This did not exist when I recorded the first video, and it removes most of the pain
that video works around.cloudflared
and run cloudflared tunnel --url http://localhost:8080. You get a random
trycloudflare.com hostname with valid TLS in about two seconds, no account
and no payment. This is what I used on camera to get past the restriction, and it is a
demo tool rather than a deployment.Be honest with yourself about the tunnel. A quick tunnel publishes
your MCP endpoint on the public internet. If no MCP token is configured, the server
accepts unauthenticated connections - which is fine on 127.0.0.1 and
distinctly not fine on a public hostname. If you are going to demo it that way, issue
a token first, keep read_only = true, and take the tunnel down when you
are finished.
Here is the number that decides whether this thing is pleasant or painful to use. Every tool the server exposes ships its JSON schema - name, description, twenty to forty optional parameters - to the model at the start of every session. That is roughly 400 to 500 tokens per tool. With all 237 tools enabled, the tool catalog alone costs somewhere around 100,000 tokens before your first question reaches the model.
It is the single largest driver of token usage with this server, far bigger than whether
responses come back compact or extended. The fix is an allowlist in
[server]:
[server]
# Problem triage and host inspection: ~15 tools, ~7k tokens
tools = ["host", "hostgroup", "problem", "trigger", "event", "item"]
# Add templates and dashboards when you need them: ~30 tools, ~15k tokens
# tools = ["host", "hostgroup", "problem", "trigger", "event", "item",
# "template", "dashboard", "maintenance"]
Or pull in whole groups by name when you want breadth:
| Group | Tools | Roughly what it covers |
|---|---|---|
monitoring | 87 | hosts, items, triggers, problems, events, history, trends, graphs, SLA, discovery, plus the five pre-correlated views |
administration | 59 | settings, housekeeping, authentication, maintenance, maps, proxies, autoregistration |
users | 39 | users, user groups, directories, macros, tokens, roles, MFA |
data_collection | 27 | templates, template groups, template dashboards, value maps, dashboards |
alerts | 16 | actions, alerts, media types, scripts |
extensions | 14 | the correlated views, anomaly detection, forecasting, graph rendering, PDF reports, raw API call |
Start narrow. A triage-shaped allowlist of six prefixes covers the overwhelming majority of what anyone actually asks a monitoring assistant, costs about 7% of the full catalog, and leaves the context window free for the conversation. Widen it when a real task needs it, not in advance. The same allowlist mechanism exists per token, so different clients can see different tool sets against the same server.
Compact output mode is worth leaving on as well: get methods return a reduced field set
by default and the model can ask for extend when it genuinely needs
everything. Zabbix API objects are wide, and most of that width is noise to a language
model.
One more lever, if your client is current enough: the newest MCP protocol revision makes
tool listings cacheable, and the server exposes a
tools_list_cache_ttl to go with it. The catalog only changes when the server
restarts, so a client that honours the hint stops re-sending every schema at the start of
every session. Useful, but treat it as a discount on a bill you should shrink first with
the allowlist.
There are two different tokens in play and mixing them up is the most common source of confusion. Keeping them straight is most of the security story:
| Token | Sits between | What it controls |
|---|---|---|
api_token in [zabbix.*] | MCP server and Zabbix | Required. Inherits the permissions of the Zabbix user it belongs to. This is the hard ceiling on everything. |
| MCP token (bearer) | AI client and MCP server | Optional but essential once you bind to anything other than localhost. Carries its own scopes, IP restrictions, server binding, expiry and read-only flag. |
The ceiling is the important idea. No configuration of the MCP server can make an assistant do something the Zabbix API token is not permitted to do. So the first decision is not a TOML setting - it is which Zabbix user that token belongs to. A dedicated user with a role scoped to the host groups in question is a very different risk profile from a Super admin token, and it takes two minutes to create.
The layers, from outermost in:
read_only = true per Zabbix server. On by default, and
worth leaving on for a good while. Every demo that goes wrong on the internet involves
somebody turning this off on day one.user_create exists cannot call it.
There is also a two-step write pattern built in - action_prepare followed by
action_confirm - for cases where you want the model to describe the change
before anything commits. Newer releases add OAuth 2.1 with discovery for clients that
expect it, using the admin portal's own user accounts for login.
Since v1.23 the server ships a web portal on port 9090, and it is the difference between
this being a config-file exercise and something you can hand to a colleague. It is enabled
out of the box; admin_enabled = false in config.toml turns it off
if you would rather it were not there.
The first-time password is printed to the terminal by the installer and nowhere
else. Username admin, generated password, scrolled well up your
shell history. If you have lost it, do not reinstall - run
sudo ./deploy/install.sh set-admin-password and follow the prompt.
What is worth going in for:
server parameter to pick between them.config.toml, editable in the
browser: listen host and port, compact output, response size caps, TLS paths, CORS
allow list, rate limits and tool exposure.
report_generate is still marked beta, and it is the feature I would point a
sceptical manager at first. Ask any chat model to produce a PDF and you get a mess.
Here the model does not build the document at all: it picks a report type and its
parameters, and the server renders the PDF from a Jinja2 template with WeasyPrint. The
output is deterministic and identical run to run, which is the whole point of a report.
| Built-in template | Contents |
|---|---|
availability | Availability per host with an SLA gauge and event counts |
capacity_host | CPU, memory and disk - average, minimum, maximum - from trend data |
capacity_network | Interface bandwidth in Mbit/s plus per-host CPU |
backup | Success and failure matrix by host and day, auto-detecting Veeam, Bacula, Borg and restic item keys |
showcase | Every widget the visual editor ships, as a starting point to copy and trim |
Reporting is not installed by default, and it is not a pure-Python add-on: the renderer
needs native libraries (cairo, pango, gdk-pixbuf) alongside the Python packages. Install
or update with --with-reporting to have the installer handle all of it, or
add pip install zabbix-mcp-server[reporting] by hand, then restart the
service. The portal tells
you plainly that reports are unavailable until you do this, and the log confirms it
afterwards with a line about the report generate tool being registered. Branding
(report_logo, report_company, report_subtitle) is
three lines of config and makes the output look like it came from your team rather than
from a monitoring tool.
Custom templates can be built three ways: a drag-and-drop visual editor in the portal, an AI generator that turns a plain-English description into a validated Jinja2 template, or hand-written HTML registered in the config. The AI route needs your own provider API key - Anthropic, OpenAI, Gemini, Azure, Ollama, Mistral or Groq - set in the portal settings. All three paths validate through a sandboxed environment before saving, so a broken template never reaches disk. Then generating a report is just: "generate an availability report for host group Linux servers for the last 30 days".
One trap worth its own line, because it looks like it worked. A git pull
in the checkout is not an upgrade. The code updates, the running service does not,
and nothing in the frontend tells you. Fetch the changes and then run the installer's
update path:
cd zabbix-mcp-server
git pull
sudo ./deploy/install.sh update
That refreshes the virtualenv, migrates the config, and restarts the service. The project moves quickly - the admin portal landed in v1.23 and the current release is already v1.36 - so it is worth checking the release notes rather than assuming your install still matches what you read six weeks ago, including this page.
For anyone who works in Zabbix daily, yes, and mostly for the read path. Being able to ask "what is broken, on which hosts, since when" and get a correlated answer without building the query is a real saving, several times a day. The analytics extensions push that further - anomaly detection and capacity forecasting over trend data are questions that would otherwise mean exporting to a spreadsheet.
The write path is genuinely capable and genuinely worth being careful with. Creating a host with the right template and an action attached, from one sentence, is the kind of thing that makes the tooling look magical - right up until an ambiguous instruction gets resolved confidently in a direction you did not intend. Read-only first, a scoped Zabbix role behind the token, and a tool allowlist that reflects the job. Then widen it once you have watched it work for a week.
The first video is the full install on a live system: cloning, running the installer,
editing config.toml, hitting the log permission error and fixing it, getting
past the HTTPS restriction with a Cloudflare tunnel, and then driving Zabbix by prompt -
listing hosts, creating a monitored Windows host from one sentence, and asking for a CPU
health summary.
The second picks up after the admin portal arrived: upgrading an existing install properly, the portal tour, issuing scoped MCP tokens, the client wizard, admin roles and the audit log, and the beta reporting engine end to end.
The interesting question is rarely "does it install" - it is which Zabbix role the token should carry, which tools to expose, and where the write boundary sits. Tell me what your environment looks like and what you want the assistant to do, and I will tell you how I would scope it.