5 releases
Uses new Rust 2024
| 0.2.0 | Jun 16, 2026 |
|---|---|
| 0.1.3 | Jun 16, 2026 |
| 0.1.2 | Jun 15, 2026 |
| 0.1.1 | Jun 14, 2026 |
| 0.1.0 | Jun 14, 2026 |
#432 in Command line utilities
90KB
2.5K
SLoC
miru
A local MCP server that gives LLMs direct read access to Grafana Loki logs and Prometheus metrics. Add it to any MCP host (Claude Code, Codex, Gemini CLI, and others) and ask for logs or metrics in plain language.
How it works
An MCP host launches miru as a subprocess and talks to it over stdio. When you ask "show me errors from the auth service in the last 30 minutes", the model calls miru's tools, which query Loki over HTTP and return the matching log lines into the conversation. Ask about a CPU spike instead and it queries Prometheus, summarising each series before handing it back.
Logs and metrics are independent: configure [loki], [prometheus], or both. Serving both at once requires a Grafana instance, since the two datasources are proxied through it.
Installation
cargo install miru-mcp
The crate is miru-mcp. The command is miru.
Or build from git:
git clone https://github.com/ConeDjordjic/miru
cd miru
cargo build --release
cp target/release/miru ~/.local/bin/
Setup
1. Get credentials
Grafana instance (self-hosted or Grafana Cloud UI):
In Grafana: Users and Access > Service Accounts > Add service account token
Read-only scope is sufficient. Copy the token (starts with glsa_).
Grafana Cloud direct Loki:
Use your numeric org ID as username and a service account token as api_key.
The url should be your Grafana instance URL. miru will auto-detect and proxy through it.
Self-hosted Loki (no auth):
Set url to your Loki URL and omit api_key. miru detects direct Loki automatically.
2. Create the config file
mkdir -p ~/.config/miru
cp config.example.toml ~/.config/miru/config.toml
Edit ~/.config/miru/config.toml:
[grafana]
url = "https://grafana.yourcompany.com"
api_key = "glsa_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
[loki]
service_label = "app" # the Loki label that identifies services
default_limit = 200 # lines returned when not specified
max_limit = 1000 # hard cap, model cannot exceed this
# Optional: add a metrics backend (requires Grafana). Omit for logs-only.
# [prometheus]
# datasource = "Prometheus" # defaults to the first Prometheus datasource
The service_label key varies by setup. To run metrics-only, omit [loki] and keep [prometheus].
Keeping the token out of the config file
Setting the MIRU_API_KEY environment variable overrides api_key from the
file, so you can keep the token out of the file entirely. This is the
recommended way to supply the secret. Leave api_key unset and let your MCP
host pass it through its env block:
{
"mcpServers": {
"miru": {
"command": "miru",
"args": [],
"env": { "MIRU_API_KEY": "glsa_xxxxxxxxxxxxxxxxxxxxxxxx" }
}
}
}
Some hosts (for example Gemini CLI) expand $VARS, so you can reference a
value from your shell or a secret manager instead of pasting the token:
"MIRU_API_KEY": "$GRAFANA_TOKEN".
If you do keep the token in the config file, restrict it to your user:
chmod 600 ~/.config/miru/config.toml
3. Add to your MCP host
miru is a standard stdio MCP server: the host runs the miru binary and talks to it over stdin/stdout. Most hosts use the same JSON block; Codex uses TOML.
JSON hosts (Claude Code via .mcp.json or claude mcp add, Gemini CLI via ~/.gemini/settings.json):
{
"mcpServers": {
"miru": {
"command": "miru",
"args": []
}
}
}
Codex (~/.codex/config.toml):
[mcp_servers.miru]
command = "miru"
args = []
If miru is not on your PATH, use the full path, e.g. /home/you/.local/bin/miru.
To use a non-default config location, set MIRU_CONFIG in the server's environment:
{
"mcpServers": {
"miru": {
"command": "miru",
"args": [],
"env": { "MIRU_CONFIG": "/path/to/your/config.toml" }
}
}
}
[mcp_servers.miru]
command = "miru"
args = []
[mcp_servers.miru.env]
MIRU_CONFIG = "/path/to/your/config.toml"
Tools
| Tool | Description |
|---|---|
list_services |
Lists all services in Loki. Call this first. (logs) |
query_logs |
Fetches log lines from a service. Optional: level (any level name your logs use, e.g. error, warn, crit), search (text or regex). (logs) |
list_metrics |
Lists Prometheus metric names with their type (counter/gauge/histogram) and help. Call this first. Optional: filter substring. (metrics) |
query_metrics |
Runs a PromQL query. Returns a per-series summary plus data points over time, or current values with instant=true. (metrics) |
Metric tools return an error if [prometheus] is not configured; log tools error if [loki] is not configured.
Example prompts
What services are available in Loki?
Show me the last 50 error logs from the auth service in the past hour.
Show me warnings from api-gateway in the last hour.
Search for "connection refused" errors in the db-proxy service in the last 30 minutes.
What errors is the payment service throwing right now? (last 15 minutes)
Compare error rates between the auth and db-proxy services over the last 30 minutes.
Which pods had a CPU spike in the last hour?
What's the current memory usage per instance of the api service?
Show me the 95th percentile request latency for the checkout service over the last 3 hours.
Configuration reference
| Key | Required | Default | Description |
|---|---|---|---|
grafana.url |
yes | Grafana instance URL or direct Loki URL. No trailing slash. | |
grafana.api_key |
no | Service account token for Bearer auth, or password when username is set. Overridden by the MIRU_API_KEY environment variable. |
|
grafana.username |
no | Enables Basic auth. Set to your Grafana Cloud org ID for direct Loki. | |
grafana.datasource |
no | Deprecated alias for loki.datasource. |
|
loki |
no* | Logs backend. Omit for metrics-only. | |
loki.service_label |
yes | Loki label key that identifies services. Required when [loki] is set. |
|
loki.level_label |
no | Loki label for log level. Enables label-selector filtering when set. | |
loki.datasource |
no | Grafana Loki datasource name. Defaults to the first Loki datasource found. | |
loki.default_limit |
no | 200 |
Default max log lines per query |
loki.max_limit |
no | 1000 |
Hard cap. Model cannot request more than this. |
prometheus |
no* | Metrics backend. Omit for logs-only. Requires Grafana. | |
prometheus.datasource |
no | Grafana Prometheus datasource name. Defaults to the first one found. | |
prometheus.target_points |
no | 100 |
Target data points per series; miru derives the step from the time window. |
prometheus.max_series |
no | 20 |
Max series returned before miru asks the model to aggregate in PromQL. |
prometheus.min_step_seconds |
no | 15 |
Smallest step miru will compute for a range query. |
* At least one of [loki] or [prometheus] is required.
Config file location: ~/.config/miru/config.toml
Override with: MIRU_CONFIG=/path/to/config.toml
Environment variables:
MIRU_API_KEY: overridesgrafana.api_keyMIRU_CONFIG: path to the config file
License
Licensed under either of MIT (LICENSE-MIT) or Apache-2.0 (LICENSE-APACHE) at your option.
Dependencies
~24–42MB
~635K SLoC