Mirador MCP
OfficialAllows Mirador to analyze a Prometheus-compatible metrics backend, discovering metric families, resolving entity topology, detecting anomalies, correlating signals, running root-cause analysis, and forecasting capacity.
Supports using Thanos as a Prometheus-compatible metrics backend for Mirador's observability analysis, including metric discovery, topology resolution, anomaly detection, RCA, correlation, and capacity forecasting.
Integrates with VictoriaMetrics as a Prometheus-compatible metrics backend, enabling catalog discovery, anomaly detection, root-cause analysis, signal correlation, and capacity forecasting on the monitored estate.
Click on "Deploy 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., "@Mirador MCPWhy is checkout error rate up? Trace the affected services and probable root cause."
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.
MIRASTACK Mirador MCP Server
Mirador Core is a zero-vendor-knowledge observability analysis engine. Pointed at a Prometheus-compatible metrics backend and (optionally) a log backend belonging to a deployment it has never seen, it discovers the signal inventory, resolves the entity topology, accepts anchors that focus an analysis, correlates metrics and logs, runs root-cause analysis and capacity forecasting inside that scope, and reports every finding with a four-dimensional confidence vector plus an explicit statement of what input would improve it. It ships as an MCP server, an importable Python library, and a CLI, under Apache-2.0.
Zero vendor knowledge
Mirador contains no metric names, no label names, and no vendor vocabulary — the rule is
mechanically enforced by scripts/check_no_vendor_vocabulary.sh, which rejects any
metric-name-shaped string literal in src/mirador/**.
Everything vendor-specific is either discovered from the data or supplied at query time as an anchor:
Signals — metric families are grouped from the backend's own series index; type, unit, role, cadence, discriminator dimensions, and log fields/templates are inferred (
src/mirador/catalog/), never read from a built-in table.Entities — identifier keys are profiled from label cardinality and cross-family reuse, and edges come from FK/overlap/affix/conservation/component evidence with named guards for every rejection (
src/mirador/entity/). A fixture designed to look joinable but that is not (tests/fixtures/negative) must yield zero edges.Topology — user-supplied topology is a merge input (
entity/usertopo.py) with conflicts surfaced, not a required configuration file.Anchors — a user- or agent-supplied term is resolved across five lexical channels into the exact families, label keys, label values, and log fields an analysis may touch. See docs/anchors.md.
The practical consequence: a new backend needs no onboarding config. The practical cost: early findings are honestly labelled low-confidence rather than confidently wrong — see docs/confidence.md.
Related MCP server: Prometheus MCP Server
Getting started
This section assumes no prior knowledge of the project. Every command below is copy-pasteable.
1. Prerequisites
You need | Why | Check it |
Python 3.11 or newer |
|
|
git | to clone the repo |
|
~2 GB free disk | virtualenv + the committed test fixtures | — |
(optional) Docker | only for the container deployment in §6 |
|
You do not need Prometheus, Kubernetes, or any monitoring system to build, test, and try
the engine — four synthetic estates are committed under tests/fixtures/.
2. Build it
git clone https://github.com/mirastacklabs-ai/mirastack-mirador-mcp
cd mirastack-mirador-mcp
python3 -m venv .venv # create an isolated Python environment
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -e ".[dev]" # editable install + dev tools (pytest, ruff, mypy)-e means editable: your source edits take effect without reinstalling.
Confirm the console script is on your PATH:
mirastack-mirador-mcp --help3. Prove the build is healthy
make checkThat runs, in order: ruff (lint) → mypy (types) → the 9 gate scripts → the test suite →
a determinism check that runs the engine twice and diffs the bytes. Expect
every test to pass, with 8 skipped (the skips are live-backend tests that need env vars from §5).
Individual pieces, if you prefer:
make lint # ruff
make typecheck # mypy
make gates # the 9 scripts/check_*.sh invariant guards
make test # pytest
make determinism # byte-identical output across two runs4. First run, with no backend at all
Mirador stores everything it learns in one embedded DuckDB file. Two steps: build a catalog, then ask a question.
# (a) Learn the estate from a committed synthetic fixture.
mirastack-mirador-mcp \
--fixture-dir tests/fixtures/vmware_pure \
--duckdb /tmp/mirador.duckdb \
--json catalog build
# (b) Confirm it is ready.
mirastack-mirador-mcp \
--fixture-dir tests/fixtures/vmware_pure \
--duckdb /tmp/mirador.duckdb \
--json catalog statecatalog build is synchronous — it returns only when the sweep has finished, so it is
safe to script. catalog state should report ready.
vmware_pure is a VMware + Pure Storage estate (a classic datacenter shape, no Kubernetes).
From it Mirador discovers ~16 metric families, ~780 entities of types vm, host, volume,
disk, backing_serial, and the cross-source joins that link compute to storage — without
being told a single metric or label name.
Now ask it something:
BASE="--fixture-dir tests/fixtures/vmware_pure --duckdb /tmp/mirador.duckdb --json"
# --lookback-s sizes the analysis window. 900 = 15 minutes, the usual shape of an
# outage investigation. Without it the tools search a full day, which is far slower.
mirastack-mirador-mcp $BASE --lookback-s 900 detect vm # ranked anomalies
mirastack-mirador-mcp $BASE --lookback-s 900 rca vm # causal chains + evidence
mirastack-mirador-mcp $BASE --lookback-s 900 correlate vm # signal pairs with q-values
mirastack-mirador-mcp $BASE topology get # discovered entity graph
mirastack-mirador-mcp $BASE capacity vm # exhaustion ETA distributionscapacity deliberately takes no --lookback-s: a forecast needs long history, so an
outage-sized window would be meaningless for it.
Pick the window to match the question. 15 minutes for an active incident, an hour for a slow degradation, a day for a trend. The cost difference is large — per-series analysis over a day of fine-cadence data can take minutes, where 15 minutes of it takes seconds.
The word vm is an anchor — a plain-language hint about what you care about. You are not
naming a metric; Mirador resolves the term against the catalog it discovered. Anchors also
accept explicit globs when you want precision:
mirastack-mirador-mcp $BASE detect '{"term": "memory", "metrics": ["arr_volume_*"]}'5. Point it at a real backend
Any Prometheus-compatible endpoint works (Prometheus, VictoriaMetrics, Thanos, Mimir). Logs are optional.
# VictoriaMetrics cluster (note the /select/0/prometheus path prefix)
METRICS="http://localhost:8481/select/0/prometheus"
mirastack-mirador-mcp \
--metrics-url "$METRICS" \
--logs-url http://localhost:9471 --logs-kind vlogs \
--duckdb ~/mirador-prod.duckdb \
--json catalog build--logs-kind accepts vlogs, loki, file, or none.
Expect the first build to take minutes, not seconds — it profiles every metric family in the estate. On a ~700-family estate it takes about 10 minutes. Re-runs are incremental.
Global flags worth knowing:
Flag | Meaning |
| Prometheus-compatible query endpoint (required unless |
| use committed fixture data instead of a live backend |
| optional log backend |
| where the learned catalog lives (default |
| YAML config; precedence is CLI > environment > file > default |
| machine-readable output (use this when scripting) |
| raise the query budget for very large estates |
| analysis window in seconds (900 = 15 min). Default is a full day |
| exit non-zero when confidence is |
Environment variables use the MIRADOR_ prefix with __ for nesting, e.g.
MIRADOR_BUDGET__MAX_POINTS_PER_RUN=50000000. Note the double underscore: it is the nesting
delimiter, so MIRADOR_METRICS__KIND configures the backend and MIRADOR_METRICS_KIND does
nothing. Flags are optional — the environment alone is a complete configuration, which is how MCP
clients and orchestrators (which pass no arguments) run this server:
MIRADOR_METRICS__KIND=prometheus \
MIRADOR_METRICS__BASE_URL="$METRICS" \
mirastack-mirador-mcp serveA config file version of the same thing:
# mirador.yaml
metrics:
kind: prometheus
base_url: http://localhost:8481/select/0/prometheus
logs:
kind: vlogs
base_url: http://localhost:9471
duckdb_path: /var/lib/mirador/mirador.duckdb
budget:
max_points_per_run: 20000000
max_concurrent: 8mirastack-mirador-mcp --config mirador.yaml --json catalog build6. Deploy it
As an MCP server (this is the primary mode — an AI agent calls the tools):
# stdio: the transport MCP clients such as Claude Desktop expect (the default)
mirastack-mirador-mcp --metrics-url "$METRICS" --duckdb ~/mirador.duckdb serve
# streamable HTTP, for remote clients and for a governing proxy
mirastack-mirador-mcp --metrics-url "$METRICS" serve \
--transport streamable-http --listen 127.0.0.1:9001serve flags: --transport {stdio,streamable-http,sse} (--http is shorthand for
streamable-http), --listen HOST:PORT (or --host/--port), --path (default /mcp), and
--json-response / --stateless (both on by default; --no-json-response restores SSE
framing and --no-stateless restores server-side sessions). Each has an environment equivalent —
MIRADOR_SERVER__TRANSPORT, MIRADOR_SERVER__LISTEN, and so on — plus the cross-server aliases
MCP_TRANSPORT, MCP_LISTEN_ADDR / MCP_HTTP_LISTEN, MCP_HTTP_HOST, MCP_HTTP_PORT,
MCP_HTTP_PATH, so one operator template configures this server and its siblings identically.
In serve mode a scheduler keeps the catalog fresh: one sweep immediately at startup, then
incremental sweeps every 15 minutes and a full sweep daily.
Traces (optional). Point Mirador at a VictoriaTraces instance with --traces-url http://vt:10428 (or MIRADOR_TRACES__KIND=vtraces + MIRADOR_TRACES__BASE_URL=...; auth/TLS
mirror the metrics/logs blocks as MIRADOR_TRACES__AUTH__* / MIRADOR_TRACES__TLS_VERIFY).
Mirador queries the Jaeger-compatible API (/select/jaeger/api/...) and derives three per-service
signals — span rate, error rate, and a span-duration percentile — that participate in correlate,
rca, and detect_anomalies alongside metrics and log-template signals. Trace series join the
entity graph through the service label. An unreachable trace backend degrades honestly to
metrics+logs; trace metadata is never persisted to DuckDB and never feeds anchor lexical search
(scope boundary, documented here on purpose).
MCP client configuration (e.g. Claude Desktop's claude_desktop_config.json):
{
"mcpServers": {
"mirador": {
"command": "/absolute/path/to/.venv/bin/mirastack-mirador-mcp",
"args": ["--metrics-url", "http://localhost:8481/select/0/prometheus",
"--duckdb", "/absolute/path/to/mirador.duckdb", "serve"]
}
}
}In Docker:
docker build -t mirador-mcp .
docker run --rm -it \
-e MIRADOR_DUCKDB_PATH=/home/mirador/mirador.duckdb \
mirador-mcp --metrics-url "$METRICS" serveThe image runs as a non-root user (uid 10001) and excludes the test fixtures.
7. Reading the output
Every response is an envelope with the same shape, so one reading habit covers all tools:
{
"resolved": { "anchors": [ { "term": "vm", "families": ["cmp_vm_info"],
"confidence": 0.9, "via": ["family_name"] } ] },
"result": { "chains": [], "windows": [] },
"confidence": { "scope": 0.9, "structure": 1.0, "statistical": 0.4,
"data": 0.8, "tier": "indicative" },
"missing_inputs": [ { "what": "longer window, more samples" } ],
"next_actions": [ ]
}Read it in this order:
resolved.anchors[].via— how your term was understood.family_name(0.9) is a confident match;lexical_unconfirmed(0.55) means "this is my best evidence-backed guess, please confirm";structural_fallbackmeans the term matched nothing and the shape of the data was used instead.confidence.tier—insufficient→indicative→probable→strong.insufficientis a feature, not an error. Mirador refuses to sound certain when the evidence is thin.missing_inputs— the concrete thing that would raise the tier (a longer window, a confirmed anchor, retention metadata).result— the findings themselves.
An empty result with an honest low tier is a valid, deliberate answer.
8. When something looks wrong
Symptom | Cause and fix |
| Give one of them, or use |
| Run |
Every result is | Usually genuine. Check |
A command seems to hang | You are probably on the default one-day window. Add |
| Very large estate: raise |
| The metrics URL is unreachable (dead port-forward, wrong path prefix). |
Anchor resolves to unrelated families | Use the explicit form: |
Results differ between two identical runs | Should be impossible — |
9. Using it as a Python library
The CLI and the MCP server are thin wrappers; the engine is importable.
import asyncio
from pathlib import Path
from mirador.catalog.build import build_catalog
from mirador.datasource.base import BudgetLedger, QueryBudget
from mirador.datasource.file import FileDataSource
from mirador.store.repo import Repo
budget = QueryBudget()
ds = FileDataSource(Path("tests/fixtures/vmware_pure"), budget)
repo = Repo(Path("mirador.duckdb"))
result = asyncio.run(
build_catalog(ds, repo, now=1_730_000_000.0, ledger=BudgetLedger(budget), logs=ds)
)
print(len(result.families), "families,", f"coverage {result.coverage:.2%}")
for signal in result.signals[:5]:
print(signal.signal_id, signal.mtype, signal.unit, signal.role)Note the injected now: Mirador never reads the wall clock inside the analysis path, which
is what makes runs bit-for-bit reproducible (scripts/check_no_wallclock.sh,
scripts/check_determinism.sh). Regenerate fixtures with make fixtures.
Onboarding into MIRASTACK
MIRASTACK does not talk to this server directly. It runs the MCP sidecar (mira-mcp-wrapper)
as the pod's only listener; the sidecar spawns this server on loopback and governs every tool call
(RBAC, audit, serialization). Two things must line up: the Helm entry (plumbing) and the target env
template (configuration). They are edited in different places on purpose.
Why the container ships a runtime tree
The sidecar runs on gcr.io/distroless/base-debian12 and fork/execs the child inside its own
container, after an initContainer copies the server out of this image. That container has no Python
and no shell. The pip console script mirastack-mirador-mcp cannot work there: it is a text file
whose #!/usr/local/bin/python shebang and mirador package both stay behind, so exec fails with
start http child: fork/exec /shared/mcp-server: no such file or directorywhich is misleading — the file is there; the missing thing is the shebang interpreter.
The sibling Go and Bun servers answer this with a single self-contained binary, but Python has no
equivalent: PyInstaller and Nuitka cannot cross-compile, so building one would force them to run
under emulation for a foreign architecture, which is not dependable. Instead this image ships a
relocatable CPython at /opt/mirador-runtime with Mirador pip installed into it. The
initContainer copies that tree to /shared/runtime and the wrapper runs it directly, so the chart
uses serverDir + childCmd rather than serverBinary.
Nothing is compiled to achieve this, which is what keeps the image cleanly multi-architecture: the
interpreter and every wheel (duckdb, numpy, scipy, pandas, statsmodels, ruptures) are downloads
published for both x86_64 and aarch64. The tree also vendors the three libraries distroless does
not ship — libz.so.1, libstdc++.so.6, libgcc_s.so.1 — so the shared sidecar image needs no
modification for this server.
1. Helm entry (plumbing only)
In deployments/reference/kubernetes/helm/mirastack/values.yaml under mcpServers.servers:
- name: mirastack-mirador-mcp
enabled: true
serverImage: docker.io/mirastack/mirastack-mirador-mcp
serverTag: "0.1.0" # must match the tag you built/pushed; the package version is 0.1.0 (pyproject.toml)
serverDir: /opt/mirador-runtime # the relocatable tree, not a single binary
childCmd: /shared/runtime/bin/python3 -m mirador serve
backend: http
childHTTP: http://127.0.0.1:9003/mcp # loopback; must be unique per pod
endpoint: /mcp
integrationID: ""
resources: {}
registrationTokenSecretKey: ""childCmd carries arguments because the wrapper splits the value, so no launcher script is needed —
which matters, since distroless has no shell.
The chart carries no target configuration — no endpoints, no credentials, no target-facing env
var names. That is deliberate and enforced by comments in values.yaml.
2. Target env template (configuration)
Set these in the MIRASTACK UI at Admin → MIRA → MCP Ecosystem → Runtimes →
mirastack-mirador-mcp → target env template, then bind an Integration to the runtime.
Variable | Value | Why it is required |
|
| Transport defaults to |
|
| Resolves to |
|
| Required field with no default. Note the double underscore — the nesting delimiter is |
|
| Comes from the bound Integration. |
|
| The default is |
Optional, only if you also want log-based analysis:
Variable | Value |
|
|
| the logs endpoint |
You do not need to set MCP_HTTP_JSON_RESPONSE, MCP_HTTP_STATELESS, or MCP_HTTP_PATH.
JSON responses and stateless mode are already the defaults because the sidecar issues its own
Mcp-Session-Id and relabels responses application/json; the default path is already /mcp.
3. Two vocabularies that look alike
The commonest onboarding mistake is mixing them up:
Setting | Owner | Value |
| the wrapper |
|
| Mirador |
|
Equally, VM_INSTANCE_ENTRYPOINT / VL_INSTANCE_ENTRYPOINT belong to the VictoriaMetrics and
VictoriaLogs MCP servers. Mirador does not read them; it uses MIRADOR_METRICS__BASE_URL.
4. Verifying it worked
kubectl -n mirastack logs deploy/mirastack-mirador-mcp -c sidecar --tail=20Expected: runtime registered with engine, then no child start failed.
Sidecar error | Meaning |
| The chart is using |
| An old image built before the runtime tree vendored |
|
|
| The runtime has no env template yet. |
| Bind an Integration to the runtime. |
child starts, then exits complaining about a missing backend |
|
child never becomes ready |
|
The 15 MCP tools
Full parameter documentation: docs/mcp-tools.md.
Tool | Tier | Purpose |
| composite | Chains, per-step evidence, confidence. Self-sufficient. |
| composite | ETA distributions, steps/month, cross-layer divergence. |
| composite | Signal pairs with q-values, effect sizes, change-point order. |
| composite | Ranked anomalies with a baseline-quality flag. |
| inspection |
|
| inspection | Catalog summary, coverage percentage, maturity. |
| inspection | Families with inferred type/unit/role/cadence. |
| inspection | Dry run — inspect the scope manifest before analysing. |
| inspection | Entity subgraph with per-edge provenance. |
| inspection | Narrative for a finding — tier-gated. |
| mutation | Persist a learned anchor alias. |
| mutation | Merge user topology; conflicts surfaced. |
| utility | Job state plus partial output. |
| utility | Cancel a running job. |
| utility | Escape hatch, budget-capped. |
The four composite tools are self-sufficient: each performs scope resolution, window localization, detection, and analysis internally, so no prerequisite call is required.
Confidence tiers
Confidence is reported as four independent dimensions (scope, structure, statistical,
data). rank() is their product and exists only to sort and to tier. Thresholds come
from src/mirador/constants.py (TIER_SUPPORTED, TIER_INDICATED, TIER_WEAK):
Tier |
| Behaviour it triggers |
|
| Names a root cause; a single chain is led; LLM narration is permitted. |
|
| Leading hypothesis plus alternatives, always; template narration only. |
|
| Ranked candidates only — no causal language anywhere in the output. |
|
| Reports what is missing instead of a finding, with remediation steps. |
A day-one deployment with little history will legitimately report weak. That is the system
being honest, not a defect — the reasoning is spelled out in
docs/confidence.md.
Scale envelope
Mirador is designed for a single container on 2–4 vCPU / 8 GB with embedded DuckDB and
one environment per instance. It is comfortable with backends up to roughly 5 M active
series (the catalog tracks families, on the order of thousands, never every series), up to
about 100 k entities in the networkx graph, 200–2 000 signals per analysis, and up to about
5 000 capacity signals at 90 days / 1 h resolution. A warm RCA takes seconds to tens of
seconds and a full background catalog sweep takes minutes. The bottleneck is backend I/O
against the customer's Prometheus, not Mirador's arithmetic — which is exactly why
QueryBudget is a first-class object (tuning guide: docs/operations.md).
For more than one environment, run more instances.
Documentation
docs/architecture.md — the L0–L5b layer pipeline, module by module
docs/anchors.md — anchor resolution and the five lexical channels
docs/confidence.md — the four-dimensional confidence vector
docs/mcp-tools.md — the 15 tools with parameters
docs/fixtures.md — the four committed synthetic fixtures
docs/operations.md —
QueryBudgettuning and scheduler intervalsdeveloper/MIRADOR_CORE_ENGINEERING_SPEC.md— the authoritative design document
Development
make install-dev
make check # lint + typecheck + gates + tests + verify-done + determinismSee CONTRIBUTING.md for the non-negotiable engineering rules and SECURITY.md for the security posture.
License
Apache-2.0. See LICENSE.
This server cannot be deployed
Maintenance
Related MCP Connectors
AI agent observability for production traces, natural-language insights, and improvement loops.
- SuperlogOAuthsh.superlog
Open-source agent that observes and fixes your application. Query logs, traces, metrics, incidents.
Data + AI observability — monitor and troubleshoot production-grade agents and the context they use.
Synthetic checks, nightly regression replay and model-drift alerts for AI agents
Related MCP Servers
- FlicenseAqualityDmaintenanceEnables AI agents to query Prometheus metrics and Loki logs for intelligent alert investigation and troubleshooting. Provides service discovery, metric querying, log searching, and correlation tools to help identify root causes of issues.9-
- AlicenseNot gradedqualityDmaintenanceEnables AI assistants to query Prometheus metrics, monitor alerts, and analyze system health through read-only access to your Prometheus server with built-in query safety and optional AI-powered metric analysis.MIT
- AlicenseNot gradedqualityDmaintenanceEnables natural language querying and analysis of OpenTelemetry traces, metrics, and logs stored in Elasticsearch/OpenSearch, allowing AI assistants to investigate performance issues, find root causes, and explore system behavior.2114MIT
- AlicenseNot gradedqualityDmaintenanceA Model Context Protocol server that enables AI agents to interact directly with Prometheus metrics data through natural language queries.MIT