MCPInstall

dt-obs-analytics

Analyze dashboards and notebooks using Davis analyzers — anomaly detection, novelty scoring, and correlation. Use when the user references a specific Dynatrace dashboard or notebook (by URL, UUID, or name) and asks what it shows, which DQL queries it runs, whether a tile looks off, or wants to find anomalies, score novelty, or correlate its metrics. The trigger is a dashboard or notebook as the data source, not a general DQL question. This skill extracts timeseries queries efficiently without reading the full raw document JSON, then optionally runs Davis analyzers on the extracted metrics. Trigger phrases: "what's wrong on this dashboard", "analyze this notebook", "find anomalies", "novelty score", "correlate metrics", "extract DQL from dashboard", "dashboard URL", "tile", "run-analyzer", "timeseries extraction", "Davis analyzer".

Installs
1.1K
Last 24h
+15
Trending rank
#7098
Security
Not audited

Install

Works with every agent the skills CLI supports.

npx skills add https://github.com/dynatrace/dynatrace-for-ai --skill dt-obs-analytics
  1. 01

    Install

    Run the command in your project. The CLI asks which agents to install for.

  2. 02

    Describe the task

    Ask your agent for something this skill covers. It loads dt-obs-analytics automatically when relevant.

  3. 03

    Review the result

    The agent follows the SKILL.md steps, scripts and references bundled with the skill.

SKILL.md

The instructions your agent reads when the skill is triggered.

Analytics — Dashboard & Notebook Query Extraction

A pipeline of three platform JavaScript scripts under scripts/ — two extractors feeding a shared analyzer runner:

scripts/extract-timeseries-dashboard.js ──┐
scripts/extract-timeseries-notebook.js  ──┴──► queryset.json ──► scripts/run-analyzer.js (any Davis analyzer)

Each script is invoked via:

dtctl exec function -f scripts/<script>.js --payload '<json>' -o json

-o json wraps the function return value under .result. When you need to inspect the result, read it directly from the output — no jq required. Pass the full raw output as queries to run-analyzer.js and it unwraps automatically.

Parsing a dashboard URL

When the entry point is a Dynatrace dashboard URL, extract the three components the scripts need:

