datadog-mcp 5.7.0 → 5.8.1

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
@@ -158,7 +158,7 @@ When running with `--transport=http`:
158
158
  | `events` | get | Events | Get event by ID | `events_read` |
159
159
  | `events` | create | Events | Create an event | `events_read` |
160
160
  | `events` | search | Events | Search events with v2 API and cursor pagination. Optional `transitionType` filter (e.g. `["alert","alert recovery"]`) restricts to monitor state-transition events — without it, `source:alert` includes renotifies. For monitor-specific fires use `monitors action=history`. Optional `timezone` adds `*Local` ISO 8601 siblings to every timestamp. Zero-result responses include a `diagnostics` array hinting at the cause (`UNINDEXED_TAG_PREFIX`, `NARROW_TIME_RANGE`, `RESTRICTIVE_SOURCE_FILTER`). | `events_read` |
161
- | `events` | histogram | Events | Server-side bucketing of events by `hour_of_day`, `day_of_week`, or `day_of_month` in an IANA `timezone` (DST-safe via `Intl.DateTimeFormat`). Cursor-paginates the underlying search; cap at `limits.maxEventsForHistogram` (default 5000, `MCP_MAX_EVENTS_HISTOGRAM` env var). When the cap is hit, returns `bucketCountIncomplete: true` and `nextCursor` for continuation. | `events_read` |
161
+ | `events` | histogram | Events | Server-side bucketing of events by `hour_of_day`, `day_of_week`, or `day_of_month` in an IANA `timezone` (DST-safe via `Intl.DateTimeFormat`). Accepts the same `transitionType` filter as `search` so monitor histograms can exclude renotifies. Cursor-paginates the underlying search; cap at `limits.maxEventsForHistogram` (default 5000, `MCP_MAX_EVENTS_HISTOGRAM` env var). When the cap is hit, returns `bucketCountIncomplete: true` and `nextCursor` for continuation. | `events_read` |
162
162
  | `events` | aggregate | Events | Client-side aggregation by monitor_name, source, etc. | `events_read` |
163
163
  | `events` | top | Events | Top N event groups by count with generic groupBy support (deployments, configs, alerts, etc.). Groups without context tags are included as "no_context" | `events_read` |
164
164
  | `events` | timeseries | Events | Time-bucketed alert trends (hourly/daily counts) | `events_read` |
package/dist/index.js CHANGED
@@ -302786,6 +302786,10 @@ function buildRumSessionUrl(applicationId, sessionId, site = "datadoghq.com") {
302786
302786
  const base = getAppBaseUrl(site);
302787
302787
  return `${base}/rum/replay/sessions/${sessionId}?applicationId=${encodeURIComponent(applicationId)}`;
302788
302788
  }
302789
+ function buildSloUrl(sloId, site = "datadoghq.com") {
302790
+ const base = getAppBaseUrl(site);
302791
+ return `${base}/slo/${sloId}`;
302792
+ }
302789
302793
 
302790
302794
  // src/utils/time.ts
302791
302795
  function hoursAgo(hours) {
@@ -303150,8 +303154,41 @@ var InputSchema = {
303150
303154
  ),
303151
303155
  timezone: external_exports.string().optional().describe(
303152
303156
  'Optional IANA timezone (e.g. "UTC", "Europe/Paris"). DST-safe. For histogram: controls hour/day bucketing (default: UTC). For search/aggregate/top/incidents read actions: adds sibling *Local ISO 8601 strings (e.g. timestampLocal) next to existing timestamps. Omit for byte-identical legacy shape.'
303157
+ ),
303158
+ // Projection — applied to search action only. Reduces response payload
303159
+ // when callers only need a subset of fields. Unknown field names are silently ignored so
303160
+ // older clients are not broken when the EventSummary shape evolves.
303161
+ fields: external_exports.array(external_exports.string()).optional().describe(
303162
+ "Search action only: return only these event fields. Allowed values: id, title, message, timestamp, priority, source, tags, alertType, host, monitorId, monitorInfo, monitorMetadata (only populated when enrich=true). Default: full event."
303153
303163
  )
303154
303164
  };
