Skip to main content
Glama
mcp-loki

mcp-loki

by mgcrea
README.md
# @mgcrea/mcp-loki

A [Model Context Protocol](https://modelcontextprotocol.io) server for the
[Grafana Loki](https://grafana.com/oss/loki/) HTTP API: query logs and LogQL metrics, explore
labels, fields, volume and patterns, with answers shaped to fit a context window.

The server is **read-only**. It sends only reads: Loki's push, delete-request and ruler-write
endpoints are not wrapped, and the `loki_request` escape hatch only sends GET.

## Features

- **Logs merged across streams, in time order.** Labels every stream shares come once as
  `common_labels`. Each line keeps only the labels that tell its stream apart, plus `level` and
  structured metadata. Timestamps are exact RFC3339 nanoseconds, so the `next` cursor and
  `loki_get_log_context` anchors round-trip without loss.
- **Cheap before expensive.** `loki_get_stats` and `loki_get_volume` answer from the index
  without reading a line. `loki_list_patterns` summarizes what a service logs as templates.
  `loki_query_metric` counts and ranks without returning lines.
- **Long lines are cut** at `max_line_chars` (some services log 300 KB lines), ANSI colour
  codes are stripped, and the Kubernetes `filename` label is hidden by default.
- **Errors carry a remedy.** Loki answers a rejected query with plain text. The common ones
  (empty-compatible matchers, parse errors, range and series limits, timeouts) come back with
  what to change.
- **Identity and limits up front.** `loki_auth_status` names the tenant, the version,
  retention, max query length, max lines and series per query, and whether volume and patterns
  are enabled. Every data answer carries `tenant`.

## Configure

| Variable           | Required | Description                                                                                                                      |
| ------------------ | -------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `LOKI_URL`         | yes      | Loki base URL (gateway, query-frontend or single binary), with any path prefix.                                                  |
| `LOKI_USERNAME`    | no       | Basic auth. Behind the Loki Helm chart's nginx gateway the username **is the tenant**.                                           |
| `LOKI_PASSWORD`    | no       | Basic auth password.                                                                                                             |
| `LOKI_TOKEN`       | no       | Bearer token instead of basic auth (Grafana Cloud, an OAuth proxy).                                                              |
| `LOKI_TENANT_ID`   | no       | Sent as `X-Scope-OrgID`, for a multi-tenant Loki reached directly. A gateway that maps the basic-auth username overrides it.     |
| `LOKI_HEADERS`     | no       | Extra headers as a JSON object.                                                                                                  |
| `LOKI_TIMEOUT`     | no       | HTTP timeout per request, seconds (default 60).                                                                                  |
| `LOKI_MAX_RETRIES` | no       | Retries for 429 and proxy 502/503 (default 2). A 504 is never retried: on Loki it is a slow query, and a retry triples the load. |

With nothing configured the server still starts and serves `loki_auth_status`, which says what
to set.

## Quick start

```bash
pnpm install
pnpm build
```

Then copy `.mcp.json.example` to `.mcp.json` and fill it in.

## Tools

| Tool                        | What it does                                                                       |
| --------------------------- | ---------------------------------------------------------------------------------- |
| `loki_auth_status`          | URL, auth, tenant, version and this tenant's limits; setup steps when unconfigured |
| `loki_query_logs`           | LogQL log query over a range, lines merged across streams, with a `next` cursor    |
| `loki_get_log_context`      | The lines just before and after one line, in its own stream                        |
| `loki_query_metric`         | LogQL metric query at an instant, ranked largest first                             |
| `loki_query_metric_range`   | LogQL metric query over a range, on a step grid, with per-series stats             |
| `loki_list_labels`          | Stream labels with their cardinality                                               |
| `loki_list_label_values`    | Values of one label, optionally within a selector                                  |
| `loki_list_series`          | Streams matching a selector                                                        |
| `loki_list_detected_fields` | JSON / logfmt / structured-metadata fields in the lines, or one field's values     |
| `loki_get_volume`           | Bytes per namespace / app / stream from the index, largest first                   |
| `loki_get_stats`            | Streams, chunks, lines and bytes a selector covers, from the index                 |
| `loki_list_patterns`        | Line templates with counts, from the pattern ingester                              |
| `loki_list_rules`           | The Loki ruler's alerting and recording rules                                      |
| `loki_request`              | GET any other read endpoint (`/loki/api/v1/*`, `/prometheus/api/v1/*`, `/config`)  |

## Develop

```bash
pnpm dev            # tsdown --watch
pnpm test           # vitest
pnpm typecheck
pnpm lint
pnpm format
```

### Publish

```bash
pnpm dlx release-it       # bump, commit, tag
git push --follow-tags    # CI publishes to npm from the tag
```

## License

MIT