https://<tenant>/ui/apps/dynatrace.dashboards/dashboard/<ID>#from=<from>&to=<to>&vfilter_<name>=<val>...
URL partScript destination
Path segment after /dashboard/ (before #)id in extract-timeseries-dashboard.js payload
#from= value (URL-decode %3A → :)timeframe.startTime in run-analyzer.js (only needed if running analysis)
#to= value (URL-decode)timeframe.endTime in run-analyzer.js (only needed if running analysis)
vfilter_<name>=<value> paramsvariables map in run-analyzer.js (strip vfilter_ prefix)

The timeframe in the URL fragment is the dashboard's display window. It is not injected into the extracted DQL — the extractor returns the DQL verbatim with its original $variable tokens and any embedded | timeframe clauses intact. Use the parsed from/to values only when calling run-analyzer.js to set the analysis window. If the user just wants to list the queries, the timeframe is informational only.

Note: when you pass run-analyzer.js an absolute timeframe.startTime (not a now... expression), it strips any embedded | timeframe ... stage from the DQL before analysis, so the analyzer honors your requested window rather than the query's baked-in one. For relative (now...) windows the embedded | timeframe is left intact. This means the query actually analyzed can differ from the extracted text — expected behavior, noted here so results line up with the window you asked for.

Quick bash parse (pure bash + sed/awk — no python needed):

DASHBOARD_URL="https://abc123.apps.dynatrace.com/ui/apps/dynatrace.dashboards/dashboard/5bea16c7-029b-43b6-9735-459db2d25bbf#from=2026-05-28T04%3A00Z&to=2026-05-28T05%3A00Z&vfilter_host_group=prod&vfilter_workload=my-svc"

# Minimal URL-decoder: turn %XX into \xXX and let printf interpret it.
urldecode() { local s="${1//+/ }"; printf '%b' "${s//%/\\x}"; }

DOC_ID=$(echo "$DASHBOARD_URL" | sed 's/#.*//' | awk -F/ '{print $NF}')
FROM=$(urldecode "$(echo "$DASHBOARD_URL" | sed -n 's/.*[#&]from=\([^&]*\).*/\1/p')")
TO=$(urldecode "$(echo "$DASHBOARD_URL"   | sed -n 's/.*[#&]to=\([^&]*\).*/\1/p')")
HOST_GROUP=$(echo "$DASHBOARD_URL" | sed -n 's/.*[#&]vfilter_host_group=\([^&]*\).*/\1/p')
WORKLOAD=$(echo "$DASHBOARD_URL"   | sed -n 's/.*[#&]vfilter_workload=\([^&]*\).*/\1/p')

For notebooks: path segment after /notebook/, or #share= value for /document/v0/#share=<ID> links.

Step 1 — Extract queries

From a dashboard

# All tiles
dtctl exec function -f scripts/extract-timeseries-dashboard.js \
  --payload '{"id":"<dashboard-id-or-name>"}' -o json

# Only tiles whose title matches a name the user mentioned (e.g. "CPU usage", "Kafka lag")
dtctl exec function -f scripts/extract-timeseries-dashboard.js \
  --payload '{"id":"<dashboard-id>","titleFilter":"CPU usage"}' -o json

When the user names a specific tile, chart, or section, pass its name as titleFilter rather than extracting the full dashboard. titleFilter is a case-insensitive substring or /regex/flags pattern. This keeps the queryset small and focused.

When it is not clear which tile(s) the user wants, do NOT extract all DQL — dashboards can have 20–50 tiles and returning all queries causes significant context bloat. Instead use a two-step flow:

  1. List tile names with listOnly: true (no DQL, just titles):
    dtctl exec function -f scripts/extract-timeseries-dashboard.js \
      --payload '{"id":"<dashboard-id>","listOnly":true}' -o json
    # Returns: {"result":{"ok":true,"tiles":[{"id":"...","title":"CPU Usage","visualization":"lineChart"},...]}}
    
  2. Show the tile names to the user and ask which tile(s) they mean.
  3. Re-run with titleFilter for only the tile(s) of interest.

This avoids pulling 20–50 DQL queries into context when only 1–2 are relevant.

Payload knobs:

  • id (required) — dashboard ID (UUID) or exact name. Preset IDs like my.dynatrace.infraops.preview.* work.
  • titleFilter — case-insensitive substring ("CPU usage") or /regex/flags ("/^kafka/i").
  • listOnly — when true, returns tiles: [{id, title, visualization}] without DQL. Use for disambiguation.
  • compact — when true, returns only {id, title, dqlQuery} per tile (drops description, visualization, isTimeseries). Saves ~40% per-tile tokens. In listOnly mode, drops visualization too.
  • includeSkipped — when true, returns the full skipped[] array. Default: only skippedCount is returned.

Response envelope:

{
  "ok": true,
  "documentId": "...", "documentName": "...", "documentVersion": 7,
  "queries": [
    { "id": "<tile-key>", "title": "...", "description": "...",
      "dqlQuery": "timeseries avg(dt.host.cpu.usage)",
      "visualization": "lineChart", "isTimeseries": true }
  ],
  "skipped": [{ "id": "...", "reason": "non-data tile (markdown)" }]
}

On failure: { "ok": false, "error": { "code": "...", "message": "..." } }.

From a notebook

Same envelope, different schema walk:

# All cells
dtctl exec function -f scripts/extract-timeseries-notebook.js \
  --payload '{"id":"<notebook-id-or-name>"}' -o json

# A specific section (if cell titles are set)
dtctl exec function -f scripts/extract-timeseries-notebook.js \
  --payload '{"id":"<notebook-id>","titleFilter":"JVM memory"}' -o json

Notebook cells often have empty titles — prefer addressing cells by id from the envelope if targeting a specific one.

Step 2 — Run an analyzer

Save the extractor output to a file, then pass it via shell substitution — the shell reads the file, so the JSON never enters the model's context:

# Run extractor, save output
dtctl exec function -f scripts/extract-timeseries-dashboard.js \
  --payload '{"id":"<id>","titleFilter":"CPU usage","compact":true}' -o json > queryset.json

# Shell substitution: $(cat queryset.json) is expanded by the shell, not the model
dtctl exec function -f scripts/run-analyzer.js \
  --payload '{
    "analyzerName": "dt.statistics.NoveltyScoreAnalyzer",
    "queries": '"$(cat queryset.json)"',
    "timeframe": { "startTime": "...", "endTime": "..." },
    "analyzerParams": { ... }
  }' -o json