303165
+ var ALLOWED_EVENT_FIELDS = /* @__PURE__ */ new Set([
303166
+ "id",
303167
+ "title",
303168
+ "message",
303169
+ "timestamp",
303170
+ "priority",
303171
+ "source",
303172
+ "tags",
303173
+ "alertType",
303174
+ "host",
303175
+ "monitorId",
303176
+ "monitorInfo",
303177
+ "monitorMetadata"
303178
+ ]);
303179
+ function pickEventFields(event, fields) {
303180
+ if (!fields || fields.length === 0) {
303181
+ return event;
303182
+ }
303183
+ const projection = {};
303184
+ for (const key of fields) {
303185
+ if (ALLOWED_EVENT_FIELDS.has(key)) {
303186
+ ;
303187
+ projection[key] = event[key];
303188
+ }
303189
+ }
303190
+ return projection;
303191
+ }
303155
303192
  function annotateEventTimezone(event, tz) {
303156
303193
  if (!event.timestamp) return event;
303157
303194
  const ms = new Date(event.timestamp).getTime();
@@ -303585,7 +303622,8 @@ async function histogramEventsV2(api, params, limits, site) {
303585
303622
  const fullQuery = buildEventQuery({
303586
303623
  query: params.query,
303587
303624
  sources: params.sources,
303588
- tags: params.tags
303625
+ tags: params.tags,
303626
+ transitionType: params.transitionType
303589
303627
  });
303590
303628
  const cap = limits.maxEventsForHistogram;
303591
303629
  const perPage = Math.max(1, Math.min(1e3, cap));
@@ -304185,7 +304223,8 @@ histogram: Bucket events by local hour_of_day / day_of_week / day_of_month in th
304185
304223
  maxEvents,
304186
304224
  transitionType,
304187
304225
  bucket_by,
304188
- timezone
304226
+ timezone,
304227
+ fields
304189
304228
  }) => {
304190
304229
  try {
304191
304230
  checkReadOnly(action, readOnly);
@@ -304243,9 +304282,11 @@ histogram: Bucket events by local hour_of_day / day_of_week / day_of_month in th
304243
304282
  );
304244
304283
  if (enrich && result.events.length > 0) {
304245
304284
  const enrichedEvents = await enrichWithMonitorMetadata(result.events, monitorsApi);
304246
- return toolResult({ ...result, events: enrichedEvents });
304285
+ const projected = fields?.length ? enrichedEvents.map((e) => pickEventFields(e, fields)) : enrichedEvents;
304286
+ return toolResult({ ...result, events: projected });
304247
304287
  }
304248
- return toolResult(result);
304288
+ const projectedEvents = fields?.length ? result.events.map((e) => pickEventFields(e, fields)) : result.events;
304289
+ return toolResult({ ...result, events: projectedEvents });
304249
304290
  }
304250
304291
  case "aggregate":
304251
304292
  return toolResult(
@@ -304354,7 +304395,8 @@ histogram: Bucket events by local hour_of_day / day_of_week / day_of_month in th
304354
304395
  tags,
304355
304396
  bucket_by: histogramBucketBy,
304356
304397
  timezone,
304357
- cursor
304398
+ cursor,
304399
+ transitionType
304358
304400
  },
304359
304401
  limits,
304360
304402
  site
@@ -306797,10 +306839,11 @@ var InputSchema8 = {
306797
306839
  from: external_exports.string().optional().describe('Start time for history (ISO 8601 or relative like "7d", "1w")'),
306798
306840
  to: external_exports.string().optional().describe("End time for history (ISO 8601 or relative, default: now)")
306799
306841
  };
