datadog-mcp 5.4.0 → 5.6.0

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/dist/index.js CHANGED
@@ -134929,7 +134929,7 @@ var require_InputSchema = __commonJS({
134929
134929
  "use strict";
134930
134930
  Object.defineProperty(exports, "__esModule", { value: true });
134931
134931
  exports.InputSchema = void 0;
134932
- var InputSchema21 = class _InputSchema {
134932
+ var InputSchema24 = class _InputSchema {
134933
134933
  constructor() {
134934
134934
  }
134935
134935
  /**
@@ -134939,8 +134939,8 @@ var require_InputSchema = __commonJS({
134939
134939
  return _InputSchema.attributeTypeMap;
134940
134940
  }
134941
134941
  };
134942
- exports.InputSchema = InputSchema21;
134943
- InputSchema21.attributeTypeMap = {
134942
+ exports.InputSchema = InputSchema24;
134943
+ InputSchema24.attributeTypeMap = {
134944
134944
  parameters: {
134945
134945
  baseName: "parameters",
134946
134946
  type: "Array<InputSchemaParameters>"
@@ -281731,10 +281731,10 @@ var require_object_inspect = __commonJS({
281731
281731
  }
281732
281732
  if (!isDate(obj) && !isRegExp(obj)) {
281733
281733
  var ys = arrObjKeys(obj, inspect);
281734
- var isPlainObject3 = gPO ? gPO(obj) === Object.prototype : obj instanceof Object || obj.constructor === Object;
281734
+ var isPlainObject4 = gPO ? gPO(obj) === Object.prototype : obj instanceof Object || obj.constructor === Object;
281735
281735
  var protoTag = obj instanceof Object ? "" : "null prototype";
281736
- var stringTag = !isPlainObject3 && toStringTag && Object(obj) === obj && toStringTag in obj ? $slice.call(toStr(obj), 8, -1) : protoTag ? "Object" : "";
281737
- var constructorTag = isPlainObject3 || typeof obj.constructor !== "function" ? "" : obj.constructor.name ? obj.constructor.name + " " : "";
281736
+ var stringTag = !isPlainObject4 && toStringTag && Object(obj) === obj && toStringTag in obj ? $slice.call(toStr(obj), 8, -1) : protoTag ? "Object" : "";
281737
+ var constructorTag = isPlainObject4 || typeof obj.constructor !== "function" ? "" : obj.constructor.name ? obj.constructor.name + " " : "";
281738
281738
  var tag = constructorTag + (stringTag || protoTag ? "[" + $join.call($concat.call([], stringTag || [], protoTag || []), ": ") + "] " : "");
281739
281739
  if (ys.length === 0) {
281740
281740
  return tag + "{}";
@@ -302583,7 +302583,10 @@ function createDatadogClients(config2) {
302583
302583
  usage: new import_datadog_api_client.v1.UsageMeteringApi(configuration),
302584
302584
  spans: new import_datadog_api_client.v2.SpansApi(configuration),
302585
302585
  services: new import_datadog_api_client.v2.ServiceDefinitionApi(configuration),
302586
- auth: new import_datadog_api_client.v1.AuthenticationApi(configuration)
302586
+ auth: new import_datadog_api_client.v1.AuthenticationApi(configuration),
302587
+ logsPipelines: new import_datadog_api_client.v1.LogsPipelinesApi(configuration),
302588
+ logsIndexes: new import_datadog_api_client.v1.LogsIndexesApi(configuration),
302589
+ logsArchives: new import_datadog_api_client.v2.LogsArchivesApi(configuration)
302587
302590
  };
302588
302591
  }
302589
302592
 
@@ -302661,7 +302664,8 @@ var WRITE_ACTIONS = /* @__PURE__ */ new Set([
302661
302664
  "unmute",
302662
302665
  "cancel",
302663
302666
  "add",
302664
- "trigger"
302667
+ "trigger",
302668
+ "reorder"
302665
302669
  ]);
302666
302670
  function checkReadOnly(action, readOnly) {
302667
302671
  if (readOnly && WRITE_ACTIONS.has(action)) {
@@ -302941,6 +302945,20 @@ var InputSchema = {
302941
302945
  ),
302942
302946
  maxEvents: external_exports.number().min(1).max(5e3).optional().describe(
302943
302947
  "Maximum events to fetch for grouping in top action (default: 5000, max: 5000). Higher = more accurate but slower"
302948
+ ),
302949
+ // Monitor transition filter (additive — see requirement 5.2 / monitors action=history)
302950
+ transitionType: external_exports.array(
302951
+ external_exports.enum([
302952
+ "alert",
302953
+ "alert recovery",
302954
+ "warning",
302955
+ "warning recovery",
302956
+ "no data",
302957
+ "no data recovery",
302958
+ "renotify"
302959
+ ])
302960
+ ).optional().describe(
302961
+ 'Filter events by monitor state transition type. When set, restricts results to events with @monitor.transition.transition_type matching any value. Use ["alert","alert recovery"] to count real fires/recoveries and skip renotifies. Empty array is treated as undefined (no filter). For a fires-only count by monitor ID, prefer monitors action=history.'
302944
302962
  )
302945
302963
  };
302946
302964
  function extractMonitorInfo(title) {
@@ -303168,6 +303186,9 @@ async function createEventV1(api, params) {
303168
303186
  }
303169
303187
  };
303170
303188
  }
303189
+ function quoteIfNeeded(value) {
303190
+ return /^[A-Za-z0-9_.-]+$/.test(value) ? value : `"${value}"`;
303191
+ }
303171
303192
  function buildEventQuery(params) {
303172
303193
  const parts = [];
303173
303194
  if (params.query) {
@@ -303185,6 +303206,10 @@ function buildEventQuery(params) {
303185
303206
  if (params.priority) {
303186
303207
  parts.push(`priority:${params.priority}`);
303187
303208
  }
303209
+ if (params.transitionType && params.transitionType.length > 0) {
303210
+ const inner = params.transitionType.map(quoteIfNeeded).join(" OR ");
303211
+ parts.push(`@monitor.transition.transition_type:(${inner})`);
303212
+ }
303188
303213
  return parts.length > 0 ? parts.join(" ") : "*";
303189
303214
  }
303190
303215
  async function searchEventsV2(api, params, limits, site) {
@@ -303200,7 +303225,8 @@ async function searchEventsV2(api, params, limits, site) {
303200
303225
  query: params.query,
303201
303226
  sources: params.sources,
303202
303227
  tags: params.tags,
303203
- priority: params.priority
303228
+ priority: params.priority,
303229
+ transitionType: params.transitionType
303204
303230
  });
303205
303231
  const effectiveLimit = params.limit ?? limits.defaultLimit;
303206
303232
  const body = {
@@ -303243,7 +303269,8 @@ async function aggregateEventsV2(api, params, limits, site) {
303243
303269
  const fullQuery = buildEventQuery({
303244
303270
  query: params.query,
303245
303271
  sources: params.sources,
303246
- tags: params.tags
303272
+ tags: params.tags,
303273
+ transitionType: params.transitionType
303247
303274
  });
303248
303275
  const groupByFields = params.groupBy ?? ["monitor_name"];
303249
303276
  const maxEventsToAggregate = 1e4;
@@ -303325,7 +303352,8 @@ async function topEventsV2(api, params, limits, site) {
303325
303352
  to: params.to,
303326
303353
  sources: params.sources,
303327
303354
  tags: effectiveTags,
303328
- limit: params.maxEvents ?? 5e3
303355
+ limit: params.maxEvents ?? 5e3,
303356
+ transitionType: params.transitionType
303329
303357
  },
303330
303358
  limits,
303331
303359
  site
@@ -303414,7 +303442,8 @@ async function timeseriesEventsV2(api, params, limits, site) {
303414
303442
  const fullQuery = buildEventQuery({
303415
303443
  query: params.query ?? "source:alert",
303416
303444
  sources: params.sources,
303417
- tags: params.tags
303445
+ tags: params.tags,
303446
+ transitionType: params.transitionType
303418
303447
  });
303419
303448
  const intervalMs = parseIntervalToMs(params.interval);
303420
303449
  const groupByFields = params.groupBy ?? ["monitor_name"];
@@ -303499,7 +303528,8 @@ async function incidentsEventsV2(api, params, limits, site) {
303499
303528
  const fullQuery = buildEventQuery({
303500
303529
  query: params.query ?? "source:alert",
303501
303530
  sources: params.sources,
303502
- tags: params.tags
303531
+ tags: params.tags,
303532
+ transitionType: params.transitionType
303503
303533
  });
303504
303534
  const dedupeWindowNs = parseDurationToNs(params.dedupeWindow ?? "5m");
303505
303535
  const dedupeWindowMs = dedupeWindowNs ? Math.floor(dedupeWindowNs / 1e6) : 3e5;
@@ -303687,10 +303717,17 @@ function registerEventsTool(server, apiV1, apiV2, monitorsApi, limits, readOnly
303687
303717
  `Track Datadog events. Actions: list, get, create, search, aggregate, top, timeseries, incidents, discover.
303688
303718
  For monitor alerts, use tags: ["source:alert"].
303689
303719
 
303720
+ IMPORTANT \u2014 re-evaluation vs transition:
303721
+ - source:alert events INCLUDE renotifies and re-evaluations (every Datadog re-evaluation of an alerting monitor emits an event). A "how many times did monitor X fire" question answered with source:alert alone over-counts.
303722
+ - To restrict to real state transitions, pass transitionType (e.g. ["alert","alert recovery"]). This appends @monitor.transition.transition_type:(...) to the query and matches the design's live investigation.
303723
+ - For a fires-only numeric count rooted in a single monitor ID, prefer the higher-level primitive monitors action=history \u2014 it returns {transitions, count, meta} with the same filter applied for you.
303724
+
303725
+ transitionType: Optional array of monitor transition types (alert, alert recovery, warning, warning recovery, no data, no data recovery, renotify). Empty array is treated as undefined.
303690
303726
  top: Generic event grouping by any fields (groupBy parameter). Returns groups ranked by count with optional context breakdown.
303691
303727
  - Example: {groupBy: ["service"], message: "...", service: "api", total_count: 50, by_context: [{context: "queue:X", count: 30}]}
303692
303728
  - Use for deployments, configs, custom events, or monitor alerts
303693
303729
  - Returns "message" field (event title), NOT monitor name (use monitors tool for real names)
303730
+ - total_count includes renotifies when source:alert is used without transitionType \u2014 see monitors action=history for fires-only counts
303694
303731
  discover: Returns available tag prefixes from events.
303695
303732
  aggregate: Custom groupBy, returns pipe-delimited keys.
303696
303733
  search: Full event details.
@@ -303716,7 +303753,8 @@ incidents: Deduplicate alerts with dedupeWindow.`,
303716
303753
  dedupeWindow,
303717
303754
  enrich,
303718
303755
  contextTags,
303719
- maxEvents
303756
+ maxEvents,
303757
+ transitionType
303720
303758
  }) => {
303721
303759
  try {
303722
303760
  checkReadOnly(action, readOnly);
@@ -303765,7 +303803,8 @@ incidents: Deduplicate alerts with dedupeWindow.`,
303765
303803
  tags,
303766
303804
  priority,
303767
303805
  limit,
303768
- cursor
303806
+ cursor,
303807
+ transitionType
303769
303808
  },
303770
303809
  limits,
303771
303810
  site
@@ -303787,7 +303826,8 @@ incidents: Deduplicate alerts with dedupeWindow.`,
303787
303826
  sources,
303788
303827
  tags,
303789
303828
  groupBy,
303790
- limit
303829
+ limit,
303830
+ transitionType
303791
303831
  },
303792
303832
  limits,
303793
303833
  site
@@ -303806,7 +303846,8 @@ incidents: Deduplicate alerts with dedupeWindow.`,
303806
303846
  limit,
303807
303847
  groupBy,
303808
303848
  contextTags,
303809
- maxEvents
303849
+ maxEvents,
303850
+ transitionType
303810
303851
  },
303811
303852
  limits,
303812
303853
  site
@@ -303839,7 +303880,8 @@ incidents: Deduplicate alerts with dedupeWindow.`,
303839
303880
  tags,
303840
303881
  groupBy,
303841
303882
  interval,
303842
- limit
303883
+ limit,
303884
+ transitionType
303843
303885
  },
303844
303886
  limits,
303845
303887
  site
@@ -303856,7 +303898,8 @@ incidents: Deduplicate alerts with dedupeWindow.`,
303856
303898
  sources,
303857
303899
  tags,
303858
303900
  dedupeWindow,
303859
- limit
303901
+ limit,
303902
+ transitionType
303860
303903
  },
303861
303904
  limits,
303862
303905
  site
@@ -303882,7 +303925,8 @@ var ActionSchema2 = external_exports.enum([
303882
303925
  "delete",
303883
303926
  "mute",
303884
303927
  "unmute",
303885
- "top"
303928
+ "top",
303929
+ "history"
303886
303930
  ]);
303887
303931
  var InputSchema2 = {
303888
303932
  action: ActionSchema2.describe("Action to perform"),
@@ -303903,8 +303947,117 @@ var InputSchema2 = {
303903
303947
  contextTags: external_exports.array(external_exports.string()).optional().describe(
303904
303948
  "Tag prefixes for context breakdown in top action (default: queue, service, ingress, pod_name, kube_namespace, kube_container_name)"
303905
303949
  ),
303906
- maxEvents: external_exports.number().min(1).max(5e3).optional().describe("Maximum events to fetch for top action (default: 5000, max: 5000)")
303950
+ maxEvents: external_exports.number().min(1).max(5e3).optional().describe("Maximum events to fetch for top action (default: 5000, max: 5000)"),
303951
+ // History action parameters
303952
+ transitionType: external_exports.array(
303953
+ external_exports.enum([
303954
+ "alert",
303955
+ "alert recovery",
303956
+ "warning",
303957
+ "warning recovery",
303958
+ "no data",
303959
+ "no data recovery",
303960
+ "renotify"
303961
+ ])
303962
+ ).optional().describe(
303963
+ 'For history action: filter by monitor state transition types. Default: ["alert","alert recovery"] (real fires + recoveries, excludes renotifies). Pass ["alert"] for fires only, or include "renotify" for full chronological audit.'
303964
+ ),
303965
+ group: external_exports.string().optional().describe(
303966
+ 'For history action: filter transitions to a specific multi-alert monitor group (e.g., "pod_name:foo,kube_namespace:bar"). Optional; omit for all groups.'
303967
+ )
303907
303968
  };
303969
+ var MonitorThresholdsSchema = external_exports.object({
303970
+ critical: external_exports.number().optional(),
303971
+ warning: external_exports.number().optional(),
303972
+ ok: external_exports.number().optional(),
303973
+ criticalRecovery: external_exports.number().optional(),
303974
+ warningRecovery: external_exports.number().optional(),
303975
+ unknown: external_exports.number().optional()
303976
+ }).passthrough();
303977
+ var MonitorThresholdWindowsSchema = external_exports.object({
303978
+ triggerWindow: external_exports.string().optional(),
303979
+ recoveryWindow: external_exports.string().optional()
303980
+ }).passthrough();
303981
+ var SchedulingOptionsSchema = external_exports.object({
303982
+ evaluationWindow: external_exports.record(external_exports.unknown()).optional(),
303983
+ customSchedule: external_exports.record(external_exports.unknown()).optional()
303984
+ }).passthrough();
303985
+ var MonitorOptionsSchema = external_exports.object({
303986
+ // Notification
303987
+ notifyNoData: external_exports.boolean().optional(),
303988
+ noDataTimeframe: external_exports.number().optional(),
303989
+ notifyAudit: external_exports.boolean().optional(),
303990
+ notificationPresetName: external_exports.string().optional(),
303991
+ // Evaluation / delay
303992
+ newHostDelay: external_exports.number().optional(),
303993
+ newGroupDelay: external_exports.number().optional(),
303994
+ evaluationDelay: external_exports.number().optional(),
303995
+ requireFullWindow: external_exports.boolean().optional(),
303996
+ onMissingData: external_exports.string().optional(),
303997
+ // Renotification
303998
+ renotifyInterval: external_exports.number().nullable().optional(),
303999
+ renotifyOccurrences: external_exports.number().optional(),
304000
+ renotifyStatuses: external_exports.array(external_exports.string()).optional(),
304001
+ escalationMessage: external_exports.string().optional(),
304002
+ // Lifecycle
304003
+ timeoutH: external_exports.number().nullable().optional(),
304004
+ includeTags: external_exports.boolean().optional(),
304005
+ locked: external_exports.boolean().optional(),
304006
+ silenced: external_exports.record(external_exports.number().nullable()).optional(),
304007
+ groupRetentionDuration: external_exports.string().optional(),
304008
+ // Thresholds & scheduling
304009
+ thresholds: MonitorThresholdsSchema.optional(),
304010
+ thresholdWindows: MonitorThresholdWindowsSchema.optional(),
304011
+ schedulingOptions: SchedulingOptionsSchema.optional()
304012
+ }).passthrough();
304013
+ var MonitorConfigSchema = external_exports.object({
304014
+ name: external_exports.string().optional(),
304015
+ type: external_exports.string().optional(),
304016
+ query: external_exports.string().optional(),
304017
+ message: external_exports.string().optional(),
304018
+ tags: external_exports.array(external_exports.string()).optional(),
304019
+ priority: external_exports.number().int().min(1).max(5).nullable().optional(),
304020
+ restrictedRoles: external_exports.array(external_exports.string()).nullable().optional(),
304021
+ multi: external_exports.boolean().optional(),
304022
+ options: MonitorOptionsSchema.optional()
304023
+ }).passthrough();
304024
+ var KNOWN_TOP_LEVEL_KEYS = new Set(
304025
+ Object.keys(MonitorConfigSchema.shape).filter((key) => key !== "options")
304026
+ );
304027
+ var KNOWN_OPTIONS_KEYS = new Set(
304028
+ Object.keys(MonitorOptionsSchema.shape)
304029
+ );
304030
+ function collectUnknownKeyWarnings(config2) {
304031
+ const warnings = [];
304032
+ for (const key of Object.keys(config2)) {
304033
+ if (!KNOWN_TOP_LEVEL_KEYS.has(key) && key !== "options") {
304034
+ warnings.push(`unknown top-level key '${key}' under config forwarded without validation`);
304035
+ }
304036
+ }
304037
+ const options = config2.options;
304038
+ if (isPlainObject3(options)) {
304039
+ for (const key of Object.keys(options)) {
304040
+ if (!KNOWN_OPTIONS_KEYS.has(key)) {
304041
+ warnings.push(
304042
+ `unknown option key '${key}' under config.options forwarded without validation`
304043
+ );
304044
+ }
304045
+ }
304046
+ }
304047
+ return warnings;
304048
+ }
304049
+ function isPlainObject3(value) {
304050
+ return typeof value === "object" && value !== null && !Array.isArray(value);
304051
+ }
304052
+ function summarizeZodIssue(error2) {
304053
+ const issue2 = error2.issues[0];
304054
+ if (!issue2) {
304055
+ return "validation failed";
304056
+ }
304057
+ const path = issue2.path.length > 0 ? issue2.path.join(".") : "<root>";
304058
+ const expected = issue2.code === "invalid_type" && "expected" in issue2 ? `expected ${String(issue2.expected)}` : issue2.message;
304059
+ return `${path}: ${expected}`;
304060
+ }
303908
304061
  function formatMonitor(m, site = "datadoghq.com") {
303909
304062
  const monitorId = m.id ?? 0;
303910
304063
  return {
@@ -303936,6 +304089,153 @@ function formatMonitorDetail(m, site = "datadoghq.com") {
303936
304089
  }
303937
304090
  return detail;
303938
304091
  }
304092
+ var DEFAULT_HISTORY_TRANSITION_TYPES = [
304093
+ "alert",
304094
+ "alert recovery"
304095
+ ];
304096
+ function quoteIfNeeded2(value) {
304097
+ return /^[A-Za-z0-9_.-]+$/.test(value) ? value : `"${value}"`;
304098
+ }
304099
+ function buildMonitorHistoryQuery(params) {
304100
+ const parts = ["source:alert", `@monitor.id:${params.monitorId}`];
304101
+ const transitionTypes = params.transitionType && params.transitionType.length > 0 ? params.transitionType : void 0;
304102
+ if (transitionTypes) {
304103
+ const inner = transitionTypes.map(quoteIfNeeded2).join(" OR ");
304104
+ parts.push(`@monitor.transition.transition_type:(${inner})`);
304105
+ }
304106
+ if (params.group && params.group.length > 0) {
304107
+ const escaped = params.group.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
304108
+ parts.push(`@monitor.groups:"${escaped}"`);
304109
+ }
304110
+ return parts.join(" ");
304111
+ }
304112
+ function isMonitorState(value) {
304113
+ return value === "Alert" || value === "Warn" || value === "OK" || value === "No Data";
304114
+ }
304115
+ function isTransitionType(value) {
304116
+ return value === "alert" || value === "alert recovery" || value === "warning" || value === "warning recovery" || value === "no data" || value === "no data recovery" || value === "renotify";
304117
+ }
304118
+ function extractTimestamp(outer, inner) {
304119
+ const outerTs = outer.timestamp;
304120
+ if (outerTs instanceof Date) {
304121
+ return outerTs.toISOString();
304122
+ }
304123
+ if (typeof outerTs === "string" && outerTs.length > 0) {
304124
+ const d = new Date(outerTs);
304125
+ if (!Number.isNaN(d.getTime())) return d.toISOString();
304126
+ }
304127
+ const innerTs = inner.timestamp;
304128
+ if (typeof innerTs === "number" && Number.isFinite(innerTs)) {
304129
+ return new Date(innerTs).toISOString();
304130
+ }
304131
+ if (typeof innerTs === "string" && innerTs.length > 0) {
304132
+ const parsed = Number.parseInt(innerTs, 10);
304133
+ if (!Number.isNaN(parsed)) {
304134
+ return new Date(parsed).toISOString();
304135
+ }
304136
+ }
304137
+ return "";
304138
+ }
304139
+ function formatMonitorTransition(event) {
304140
+ const outer = event.attributes ?? {};
304141
+ const inner = outer.attributes ?? {};
304142
+ const monitor = inner.monitor;
304143
+ if (!monitor) {
304144
+ return null;
304145
+ }
304146
+ const transition = monitor.transition ?? monitor.additionalProperties?.transition;
304147
+ if (!transition) {
304148
+ return null;
304149
+ }
304150
+ const fromState = isMonitorState(transition.source_state) ? transition.source_state : null;
304151
+ const toState = isMonitorState(transition.destination_state) ? transition.destination_state : null;
304152
+ const transitionType = isTransitionType(transition.transition_type) ? transition.transition_type : null;
304153
+ if (!fromState || !toState || !transitionType) {
304154
+ return null;
304155
+ }
304156
+ const groupsRaw = monitor.groups;
304157
+ const group = Array.isArray(groupsRaw) && groupsRaw.length > 0 ? groupsRaw.map((g) => String(g)).join(",") : null;
304158
+ const monitorId = typeof monitor.id === "number" ? monitor.id : 0;
304159
+ const monitorName = typeof monitor.name === "string" && monitor.name.length > 0 ? monitor.name : `Monitor ${monitorId}`;
304160
+ return {
304161
+ timestamp: extractTimestamp(outer, inner),
304162
+ monitorId,
304163
+ monitorName,
304164
+ group,
304165
+ fromState,
304166
+ toState,
304167
+ transitionType,
304168
+ eventId: String(event.id ?? "")
304169
+ };
304170
+ }
304171
+ async function historyMonitor(eventsApi, monitorId, params, limits, site) {
304172
+ const defaultFrom = hoursAgo(limits.defaultTimeRangeHours);
304173
+ const defaultTo = now();
304174
+ const [validFrom, validTo] = ensureValidTimeRange(
304175
+ parseTime(params.from, defaultFrom),
304176
+ parseTime(params.to, defaultTo)
304177
+ );
304178
+ const fromTime = new Date(validFrom * 1e3).toISOString();
304179
+ const toTime = new Date(validTo * 1e3).toISOString();
304180
+ const effectiveTransitionTypes = params.transitionType && params.transitionType.length > 0 ? params.transitionType : [...DEFAULT_HISTORY_TRANSITION_TYPES];
304181
+ const query = buildMonitorHistoryQuery({
304182
+ monitorId,
304183
+ transitionType: effectiveTransitionTypes,
304184
+ group: params.group
304185
+ });
304186
+ const transitions = [];
304187
+ const maxEventsToProcess = 1e4;
304188
+ const maxPages = 100;
304189
+ let eventCount = 0;
304190
+ let pageCount = 0;
304191
+ const body = {
304192
+ filter: {
304193
+ query,
304194
+ from: fromTime,
304195
+ to: toTime
304196
+ },
304197
+ sort: "timestamp",
304198
+ page: { limit: 1e3 }
304199
+ };
304200
+ let cursor;
304201
+ while (pageCount < maxPages && eventCount < maxEventsToProcess) {
304202
+ const pageBody = { ...body, page: { ...body.page, cursor } };
304203
+ const response = await eventsApi.searchEvents({ body: pageBody });
304204
+ const events = response.data ?? [];
304205
+ if (events.length === 0) break;
304206
+ for (const event of events) {
304207
+ const transition = formatMonitorTransition(event);
304208
+ if (transition !== null) {
304209
+ transitions.push(transition);
304210
+ }
304211
+ eventCount++;
304212
+ if (eventCount >= maxEventsToProcess) break;
304213
+ }
304214
+ cursor = response.meta?.page?.after;
304215
+ if (!cursor) break;
304216
+ pageCount++;
304217
+ }
304218
+ const truncated = eventCount >= maxEventsToProcess;
304219
+ const resolvedGroup = params.group && params.group.length > 0 ? params.group : null;
304220
+ const count = transitions.length;
304221
+ const meta = {
304222
+ monitorId,
304223
+ query,
304224
+ from: fromTime,
304225
+ to: toTime,
304226
+ transitionTypes: effectiveTransitionTypes,
304227
+ group: resolvedGroup,
304228
+ count,
304229
+ totalFetched: eventCount,
304230
+ truncated,
304231
+ datadog_url: buildEventsUrl(query, validFrom, validTo, site)
304232
+ };
304233
+ return {
304234
+ transitions,
304235
+ count,
304236
+ meta
304237
+ };
304238
+ }
303939
304239
  async function listMonitors(api, params, limits, site) {
303940
304240
  const effectiveLimit = params.limit ?? limits.defaultLimit;
303941
304241
  const response = await api.listMonitors({
@@ -304012,6 +304312,11 @@ function normalizeMonitorConfig(config2, isUpdate = false) {
304012
304312
  ["include_tags", "includeTags"],
304013
304313
  ["require_full_window", "requireFullWindow"],
304014
304314
  ["escalation_message", "escalationMessage"],
304315
+ ["notification_preset_name", "notificationPresetName"],
304316
+ ["on_missing_data", "onMissingData"],
304317
+ ["group_retention_duration", "groupRetentionDuration"],
304318
+ ["threshold_windows", "thresholdWindows"],
304319
+ ["scheduling_options", "schedulingOptions"],
304015
304320
  ["locked", "locked"],
304016
304321
  ["silenced", "silenced"]
304017
304322
  ];
@@ -304043,21 +304348,49 @@ function normalizeMonitorConfig(config2, isUpdate = false) {
304043
304348
  return normalized;
304044
304349
  }
304045
304350
  async function createMonitor(api, config2, site = "datadoghq.com") {
304046
- const body = normalizeMonitorConfig(config2);
304351
+ const normalized = normalizeMonitorConfig(config2);
304352
+ try {
304353
+ MonitorConfigSchema.parse(normalized);
304354
+ } catch (error2) {
304355
+ if (error2 instanceof external_exports.ZodError) {
304356
+ throw new Error(`EINVALID_MONITOR_CONFIG: ${summarizeZodIssue(error2)}`);
304357
+ }
304358
+ throw error2;
304359
+ }
304360
+ const warnings = collectUnknownKeyWarnings(normalized);
304361
+ const body = normalized;
304047
304362
  const monitor = await api.createMonitor({ body });
304048
- return {
304363
+ const result = {
304049
304364
  success: true,
304050
304365
  monitor: formatMonitorDetail(monitor, site)
304051
304366
  };
304367
+ if (warnings.length > 0) {
304368
+ result.warnings = warnings;
304369
+ }
304370
+ return result;
304052
304371
  }
304053
304372
  async function updateMonitor(api, id, config2, site = "datadoghq.com") {
304054
304373
  const monitorId = Number.parseInt(id, 10);
304055
- const body = normalizeMonitorConfig(config2, true);
304374
+ const normalized = normalizeMonitorConfig(config2, true);
304375
+ try {
304376
+ MonitorConfigSchema.parse(normalized);
304377
+ } catch (error2) {
304378
+ if (error2 instanceof external_exports.ZodError) {
304379
+ throw new Error(`EINVALID_MONITOR_CONFIG: ${summarizeZodIssue(error2)}`);
304380
+ }
304381
+ throw error2;
304382
+ }
304383
+ const warnings = collectUnknownKeyWarnings(normalized);
304384
+ const body = normalized;
304056
304385
  const monitor = await api.updateMonitor({ monitorId, body });
304057
- return {
304386
+ const result = {
304058
304387
  success: true,
304059
304388
  monitor: formatMonitorDetail(monitor, site)
304060
304389
  };
304390
+ if (warnings.length > 0) {
304391
+ result.warnings = warnings;
304392
+ }
304393
+ return result;
304061
304394
  }
304062
304395
  async function deleteMonitor(api, id) {
304063
304396
  const monitorId = Number.parseInt(id, 10);
@@ -304210,16 +304543,47 @@ async function topMonitors(eventsApi, monitorsApi, params, limits, site) {
304210
304543
  function registerMonitorsTool(server, api, eventsApi, limits, readOnly = false, site = "datadoghq.com") {
304211
304544
  server.tool(
304212
304545
  "monitors",
304213
- `Manage Datadog monitors. Actions: list, get, search, create, update, delete, mute, unmute, top.
304546
+ `Manage Datadog monitors. Actions: list, get, search, create, update, delete, mute, unmute, top, history.
304214
304547
  Filters: name, tags, groupStates (alert/warn/ok/no data).
304215
- get/create/update return the full options object (notify_no_data, renotify_interval, thresholds, etc.) so callers can safely read-then-patch.
304548
+ get/create/update return the full options object so callers can safely read-then-patch.
304549
+
304550
+ create/update accept a config object validated against a typed schema covering the documented Datadog Monitor fields:
304551
+ - Top-level: name, type, query, message, tags, priority (1-5, nullable), restrictedRoles, multi, options.
304552
+ - options.* validated keys grouped by category:
304553
+ - notification: notifyNoData, noDataTimeframe, notifyAudit, notificationPresetName.
304554
+ - evaluation/delay: newHostDelay, newGroupDelay, evaluationDelay, requireFullWindow, onMissingData.
304555
+ - renotification: renotifyInterval (nullable), renotifyOccurrences, renotifyStatuses, escalationMessage.
304556
+ - lifecycle: timeoutH (nullable), includeTags, locked, silenced (record of timestamps/null), groupRetentionDuration.
304557
+ - thresholds: thresholds (critical/warning/ok/criticalRecovery/warningRecovery/unknown), thresholdWindows.
304558
+ - scheduling: schedulingOptions.
304559
+ Unknown keys (top-level or under options) are forwarded to Datadog as-is and surfaced via an optional warnings array on the response, so the schema does not lag the API.
304560
+ snake_case aliases are accepted on input and normalized to camelCase before validation.
304561
+ Validation errors short-circuit before any HTTP call and surface as 'EINVALID_MONITOR_CONFIG: <path>: <expected>'.
304562
+ Reference: https://docs.datadoghq.com/api/latest/monitors/
304216
304563
 
304217
304564
  top: Ranked monitors by alert frequency with real monitor names and context breakdown.
304218
304565
  - Returns: {rank, monitor_id, name (with {{template.vars}}), message (template), total_count, by_context}
304219
304566
  - Perfect for weekly/daily alert reports
304220
304567
  - Gets real monitor names from monitors API (not event titles)
304221
-
304222
- For generic event grouping (deployments, configs), use events tool instead.`,
304568
+ - WARNING: total_count is the raw alert-event count and INCLUDES renotifies/re-evaluations.
304569
+ For monitors stuck in Alert state, Datadog emits a renotify event every renotify_interval
304570
+ minutes, which inflates this count well beyond the number of real fires. When the question
304571
+ is "how many times did this monitor actually fire", use action=history instead.
304572
+
304573
+ history: Count and list real state transitions for one monitor over a time window.
304574
+ - Inputs: id (required, monitor ID), from/to (optional time range), transitionType (optional
304575
+ filter, defaults to ["alert","alert recovery"]), group (optional multi-alert group filter).
304576
+ - Returns: {transitions: [{timestamp, monitorId, monitorName, group, fromState, toState,
304577
+ transitionType, eventId}], count, meta}
304578
+ - count = transitions.length \u2014 the number of REAL state changes (fires + recoveries by
304579
+ default), NOT the renotify-inflated count returned by action=top or events action=search.
304580
+ - Backed by Datadog v2 events search with a hardcoded source:alert + @monitor.transition.
304581
+ transition_type filter that excludes renotifies by default. To include renotifies, pass
304582
+ transitionType including "renotify".
304583
+
304584
+ For generic event grouping (deployments, configs), use events tool instead. Note that the
304585
+ events tool's action=search with source:alert ALSO includes renotifies; use its
304586
+ transitionType filter (or this action=history) for fires-only counts.`,
304223
304587
  InputSchema2,
304224
304588
  async ({
304225
304589
  action,
@@ -304234,7 +304598,9 @@ For generic event grouping (deployments, configs), use events tool instead.`,
304234
304598
  from,
304235
304599
  to,
304236
304600
  contextTags,
304237
- maxEvents
304601
+ maxEvents,
304602
+ transitionType,
304603
+ group
304238
304604
  }) => {
304239
304605
  try {
304240
304606
  checkReadOnly(action, readOnly);
@@ -304289,6 +304655,22 @@ For generic event grouping (deployments, configs), use events tool instead.`,
304289
304655
  site
304290
304656
  )
304291
304657
  );
304658
+ case "history": {
304659
+ const monitorIdString = requireParam(id, "id", "history");
304660
+ const monitorId = Number.parseInt(monitorIdString, 10);
304661
+ if (Number.isNaN(monitorId)) {
304662
+ throw new Error(`Invalid monitor ID: ${monitorIdString}`);
304663
+ }
304664
+ return toolResult(
304665
+ await historyMonitor(
304666
+ eventsApi,
304667
+ monitorId,
304668
+ { from, to, transitionType, group },
304669
+ limits,
304670
+ site
304671
+ )
304672
+ );
304673
+ }
304292
304674
  default:
304293
304675
  throw new Error(`Unknown action: ${action}`);
304294
304676
  }
@@ -305584,6 +305966,22 @@ function registerIncidentsTool(server, api, limits, readOnly = false, _site = "d
305584
305966
  );
305585
305967
  }
305586
305968
 
305969
+ // src/utils/normalize.ts
305970
+ function snakeToCamel2(str) {
305971
+ return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
305972
+ }
305973
+ function normalizeConfigKeys(obj) {
305974
+ if (obj === null || obj === void 0) return obj;
305975
+ if (Array.isArray(obj)) return obj.map(normalizeConfigKeys);
305976
+ if (typeof obj !== "object") return obj;
305977
+ const normalized = {};
305978
+ for (const [key, value] of Object.entries(obj)) {
305979
+ const camelKey = snakeToCamel2(key);
305980
+ normalized[camelKey] = normalizeConfigKeys(value);
305981
+ }
305982
+ return normalized;
305983
+ }
305984
+
305587
305985
  // src/tools/slos.ts
305588
305986
  var ActionSchema8 = external_exports.enum(["list", "get", "create", "update", "delete", "history"]);
305589
305987
  var InputSchema8 = {
@@ -305599,7 +305997,7 @@ var InputSchema8 = {
305599
305997
  };
305600
305998
  function formatSlo(s) {
305601
305999
  const primaryThreshold = s.thresholds?.[0];
305602
- return {
306000
+ const summary = {
305603
306001
  id: s.id ?? "",
305604
306002
  name: s.name ?? "",
305605
306003
  description: s.description ?? null,
@@ -305617,11 +306015,24 @@ function formatSlo(s) {
305617
306015
  createdAt: s.createdAt ? new Date(s.createdAt * 1e3).toISOString() : "",
305618
306016
  modifiedAt: s.modifiedAt ? new Date(s.modifiedAt * 1e3).toISOString() : ""
305619
306017
  };
306018
+ if (s.query?.numerator && s.query.denominator) {
306019
+ summary.query = { numerator: s.query.numerator, denominator: s.query.denominator };
306020
+ }
306021
+ if (Array.isArray(s.monitorIds) && s.monitorIds.length > 0) {
306022
+ summary.monitorIds = s.monitorIds;
306023
+ }
306024
+ if (Array.isArray(s.monitorTags) && s.monitorTags.length > 0) {
306025
+ summary.monitorTags = s.monitorTags;
306026
+ }
306027
+ if (Array.isArray(s.groups) && s.groups.length > 0) {
306028
+ summary.groups = s.groups;
306029
+ }
306030
+ return summary;
305620
306031
  }
305621
306032
  function formatSearchSlo(slo) {
305622
306033
  const attrs = slo.data?.attributes;
305623
306034
  const primaryThreshold = attrs?.thresholds?.[0];
305624
- return {
306035
+ const summary = {
305625
306036
  id: slo.data?.id ?? "",
305626
306037
  name: attrs?.name ?? "",
305627
306038
  description: attrs?.description ?? null,
@@ -305645,6 +306056,16 @@ function formatSearchSlo(slo) {
305645
306056
  createdAt: attrs?.createdAt ? new Date(attrs.createdAt * 1e3).toISOString() : "",
305646
306057
  modifiedAt: attrs?.modifiedAt ? new Date(attrs.modifiedAt * 1e3).toISOString() : ""
305647
306058
  };
306059
+ if (attrs?.query?.numerator && attrs.query.denominator) {
306060
+ summary.query = { numerator: attrs.query.numerator, denominator: attrs.query.denominator };
306061
+ }
306062
+ if (Array.isArray(attrs?.monitorIds) && attrs.monitorIds.length > 0) {
306063
+ summary.monitorIds = attrs.monitorIds;
306064
+ }
306065
+ if (Array.isArray(attrs?.groups) && attrs.groups.length > 0) {
306066
+ summary.groups = attrs.groups;
306067
+ }
306068
+ return summary;
305648
306069
  }
305649
306070
  function buildSearchQuery(query, tags) {
305650
306071
  const parts = [];
@@ -305680,20 +306101,6 @@ async function getSlo(api, id) {
305680
306101
  slo: response.data ? formatSlo(response.data) : null
305681
306102
  };
305682
306103
  }
305683
- function snakeToCamel2(str) {
305684
- return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
305685
- }
305686
- function normalizeConfigKeys(obj) {
305687
- if (obj === null || obj === void 0) return obj;
305688
- if (Array.isArray(obj)) return obj.map(normalizeConfigKeys);
305689
- if (typeof obj !== "object") return obj;
305690
- const normalized = {};
305691
- for (const [key, value] of Object.entries(obj)) {
305692
- const camelKey = snakeToCamel2(key);
305693
- normalized[camelKey] = normalizeConfigKeys(value);
305694
- }
305695
- return normalized;
305696
- }
305697
306104
  function normalizeSloConfig(config2) {
305698
306105
  const normalized = normalizeConfigKeys(config2);
305699
306106
  if (!normalized.name) {
@@ -307944,6 +308351,488 @@ function registerSchemaTool(server) {
307944
308351
  );
307945
308352
  }
307946
308353
 
308354
+ // src/tools/logs_pipelines.ts
308355
+ var ActionSchema20 = external_exports.enum(["list", "get", "create", "update", "delete", "reorder", "get_order"]);
308356
+ var InputSchema21 = {
308357
+ action: ActionSchema20.describe("Action to perform"),
308358
+ id: external_exports.string().optional().describe("Pipeline ID (required for get/update/delete)"),
308359
+ config: external_exports.record(external_exports.unknown()).optional().describe(
308360
+ "Pipeline configuration (for create/update). Requires name and filter.query. Processors are forwarded unchanged."
308361
+ ),
308362
+ pipeline_ids: external_exports.array(external_exports.string()).optional().describe("Ordered pipeline ID list (required for reorder)"),
308363
+ verbose: external_exports.boolean().optional().describe("Return full SDK payload alongside summary (default false)")
308364
+ };
308365
+ function formatPipeline(p, verbose = false) {
308366
+ const summary = {
308367
+ id: p.id ?? "",
308368
+ name: p.name ?? "",
308369
+ filterQuery: p.filter?.query ?? null,
308370
+ isEnabled: p.isEnabled ?? false,
308371
+ isReadOnly: p.isReadOnly ?? false,
308372
+ type: p.type ?? null,
308373
+ processorsCount: p.processors?.length ?? 0
308374
+ };
308375
+ if (verbose && p.processors) {
308376
+ summary.processors = p.processors;
308377
+ }
308378
+ return summary;
308379
+ }
308380
+ function normalizePipelineConfig(config2) {
308381
+ const normalized = normalizeConfigKeys(config2);
308382
+ if (!normalized.name) {
308383
+ throw new Error("Pipeline config requires 'name' field");
308384
+ }
308385
+ const filter = normalized.filter;
308386
+ if (!filter || typeof filter.query !== "string" || filter.query.length === 0) {
308387
+ throw new Error("Pipeline config requires 'filter.query' field");
308388
+ }
308389
+ return normalized;
308390
+ }
308391
+ async function listPipelines(api, verbose = false) {
308392
+ const response = await api.listLogsPipelines();
308393
+ const pipelines = (response ?? []).map((p) => formatPipeline(p, verbose));
308394
+ const result = { pipelines, total: pipelines.length };
308395
+ if (verbose) {
308396
+ result.raw = response;
308397
+ }
308398
+ return result;
308399
+ }
308400
+ async function getPipeline(api, id, verbose = false) {
308401
+ const response = await api.getLogsPipeline({ pipelineId: id });
308402
+ const result = { pipeline: formatPipeline(response, verbose) };
308403
+ if (verbose) {
308404
+ result.raw = response;
308405
+ }
308406
+ return result;
308407
+ }
308408
+ async function createPipeline(api, config2, verbose = false) {
308409
+ const body = normalizePipelineConfig(config2);
308410
+ const response = await api.createLogsPipeline({ body });
308411
+ const result = {
308412
+ success: true,
308413
+ pipeline: formatPipeline(response, verbose)
308414
+ };
308415
+ if (verbose) {
308416
+ result.raw = response;
308417
+ }
308418
+ return result;
308419
+ }
308420
+ async function updatePipeline(api, id, config2, verbose = false) {
308421
+ const body = normalizePipelineConfig(config2);
308422
+ const response = await api.updateLogsPipeline({ pipelineId: id, body });
308423
+ const result = {
308424
+ success: true,
308425
+ pipeline: formatPipeline(response, verbose)
308426
+ };
308427
+ if (verbose) {
308428
+ result.raw = response;
308429
+ }
308430
+ return result;
308431
+ }
308432
+ async function deletePipeline(api, id) {
308433
+ await api.deleteLogsPipeline({ pipelineId: id });
308434
+ return {
308435
+ success: true,
308436
+ message: `Pipeline ${id} deleted`
308437
+ };
308438
+ }
308439
+ async function reorderPipelines(api, pipelineIds) {
308440
+ const body = { pipelineIds };
308441
+ const response = await api.updateLogsPipelineOrder({ body });
308442
+ return {
308443
+ success: true,
308444
+ order: {
308445
+ pipelineIds: response.pipelineIds ?? []
308446
+ }
308447
+ };
308448
+ }
308449
+ async function getPipelineOrder(api) {
308450
+ const response = await api.getLogsPipelineOrder();
308451
+ return {
308452
+ order: {
308453
+ pipelineIds: response.pipelineIds ?? []
308454
+ }
308455
+ };
308456
+ }
308457
+ function registerLogsPipelinesTool(server, api, _limits, readOnly = false, _site = "datadoghq.com") {
308458
+ server.tool(
308459
+ "logs_pipelines",
308460
+ "Manage Datadog Logs pipelines (parsing & processor chains). Actions: list, get, create, update, delete, reorder, get_order. Pipelines run sequentially on incoming logs; reorder changes the structure of downstream data. Mutations are blocked when the server is in read-only mode. Unknown processor types in 'config.processors' are forwarded to Datadog unchanged.",
308461
+ InputSchema21,
308462
+ async ({ action, id, config: config2, pipeline_ids, verbose }) => {
308463
+ try {
308464
+ checkReadOnly(action, readOnly);
308465
+ const isVerbose = verbose ?? false;
308466
+ switch (action) {
308467
+ case "list":
308468
+ return toolResult(await listPipelines(api, isVerbose));
308469
+ case "get": {
308470
+ const pipelineId = requireParam(id, "id", "get");
308471
+ return toolResult(await getPipeline(api, pipelineId, isVerbose));
308472
+ }
308473
+ case "create": {
308474
+ const pipelineConfig = requireParam(config2, "config", "create");
308475
+ return toolResult(await createPipeline(api, pipelineConfig, isVerbose));
308476
+ }
308477
+ case "update": {
308478
+ const pipelineId = requireParam(id, "id", "update");
308479
+ const pipelineConfig = requireParam(config2, "config", "update");
308480
+ return toolResult(await updatePipeline(api, pipelineId, pipelineConfig, isVerbose));
308481
+ }
308482
+ case "delete": {
308483
+ const pipelineId = requireParam(id, "id", "delete");
308484
+ return toolResult(await deletePipeline(api, pipelineId));
308485
+ }
308486
+ case "reorder": {
308487
+ const ids = requireParam(pipeline_ids, "pipeline_ids", "reorder");
308488
+ return toolResult(await reorderPipelines(api, ids));
308489
+ }
308490
+ case "get_order":
308491
+ return toolResult(await getPipelineOrder(api));
308492
+ default:
308493
+ throw new Error(`Unknown action: ${String(action)}`);
308494
+ }
308495
+ } catch (error2) {
308496
+ handleDatadogError(error2);
308497
+ }
308498
+ }
308499
+ );
308500
+ }
308501
+
308502
+ // src/tools/logs_indexes.ts
308503
+ var ActionSchema21 = external_exports.enum(["list", "get", "update", "reorder", "get_order"]);
308504
+ var InputSchema22 = {
308505
+ action: ActionSchema21.describe("Action to perform"),
308506
+ name: external_exports.string().optional().describe("Index name (required for get/update). Datadog identifies indexes by name, not id."),
308507
+ config: external_exports.record(external_exports.unknown()).optional().describe(
308508
+ "Index configuration (for update). Requires filter.query and numRetentionDays. Exclusion filters are forwarded unchanged."
308509
+ ),
308510
+ index_names: external_exports.array(external_exports.string()).optional().describe("Ordered index name list (required for reorder)"),
308511
+ verbose: external_exports.boolean().optional().describe("Return full SDK payload alongside summary (default false)")
308512
+ };
308513
+ function formatIndex(idx, verbose = false) {
308514
+ const summary = {
308515
+ name: idx.name ?? "",
308516
+ filterQuery: idx.filter?.query ?? null,
308517
+ exclusionFiltersCount: idx.exclusionFilters?.length ?? 0,
308518
+ numRetentionDays: idx.numRetentionDays ?? null,
308519
+ numFlexLogsRetentionDays: idx.numFlexLogsRetentionDays ?? null,
308520
+ dailyLimit: idx.dailyLimit ?? null,
308521
+ isRateLimited: idx.isRateLimited ?? false
308522
+ };
308523
+ if (verbose && idx.exclusionFilters) {
308524
+ summary.exclusionFilters = idx.exclusionFilters;
308525
+ }
308526
+ return summary;
308527
+ }
308528
+ function normalizeIndexConfig(config2) {
308529
+ const normalized = normalizeConfigKeys(config2);
308530
+ const filter = normalized.filter;
308531
+ if (!filter || typeof filter.query !== "string" || filter.query.length === 0) {
308532
+ throw new Error("Index config requires 'filter.query' field");
308533
+ }
308534
+ if (typeof normalized.numRetentionDays !== "number") {
308535
+ throw new Error("Index config requires 'numRetentionDays' field");
308536
+ }
308537
+ return normalized;
308538
+ }
308539
+ async function listIndexes(api, verbose = false) {
308540
+ const response = await api.listLogIndexes();
308541
+ const indexes = (response.indexes ?? []).map((idx) => formatIndex(idx, verbose));
308542
+ const result = { indexes, total: indexes.length };
308543
+ if (verbose) {
308544
+ result.raw = response;
308545
+ }
308546
+ return result;
308547
+ }
308548
+ async function getIndex(api, name, verbose = false) {
308549
+ const response = await api.getLogsIndex({ name });
308550
+ const result = { index: formatIndex(response, verbose) };
308551
+ if (verbose) {
308552
+ result.raw = response;
308553
+ }
308554
+ return result;
308555
+ }
308556
+ async function updateIndex(api, name, config2, verbose = false) {
308557
+ const body = normalizeIndexConfig(config2);
308558
+ const response = await api.updateLogsIndex({ name, body });
308559
+ const result = {
308560
+ success: true,
308561
+ index: formatIndex(response, verbose)
308562
+ };
308563
+ if (verbose) {
308564
+ result.raw = response;
308565
+ }
308566
+ return result;
308567
+ }
308568
+ async function reorderIndexes(api, indexNames) {
308569
+ const body = { indexNames };
308570
+ const response = await api.updateLogsIndexOrder({ body });
308571
+ return {
308572
+ success: true,
308573
+ order: {
308574
+ indexNames: response.indexNames ?? []
308575
+ }
308576
+ };
308577
+ }
308578
+ async function getIndexOrder(api) {
308579
+ const response = await api.getLogsIndexOrder();
308580
+ return {
308581
+ order: {
308582
+ indexNames: response.indexNames ?? []
308583
+ }
308584
+ };
308585
+ }
308586
+ function registerLogsIndexesTool(server, api, _limits, readOnly = false, _site = "datadoghq.com") {
308587
+ server.tool(
308588
+ "logs_indexes",
308589
+ "Manage Datadog Logs indexes (filters, retention, exclusion filters, daily limits). Actions: list, get, update, reorder, get_order. Datadog identifies indexes by 'name', not 'id'. Note: create/delete are UI-only per Datadog and not supported through the API. Mutations (update, reorder) are blocked when the server is in read-only mode.",
308590
+ InputSchema22,
308591
+ async ({ action, name, config: config2, index_names, verbose }) => {
308592
+ try {
308593
+ checkReadOnly(action, readOnly);
308594
+ const isVerbose = verbose ?? false;
308595
+ switch (action) {
308596
+ case "list":
308597
+ return toolResult(await listIndexes(api, isVerbose));
308598
+ case "get": {
308599
+ const indexName = requireParam(name, "name", "get");
308600
+ return toolResult(await getIndex(api, indexName, isVerbose));
308601
+ }
308602
+ case "update": {
308603
+ const indexName = requireParam(name, "name", "update");
308604
+ const indexConfig = requireParam(config2, "config", "update");
308605
+ return toolResult(await updateIndex(api, indexName, indexConfig, isVerbose));
308606
+ }
308607
+ case "reorder": {
308608
+ const names = requireParam(index_names, "index_names", "reorder");
308609
+ return toolResult(await reorderIndexes(api, names));
308610
+ }
308611
+ case "get_order":
308612
+ return toolResult(await getIndexOrder(api));
308613
+ default:
308614
+ throw new Error(`Unknown action: ${String(action)}`);
308615
+ }
308616
+ } catch (error2) {
308617
+ handleDatadogError(error2);
308618
+ }
308619
+ }
308620
+ );
308621
+ }
308622
+
308623
+ // src/tools/logs_archives.ts
308624
+ var ActionSchema22 = external_exports.enum(["list", "get", "create", "update", "delete", "reorder", "get_order"]);
308625
+ var InputSchema23 = {
308626
+ action: ActionSchema22.describe("Action to perform"),
308627
+ id: external_exports.string().optional().describe("Archive ID (required for get/update/delete)"),
308628
+ config: external_exports.record(external_exports.unknown()).optional().describe(
308629
+ "Archive configuration (for create/update). Requires name, query, and destination with type \u2208 { s3, gcs, azure_storage }. Provider credential / integration fields are forwarded unchanged."
308630
+ ),
308631
+ archive_ids: external_exports.array(external_exports.string()).optional().describe("Ordered archive ID list (required for reorder)"),
308632
+ verbose: external_exports.boolean().optional().describe("Return full SDK payload alongside summary (default false)")
308633
+ };
308634
+ var VALID_DESTINATION_TYPES = ["s3", "gcs", "azure_storage"];
308635
+ function extractDestinationContainer(destination) {
308636
+ if (!destination || typeof destination !== "object") return null;
308637
+ const dest = destination;
308638
+ if (typeof dest.bucket === "string") return dest.bucket;
308639
+ if (typeof dest.container === "string") return dest.container;
308640
+ return null;
308641
+ }
308642
+ function formatArchive(archive, verbose = false) {
308643
+ const attrs = archive.attributes;
308644
+ const destination = attrs?.destination ?? null;
308645
+ const destinationType = destination && typeof destination === "object" && "type" in destination ? String(destination.type ?? "") : "";
308646
+ const summary = {
308647
+ id: archive.id ?? "",
308648
+ name: attrs?.name ?? "",
308649
+ query: attrs?.query ?? null,
308650
+ destinationType,
308651
+ destinationContainer: extractDestinationContainer(destination),
308652
+ includeTags: attrs?.includeTags ?? false,
308653
+ rehydrationTags: attrs?.rehydrationTags ?? [],
308654
+ state: attrs?.state ?? null
308655
+ };
308656
+ if (verbose && destination) {
308657
+ summary.destination = destination;
308658
+ }
308659
+ return summary;
308660
+ }
308661
+ function normalizeArchiveConfig(config2) {
308662
+ const normalized = normalizeConfigKeys(config2);
308663
+ if (typeof normalized.name !== "string" || normalized.name.length === 0) {
308664
+ throw new Error("Archive config requires 'name' field");
308665
+ }
308666
+ if (typeof normalized.query !== "string" || normalized.query.length === 0) {
308667
+ throw new Error("Archive config requires 'query' field");
308668
+ }
308669
+ const destination = normalized.destination;
308670
+ if (!destination || typeof destination !== "object") {
308671
+ throw new Error("Archive config requires 'destination' object");
308672
+ }
308673
+ const destinationType = destination.type;
308674
+ if (typeof destinationType !== "string" || !VALID_DESTINATION_TYPES.includes(destinationType)) {
308675
+ throw new Error("destination.type must be one of: s3, gcs, azure_storage");
308676
+ }
308677
+ if (destinationType === "azure_storage") {
308678
+ ;
308679
+ destination.type = "azure";
308680
+ }
308681
+ return normalized;
308682
+ }
308683
+ function buildArchiveRequestBody(normalizedConfig) {
308684
+ const { name, query, destination, includeTags, rehydrationTags, rehydrationMaxScanSizeInGb } = normalizedConfig;
308685
+ const attributes = {
308686
+ name,
308687
+ query,
308688
+ destination
308689
+ };
308690
+ if (includeTags !== void 0) attributes.includeTags = includeTags;
308691
+ if (rehydrationTags !== void 0) attributes.rehydrationTags = rehydrationTags;
308692
+ if (rehydrationMaxScanSizeInGb !== void 0) {
308693
+ attributes.rehydrationMaxScanSizeInGb = rehydrationMaxScanSizeInGb;
308694
+ }
308695
+ return {
308696
+ data: {
308697
+ type: "archives",
308698
+ attributes
308699
+ }
308700
+ };
308701
+ }
308702
+ async function listArchives(api, verbose = false) {
308703
+ const response = await api.listLogsArchives();
308704
+ const archives = (response.data ?? []).map((archive) => formatArchive(archive, verbose));
308705
+ const result = { archives, total: archives.length };
308706
+ if (verbose) {
308707
+ result.raw = response;
308708
+ }
308709
+ return result;
308710
+ }
308711
+ async function getArchive(api, id, verbose = false) {
308712
+ const response = await api.getLogsArchive({ archiveId: id });
308713
+ const archive = response.data;
308714
+ if (!archive) {
308715
+ throw new Error(`Archive ${id} returned an empty payload`);
308716
+ }
308717
+ const result = { archive: formatArchive(archive, verbose) };
308718
+ if (verbose) {
308719
+ result.raw = response;
308720
+ }
308721
+ return result;
308722
+ }
308723
+ async function createArchive(api, config2, verbose = false) {
308724
+ const normalized = normalizeArchiveConfig(config2);
308725
+ const body = buildArchiveRequestBody(normalized);
308726
+ const response = await api.createLogsArchive({ body });
308727
+ const archive = response.data;
308728
+ if (!archive) {
308729
+ throw new Error("Archive creation returned an empty payload");
308730
+ }
308731
+ const result = {
308732
+ success: true,
308733
+ archive: formatArchive(archive, verbose)
308734
+ };
308735
+ if (verbose) {
308736
+ result.raw = response;
308737
+ }
308738
+ return result;
308739
+ }
308740
+ async function updateArchive(api, id, config2, verbose = false) {
308741
+ const normalized = normalizeArchiveConfig(config2);
308742
+ const body = buildArchiveRequestBody(normalized);
308743
+ const response = await api.updateLogsArchive({ archiveId: id, body });
308744
+ const archive = response.data;
308745
+ if (!archive) {
308746
+ throw new Error(`Archive ${id} update returned an empty payload`);
308747
+ }
308748
+ const result = {
308749
+ success: true,
308750
+ archive: formatArchive(archive, verbose)
308751
+ };
308752
+ if (verbose) {
308753
+ result.raw = response;
308754
+ }
308755
+ return result;
308756
+ }
308757
+ async function deleteArchive(api, id) {
308758
+ await api.deleteLogsArchive({ archiveId: id });
308759
+ return {
308760
+ success: true,
308761
+ message: `Archive ${id} deleted`
308762
+ };
308763
+ }
308764
+ async function reorderArchives(api, archiveIds) {
308765
+ const body = {
308766
+ data: {
308767
+ type: "archive_order",
308768
+ attributes: {
308769
+ archiveIds
308770
+ }
308771
+ }
308772
+ };
308773
+ const response = await api.updateLogsArchiveOrder({ body });
308774
+ const resultIds = response.data?.attributes?.archiveIds ?? [];
308775
+ return {
308776
+ success: true,
308777
+ order: {
308778
+ archiveIds: resultIds
308779
+ }
308780
+ };
308781
+ }
308782
+ async function getArchiveOrder(api) {
308783
+ const response = await api.getLogsArchiveOrder();
308784
+ const resultIds = response.data?.attributes?.archiveIds ?? [];
308785
+ return {
308786
+ order: {
308787
+ archiveIds: resultIds
308788
+ }
308789
+ };
308790
+ }
308791
+ function registerLogsArchivesTool(server, api, _limits, readOnly = false, _site = "datadoghq.com") {
308792
+ server.tool(
308793
+ "logs_archives",
308794
+ "Manage Datadog Logs archives (long-term log retention to S3 / GCS / Azure Blob). Actions: list, get, create, update, delete, reorder, get_order. Archives accept destinations of type 's3', 'gcs', or 'azure_storage'; per-provider credential and integration fields (S3 IAM role ARN, GCS service account, Azure tenant/secret) are forwarded unchanged. Mutations (create, update, delete, reorder) are blocked when the server is in read-only mode.",
308795
+ InputSchema23,
308796
+ async ({ action, id, config: config2, archive_ids, verbose }) => {
308797
+ try {
308798
+ checkReadOnly(action, readOnly);
308799
+ const isVerbose = verbose ?? false;
308800
+ switch (action) {
308801
+ case "list":
308802
+ return toolResult(await listArchives(api, isVerbose));
308803
+ case "get": {
308804
+ const archiveId = requireParam(id, "id", "get");
308805
+ return toolResult(await getArchive(api, archiveId, isVerbose));
308806
+ }
308807
+ case "create": {
308808
+ const archiveConfig = requireParam(config2, "config", "create");
308809
+ return toolResult(await createArchive(api, archiveConfig, isVerbose));
308810
+ }
308811
+ case "update": {
308812
+ const archiveId = requireParam(id, "id", "update");
308813
+ const archiveConfig = requireParam(config2, "config", "update");
308814
+ return toolResult(await updateArchive(api, archiveId, archiveConfig, isVerbose));
308815
+ }
308816
+ case "delete": {
308817
+ const archiveId = requireParam(id, "id", "delete");
308818
+ return toolResult(await deleteArchive(api, archiveId));
308819
+ }
308820
+ case "reorder": {
308821
+ const ids = requireParam(archive_ids, "archive_ids", "reorder");
308822
+ return toolResult(await reorderArchives(api, ids));
308823
+ }
308824
+ case "get_order":
308825
+ return toolResult(await getArchiveOrder(api));
308826
+ default:
308827
+ throw new Error(`Unknown action: ${String(action)}`);
308828
+ }
308829
+ } catch (error2) {
308830
+ handleDatadogError(error2);
308831
+ }
308832
+ }
308833
+ );
308834
+ }
308835
+
307947
308836
  // src/tools/index.ts
307948
308837
  function registerAllTools(server, clients, limits, features, site = "datadoghq.com", datadogConfig) {
307949
308838
  const { readOnly, disabledTools } = features;
@@ -307958,6 +308847,12 @@ function registerAllTools(server, clients, limits, features, site = "datadoghq.c
307958
308847
  if (enabled("dashboards"))
307959
308848
  registerDashboardsTool(server, clients.dashboards, limits, readOnly, credentials);
307960
308849
  if (enabled("logs")) registerLogsTool(server, clients.logs, limits, site);
308850
+ if (enabled("logs_pipelines"))
308851
+ registerLogsPipelinesTool(server, clients.logsPipelines, limits, readOnly, site);
308852
+ if (enabled("logs_indexes"))
308853
+ registerLogsIndexesTool(server, clients.logsIndexes, limits, readOnly, site);
308854
+ if (enabled("logs_archives"))
308855
+ registerLogsArchivesTool(server, clients.logsArchives, limits, readOnly, site);
307961
308856
  if (enabled("metrics"))
307962
308857
  registerMetricsTool(server, clients.metricsV1, clients.metricsV2, limits, site);
307963
308858
  if (enabled("traces")) registerTracesTool(server, clients.spans, clients.services, limits, site);