Skip to content

Grafana

The Grafana connector gives Agentcy agents read access to a running Grafana instance. They can list and inspect dashboards, query datasources (Prometheus, Loki, SQL, …), and render panels back as charts the LLM can reason about.

The connector pairs well with the bundled Grafana stack that ships with Agentcy releases — a self-contained Grafana + image renderer + seed dashboards you can run with one Docker Compose command for evaluation or local development.

v1 scope

The Grafana connector is read-only in v1 — six tools, no writes, and no graph ingestion. Silencing alerts, posting annotations, and ingesting Grafana objects into the knowledge graph are on the roadmap.

Prerequisites

Before configuring an Agentcy source you need:

  • A reachable Grafana instance (your own, Grafana Cloud, or the bundled stack).
  • A service-account token for that Grafana with Viewer role (read-only is enough).
  • For the panel/dashboard render tools: the grafana-image-renderer must be reachable to your Grafana — either as an installed plugin on the Grafana server, or as a sidecar HTTP service. The bundled stack ships the sidecar option preconfigured.

Configure the source

bash
curl -X POST http://localhost:8080/api/v1/sources \
  -H "authorization: Bearer $TOKEN" -H 'content-type: application/json' \
  -d '{
    "name":"grafana-prod",
    "connector":"grafana",
    "realm":"infrastructure",
    "config":{
      "base_url":"https://grafana.internal",
      "api_token":"glsa_…",
      "org_id": 1
    }
  }'
FieldRequiredNotes
base_urlyesRoot URL, e.g. https://grafana.internal or https://acme.grafana.net. No trailing /api.
api_tokenyesService-account token. Format: glsa_… for self-hosted, glc_… for Grafana Cloud. Stored as a secret.
org_idnoDefaults to 1. Set if your token spans multiple Grafana orgs.

Validation hits /api/user and rejects the source on 401/403, so bad credentials fail fast at creation.

Tools

All six tools have effect: "read" and never modify Grafana state.

ToolPurpose
grafana_list_dashboardsSearch dashboards by title query, optional folder, limit. Returns uid/title/folder/tags.
grafana_get_dashboardFetch a dashboard by uid. Returns metadata + panel list (id, title, type, datasource).
grafana_list_datasourcesList datasources visible to the token (uid, name, type).
grafana_queryAd-hoc query against a datasource. Returns rendered SVG time-series + raw frames for the LLM. Pass datasource_uid, query, time_range.
grafana_render_panelRender a single panel to SVG (preferred) with PNG fallback. Accepts dashboard uid, panel id, time range, theme, width/height.
grafana_render_dashboardRender every panel in a dashboard. Useful for "show me the deploy dashboard".

The connector returns SVG when it can synthesize one in-process (timeseries / stat / gauge / bar / table / text panel types). For everything else it falls back to Grafana's /render/d-solo/.../panel?... PNG endpoint, which requires the renderer sidecar or plugin.

When a panel type is unsupported and renderer is unreachable, the tool returns a structured error (renderer_unavailable) so the agent can adapt.

Calling a tool

The catalog meta-tool route an agent uses internally:

bash
curl -X POST http://localhost:8080/api/v1/chat/conversations/$CONV/tool-calls \
  -H "authorization: Bearer $TOKEN" -H 'content-type: application/json' \
  -d '{
    "connector":"grafana",
    "tool":"grafana_query",
    "args":{
      "datasource_uid":"prometheus",
      "query":"sum(rate(http_requests_total[5m])) by (route)",
      "time_range":{"from":"now-30m","to":"now"}
    }
  }'

Image renderer prerequisite

PNG renders go through Grafana's built-in /render/... endpoint. That endpoint requires either:

  1. grafana-image-renderer plugin installed in Grafana — slower to bootstrap, no extra container.
  2. grafana-image-renderer as a sidecar HTTP service — recommended for self-hosted. Set GF_RENDERING_SERVER_URL=http://renderer:8081/render on the Grafana process. The bundled Compose stack does this automatically.

If neither is available, the *_render_* tools return renderer_unavailable and the agent will (correctly) skip them. SVG renders for the supported panel types still work without the renderer.

Local development with the bundled stack

Agentcy ships a self-contained Grafana stack at infra/grafana/. It's the fastest way to evaluate the connector or develop against it without a real Grafana.

Start it

From the unpacked release tarball at the repo root:

bash
docker compose --profile grafana up -d

The profile starts two services:

ServiceWhat it doesWhere
grafanaGrafana 11.3 OSS with provisioned datasources + seed dashboardshttp://localhost:13000
grafana-renderergrafana/grafana-image-renderer:latest, internal port 8081sidecar, no public port

Default credentials: admin / admin. Override via env:

env
GRAFANA_ADMIN_USER=admin
GRAFANA_ADMIN_PASSWORD=change-me