run-analyzer.js unwraps the {"result":{...}} dtctl envelope automatically — pass the raw saved output as-is. No jq or parsing step is needed: normalization of the array / envelope / dtctl-output shapes happens inside the script.

For large querysets (many tiles), the inline $(cat ...) form can hit shell argument-length limits. Build the payload file and use dtctl's --data flag instead — still no jq and still out of model context:

{ printf '{"analyzerName":"dt.statistics.NoveltyScoreAnalyzer","timeframe":{"startTime":"now-1h","endTime":"now"},"queries":'
  cat queryset.json
  printf '}'; } > payload.json

dtctl exec function -f scripts/run-analyzer.js --data payload.json -o json

Key payload knobs for run-analyzer.js:

  • minScore — drop results below this threshold (e.g. 0.5). Auto-detects score field from noveltyScore, anomalyScore, correlationCoefficient, correlation, coefficient. Pass scoreField to override.
  • scoreField — explicit field name to read score from (e.g. "noveltyScore").

The queries field accepts any of: a raw array, the extractor envelope ({queries:[...]}), or the full dtctl output ({"result":{"queries":[...]}}). All three are normalized automatically.

Common analyzers

GoalanalyzerNameanalyzerParams
Find anomalous metricsdt.statistics.anomaly_detection.SeasonalBaselineAnomalyDetectionAnalyzer{ "trainingTimeframe": { "startTime": "now-8d", "endTime": "now-1d" } } (optional)
Score how novel each metric isdt.statistics.NoveltyScoreAnalyzer{ "detectionMode": "ALL", "minNoveltyScore": 0 } (optional)
Correlate against a primary metricdt.statistics.SimplePearsonCorrelationAnalyzerset metricQuery instead (changes call shape)

Correlation mode

Pass metricQuery to correlate every query in the set against a single primary DQL string:

dtctl exec function -f scripts/run-analyzer.js \
  --payload '{
    "analyzerName": "dt.statistics.SimplePearsonCorrelationAnalyzer",
    "queries": '"$(cat queryset.json)"',
    "metricQuery": "<dqlQuery of the primary tile, copied from extractor output>",
    "timeframe": { "startTime": "...", "endTime": "..." }
  }' -o json

When chaining from a previous analyzer run (e.g. anomaly detection → correlation), use metricQueryFrom instead. The script picks the highest-scored result's dqlQuery automatically:

dtctl exec function -f scripts/run-analyzer.js \
  --payload '{
    "analyzerName": "dt.statistics.SimplePearsonCorrelationAnalyzer",
    "queries": '"$(cat queryset.json)"',
    "metricQueryFrom": '"$(cat findings.json)"',
    "timeframe": { "startTime": "...", "endTime": "..." }
  }' -o json

metricQuery takes precedence if both are set.

Variable substitution

Dashboard queries often contain $variable tokens (from URL vfilter_* params). Pass them via variables to substitute before execution:

