datadog-mcp 5.3.0 → 5.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -461,7 +461,8 @@ Note: Enrichment adds latency (fetches monitor list). Use for detailed investiga
461
461
  1. **Find errors in logs**: `logs({ action: "search", status: "error", sample: "diverse" })`
462
462
  2. **Extract trace_id** from log attributes (`dd.trace_id`)
463
463
  3. **Get full trace**: `traces({ action: "search", query: "trace_id:<id>" })`
464
- 4. **Query APM metrics**: `metrics({ action: "query", query: "avg:trace.<service>.request.duration{*}" })`
464
+ 4. **Query APM metrics** (avg): `metrics({ action: "query", query: "avg:trace.express.request.duration{service:my-service}" })`
465
+ 5. **Query APM latency percentiles** (p95): `metrics({ action: "query", query: "p95:trace.express.request{service:my-service}" })` — note: use root metric without `.duration` suffix for percentiles
465
466
 
466
467
  ## Deep Links
467
468
 
package/dist/index.js CHANGED
@@ -2572,10 +2572,21 @@ function registerMetricsTool(server, metricsV1Api, metricsV2Api, limits, site =
2572
2572
  - metadata: Get metric details (unit, type, description)
2573
2573
 
2574
2574
  APM METRICS (auto-generated from traces):
2575
- - trace.{service}.hits - Request count
2576
- - trace.{service}.errors - Error count
2577
- - trace.{service}.duration - Latency (use avg:, p95:, max:)
2578
- Example: max:trace.{service}.request.duration{*}`,
2575
+ Keyed by OPERATION name (e.g. express.request, pg.query), NOT service name.
2576
+ Filter by service using tags: {service:my-service}
2577
+
2578
+ PERCENTILES (p50/p75/p90/p95/p99) \u2014 use the ROOT metric (distribution type):
2579
+ p95:trace.express.request{service:my-service}
2580
+
2581
+ AVG/SUM/MIN/MAX \u2014 use the .duration SUFFIX (pre-aggregated gauge):
2582
+ avg:trace.express.request.duration{service:my-service}
2583
+
2584
+ Other trace metrics (gauges):
2585
+ - trace.<operation>.hits - Request count
2586
+ - trace.<operation>.errors - Error count
2587
+ - trace.<operation>.apdex - Apdex score
2588
+
2589
+ To discover operation names for a service, use: traces tool with action "services"`,
2579
2590
  InputSchema5,
2580
2591
  async ({ action, query, from, to, metric, limit, pointLimit }) => {
2581
2592
  try {
@@ -2923,7 +2934,7 @@ function registerTracesTool(server, spansApi, _servicesApi, limits, site = "data
2923
2934
  server.tool(
2924
2935
  "traces",
2925
2936
  `Analyze APM traces for request flow and latency debugging. Actions: search (find spans), aggregate (group stats), services (list APM services). Key filters: minDuration/maxDuration ("500ms", "2s"), httpStatus ("5xx", ">=400"), status (ok/error), errorMessage (grep).
2926
- APM METRICS: Traces auto-generate metrics in trace.{service}.* namespace. Use metrics tool to query: avg:trace.{service}.request.duration{*}`,
2937
+ APM METRICS: Traces auto-generate metrics in trace.<operation>.* namespace (e.g. trace.express.request). Use metrics tool to query: avg:trace.express.request.duration{service:my-service}. For percentiles (p95), use the root metric WITHOUT .duration suffix: p95:trace.express.request{service:my-service}`,
2927
2938
  InputSchema6,
2928
2939
  async ({
2929
2940
  action,
@@ -3186,27 +3197,69 @@ function formatSlo(s) {
3186
3197
  timeframe: String(primaryThreshold?.timeframe ?? ""),
3187
3198
  tags: s.tags ?? [],
3188
3199
  status: {
3189
- // Note: SLI status requires a separate API call to getSLOHistory
3190
3200
  sli: null,
3191
3201
  errorBudgetRemaining: null,
3192
3202
  state: "unknown"
3193
3203
  },
3204
+ overallStatus: [],
3194
3205
  createdAt: s.createdAt ? new Date(s.createdAt * 1e3).toISOString() : "",
3195
3206
  modifiedAt: s.modifiedAt ? new Date(s.modifiedAt * 1e3).toISOString() : ""
3196
3207
  };
3197
3208
  }
3209
+ function formatSearchSlo(slo) {
3210
+ const attrs = slo.data?.attributes;
3211
+ const primaryThreshold = attrs?.thresholds?.[0];
3212
+ return {
3213
+ id: slo.data?.id ?? "",
3214
+ name: attrs?.name ?? "",
3215
+ description: attrs?.description ?? null,
3216
+ type: String(attrs?.sloType ?? "unknown"),
3217
+ targetThreshold: primaryThreshold?.target ?? 0,
3218
+ warningThreshold: primaryThreshold?.warning ?? null,
3219
+ timeframe: String(primaryThreshold?.timeframe ?? ""),
3220
+ tags: attrs?.allTags ?? [],
3221
+ status: {
3222
+ sli: attrs?.status?.sli ?? null,
3223
+ errorBudgetRemaining: attrs?.status?.errorBudgetRemaining ?? null,
3224
+ state: String(attrs?.status?.state ?? "unknown")
3225
+ },
3226
+ overallStatus: (attrs?.overallStatus ?? []).map((os) => ({
3227
+ sli: os.status ?? null,
3228
+ errorBudgetRemaining: os.errorBudgetRemaining ?? null,
3229
+ state: String(os.state ?? "unknown"),
3230
+ target: os.target ?? null,
3231
+ timeframe: String(os.timeframe ?? "")
3232
+ })),
3233
+ createdAt: attrs?.createdAt ? new Date(attrs.createdAt * 1e3).toISOString() : "",
3234
+ modifiedAt: attrs?.modifiedAt ? new Date(attrs.modifiedAt * 1e3).toISOString() : ""
3235
+ };
3236
+ }
3237
+ function buildSearchQuery(query, tags) {
3238
+ const parts = [];
3239
+ if (query) parts.push(query);
3240
+ if (tags?.length) parts.push(...tags);
3241
+ return parts.join(" ");
3242
+ }
3198
3243
  async function listSlos(api, params, limits) {
3199
3244
  const effectiveLimit = params.limit ?? limits.defaultLimit;
3200
- const response = await api.listSLOs({
3201
- ids: params.ids?.join(","),
3202
- query: params.query,
3203
- tagsQuery: params.tags?.join(","),
3204
- limit: effectiveLimit
3245
+ if (params.ids?.length) {
3246
+ const response2 = await api.listSLOs({
3247
+ ids: params.ids.join(","),
3248
+ limit: effectiveLimit
3249
+ });
3250
+ const slos3 = (response2.data ?? []).map(formatSlo);
3251
+ return { slos: slos3, total: slos3.length };
3252
+ }
3253
+ const searchQuery = buildSearchQuery(params.query, params.tags);
3254
+ const response = await api.searchSLO({
3255
+ query: searchQuery || void 0,
3256
+ pageSize: effectiveLimit
3205
3257
  });
3206
- const slos2 = (response.data ?? []).map(formatSlo);
3258
+ const searchSlos = response.data?.attributes?.slos ?? [];
3259
+ const slos2 = searchSlos.map(formatSearchSlo);
3207
3260
  return {
3208
3261
  slos: slos2,
3209
- total: response.data?.length ?? 0
3262
+ total: slos2.length
3210
3263
  };
3211
3264
  }
3212
3265
  async function getSlo(api, id) {
@@ -3298,7 +3351,7 @@ async function getSloHistory(api, id, params) {
3298
3351
  function registerSlosTool(server, api, limits, readOnly = false, _site = "datadoghq.com") {
3299
3352
  server.tool(
3300
3353
  "slos",
3301
- "Manage Datadog Service Level Objectives. Actions: list, get, create, update, delete, history. SLO types: metric-based, monitor-based. Use for: reliability tracking, error budgets, SLA compliance, performance targets.",
3354
+ "Manage Datadog Service Level Objectives. Actions: list (with SLI status & error budget), get, create, update, delete, history. SLO types: metric-based, monitor-based. Use for: reliability tracking, error budgets, SLA compliance, performance targets.",
3302
3355
  InputSchema8,
3303
3356
  async ({ action, id, ids, query, tags, limit, config, from, to }) => {
3304
3357
  try {