What's inside

  • Datasource — TestData, set as default. Lets the seed dashboards render without a real Prometheus.
  • Seed dashboards — three live in infra/grafana/dashboards/:
    • cpu-memory.json — timeseries CPU + memory panels (random_walk).
    • request-stats.json — request volume + latency panels.
    • heatmap-sample.json — heatmap panel with simulated buckets.
  • Renderer wired — Grafana's GF_RENDERING_SERVER_URL points at the sidecar, so PNG fallbacks work for tools that need them.
  • Branding — admin user/pass and GF_AUTH_ANONYMOUS_ENABLED=false are set explicitly so the local instance behaves like prod.

Issue a service-account token

Once Grafana is up at http://localhost:13000:

  1. Sign in (admin / admin).
  2. Administration → Service accounts → Add service account. Give it the Viewer role.
  3. Add service account token. Copy the glsa_… value.
  4. Register an Agentcy source with base_url=http://host.docker.internal:13000 (or http://grafana:3000 if your Agentcy backend is in the same Compose network) and that token.

Talking to the local stack from a containerized Agentcy

If both Agentcy and Grafana run in Docker Compose on the same network, use the service name (http://grafana:3000) as the base URL. From a host-mode Agentcy (running on the host directly), use http://localhost:13000.

Tear it down

bash
docker compose --profile grafana down            # keeps the volume
docker compose --profile grafana down -v          # wipes the volume too

The grafana_data named volume preserves the SQLite DB, sessions, and any plugins you install — but seed dashboards are re-provisioned on every boot because they live outside the volume mount path.

Production / Railway deployment

The same infra/grafana/ directory is also a complete Railway service. Highlights:

  • Dockerfile — based on grafana/grafana-oss:11.3.0. Bakes the provisioning + seed dashboards in.
  • Entrypoint (agentcy-grafana-entrypoint.sh) — honors Railway's $PORT (sets GF_SERVER_HTTP_PORT), fixes ownership of the mounted volume on first boot, then drops back to the grafana user.
  • Persistent volume/var/lib/grafana for SQLite, sessions, plugins, dashboard edits.
  • Seed-dashboards path/etc/grafana/seed-dashboards, kept outside /var/lib/grafana so the persistent volume doesn't shadow the bundled JSON.
  • Health check/api/health, 180-second timeout (Grafana cold-boot can be slow).

infra/grafana/railway.toml:

toml
[build]
builder = "DOCKERFILE"
dockerfilePath = "Dockerfile"

[deploy]
healthcheckPath = "/api/health"
healthcheckTimeout = 180
restartPolicyType = "ON_FAILURE"
restartPolicyMaxRetries = 3

[[deploy.volumes]]
mountPath = "/var/lib/grafana"

To add the renderer in production, run grafana/grafana-image-renderer:latest as a separate Railway service in the same project and point the Grafana service's GF_RENDERING_SERVER_URL at it.

Rate limits and back-pressure

Grafana's HTTP API has no published per-token quota; the connector applies a conservative token-bucket limiter (default 10 req/s) and respects HTTP 429 with exponential backoff. For chatty agent flows (many grafana_query calls in a row), tune:

bash
curl -X PATCH http://localhost:8080/api/v1/sources/$SOURCE_ID \
  -H "authorization: Bearer $TOKEN" -H 'content-type: application/json' \
  -d '{"config":{"rate_limit_rps":25}}'

Gotchas

  • Grafana Cloud uses https://<stack>.grafana.net. Tokens look like glc_…. The connector handles both formats but the URL must include the stack name.
  • Multi-org Grafanas — without org_id the token's default org is used. If your dashboards live in a different org you'll see "dashboard not found" on tools that should work; set org_id explicitly.
  • Renderer 404s — if grafana_render_panel returns renderer_unavailable the message will tell you whether the plugin is missing or the sidecar URL is unreachable. Visit Grafana's Configuration → Plugins page to confirm.
  • First sync looks empty — there's no ingestion in v1, so GET /api/v1/sources/$SOURCE_ID/sync is a no-op other than refreshing the auth check. Don't expect graph nodes to appear.

What's not in v1

  • No graph ingestion. :GrafanaDashboard, :GrafanaPanel, :GrafanaAlert, :GrafanaDatasource, :GrafanaFolder nodes are not written to Neo4j. If you need them in the graph today, use the grafana_query tool from a scheduled task and write nodes via the graph.write_node tool.
  • No write tools. Silencing alerts, posting annotations, and pausing dashboards aren't available. Use Grafana's UI or its API directly for now.
  • No alert ingestion. Listing firing alerts via tool isn't shipped. The pattern that works today: register a Grafana alert webhook that posts to Agentcy (/api/v1/hooks/<trigger_id>) and run a Task on it. See SRE / Incident Response use case.

Next

Built by AgentcyLabs. For in-house deployment or Agentcy Cloud (PaaS) access, visit agentcylabs.com.