306800
- function formatSlo(s) {
306842
+ function formatSlo(s, site = "datadoghq.com") {
306801
306843
  const primaryThreshold = s.thresholds?.[0];
306844
+ const id = s.id ?? "";
306802
306845
  const summary = {
306803
- id: s.id ?? "",
306846
+ id,
306804
306847
  name: s.name ?? "",
306805
306848
  description: s.description ?? null,
306806
306849
  type: String(s.type ?? "unknown"),
@@ -306815,7 +306858,8 @@ function formatSlo(s) {
306815
306858
  },
306816
306859
  overallStatus: [],
306817
306860
  createdAt: s.createdAt ? new Date(s.createdAt * 1e3).toISOString() : "",
306818
- modifiedAt: s.modifiedAt ? new Date(s.modifiedAt * 1e3).toISOString() : ""
306861
+ modifiedAt: s.modifiedAt ? new Date(s.modifiedAt * 1e3).toISOString() : "",
306862
+ url: id ? buildSloUrl(id, site) : ""
306819
306863
  };
306820
306864
  if (s.query?.numerator && s.query.denominator) {
306821
306865
  summary.query = { numerator: s.query.numerator, denominator: s.query.denominator };
@@ -306831,11 +306875,12 @@ function formatSlo(s) {
306831
306875
  }
306832
306876
  return summary;
306833
306877
  }
306834
- function formatSearchSlo(slo) {
306878
+ function formatSearchSlo(slo, site = "datadoghq.com") {
306835
306879
  const attrs = slo.data?.attributes;
306836
306880
  const primaryThreshold = attrs?.thresholds?.[0];
306881
+ const id = slo.data?.id ?? "";
306837
306882
  const summary = {
306838
- id: slo.data?.id ?? "",
306883
+ id,
306839
306884
  name: attrs?.name ?? "",
306840
306885
  description: attrs?.description ?? null,
306841
306886
  type: String(attrs?.sloType ?? "unknown"),
@@ -306856,7 +306901,8 @@ function formatSearchSlo(slo) {
306856
306901
  timeframe: String(os.timeframe ?? "")
306857
306902
  })),
306858
306903
  createdAt: attrs?.createdAt ? new Date(attrs.createdAt * 1e3).toISOString() : "",
306859
- modifiedAt: attrs?.modifiedAt ? new Date(attrs.modifiedAt * 1e3).toISOString() : ""
306904
+ modifiedAt: attrs?.modifiedAt ? new Date(attrs.modifiedAt * 1e3).toISOString() : "",
306905
+ url: id ? buildSloUrl(id, site) : ""
306860
306906
  };
306861
306907
  if (attrs?.query?.numerator && attrs.query.denominator) {
306862
306908
  summary.query = { numerator: attrs.query.numerator, denominator: attrs.query.denominator };
@@ -306875,14 +306921,14 @@ function buildSearchQuery(query, tags) {
306875
306921
  if (tags?.length) parts.push(...tags);
306876
306922
  return parts.join(" ");
306877
306923
  }
306878
- async function listSlos(api, params, limits) {
306924
+ async function listSlos(api, params, limits, site = "datadoghq.com") {
306879
306925
  const effectiveLimit = params.limit ?? limits.defaultLimit;
306880
306926
  if (params.ids?.length) {
306881
306927
  const response2 = await api.listSLOs({
306882
306928
  ids: params.ids.join(","),
306883
306929
  limit: effectiveLimit
306884
306930
  });
306885
- const slos3 = (response2.data ?? []).map(formatSlo);
306931
+ const slos3 = (response2.data ?? []).map((s) => formatSlo(s, site));
306886
306932
  return { slos: slos3, total: slos3.length };
306887
306933
  }
306888
306934
  const searchQuery = buildSearchQuery(params.query, params.tags);
@@ -306891,16 +306937,16 @@ async function listSlos(api, params, limits) {
306891
306937
  pageSize: effectiveLimit
306892
306938
  });
306893
306939
  const searchSlos = response.data?.attributes?.slos ?? [];
306894
- const slos2 = searchSlos.map(formatSearchSlo);
306940
+ const slos2 = searchSlos.map((s) => formatSearchSlo(s, site));
306895
306941
  return {
306896
306942
  slos: slos2,
306897
306943
  total: slos2.length
306898
306944
  };
306899
306945
  }
306900
- async function getSlo(api, id) {
306946
+ async function getSlo(api, id, site = "datadoghq.com") {
306901
306947
  const response = await api.getSLO({ sloId: id });
306902
306948
  return {
306903
- slo: response.data ? formatSlo(response.data) : null
306949
+ slo: response.data ? formatSlo(response.data, site) : null
306904
306950
  };
306905
306951
  }
306906
306952
  function normalizeSloConfig(config2) {
@@ -306916,20 +306962,20 @@ function normalizeSloConfig(config2) {
306916
306962
  }
306917
306963
  return normalized;
306918
306964
  }
306919
- async function createSlo(api, config2) {
306965
+ async function createSlo(api, config2, site = "datadoghq.com") {
306920
306966
  const body = normalizeSloConfig(config2);
306921
306967
  const response = await api.createSLO({ body });
306922
306968
  return {
306923
306969
  success: true,
306924
- slo: response.data?.[0] ? formatSlo(response.data[0]) : null
306970
+ slo: response.data?.[0] ? formatSlo(response.data[0], site) : null
306925
306971
  };
306926
306972
  }
306927
- async function updateSlo(api, id, config2) {
306973
+ async function updateSlo(api, id, config2, site = "datadoghq.com") {
306928
306974
  const body = normalizeConfigKeys(config2);
306929
306975
  const response = await api.updateSLO({ sloId: id, body });
306930
306976
  return {
306931
306977
  success: true,
306932
- slo: response.data?.[0] ? formatSlo(response.data[0]) : null
306978
+ slo: response.data?.[0] ? formatSlo(response.data[0], site) : null
306933
306979
  };
306934
306980
  }
306935
306981
  async function deleteSlo(api, id) {
@@ -306969,29 +307015,29 @@ async function getSloHistory(api, id, params) {
306969
307015
  }
306970
307016
  };
306971
307017
  }
306972
- function registerSlosTool(server, api, limits, readOnly = false, _site = "datadoghq.com") {
307018
+ function registerSlosTool(server, api, limits, readOnly = false, site = "datadoghq.com") {
306973
307019
  server.tool(
306974
307020
  "slos",
306975
- "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.",
307021
+ "Manage Datadog Service Level Objectives. Actions: list (with SLI status & error budget), get, create, update, delete, history. SLO types: metric-based, monitor-based. Each list/get/create/update response includes a `url` field deep-linking to the Datadog UI. Use for: reliability tracking, error budgets, SLA compliance, performance targets.",
306976
307022
  InputSchema8,
306977
307023
  async ({ action, id, ids, query, tags, limit, config: config2, from, to }) => {
306978
307024
  try {
306979
307025
  checkReadOnly(action, readOnly);
306980
307026
  switch (action) {
306981
307027
  case "list":
306982
- return toolResult(await listSlos(api, { ids, query, tags, limit }, limits));
307028
+ return toolResult(await listSlos(api, { ids, query, tags, limit }, limits, site));
306983
307029
  case "get": {
306984
307030
  const sloId = requireParam(id, "id", "get");
306985
- return toolResult(await getSlo(api, sloId));
307031
+ return toolResult(await getSlo(api, sloId, site));
306986
307032
  }
306987
307033
  case "create": {
306988
307034
  const sloConfig = requireParam(config2, "config", "create");
306989
- return toolResult(await createSlo(api, sloConfig));
307035
+ return toolResult(await createSlo(api, sloConfig, site));
306990
307036
  }
306991
307037
  case "update": {
306992
307038
  const sloId = requireParam(id, "id", "update");
306993
307039
  const sloConfig = requireParam(config2, "config", "update");
306994
- return toolResult(await updateSlo(api, sloId, sloConfig));
307040
+ return toolResult(await updateSlo(api, sloId, sloConfig, site));
306995
307041
  }
306996
307042
  case "delete": {
306997
307043
  const sloId = requireParam(id, "id", "delete");