dtctl exec function -f scripts/run-analyzer.js \
  --payload '{
    "analyzerName": "dt.statistics.anomaly_detection.SeasonalBaselineAnomalyDetectionAnalyzer",
    "queries": [...],
    "timeframe": { "startTime": "2026-05-28T04:00Z", "endTime": "2026-05-28T05:00Z" },
    "variables": { "host_group": "prod", "workload": "my-svc" }
  }' -o json

Build variables from vfilter_* URL params by stripping the vfilter_ prefix. Trailing * wildcards are stripped automatically. Unresolved tokens are cleaned up from DQL filter clauses rather than left to error.

Response shape

{
  "ok": true,
  "checkedAt": "...",
  "analyzerName": "...",
  "summary": { "checked": 12, "completed": 11, "errors": 1 },
  "results": [
    {
      "id": "tile-key", "title": "CPU usage", "dqlQuery": "...",
      "output": <raw analyzer output>,
      "executionStatus": "COMPLETED"
    }
  ],
  "errors": [ { "id": "...", "error": "..." } ]
}

The output field is the raw analyzer result. Interpret it based on the analyzer:

  • Anomaly detection: look for anomalyScore, anomalies[], or raisedAlerts[] in each output entry. Score ≥ 0.7 → abnormal, ≥ 0.4 → borderline.
  • Novelty: look for noveltyScore (or the closest score-like numeric field). Score ≥ 0.7 → novel.
  • Correlation: look for correlationCoefficient. Sort by |correlationCoefficient| descending; drop entries where |correlationCoefficient| < 0.5.

End-to-end: "what's abnormal on this dashboard?"

# 1. Extract queries — shell reads file, JSON stays out of model context
dtctl exec function -f scripts/extract-timeseries-dashboard.js \
  --payload '{"id":"5bea16c7-029b-43b6-9735-459db2d25bbf","compact":true}' \
  -o json > queryset.json

# 2. Run anomaly detection — $(cat queryset.json) expanded by shell, not model
dtctl exec function -f scripts/run-analyzer.js \
  --payload '{
    "analyzerName": "dt.statistics.anomaly_detection.SeasonalBaselineAnomalyDetectionAnalyzer",
    "queries": '"$(cat queryset.json)"',
    "timeframe": { "startTime": "2026-05-28T04:00Z", "endTime": "2026-05-28T05:00Z" },
    "variables": { "host_group": "prod", "workload": "my-svc" }
  }' -o json > findings.json

# 3. Correlate — metricQueryFrom picks the top finding automatically
dtctl exec function -f scripts/run-analyzer.js \
  --payload '{
    "analyzerName": "dt.statistics.SimplePearsonCorrelationAnalyzer",
    "queries": '"$(cat queryset.json)"',
    "metricQueryFrom": '"$(cat findings.json)"',
    "timeframe": { "startTime": "2026-05-28T04:00Z", "endTime": "2026-05-28T05:00Z" }
  }' -o json

Verifying a single extracted query

Read the dqlQuery field from the extractor output and pass it directly:

dtctl query --query "<dqlQuery copied from extractor output>" -o json | head -40

Gotchas

  • Never read raw dashboard JSON yourself. dtctl get dashboard <id> -o json is typically 50–200 KB. The extractor reads it on the platform and returns a compact envelope (~5–15 KB).
  • Never extract all tiles when only one is needed. A 50-tile dashboard returns 50 DQL queries into context. If the user names a tile, use titleFilter. If it's ambiguous, use listOnly: true first to ask which tile — then extract only that one.
  • Variables are required for filtered dashboards. Queries with unsubstituted $variable tokens silently drop entity filters (e.g. in(field, $undefined) evaluates to true). Always pass variables when the URL has vfilter_* params.
  • Schema drift. If a tile lands in skipped with reason no DQL query found, the dashboard schema has a query location the extractor doesn't know about — add it to the pickQuery candidate list in the script.
  • Analyzer availability. Not all Davis analyzers are available on every tenant. If a call comes back with Could not find an analyzer with name '...' or is not a function, list what's actually registered: dtctl get analyzers -o json.
  • Statistical fallback removed. run-analyzer.js only calls Davis analyzers. For historical anomaly detection, pass a long trainingTimeframe via analyzerParams (e.g. { "trainingTimeframe": { "startTime": "now-30d", "endTime": "now-1d" } }), or query the DQL directly.
  • Comments in queries. Queries starting with // comment lines are classified correctly by the extractor (leading line/block comments are stripped before the timeseries check).

