Metadata-Version: 2.5
Name: runspec-grafana
Version: 0.1.0
Summary: Grafana runnable for runspec — dashboards, datasources, PromQL and LogQL through Grafana, alert rules, silences, and annotations, ported from the Grafana MCP server
Project-URL: Documentation, https://runspec.app/
Project-URL: Source, https://github.com/jasonfinestone/runspec/tree/main/packages/python/runspec-grafana
Project-URL: Changelog, https://github.com/jasonfinestone/runspec/blob/main/packages/python/runspec-grafana/CHANGELOG.md
Project-URL: Issues, https://github.com/jasonfinestone/runspec/issues
Keywords: grafana,loki,mcp,monitoring,observability,prometheus,runnable,runspec
Requires-Python: >=3.10
Requires-Dist: runspec-grafana-core>=0.1.0
Requires-Dist: runspec>=0.73.1
Provides-Extra: dev
Requires-Dist: mypy; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff==0.15.20; extra == 'dev'
Description-Content-Type: text/markdown

# runspec-grafana

A Grafana runnable for [runspec](https://pypi.org/project/runspec/), ported from
the read tools of the [Grafana MCP server](https://github.com/grafana/mcp-grafana)
onto the Grafana HTTP API — **not** proxying that server. One runnable —
**`grafana`** — with a verb per tool. `pip install` it into a venv and it's
discoverable by `runspec local`, `runspec serve` (MCP), and runspec-console.

`runspec serve` flattens each verb into a leaf MCP tool
(`grafana_search-dashboards`, `grafana_query-prometheus`, …).

## Install

```
pip install runspec-grafana
```

## Verbs

All `autonomous` (read-only) except `create-annotation`, which is `confirm`.

| Area | Verbs |
|---|---|
| Identity | `whoami`, `health` |
| Dashboards | `search-dashboards`, `search-folders`, `get-dashboard`, `list-dashboard-versions` |
| Datasources | `list-datasources`, `get-datasource`, `check-datasource` |
| Prometheus | `query-prometheus`, `query-prometheus-histogram`, `list-prometheus-metric-names`, `list-prometheus-label-names`, `list-prometheus-label-values`, `list-prometheus-metric-metadata` |
| Loki | `query-loki-logs`, `list-loki-label-names`, `list-loki-label-values`, `query-loki-stats`, `query-loki-patterns` |
| Alerting | `list-alert-rules`, `get-alert-rule`, `list-contact-points`, `list-silences` |
| Annotations | `get-annotations`, `get-annotation-tags`, `create-annotation` |
| Anything else | `api-get` (GET any `/api/` path, returned as-is) |

PromQL and LogQL run through Grafana's datasource proxy, so only the Grafana
token is needed — never the datasource's own credentials. `--datasource` takes a
UID or a name; without it the default (or only) Prometheus or Loki datasource is
used.

## Output

Results are compact by default, because an agent re-reads every tool result on
each later step:

- `get-dashboard` returns a summary: folder, tags, variables, and one row per
  panel. `--include queries` adds each panel's query text, `--panel <id>` keeps
  one panel, `--raw` returns the full dashboard JSON.
- `query-prometheus --query-type range` summarises each series (points, first,
  last, min, max); `--include-values` returns every sample.
- `query-loki-logs` lists each stream's labels once and gives the lines newest
  first, each cut to `--max-line-chars` (500).
- `list-alert-rules` puts firing rules first with counts by state;
  `--include alerts` adds the active alert instances.
- Every list takes `--limit` and says `truncated: true` when it cut something.

A person at a terminal gets readable text; agents, pipes, the console's Forms
tab and schedulers get JSON.

Times accept Unix seconds or milliseconds, RFC 3339, `now-1h`, or a duration
back from now (`5m`, `1h30m`).

## Auth — env vars

```
export GRAFANA_URL=https://grafana.example.com            # default http://127.0.0.1:3000
export GRAFANA_SERVICE_ACCOUNT_TOKEN=glsa_...             # or GRAFANA_TOKEN, or:
export GRAFANA_USERNAME=<user> GRAFANA_PASSWORD=<password>
export GRAFANA_ORG_ID=2                                   # X-Grafana-Org-Id
export GRAFANA_CA_BUNDLE=/etc/pki/internal-ca.pem         # private CA
```

Use a [service account](https://grafana.com/docs/grafana/latest/administration/service-accounts/)
token. The **Viewer** role covers every read except `list-dashboard-versions`;
that and `create-annotation` need **Editor**.

`--env <label>` reads `GRAFANA_<LABEL>_*` first (`--env prod-eu` →
`GRAFANA_PROD_EU_URL`). `--url`, `--org-id` and `--grafana-token` override the
environment; the token arg is a `password` type, so it comes through the
runspec-console secret channel and never on a command line. A runspec-console
credential labelled `Grafana` of the token kind supplies `GRAFANA_TOKEN`, and
one of the username + password kind supplies `GRAFANA_USERNAME`/`PASSWORD`,
with no extra configuration.

## Building your own runnables

The logic lives in [`runspec-grafana-core`](../runspec-grafana-core), which has
no runspec dependency and surfaces no runnables. A private package can depend on
it alone, build a `GrafanaConfig` in code, and wrap the helpers as narrower
runnables — for example a `checkout-errors` runnable that runs one fixed LogQL
query for a team:

```python
from runspec_grafana_core import config_from_env, query_loki_logs

cfg = config_from_env("prod")
errors = query_loki_logs(cfg, '{app="checkout"} |= "level=error"', start="30m", limit=20)
```
