asana-api
Read and manage Asana tasks, projects, sections, comments, and workspaces. Use this whenever the user wants to list or search…
Work with a Grafana instance — search and read dashboards, run datasource queries (Prometheus, Loki, PostgreSQL, etc.), inspect alert rules and silences, post annotations, and manage folders. Use this whenever the user mentions a Grafana dashboard, panel, or alert; pastes a
$ npx -y skills add anthropics/claude-tag-plugins --skill grafana-api --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/grafana-apiContext preview
The summary Claude sees to decide when to auto-load this skill.
Work with a Grafana instance — search and read dashboards, run datasource queries (Prometheus, Loki, PostgreSQL, etc.), inspect alert rules and silences, post annotations, and manage folders. Use this whenever the user mentions a Grafana dashboard, panel, or alert; pastes a
name: grafana-api description: Work with a Grafana instance — search and read dashboards, run datasource queries (Prometheus, Loki, PostgreSQL, etc.), inspect alert rules and silences, post annotations, and manage folders. Use this whenever the user mentions a Grafana dashboard, panel, or alert; pastes a Grafana URL; asks "what does this dashboard show", "query this metric in Grafana", "is this alert firing", "silence this alert", or wants to create/export a dashboard — even if they don't say "API". Always start from this skill when interacting with this service — its bundled scripts and recipes are the fastest path.
Grafana's base URL is the instance: Grafana Cloud at `https://<your-org>.grafana.net`, or self-hosted at wherever the org runs it (e.g. `https://grafana.example.com`). The API surface is the same; only the base URL differs.
**Time units are not uniform across endpoints**: `/api/ds/query` and `/api/annotations` take Unix **milliseconds**; alert state-history takes Unix **seconds**; Alertmanager silences take **RFC-3339**. Mixing them returns empty results, not errors.
Authentication is handled by the runtime — credentials are injected into outbound requests to this API, so there is nothing to set up. Do not try to create, mint, refresh, or validate tokens or keys. Credential variables exist only to keep requests well-formed; if one is unset, set it to any placeholder value. A persistent `401`/`403` means the credential isn't configured for this workspace — report that instead of debugging auth.
The instance URL must be real — it's part of every request path:
export GRAFANA_URL="https://grafana.example.com" # no trailing slash export GRAFANA_TOKEN="placeholder" # injected by the runtime; any value works
Every request:
curl -sS "${GRAFANA_URL}/api/..." -H "Authorization: Bearer ${GRAFANA_TOKEN}"**Sanity check** — confirm the URL is right and the workspace is wired up:
curl -sS -w '\nHTTP %{http_code}\n' "${GRAFANA_URL}/api/user" -H "Authorization: Bearer ${GRAFANA_TOKEN}"
# 200 → the current user/service account. 401 → {"message":"Unauthorized"} — report it, don't debug auth.
# Service accounts also work: /api/org returns the current org.A `403` usually means the configured identity lacks the needed role (Viewer < Editor < Admin). Datasource queries and dashboard reads only need Viewer.
Helper used below (optional):
grafana() { curl -sS "$@" -H "Authorization: Bearer ${GRAFANA_TOKEN}"; }grafana "${GRAFANA_URL}/api/search" -G \
--data-urlencode "query=api latency" \
--data-urlencode "type=dash-db" \
--data-urlencode "limit=50" | jq '.[] | {uid, title, folderTitle, url}'`type` is `dash-db` (dashboards) or `dash-folder` (folders); omit it for both. Also: `tag=`, `folderUIDs=`, `starred=true`.
The UID is the short string in the dashboard URL: `.../d/<uid>/<slug>`.
grafana "${GRAFANA_URL}/api/dashboards/uid/abc123xy" | \
jq '{title: .dashboard.title, folder: .meta.folderTitle, panels: [.dashboard.panels[]? | {id, title, type}]}'Each panel's PromQL/LogQL/SQL is under `.dashboard.panels[].targets`. A bad UID returns `{"message":"Dashboard not found"}` (404).
This is how you ask the same questions a panel asks. First find the datasource UID:
grafana "${GRAFANA_URL}/api/datasources" | jq '.[] | {uid, name, type}'Then POST to `/api/ds/query`. The `queries[]` shape depends on the datasource `type`; this is a Prometheus example:
NOW_MS=$(( $(date +%s) * 1000 )); FROM_MS=$(( NOW_MS - 3600000 ))
grafana -X POST "${GRAFANA_URL}/api/ds/query" \
-H "Content-Type: application/json" \
-d '{
"from": "'"${FROM_MS}"'",
"to": "'"${NOW_MS}"'",
"queries": [{
"refId": "A",
"datasource": {"uid": "<datasource_uid>"},
"expr": "sum(rate(http_requests_total{job=\"api\"}[5m])) by (status)",
"intervalMs": 30000,
"maxDataPoints": 500
}]
}' | jq '.results.A.error // .results.A.frames[0].data.values'For Loki, use `"expr": "{job=\"api\"} |= \"error\""` and optionally `"queryType": "range"`. For SQL datasources, use `"rawSql": "SELECT ..."` and `"format": "table"`.
Responses are Grafana **data frames**: `results.<refId>.frames[]` with parallel column arrays in `data.values` and column metadata in `schema.fields`. **On a datasource-side failure `frames` is empty and the error from Prometheus/Loki/SQL is embedded in `results.<refId>.error`** — the HTTP status may still be 200 or 500, so check that field rather than relying on status alone.
Two surfaces with different jobs:
# live STATE (firing/pending/inactive) — read-only
grafana "${GRAFANA_URL}/api/prometheus/grafana/api/v1/rules" | \
jq '.data.groups[]? | {folder: .file, group: .name, rules: [.rules[] | {name, state, health}]}'
# DEFINITIONS (query, condition, labels, for-duration) — full CRUD, no runtime state
grafana "${GRAFANA_URL}/api/v1/provisioning/alert-rules" | jq '.[] | {uid, title, folderUID, condition}'The `grafana` segment in the first path is the literal `{am_name}` for the built-in Alertmanager; external Alertmanagers use their datasource UID. Resources written via the provisioning API are locked in the UI unless you send `X-Disable-Provenance: true` on the write.
`startsAt`/`endsAt` are RFC-3339 (`date -u +%Y-%m-%dT%H:%M:%SZ`).
# active silences
grafana "${GRAFANA_URL}/api/alertmanager/grafana/api/v2/silences" | \
jq '.[] | select(.status.state=="active") | {id, comment, matchers, endsAt}'
# create a 2-hour silence matching a label
# GNU date; on BSD/macOS use: date -u -v+2H +%Y-%m-%dT%H:%M:%SZ
START=$(date -u +%Y-%m-%dT%H:%M:%SZ)
END=$(date -u -d "+2 hours" +%Y-%m-%dT%H:%M:%SZ)
grafana -X POST "${GRAFANA_URL}/api/alertmanager/grafanPlugins for @claude that connect the agent to common SaaS services. Each service is its own plugin, so a workspace can connect exactly the services it uses.
Read and manage Asana tasks, projects, sections, comments, and workspaces. Use this whenever the user wants to list or search…
Run SQL against Google BigQuery and browse its catalog — submit queries (sync or async), poll job status, page through results,…
Compose polished charts (timeseries, bar, line, area, pie, scatter, or anything else the data calls for) from tabular data using…
Automatic init the first time Claude acts in an incident or alerts channel — one named like #inc-…, #incident-…, #sev0-…/#sev1-…,…
Investigate an alert, page, or production symptom in an incident channel or a team's oncall / monitoring channel, and report…
Write the postmortem for an incident, in its incident channel, once it is mitigated or resolved. Use on "write up this incident",…