Scripts reference

  • scripts/extract-timeseries-dashboard.js — extracts timeseries DQL from a dashboard
  • scripts/extract-timeseries-notebook.js — same for notebooks
  • scripts/run-analyzer.js — generic Davis analyzer runner

FAQ

What does the dt-obs-analytics skill do?

Analyze dashboards and notebooks using Davis analyzers — anomaly detection, novelty scoring, and correlation. Use when the user references a specific Dynatrace dashboard or notebook (by URL, UUID, or name) and asks what it shows, which DQL queries it runs, whether a tile looks off, or wants to find anomalies, score novelty, or correlate its metrics. The trigger is a dashboard or notebook as the data source, not a general DQL question. This skill extracts timeseries queries efficiently without reading the full raw document JSON, then optionally runs Davis analyzers on the extracted metrics. Trigger phrases: "what's wrong on this dashboard", "analyze this notebook", "find anomalies", "novelty score", "correlate metrics", "extract DQL from dashboard", "dashboard URL", "tile", "run-analyzer", "timeseries extraction", "Davis analyzer".

How do I install dt-obs-analytics?

Run npx skills add https://github.com/dynatrace/dynatrace-for-ai --skill dt-obs-analytics in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/dt-obs-analytics/ (personal) or .claude/skills/dt-obs-analytics/ (project).

Which AI agents can use dt-obs-analytics?

Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.

Is dt-obs-analytics safe to install?

No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.

Who maintains dt-obs-analytics?

It is published by dynatrace in dynatrace/dynatrace-for-ai. It has 1,124 recorded installs.

More from dynatrace/dynatrace-for-ai

View all →

dt-dql-essentials

dynatrace/dynatrace-for-ai

Core DQL syntax, pitfalls, query patterns, and query optimization. Load to write, build, fix, or OPTIMIZE a DQL query — prevents syntax errors and makes queries faster, more efficient, and cheaper (less data scanned = lower query consumption/cost per run). Covers fetch commands, data models, field namespaces, time alignment, entity/smartscape patterns, metric discovery, and performance/cost optimization (filter early, bucket filters, short time ranges, field selection, sampling, cardinality). Trigger: "write/build/fix a DQL query", "DQL syntax", "query logs/spans/metrics", "create a timeseries", "optimize my DQL", "make my query faster/cheaper", "reduce DQL cost/consumption/scanned data", "keep DQL cost under control". Do NOT use to explain an existing query or answer product questions. For MONITORING a tenant's ACTUAL query consumption/billing (how much queries cost, who scanned most, cost trends) use dt-platform-costs — this tunes the query text, not billing data.

DevOps & Cloud2.8K installs

dt-app-dashboards

dynatrace/dynatrace-for-ai

Work with Dynatrace dashboards - create, modify, query, and analyze dashboard JSON including tiles, layouts, DQL queries, variables, and visualizations.

Data & Databases2.8K installs

dt-obs-logs

dynatrace/dynatrace-for-ai

Log querying, filtering, pattern analysis, and error rate calculation. Use when searching application or infrastructure logs, analyzing error patterns, or correlating log data. Trigger: "show error logs", "search logs for keyword", "log error rate", "recent errors", "logs from last hour", "find log entries", "top error messages", "log patterns", "parse JSON logs", "logs by process group", "log trends over time", "log entry counts per minute". Do NOT use for explaining existing queries, product documentation questions, distributed tracing or span analysis (use dt-obs-tracing).

DevOps & Cloud2.7K installs

dt-obs-problems

dynatrace/dynatrace-for-ai

DAVIS problem analysis including root cause identification, impact assessment, and correlation with other telemetry. Use when querying or investigating detected problems. Trigger: "active problems", "root cause analysis", "problem impact", "affected users", "list problems", "P-12345 details", "recurring problems", "problem history", "problem trending", "blast radius", "which entity caused the problem", "problems affecting Kubernetes", "problems by service". Do NOT use for explaining existing queries, product documentation questions, generic log searching, distributed tracing, or host-level resource monitoring.

DevOps & Cloud2.7K installs

dt-app-notebooks

dynatrace/dynatrace-for-ai

Work with Dynatrace notebooks - create, modify, query, and analyze notebook JSON including sections, DQL queries, and visualizations.

2.7K installs

dt-obs-tracing

dynatrace/dynatrace-for-ai

Distributed traces, spans, service dependencies, and request flow analysis. Use when investigating span-level details, failures, performance bottlenecks, or trace correlation. Trigger: "trace analysis", "slow requests", "failed spans", "service dependencies", "distributed trace", "span details", "HTTP status codes in traces", "database query spans", "messaging spans", "gRPC calls", "Lambda cold starts", "trace ID lookup", "exception analysis", "correlate logs and traces", "request attributes". Do NOT use for explaining existing queries, product documentation or configuration questions, service-level RED metrics (use dt-obs-services), log searching (use dt-obs-logs), or problem analysis (use dt-obs-problems).

Backend & APIs2.6K installs

Popular data & databases skills

View category →

Postgres best practices maintained by Supabase, for Postgres running anywhere. Load this skill BEFORE writing or changing anything that lives in a Postgres database: creating or altering tables and columns (including choosing column types), schema design, migrations and declarative schema files, RLS policies and the tests that verify them, indexes, triggers, database functions, queues and scheduled jobs (pgcron, pgmq), vector/semantic search (pgvector), and restoring dumps (pgrestore) or importing data. Also load it when diagnosing slow queries, high CPU, timeouts, EXPLAIN plans, connection exhaustion, locking, bloat, or rows visible to the wrong user or tenant. This is not just a performance guide — schema, migration, security, and SQL authoring tasks need these rules too, even for a one-column change or a single query.

OfficialData & Databases462.2K installs

prisma-client-api

prisma/skills

Prisma Client API reference covering model queries, filters, operators, and client methods. Use when writing database queries, using CRUD operations, filtering data, or configuring Prisma Client. Triggers on "prisma query", "findMany", "create", "update", "delete", "$transaction".

OfficialData & Databases393.5K installs

Deprecated compatibility name for prisma-orm-setup. Use only when a user or existing integration explicitly invokes prisma-database-setup; otherwise select prisma-orm-setup.

OfficialData & Databases392.3K installs

prisma-cli

prisma/skills

Prisma ORM 7 CLI reference for existing Prisma 7 applications or explicitly selected Prisma 7 workflows. Covers prisma init, generate, migrate, db, dev, studio, and mcp. For setup, connection repair, or Prisma 8, use prisma-orm-setup; not the Prisma Platform CLI.

OfficialData & Databases390.8K installs

prisma-postgres

prisma/skills

Deprecated compatibility name for prisma-postgres-setup. Use only when a user or existing integration explicitly invokes prisma-postgres; otherwise select prisma-postgres-setup.

OfficialData & Databases384.4K installs

Required reference for Prisma ORM 7 SQL driver adapter work. Use when implementing or modifying adapters, adding database drivers, or touching SqlDriverAdapter, Transaction, savepoint, result mapping, or DriverAdapterError behavior. Covers current transaction lifecycle, optional savepoint hooks, original database-error preservation, and verification.

OfficialData & Databases378.9K installs