datadog-mcp 5.6.0 → 5.8.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/README.md +6 -3
- package/dist/index.js +950 -93
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -292550,7 +292550,12 @@ var configSchema = external_exports.object({
|
|
|
292550
292550
|
// Fallback when AI doesn't specify log limit
|
|
292551
292551
|
defaultMetricDataPoints: external_exports.number().default(1e3),
|
|
292552
292552
|
// Fallback for timeseries data points
|
|
292553
|
-
defaultTimeRangeHours: external_exports.number().default(24)
|
|
292553
|
+
defaultTimeRangeHours: external_exports.number().default(24),
|
|
292554
|
+
// Maximum events scanned by the `events.histogram` action before returning a
|
|
292555
|
+
// partial result with `bucketCountIncomplete: true` and `nextCursor`.
|
|
292556
|
+
// Bounds latency on bucketed queries while still giving callers a path to
|
|
292557
|
+
// continue scanning if they want a fully complete histogram.
|
|
292558
|
+
maxEventsForHistogram: external_exports.number().default(5e3)
|
|
292554
292559
|
}).default({}),
|
|
292555
292560
|
features: external_exports.object({
|
|
292556
292561
|
readOnly: external_exports.boolean().default(false),
|
|
@@ -292625,7 +292630,8 @@ function loadConfig() {
|
|
|
292625
292630
|
defaultLimit: Number.parseInt(process.env.MCP_DEFAULT_LIMIT ?? "50", 10),
|
|
292626
292631
|
defaultLogLines: Number.parseInt(process.env.MCP_DEFAULT_LOG_LINES ?? "200", 10),
|
|
292627
292632
|
defaultMetricDataPoints: Number.parseInt(process.env.MCP_DEFAULT_METRIC_POINTS ?? "1000", 10),
|
|
292628
|
-
defaultTimeRangeHours: Number.parseInt(process.env.MCP_DEFAULT_TIME_RANGE ?? "24", 10)
|
|
292633
|
+
defaultTimeRangeHours: Number.parseInt(process.env.MCP_DEFAULT_TIME_RANGE ?? "24", 10),
|
|
292634
|
+
maxEventsForHistogram: Number.parseInt(process.env.MCP_MAX_EVENTS_HISTOGRAM ?? "5000", 10)
|
|
292629
292635
|
},
|
|
292630
292636
|
features: {
|
|
292631
292637
|
readOnly: args.booleans.has("read-only") || process.env.MCP_READ_ONLY === "true",
|
|
@@ -302780,6 +302786,10 @@ function buildRumSessionUrl(applicationId, sessionId, site = "datadoghq.com") {
|
|
|
302780
302786
|
const base = getAppBaseUrl(site);
|
|
302781
302787
|
return `${base}/rum/replay/sessions/${sessionId}?applicationId=${encodeURIComponent(applicationId)}`;
|
|
302782
302788
|
}
|
|
302789
|
+
function buildSloUrl(sloId, site = "datadoghq.com") {
|
|
302790
|
+
const base = getAppBaseUrl(site);
|
|
302791
|
+
return `${base}/slo/${sloId}`;
|
|
302792
|
+
}
|
|
302783
302793
|
|
|
302784
302794
|
// src/utils/time.ts
|
|
302785
302795
|
function hoursAgo(hours) {
|
|
@@ -302904,6 +302914,182 @@ function formatDurationNs(ns) {
|
|
|
302904
302914
|
return `${(ns / 6e10).toFixed(2)}m`;
|
|
302905
302915
|
}
|
|
302906
302916
|
|
|
302917
|
+
// src/utils/timezone.ts
|
|
302918
|
+
var ERROR_PREFIX = "EINVALID_TIMEZONE";
|
|
302919
|
+
var WEEKDAY_TO_INDEX = {
|
|
302920
|
+
Sunday: 0,
|
|
302921
|
+
Monday: 1,
|
|
302922
|
+
Tuesday: 2,
|
|
302923
|
+
Wednesday: 3,
|
|
302924
|
+
Thursday: 4,
|
|
302925
|
+
Friday: 5,
|
|
302926
|
+
Saturday: 6
|
|
302927
|
+
};
|
|
302928
|
+
function validateIanaZone(tz) {
|
|
302929
|
+
if (typeof tz !== "string" || tz.length === 0) {
|
|
302930
|
+
throw new Error(
|
|
302931
|
+
`${ERROR_PREFIX}: timezone must be a non-empty IANA identifier (e.g. "UTC", "Europe/Paris", "America/New_York")`
|
|
302932
|
+
);
|
|
302933
|
+
}
|
|
302934
|
+
try {
|
|
302935
|
+
new Intl.DateTimeFormat("en-US", { timeZone: tz });
|
|
302936
|
+
return;
|
|
302937
|
+
} catch {
|
|
302938
|
+
const suggestions = suggestZones(tz);
|
|
302939
|
+
const suggestionText = suggestions.length > 0 ? ` Did you mean: ${suggestions.join(", ")}?` : "";
|
|
302940
|
+
throw new Error(`${ERROR_PREFIX}: "${tz}" is not a valid IANA timezone.${suggestionText}`);
|
|
302941
|
+
}
|
|
302942
|
+
}
|
|
302943
|
+
function suggestZones(input) {
|
|
302944
|
+
let zones;
|
|
302945
|
+
try {
|
|
302946
|
+
zones = Intl.supportedValuesOf("timeZone");
|
|
302947
|
+
} catch {
|
|
302948
|
+
return [];
|
|
302949
|
+
}
|
|
302950
|
+
const lower = input.toLowerCase();
|
|
302951
|
+
const scored = zones.map((zone) => ({
|
|
302952
|
+
zone,
|
|
302953
|
+
score: scoreZone(zone, lower)
|
|
302954
|
+
}));
|
|
302955
|
+
scored.sort((a, b) => a.score - b.score);
|
|
302956
|
+
return scored.slice(0, 3).map((s) => s.zone);
|
|
302957
|
+
}
|
|
302958
|
+
function scoreZone(zone, lowerInput) {
|
|
302959
|
+
const lowerZone = zone.toLowerCase();
|
|
302960
|
+
const baseDistance = levenshtein(lowerZone, lowerInput);
|
|
302961
|
+
if (lowerZone.includes(lowerInput) || lowerInput.includes(lowerZone)) {
|
|
302962
|
+
return baseDistance - 100;
|
|
302963
|
+
}
|
|
302964
|
+
return baseDistance;
|
|
302965
|
+
}
|
|
302966
|
+
function levenshtein(a, b) {
|
|
302967
|
+
if (a.length === 0) return b.length;
|
|
302968
|
+
if (b.length === 0) return a.length;
|
|
302969
|
+
if (a.length > b.length) {
|
|
302970
|
+
const tmp = a;
|
|
302971
|
+
a = b;
|
|
302972
|
+
b = tmp;
|
|
302973
|
+
}
|
|
302974
|
+
let previous = new Array(a.length + 1);
|
|
302975
|
+
let current = new Array(a.length + 1);
|
|
302976
|
+
for (let i = 0; i <= a.length; i++) previous[i] = i;
|
|
302977
|
+
for (let j = 1; j <= b.length; j++) {
|
|
302978
|
+
current[0] = j;
|
|
302979
|
+
const bChar = b.charCodeAt(j - 1);
|
|
302980
|
+
for (let i = 1; i <= a.length; i++) {
|
|
302981
|
+
const cost = a.charCodeAt(i - 1) === bChar ? 0 : 1;
|
|
302982
|
+
const prevI = previous[i] ?? 0;
|
|
302983
|
+
const currIMinus1 = current[i - 1] ?? 0;
|
|
302984
|
+
const prevIMinus1 = previous[i - 1] ?? 0;
|
|
302985
|
+
current[i] = Math.min(prevI + 1, currIMinus1 + 1, prevIMinus1 + cost);
|
|
302986
|
+
}
|
|
302987
|
+
const tmp = previous;
|
|
302988
|
+
previous = current;
|
|
302989
|
+
current = tmp;
|
|
302990
|
+
}
|
|
302991
|
+
return previous[a.length] ?? 0;
|
|
302992
|
+
}
|
|
302993
|
+
var partsFormatterCache = /* @__PURE__ */ new Map();
|
|
302994
|
+
var offsetFormatterCache = /* @__PURE__ */ new Map();
|
|
302995
|
+
function getPartsFormatter(tz) {
|
|
302996
|
+
const cached2 = partsFormatterCache.get(tz);
|
|
302997
|
+
if (cached2) return cached2;
|
|
302998
|
+
const fmt = new Intl.DateTimeFormat("en-US", {
|
|
302999
|
+
timeZone: tz,
|
|
303000
|
+
hourCycle: "h23",
|
|
303001
|
+
// 0-23, avoids "24" edge case at midnight
|
|
303002
|
+
year: "numeric",
|
|
303003
|
+
month: "2-digit",
|
|
303004
|
+
day: "2-digit",
|
|
303005
|
+
hour: "2-digit",
|
|
303006
|
+
minute: "2-digit",
|
|
303007
|
+
second: "2-digit",
|
|
303008
|
+
weekday: "long"
|
|
303009
|
+
});
|
|
303010
|
+
partsFormatterCache.set(tz, fmt);
|
|
303011
|
+
return fmt;
|
|
303012
|
+
}
|
|
303013
|
+
function getOffsetFormatter(tz) {
|
|
303014
|
+
const cached2 = offsetFormatterCache.get(tz);
|
|
303015
|
+
if (cached2) return cached2;
|
|
303016
|
+
const fmt = new Intl.DateTimeFormat("en-US", {
|
|
303017
|
+
timeZone: tz,
|
|
303018
|
+
timeZoneName: "longOffset"
|
|
303019
|
+
});
|
|
303020
|
+
offsetFormatterCache.set(tz, fmt);
|
|
303021
|
+
return fmt;
|
|
303022
|
+
}
|
|
303023
|
+
function getDateParts(epochMs, tz) {
|
|
303024
|
+
let parts;
|
|
303025
|
+
try {
|
|
303026
|
+
parts = getPartsFormatter(tz).formatToParts(epochMs);
|
|
303027
|
+
} catch {
|
|
303028
|
+
throw new Error(`${ERROR_PREFIX}: "${tz}" is not a valid IANA timezone.`);
|
|
303029
|
+
}
|
|
303030
|
+
const out = {
|
|
303031
|
+
year: "",
|
|
303032
|
+
month: "",
|
|
303033
|
+
day: "",
|
|
303034
|
+
hour: "",
|
|
303035
|
+
minute: "",
|
|
303036
|
+
second: "",
|
|
303037
|
+
weekday: ""
|
|
303038
|
+
};
|
|
303039
|
+
for (const part of parts) {
|
|
303040
|
+
if (part.type === "year") out.year = part.value;
|
|
303041
|
+
else if (part.type === "month") out.month = part.value;
|
|
303042
|
+
else if (part.type === "day") out.day = part.value;
|
|
303043
|
+
else if (part.type === "hour") out.hour = part.value;
|
|
303044
|
+
else if (part.type === "minute") out.minute = part.value;
|
|
303045
|
+
else if (part.type === "second") out.second = part.value;
|
|
303046
|
+
else if (part.type === "weekday") out.weekday = part.value;
|
|
303047
|
+
}
|
|
303048
|
+
return out;
|
|
303049
|
+
}
|
|
303050
|
+
function getOffsetIso(epochMs, tz) {
|
|
303051
|
+
let parts;
|
|
303052
|
+
try {
|
|
303053
|
+
parts = getOffsetFormatter(tz).formatToParts(epochMs);
|
|
303054
|
+
} catch {
|
|
303055
|
+
throw new Error(`${ERROR_PREFIX}: "${tz}" is not a valid IANA timezone.`);
|
|
303056
|
+
}
|
|
303057
|
+
const offsetPart = parts.find((p) => p.type === "timeZoneName");
|
|
303058
|
+
const raw = offsetPart?.value ?? "";
|
|
303059
|
+
if (raw === "GMT" || raw === "GMT+00:00" || raw === "GMT-00:00") {
|
|
303060
|
+
return "Z";
|
|
303061
|
+
}
|
|
303062
|
+
if (raw.startsWith("GMT")) {
|
|
303063
|
+
return raw.slice(3);
|
|
303064
|
+
}
|
|
303065
|
+
return raw;
|
|
303066
|
+
}
|
|
303067
|
+
function formatLocal(epochMs, tz) {
|
|
303068
|
+
validateIanaZone(tz);
|
|
303069
|
+
const parts = getDateParts(epochMs, tz);
|
|
303070
|
+
const offset = getOffsetIso(epochMs, tz);
|
|
303071
|
+
return `${parts.year}-${parts.month}-${parts.day}T${parts.hour}:${parts.minute}:${parts.second}${offset}`;
|
|
303072
|
+
}
|
|
303073
|
+
function bucketHourOfDay(epochMs, tz) {
|
|
303074
|
+
validateIanaZone(tz);
|
|
303075
|
+
const parts = getDateParts(epochMs, tz);
|
|
303076
|
+
return Number(parts.hour);
|
|
303077
|
+
}
|
|
303078
|
+
function bucketDayOfWeek(epochMs, tz) {
|
|
303079
|
+
validateIanaZone(tz);
|
|
303080
|
+
const parts = getDateParts(epochMs, tz);
|
|
303081
|
+
const index = WEEKDAY_TO_INDEX[parts.weekday];
|
|
303082
|
+
if (index === void 0) {
|
|
303083
|
+
throw new Error(`${ERROR_PREFIX}: could not resolve weekday for "${parts.weekday}" in "${tz}"`);
|
|
303084
|
+
}
|
|
303085
|
+
return index;
|
|
303086
|
+
}
|
|
303087
|
+
function bucketDayOfMonth(epochMs, tz) {
|
|
303088
|
+
validateIanaZone(tz);
|
|
303089
|
+
const parts = getDateParts(epochMs, tz);
|
|
303090
|
+
return Number(parts.day);
|
|
303091
|
+
}
|
|
303092
|
+
|
|
302907
303093
|
// src/tools/events.ts
|
|
302908
303094
|
var ActionSchema = external_exports.enum([
|
|
302909
303095
|
"list",
|
|
@@ -302914,8 +303100,10 @@ var ActionSchema = external_exports.enum([
|
|
|
302914
303100
|
"top",
|
|
302915
303101
|
"timeseries",
|
|
302916
303102
|
"incidents",
|
|
302917
|
-
"discover"
|
|
303103
|
+
"discover",
|
|
303104
|
+
"histogram"
|
|
302918
303105
|
]);
|
|
303106
|
+
var HistogramBucketBySchema = external_exports.enum(["hour_of_day", "day_of_week", "day_of_month"]);
|
|
302919
303107
|
var InputSchema = {
|
|
302920
303108
|
action: ActionSchema.describe("Action to perform"),
|
|
302921
303109
|
id: external_exports.string().optional().describe("Event ID (for get action)"),
|
|
@@ -302959,8 +303147,54 @@ var InputSchema = {
|
|
|
302959
303147
|
])
|
|
302960
303148
|
).optional().describe(
|
|
302961
303149
|
'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.'
|
|
303150
|
+
),
|
|
303151
|
+
// Histogram action (Requirement 3): bucket events by local hour/day-of-week/day-of-month.
|
|
303152
|
+
bucket_by: HistogramBucketBySchema.optional().describe(
|
|
303153
|
+
"Bucket dimension for histogram action: hour_of_day (0-23), day_of_week (0=Sun..6=Sat), day_of_month (1-31)."
|
|
303154
|
+
),
|
|
303155
|
+
timezone: external_exports.string().optional().describe(
|
|
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."
|
|
302962
303163
|
)
|
|
302963
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
|
+
}
|
|
303192
|
+
function annotateEventTimezone(event, tz) {
|
|
303193
|
+
if (!event.timestamp) return event;
|
|
303194
|
+
const ms = new Date(event.timestamp).getTime();
|
|
303195
|
+
if (!Number.isFinite(ms)) return event;
|
|
303196
|
+
return { ...event, timestampLocal: formatLocal(ms, tz) };
|
|
303197
|
+
}
|
|
302964
303198
|
function extractMonitorInfo(title) {
|
|
302965
303199
|
const priorityMatch = title.match(/^\[P(\d+)\]\s*/);
|
|
302966
303200
|
const priority = priorityMatch ? `P${priorityMatch[1]}` : void 0;
|
|
@@ -303147,17 +303381,17 @@ async function listEventsV1(api, params, limits) {
|
|
|
303147
303381
|
tags: params.tags?.join(","),
|
|
303148
303382
|
unaggregated: true
|
|
303149
303383
|
});
|
|
303150
|
-
let
|
|
303384
|
+
let events2 = response.events ?? [];
|
|
303151
303385
|
if (params.query) {
|
|
303152
303386
|
const lowerQuery = params.query.toLowerCase();
|
|
303153
|
-
|
|
303387
|
+
events2 = events2.filter(
|
|
303154
303388
|
(e) => e.title?.toLowerCase().includes(lowerQuery) || e.text?.toLowerCase().includes(lowerQuery)
|
|
303155
303389
|
);
|
|
303156
303390
|
}
|
|
303157
|
-
const result =
|
|
303391
|
+
const result = events2.slice(0, effectiveLimit).map(formatEventV1);
|
|
303158
303392
|
return {
|
|
303159
303393
|
events: result,
|
|
303160
|
-
total:
|
|
303394
|
+
total: events2.length
|
|
303161
303395
|
};
|
|
303162
303396
|
}
|
|
303163
303397
|
async function getEventV1(api, id) {
|
|
@@ -303186,6 +303420,85 @@ async function createEventV1(api, params) {
|
|
|
303186
303420
|
}
|
|
303187
303421
|
};
|
|
303188
303422
|
}
|
|
303423
|
+
var UNINDEXED_ALERT_TAG_PREFIXES = [
|
|
303424
|
+
"monitor_priority",
|
|
303425
|
+
"notification_preset",
|
|
303426
|
+
"monitor_tags",
|
|
303427
|
+
"alert_cycle_key",
|
|
303428
|
+
"monitor_group_key",
|
|
303429
|
+
"notification_method"
|
|
303430
|
+
];
|
|
303431
|
+
var NARROW_TIME_RANGE_THRESHOLD_MS = 5 * 60 * 1e3;
|
|
303432
|
+
function extractTagPrefixes(query, tags) {
|
|
303433
|
+
const prefixes = [];
|
|
303434
|
+
if (query) {
|
|
303435
|
+
const re = /(?:^|\s)([a-zA-Z_][a-zA-Z0-9_]*):[^\s)]+/g;
|
|
303436
|
+
let match;
|
|
303437
|
+
while ((match = re.exec(query)) !== null) {
|
|
303438
|
+
if (match[1]) {
|
|
303439
|
+
prefixes.push(match[1]);
|
|
303440
|
+
}
|
|
303441
|
+
}
|
|
303442
|
+
}
|
|
303443
|
+
if (tags) {
|
|
303444
|
+
for (const tag of tags) {
|
|
303445
|
+
const colonIdx = tag.indexOf(":");
|
|
303446
|
+
if (colonIdx > 0) {
|
|
303447
|
+
prefixes.push(tag.slice(0, colonIdx));
|
|
303448
|
+
}
|
|
303449
|
+
}
|
|
303450
|
+
}
|
|
303451
|
+
return prefixes;
|
|
303452
|
+
}
|
|
303453
|
+
function countNonSourceTerms(query) {
|
|
303454
|
+
if (!query) return 0;
|
|
303455
|
+
const tokens = query.split(/\s+/).filter((t) => t.length > 0 && t !== "OR" && t !== "AND");
|
|
303456
|
+
let nonSource = 0;
|
|
303457
|
+
for (const token of tokens) {
|
|
303458
|
+
const stripped = token.replace(/[()]/g, "");
|
|
303459
|
+
if (stripped.length === 0) continue;
|
|
303460
|
+
if (stripped.startsWith("source:")) continue;
|
|
303461
|
+
nonSource++;
|
|
303462
|
+
}
|
|
303463
|
+
return nonSource;
|
|
303464
|
+
}
|
|
303465
|
+
function computeDiagnostics(input) {
|
|
303466
|
+
const diagnostics = [];
|
|
303467
|
+
const query = input.query ?? "";
|
|
303468
|
+
const queryHasSourceAlert = /(^|\s|\()source:alert(\s|\)|$)/.test(query) || // NOSONAR S5852: anchored alternation, bounded input, no nested quantifiers
|
|
303469
|
+
(input.sources?.includes("alert") ?? false) || (input.tags?.includes("source:alert") ?? false);
|
|
303470
|
+
if (queryHasSourceAlert) {
|
|
303471
|
+
const prefixes = extractTagPrefixes(input.query, input.tags);
|
|
303472
|
+
const unindexedHits = prefixes.filter((p) => UNINDEXED_ALERT_TAG_PREFIXES.includes(p));
|
|
303473
|
+
const uniqueHits = Array.from(new Set(unindexedHits));
|
|
303474
|
+
if (uniqueHits.length > 0) {
|
|
303475
|
+
diagnostics.push({
|
|
303476
|
+
code: "UNINDEXED_TAG_PREFIX",
|
|
303477
|
+
message: `Query filters on tag prefix(es) that Datadog does not index for source:alert events: ${uniqueHits.join(", ")}.`,
|
|
303478
|
+
hint: "Drop these filters and post-filter the results client-side, or aggregate via monitors/get + monitors.list with matching options."
|
|
303479
|
+
});
|
|
303480
|
+
}
|
|
303481
|
+
}
|
|
303482
|
+
if (typeof input.fromMs === "number" && typeof input.toMs === "number" && input.toMs > input.fromMs && input.toMs - input.fromMs < NARROW_TIME_RANGE_THRESHOLD_MS) {
|
|
303483
|
+
diagnostics.push({
|
|
303484
|
+
code: "NARROW_TIME_RANGE",
|
|
303485
|
+
message: "Time range is shorter than 5 minutes; alert events may not have been indexed yet.",
|
|
303486
|
+
hint: "Widen the range (e.g. last 1h) or retry after the indexing delay (~30s) has elapsed."
|
|
303487
|
+
});
|
|
303488
|
+
}
|
|
303489
|
+
if (queryHasSourceAlert) {
|
|
303490
|
+
const otherTerms = countNonSourceTerms(input.query);
|
|
303491
|
+
const otherTags = (input.tags ?? []).filter((t) => !t.startsWith("source:")).length;
|
|
303492
|
+
if (otherTerms === 0 && otherTags === 0) {
|
|
303493
|
+
diagnostics.push({
|
|
303494
|
+
code: "RESTRICTIVE_SOURCE_FILTER",
|
|
303495
|
+
message: "Only source:alert filter was applied; the matching event set may genuinely be empty.",
|
|
303496
|
+
hint: "Use events.aggregate or monitors.list to confirm no alerts fired in the window, or broaden sources (e.g. source:monitor, source:audit)."
|
|
303497
|
+
});
|
|
303498
|
+
}
|
|
303499
|
+
}
|
|
303500
|
+
return diagnostics;
|
|
303501
|
+
}
|
|
303189
303502
|
function quoteIfNeeded(value) {
|
|
303190
303503
|
return /^[A-Za-z0-9_.-]+$/.test(value) ? value : `"${value}"`;
|
|
303191
303504
|
}
|
|
@@ -303213,6 +303526,9 @@ function buildEventQuery(params) {
|
|
|
303213
303526
|
return parts.length > 0 ? parts.join(" ") : "*";
|
|
303214
303527
|
}
|
|
303215
303528
|
async function searchEventsV2(api, params, limits, site) {
|
|
303529
|
+
if (params.timezone !== void 0) {
|
|
303530
|
+
validateIanaZone(params.timezone);
|
|
303531
|
+
}
|
|
303216
303532
|
const defaultFrom = hoursAgo(limits.defaultTimeRangeHours);
|
|
303217
303533
|
const defaultTo = now();
|
|
303218
303534
|
const [validFrom, validTo] = ensureValidTimeRange(
|
|
@@ -303242,12 +303558,13 @@ async function searchEventsV2(api, params, limits, site) {
|
|
|
303242
303558
|
}
|
|
303243
303559
|
};
|
|
303244
303560
|
const response = await api.searchEvents({ body });
|
|
303245
|
-
const
|
|
303561
|
+
const rawEvents = (response.data ?? []).map(formatEventV2);
|
|
303562
|
+
const events2 = params.timezone !== void 0 ? rawEvents.map((e) => annotateEventTimezone(e, params.timezone)) : rawEvents;
|
|
303246
303563
|
const nextCursor = response.meta?.page?.after;
|
|
303247
|
-
|
|
303248
|
-
events,
|
|
303564
|
+
const baseResult = {
|
|
303565
|
+
events: events2,
|
|
303249
303566
|
meta: {
|
|
303250
|
-
count:
|
|
303567
|
+
count: events2.length,
|
|
303251
303568
|
query: fullQuery,
|
|
303252
303569
|
from: fromTime,
|
|
303253
303570
|
to: toTime,
|
|
@@ -303255,8 +303572,131 @@ async function searchEventsV2(api, params, limits, site) {
|
|
|
303255
303572
|
datadog_url: buildEventsUrl(fullQuery, validFrom, validTo, site)
|
|
303256
303573
|
}
|
|
303257
303574
|
};
|
|
303575
|
+
if (events2.length === 0) {
|
|
303576
|
+
const diagnostics = computeDiagnostics({
|
|
303577
|
+
query: params.query,
|
|
303578
|
+
tags: params.tags,
|
|
303579
|
+
sources: params.sources,
|
|
303580
|
+
fromMs: validFrom * 1e3,
|
|
303581
|
+
toMs: validTo * 1e3
|
|
303582
|
+
});
|
|
303583
|
+
return { ...baseResult, diagnostics };
|
|
303584
|
+
}
|
|
303585
|
+
return baseResult;
|
|
303586
|
+
}
|
|
303587
|
+
function bucketEvent(epochMs, bucketBy, tz) {
|
|
303588
|
+
switch (bucketBy) {
|
|
303589
|
+
case "hour_of_day":
|
|
303590
|
+
return bucketHourOfDay(epochMs, tz);
|
|
303591
|
+
case "day_of_week":
|
|
303592
|
+
return bucketDayOfWeek(epochMs, tz);
|
|
303593
|
+
case "day_of_month":
|
|
303594
|
+
return bucketDayOfMonth(epochMs, tz);
|
|
303595
|
+
default: {
|
|
303596
|
+
const exhaustive = bucketBy;
|
|
303597
|
+
throw new Error(`Unhandled bucket_by: ${String(exhaustive)}`);
|
|
303598
|
+
}
|
|
303599
|
+
}
|
|
303600
|
+
}
|
|
303601
|
+
function eventEpochMs(event) {
|
|
303602
|
+
const ts = event.attributes?.timestamp;
|
|
303603
|
+
if (ts === void 0 || ts === null) return null;
|
|
303604
|
+
if (ts instanceof Date) {
|
|
303605
|
+
const ms2 = ts.getTime();
|
|
303606
|
+
return Number.isFinite(ms2) ? ms2 : null;
|
|
303607
|
+
}
|
|
303608
|
+
const ms = new Date(String(ts)).getTime();
|
|
303609
|
+
return Number.isFinite(ms) ? ms : null;
|
|
303610
|
+
}
|
|
303611
|
+
async function histogramEventsV2(api, params, limits, site) {
|
|
303612
|
+
const timezone = params.timezone ?? "UTC";
|
|
303613
|
+
validateIanaZone(timezone);
|
|
303614
|
+
const defaultFrom = hoursAgo(limits.defaultTimeRangeHours);
|
|
303615
|
+
const defaultTo = now();
|
|
303616
|
+
const [validFrom, validTo] = ensureValidTimeRange(
|
|
303617
|
+
parseTime(params.from, defaultFrom),
|
|
303618
|
+
parseTime(params.to, defaultTo)
|
|
303619
|
+
);
|
|
303620
|
+
const fromTime = new Date(validFrom * 1e3).toISOString();
|
|
303621
|
+
const toTime = new Date(validTo * 1e3).toISOString();
|
|
303622
|
+
const fullQuery = buildEventQuery({
|
|
303623
|
+
query: params.query,
|
|
303624
|
+
sources: params.sources,
|
|
303625
|
+
tags: params.tags
|
|
303626
|
+
});
|
|
303627
|
+
const cap = limits.maxEventsForHistogram;
|
|
303628
|
+
const perPage = Math.max(1, Math.min(1e3, cap));
|
|
303629
|
+
const buckets = {};
|
|
303630
|
+
let totalEvents = 0;
|
|
303631
|
+
let cursor = params.cursor;
|
|
303632
|
+
let bucketCountIncomplete = false;
|
|
303633
|
+
let exhaustedPages = false;
|
|
303634
|
+
const maxPages = 100;
|
|
303635
|
+
let pageCount = 0;
|
|
303636
|
+
while (pageCount < maxPages) {
|
|
303637
|
+
const body = {
|
|
303638
|
+
filter: {
|
|
303639
|
+
query: fullQuery,
|
|
303640
|
+
from: fromTime,
|
|
303641
|
+
to: toTime
|
|
303642
|
+
},
|
|
303643
|
+
sort: "timestamp",
|
|
303644
|
+
page: {
|
|
303645
|
+
limit: perPage,
|
|
303646
|
+
cursor
|
|
303647
|
+
}
|
|
303648
|
+
};
|
|
303649
|
+
const response = await api.searchEvents({ body });
|
|
303650
|
+
const data = response.data ?? [];
|
|
303651
|
+
const responseCursor = response.meta?.page?.after ?? void 0;
|
|
303652
|
+
for (const event of data) {
|
|
303653
|
+
const epochMs = eventEpochMs(event);
|
|
303654
|
+
if (epochMs === null) continue;
|
|
303655
|
+
const bucket = bucketEvent(epochMs, params.bucket_by, timezone);
|
|
303656
|
+
const key = String(bucket);
|
|
303657
|
+
buckets[key] = (buckets[key] ?? 0) + 1;
|
|
303658
|
+
totalEvents++;
|
|
303659
|
+
if (totalEvents >= cap) {
|
|
303660
|
+
bucketCountIncomplete = true;
|
|
303661
|
+
cursor = responseCursor;
|
|
303662
|
+
break;
|
|
303663
|
+
}
|
|
303664
|
+
}
|
|
303665
|
+
if (bucketCountIncomplete) break;
|
|
303666
|
+
if (data.length === 0 || !responseCursor) {
|
|
303667
|
+
exhaustedPages = true;
|
|
303668
|
+
break;
|
|
303669
|
+
}
|
|
303670
|
+
cursor = responseCursor;
|
|
303671
|
+
pageCount++;
|
|
303672
|
+
}
|
|
303673
|
+
const result = {
|
|
303674
|
+
buckets,
|
|
303675
|
+
bucketBy: params.bucket_by,
|
|
303676
|
+
timezone,
|
|
303677
|
+
totalEvents,
|
|
303678
|
+
meta: {
|
|
303679
|
+
query: fullQuery,
|
|
303680
|
+
from: fromTime,
|
|
303681
|
+
to: toTime,
|
|
303682
|
+
datadog_url: buildEventsUrl(fullQuery, validFrom, validTo, site)
|
|
303683
|
+
}
|
|
303684
|
+
};
|
|
303685
|
+
if (bucketCountIncomplete) {
|
|
303686
|
+
result.bucketCountIncomplete = true;
|
|
303687
|
+
if (cursor) {
|
|
303688
|
+
result.nextCursor = cursor;
|
|
303689
|
+
}
|
|
303690
|
+
} else if (!exhaustedPages && pageCount >= maxPages && cursor) {
|
|
303691
|
+
result.bucketCountIncomplete = true;
|
|
303692
|
+
result.nextCursor = cursor;
|
|
303693
|
+
}
|
|
303694
|
+
return result;
|
|
303258
303695
|
}
|
|
303259
303696
|
async function aggregateEventsV2(api, params, limits, site) {
|
|
303697
|
+
if (params.timezone !== void 0) {
|
|
303698
|
+
validateIanaZone(params.timezone);
|
|
303699
|
+
}
|
|
303260
303700
|
const counts = /* @__PURE__ */ new Map();
|
|
303261
303701
|
const defaultFrom = hoursAgo(limits.defaultTimeRangeHours);
|
|
303262
303702
|
const defaultTo = now();
|
|
@@ -303293,9 +303733,9 @@ async function aggregateEventsV2(api, params, limits, site) {
|
|
|
303293
303733
|
while (pageCount < maxPages && eventCount < maxEventsToAggregate) {
|
|
303294
303734
|
const pageBody = { ...body, page: { ...body.page, cursor } };
|
|
303295
303735
|
const response = await api.searchEvents({ body: pageBody });
|
|
303296
|
-
const
|
|
303297
|
-
if (
|
|
303298
|
-
for (const event of
|
|
303736
|
+
const events2 = response.data ?? [];
|
|
303737
|
+
if (events2.length === 0) break;
|
|
303738
|
+
for (const event of events2) {
|
|
303299
303739
|
const formatted = formatEventV2(event);
|
|
303300
303740
|
const groupKey = buildGroupKey(formatted, groupByFields);
|
|
303301
303741
|
const existing = counts.get(groupKey);
|
|
@@ -303316,7 +303756,8 @@ async function aggregateEventsV2(api, params, limits, site) {
|
|
|
303316
303756
|
const buckets = sorted.map(([key, data]) => ({
|
|
303317
303757
|
key,
|
|
303318
303758
|
count: data.count,
|
|
303319
|
-
|
|
303759
|
+
// Requirement 4: annotate sample timestamps only when timezone is supplied.
|
|
303760
|
+
sample: params.timezone !== void 0 ? annotateEventTimezone(data.sample, params.timezone) : data.sample
|
|
303320
303761
|
}));
|
|
303321
303762
|
return {
|
|
303322
303763
|
buckets,
|
|
@@ -303333,6 +303774,9 @@ async function aggregateEventsV2(api, params, limits, site) {
|
|
|
303333
303774
|
};
|
|
303334
303775
|
}
|
|
303335
303776
|
async function topEventsV2(api, params, limits, site) {
|
|
303777
|
+
if (params.timezone !== void 0) {
|
|
303778
|
+
validateIanaZone(params.timezone);
|
|
303779
|
+
}
|
|
303336
303780
|
if (params.contextTags !== void 0) {
|
|
303337
303781
|
if (!Array.isArray(params.contextTags)) {
|
|
303338
303782
|
throw new Error("contextTags must be an array");
|
|
@@ -303370,7 +303814,7 @@ async function topEventsV2(api, params, limits, site) {
|
|
|
303370
303814
|
value = event.monitorInfo?.name ?? event.title;
|
|
303371
303815
|
} else {
|
|
303372
303816
|
const tag = event.tags.find((t) => t.startsWith(`${field}:`));
|
|
303373
|
-
value = tag ? tag.split(":", 2)[1] : "unknown";
|
|
303817
|
+
value = tag ? tag.split(":", 2)[1] ?? "unknown" : "unknown";
|
|
303374
303818
|
}
|
|
303375
303819
|
groupValues[field] = value;
|
|
303376
303820
|
keyParts.push(`${field}:${value}`);
|
|
@@ -303431,6 +303875,9 @@ function parseIntervalToMs(interval) {
|
|
|
303431
303875
|
return ns ? Math.floor(ns / 1e6) : 36e5;
|
|
303432
303876
|
}
|
|
303433
303877
|
async function timeseriesEventsV2(api, params, limits, site) {
|
|
303878
|
+
if (params.timezone !== void 0) {
|
|
303879
|
+
validateIanaZone(params.timezone);
|
|
303880
|
+
}
|
|
303434
303881
|
const defaultFrom = hoursAgo(limits.defaultTimeRangeHours);
|
|
303435
303882
|
const defaultTo = now();
|
|
303436
303883
|
const [validFrom, validTo] = ensureValidTimeRange(
|
|
@@ -303465,9 +303912,9 @@ async function timeseriesEventsV2(api, params, limits, site) {
|
|
|
303465
303912
|
while (pageCount < maxPages && eventCount < maxEventsToProcess) {
|
|
303466
303913
|
const pageBody = { ...body, page: { ...body.page, cursor } };
|
|
303467
303914
|
const response = await api.searchEvents({ body: pageBody });
|
|
303468
|
-
const
|
|
303469
|
-
if (
|
|
303470
|
-
for (const event of
|
|
303915
|
+
const events2 = response.data ?? [];
|
|
303916
|
+
if (events2.length === 0) break;
|
|
303917
|
+
for (const event of events2) {
|
|
303471
303918
|
const formatted = formatEventV2(event);
|
|
303472
303919
|
const groupKey = buildGroupKey(formatted, groupByFields);
|
|
303473
303920
|
const eventTs = new Date(formatted.timestamp).getTime();
|
|
@@ -303484,6 +303931,7 @@ async function timeseriesEventsV2(api, params, limits, site) {
|
|
|
303484
303931
|
if (!cursor) break;
|
|
303485
303932
|
pageCount++;
|
|
303486
303933
|
}
|
|
303934
|
+
const tz = params.timezone;
|
|
303487
303935
|
const sortedBuckets = [...timeBuckets.entries()].sort((a, b) => a[0] - b[0]).map(([bucketTs, groupCounts]) => {
|
|
303488
303936
|
const counts = {};
|
|
303489
303937
|
let total = 0;
|
|
@@ -303491,12 +303939,16 @@ async function timeseriesEventsV2(api, params, limits, site) {
|
|
|
303491
303939
|
counts[key] = count;
|
|
303492
303940
|
total += count;
|
|
303493
303941
|
}
|
|
303494
|
-
|
|
303942
|
+
const bucket = {
|
|
303495
303943
|
timestamp: new Date(bucketTs).toISOString(),
|
|
303496
303944
|
timestampMs: bucketTs,
|
|
303497
303945
|
counts,
|
|
303498
303946
|
total
|
|
303499
303947
|
};
|
|
303948
|
+
if (tz !== void 0) {
|
|
303949
|
+
bucket.timestampLocal = formatLocal(bucketTs, tz);
|
|
303950
|
+
}
|
|
303951
|
+
return bucket;
|
|
303500
303952
|
});
|
|
303501
303953
|
const effectiveLimit = params.limit ?? 100;
|
|
303502
303954
|
const limitedBuckets = sortedBuckets.slice(0, effectiveLimit);
|
|
@@ -303517,6 +303969,9 @@ async function timeseriesEventsV2(api, params, limits, site) {
|
|
|
303517
303969
|
};
|
|
303518
303970
|
}
|
|
303519
303971
|
async function incidentsEventsV2(api, params, limits, site) {
|
|
303972
|
+
if (params.timezone !== void 0) {
|
|
303973
|
+
validateIanaZone(params.timezone);
|
|
303974
|
+
}
|
|
303520
303975
|
const defaultFrom = hoursAgo(limits.defaultTimeRangeHours);
|
|
303521
303976
|
const defaultTo = now();
|
|
303522
303977
|
const [validFrom, validTo] = ensureValidTimeRange(
|
|
@@ -303551,9 +304006,9 @@ async function incidentsEventsV2(api, params, limits, site) {
|
|
|
303551
304006
|
while (pageCount < maxPages && eventCount < maxEventsToProcess) {
|
|
303552
304007
|
const pageBody = { ...body, page: { ...body.page, cursor } };
|
|
303553
304008
|
const response = await api.searchEvents({ body: pageBody });
|
|
303554
|
-
const
|
|
303555
|
-
if (
|
|
303556
|
-
for (const event of
|
|
304009
|
+
const events2 = response.data ?? [];
|
|
304010
|
+
if (events2.length === 0) break;
|
|
304011
|
+
for (const event of events2) {
|
|
303557
304012
|
const formatted = formatEventV2(event);
|
|
303558
304013
|
const monitorName = formatted.monitorInfo?.name ?? formatted.title;
|
|
303559
304014
|
if (!monitorName) {
|
|
@@ -303621,6 +304076,7 @@ async function incidentsEventsV2(api, params, limits, site) {
|
|
|
303621
304076
|
if (!cursor) break;
|
|
303622
304077
|
pageCount++;
|
|
303623
304078
|
}
|
|
304079
|
+
const tz = params.timezone;
|
|
303624
304080
|
const incidentList = [...incidents.values()].map((inc) => {
|
|
303625
304081
|
let duration3;
|
|
303626
304082
|
if (inc.recoveredAt) {
|
|
@@ -303633,7 +304089,7 @@ async function incidentsEventsV2(api, params, limits, site) {
|
|
|
303633
304089
|
duration3 = `${(durationMs / 36e5).toFixed(1)}h`;
|
|
303634
304090
|
}
|
|
303635
304091
|
}
|
|
303636
|
-
|
|
304092
|
+
const base = {
|
|
303637
304093
|
monitorName: inc.monitorName,
|
|
303638
304094
|
firstTrigger: inc.firstTrigger.toISOString(),
|
|
303639
304095
|
lastTrigger: inc.lastTrigger.toISOString(),
|
|
@@ -303641,8 +304097,17 @@ async function incidentsEventsV2(api, params, limits, site) {
|
|
|
303641
304097
|
recovered: inc.recovered,
|
|
303642
304098
|
recoveredAt: inc.recoveredAt?.toISOString(),
|
|
303643
304099
|
duration: duration3,
|
|
303644
|
-
|
|
304100
|
+
// Requirement 4: annotate the nested sample event timestamp when tz is supplied.
|
|
304101
|
+
sample: tz !== void 0 ? annotateEventTimezone(inc.sample, tz) : inc.sample
|
|
303645
304102
|
};
|
|
304103
|
+
if (tz !== void 0) {
|
|
304104
|
+
base.firstTriggerLocal = formatLocal(inc.firstTrigger.getTime(), tz);
|
|
304105
|
+
base.lastTriggerLocal = formatLocal(inc.lastTrigger.getTime(), tz);
|
|
304106
|
+
if (inc.recoveredAt) {
|
|
304107
|
+
base.recoveredAtLocal = formatLocal(inc.recoveredAt.getTime(), tz);
|
|
304108
|
+
}
|
|
304109
|
+
}
|
|
304110
|
+
return base;
|
|
303646
304111
|
});
|
|
303647
304112
|
incidentList.sort(
|
|
303648
304113
|
(a, b) => new Date(b.firstTrigger).getTime() - new Date(a.firstTrigger).getTime()
|
|
@@ -303665,15 +304130,15 @@ async function incidentsEventsV2(api, params, limits, site) {
|
|
|
303665
304130
|
}
|
|
303666
304131
|
};
|
|
303667
304132
|
}
|
|
303668
|
-
async function enrichWithMonitorMetadata(
|
|
304133
|
+
async function enrichWithMonitorMetadata(events2, monitorsApi) {
|
|
303669
304134
|
const monitorIds = /* @__PURE__ */ new Set();
|
|
303670
|
-
for (const event of
|
|
304135
|
+
for (const event of events2) {
|
|
303671
304136
|
if (event.monitorId) {
|
|
303672
304137
|
monitorIds.add(event.monitorId);
|
|
303673
304138
|
}
|
|
303674
304139
|
}
|
|
303675
304140
|
if (monitorIds.size === 0) {
|
|
303676
|
-
return
|
|
304141
|
+
return events2;
|
|
303677
304142
|
}
|
|
303678
304143
|
const monitorCache = /* @__PURE__ */ new Map();
|
|
303679
304144
|
try {
|
|
@@ -303687,9 +304152,9 @@ async function enrichWithMonitorMetadata(events, monitorsApi) {
|
|
|
303687
304152
|
}
|
|
303688
304153
|
}
|
|
303689
304154
|
} catch {
|
|
303690
|
-
return
|
|
304155
|
+
return events2;
|
|
303691
304156
|
}
|
|
303692
|
-
return
|
|
304157
|
+
return events2.map((event) => {
|
|
303693
304158
|
const enriched = { ...event };
|
|
303694
304159
|
if (event.monitorId) {
|
|
303695
304160
|
const monitor = monitorCache.get(event.monitorId);
|
|
@@ -303714,7 +304179,7 @@ async function enrichWithMonitorMetadata(events, monitorsApi) {
|
|
|
303714
304179
|
function registerEventsTool(server, apiV1, apiV2, monitorsApi, limits, readOnly = false, site = "datadoghq.com") {
|
|
303715
304180
|
server.tool(
|
|
303716
304181
|
"events",
|
|
303717
|
-
`Track Datadog events. Actions: list, get, create, search, aggregate, top, timeseries, incidents, discover.
|
|
304182
|
+
`Track Datadog events. Actions: list, get, create, search, aggregate, top, timeseries, incidents, discover, histogram.
|
|
303718
304183
|
For monitor alerts, use tags: ["source:alert"].
|
|
303719
304184
|
|
|
303720
304185
|
IMPORTANT \u2014 re-evaluation vs transition:
|
|
@@ -303732,7 +304197,8 @@ discover: Returns available tag prefixes from events.
|
|
|
303732
304197
|
aggregate: Custom groupBy, returns pipe-delimited keys.
|
|
303733
304198
|
search: Full event details.
|
|
303734
304199
|
timeseries: Time-bucketed trends with interval.
|
|
303735
|
-
incidents: Deduplicate alerts with dedupeWindow
|
|
304200
|
+
incidents: Deduplicate alerts with dedupeWindow.
|
|
304201
|
+
histogram: Bucket events by local hour_of_day / day_of_week / day_of_month in the requested IANA timezone (DST-safe). Pass bucket_by (required) and optional timezone (default UTC) and cursor (for continuation). Caps at limits.maxEventsForHistogram (default 5000); when reached returns bucketCountIncomplete:true + nextCursor.`,
|
|
303736
304202
|
InputSchema,
|
|
303737
304203
|
async ({
|
|
303738
304204
|
action,
|
|
@@ -303754,7 +304220,10 @@ incidents: Deduplicate alerts with dedupeWindow.`,
|
|
|
303754
304220
|
enrich,
|
|
303755
304221
|
contextTags,
|
|
303756
304222
|
maxEvents,
|
|
303757
|
-
transitionType
|
|
304223
|
+
transitionType,
|
|
304224
|
+
bucket_by,
|
|
304225
|
+
timezone,
|
|
304226
|
+
fields
|
|
303758
304227
|
}) => {
|
|
303759
304228
|
try {
|
|
303760
304229
|
checkReadOnly(action, readOnly);
|
|
@@ -303804,16 +304273,19 @@ incidents: Deduplicate alerts with dedupeWindow.`,
|
|
|
303804
304273
|
priority,
|
|
303805
304274
|
limit,
|
|
303806
304275
|
cursor,
|
|
303807
|
-
transitionType
|
|
304276
|
+
transitionType,
|
|
304277
|
+
timezone
|
|
303808
304278
|
},
|
|
303809
304279
|
limits,
|
|
303810
304280
|
site
|
|
303811
304281
|
);
|
|
303812
304282
|
if (enrich && result.events.length > 0) {
|
|
303813
304283
|
const enrichedEvents = await enrichWithMonitorMetadata(result.events, monitorsApi);
|
|
303814
|
-
|
|
304284
|
+
const projected = fields?.length ? enrichedEvents.map((e) => pickEventFields(e, fields)) : enrichedEvents;
|
|
304285
|
+
return toolResult({ ...result, events: projected });
|
|
303815
304286
|
}
|
|
303816
|
-
|
|
304287
|
+
const projectedEvents = fields?.length ? result.events.map((e) => pickEventFields(e, fields)) : result.events;
|
|
304288
|
+
return toolResult({ ...result, events: projectedEvents });
|
|
303817
304289
|
}
|
|
303818
304290
|
case "aggregate":
|
|
303819
304291
|
return toolResult(
|
|
@@ -303827,7 +304299,8 @@ incidents: Deduplicate alerts with dedupeWindow.`,
|
|
|
303827
304299
|
tags,
|
|
303828
304300
|
groupBy,
|
|
303829
304301
|
limit,
|
|
303830
|
-
transitionType
|
|
304302
|
+
transitionType,
|
|
304303
|
+
timezone
|
|
303831
304304
|
},
|
|
303832
304305
|
limits,
|
|
303833
304306
|
site
|
|
@@ -303847,7 +304320,8 @@ incidents: Deduplicate alerts with dedupeWindow.`,
|
|
|
303847
304320
|
groupBy,
|
|
303848
304321
|
contextTags,
|
|
303849
304322
|
maxEvents,
|
|
303850
|
-
transitionType
|
|
304323
|
+
transitionType,
|
|
304324
|
+
timezone
|
|
303851
304325
|
},
|
|
303852
304326
|
limits,
|
|
303853
304327
|
site
|
|
@@ -303881,7 +304355,8 @@ incidents: Deduplicate alerts with dedupeWindow.`,
|
|
|
303881
304355
|
groupBy,
|
|
303882
304356
|
interval,
|
|
303883
304357
|
limit,
|
|
303884
|
-
transitionType
|
|
304358
|
+
transitionType,
|
|
304359
|
+
timezone
|
|
303885
304360
|
},
|
|
303886
304361
|
limits,
|
|
303887
304362
|
site
|
|
@@ -303899,12 +304374,33 @@ incidents: Deduplicate alerts with dedupeWindow.`,
|
|
|
303899
304374
|
tags,
|
|
303900
304375
|
dedupeWindow,
|
|
303901
304376
|
limit,
|
|
303902
|
-
transitionType
|
|
304377
|
+
transitionType,
|
|
304378
|
+
timezone
|
|
303903
304379
|
},
|
|
303904
304380
|
limits,
|
|
303905
304381
|
site
|
|
303906
304382
|
)
|
|
303907
304383
|
);
|
|
304384
|
+
case "histogram": {
|
|
304385
|
+
const histogramBucketBy = requireParam(bucket_by, "bucket_by", "histogram");
|
|
304386
|
+
return toolResult(
|
|
304387
|
+
await histogramEventsV2(
|
|
304388
|
+
apiV2,
|
|
304389
|
+
{
|
|
304390
|
+
query,
|
|
304391
|
+
from,
|
|
304392
|
+
to,
|
|
304393
|
+
sources,
|
|
304394
|
+
tags,
|
|
304395
|
+
bucket_by: histogramBucketBy,
|
|
304396
|
+
timezone,
|
|
304397
|
+
cursor
|
|
304398
|
+
},
|
|
304399
|
+
limits,
|
|
304400
|
+
site
|
|
304401
|
+
)
|
|
304402
|
+
);
|
|
304403
|
+
}
|
|
303908
304404
|
default:
|
|
303909
304405
|
throw new Error(`Unknown action: ${action}`);
|
|
303910
304406
|
}
|
|
@@ -303915,6 +304411,177 @@ incidents: Deduplicate alerts with dedupeWindow.`,
|
|
|
303915
304411
|
);
|
|
303916
304412
|
}
|
|
303917
304413
|
|
|
304414
|
+
// src/utils/templatePreview.ts
|
|
304415
|
+
var SUPPORTED_CONDITIONALS = [
|
|
304416
|
+
"is_alert",
|
|
304417
|
+
"is_warning",
|
|
304418
|
+
"is_no_data",
|
|
304419
|
+
"is_recovery",
|
|
304420
|
+
"is_alert_to_warning",
|
|
304421
|
+
"is_warning_to_alert"
|
|
304422
|
+
];
|
|
304423
|
+
var SUPPORTED_SET = new Set(SUPPORTED_CONDITIONALS);
|
|
304424
|
+
function unsupportedSyntaxError(detail) {
|
|
304425
|
+
const supportedList = SUPPORTED_CONDITIONALS.join(", ");
|
|
304426
|
+
return new Error(
|
|
304427
|
+
`EUNSUPPORTED_TEMPLATE_SYNTAX: ${detail}. Supported syntax is {{variable.name}} and conditionals {{#name}}...{{/name}} / {{^name}}...{{/name}} where name is one of: ${supportedList}. Loops ({{#each ...}}) and partials ({{> ...}}) are not supported.`
|
|
304428
|
+
);
|
|
304429
|
+
}
|
|
304430
|
+
function lookupVariable(path, variables) {
|
|
304431
|
+
if (Object.prototype.hasOwnProperty.call(variables, path)) {
|
|
304432
|
+
const value = variables[path];
|
|
304433
|
+
return value === void 0 || value === null ? void 0 : String(value);
|
|
304434
|
+
}
|
|
304435
|
+
const segments = path.split(".");
|
|
304436
|
+
let cursor = variables;
|
|
304437
|
+
for (const segment of segments) {
|
|
304438
|
+
if (cursor === null || cursor === void 0) {
|
|
304439
|
+
return void 0;
|
|
304440
|
+
}
|
|
304441
|
+
if (typeof cursor !== "object") {
|
|
304442
|
+
return void 0;
|
|
304443
|
+
}
|
|
304444
|
+
const record2 = cursor;
|
|
304445
|
+
if (!Object.prototype.hasOwnProperty.call(record2, segment)) {
|
|
304446
|
+
return void 0;
|
|
304447
|
+
}
|
|
304448
|
+
cursor = record2[segment];
|
|
304449
|
+
}
|
|
304450
|
+
if (cursor === void 0 || cursor === null) {
|
|
304451
|
+
return void 0;
|
|
304452
|
+
}
|
|
304453
|
+
if (typeof cursor === "object") {
|
|
304454
|
+
return void 0;
|
|
304455
|
+
}
|
|
304456
|
+
return String(cursor);
|
|
304457
|
+
}
|
|
304458
|
+
var TAG_REGEX = /\{\{\s*([#^/>])?\s*([^}]*?)\s*\}\}/g;
|
|
304459
|
+
function parseBlocks(template) {
|
|
304460
|
+
const stack = [{ children: [] }];
|
|
304461
|
+
let cursor = 0;
|
|
304462
|
+
TAG_REGEX.lastIndex = 0;
|
|
304463
|
+
let match;
|
|
304464
|
+
while ((match = TAG_REGEX.exec(template)) !== null) {
|
|
304465
|
+
const [fullTag, prefix, rawName] = match;
|
|
304466
|
+
const tagStart = match.index;
|
|
304467
|
+
const name = rawName?.trim() ?? "";
|
|
304468
|
+
if (tagStart > cursor) {
|
|
304469
|
+
const top = stack[stack.length - 1];
|
|
304470
|
+
if (top) {
|
|
304471
|
+
top.children.push({ kind: "literal", text: template.slice(cursor, tagStart) });
|
|
304472
|
+
}
|
|
304473
|
+
}
|
|
304474
|
+
if (prefix === ">") {
|
|
304475
|
+
throw unsupportedSyntaxError(`partials are not supported (found {{> ${name}}})`);
|
|
304476
|
+
}
|
|
304477
|
+
if (prefix === "#" || prefix === "^") {
|
|
304478
|
+
if (name.startsWith("each") || /\s/.test(name)) {
|
|
304479
|
+
throw unsupportedSyntaxError(`loops are not supported (found {{${prefix}${name}}})`);
|
|
304480
|
+
}
|
|
304481
|
+
if (!SUPPORTED_SET.has(name)) {
|
|
304482
|
+
throw unsupportedSyntaxError(`unknown conditional '${name}' in {{${prefix}${name}}}`);
|
|
304483
|
+
}
|
|
304484
|
+
stack.push({
|
|
304485
|
+
children: [],
|
|
304486
|
+
closer: { conditional: name, negated: prefix === "^" }
|
|
304487
|
+
});
|
|
304488
|
+
} else if (prefix === "/") {
|
|
304489
|
+
const frame = stack.pop();
|
|
304490
|
+
if (!frame || !frame.closer) {
|
|
304491
|
+
throw unsupportedSyntaxError(`unmatched closing tag {{/${name}}}`);
|
|
304492
|
+
}
|
|
304493
|
+
if (frame.closer.conditional !== name) {
|
|
304494
|
+
throw unsupportedSyntaxError(
|
|
304495
|
+
`mismatched closing tag {{/${name}}} (expected {{/${frame.closer.conditional}}})`
|
|
304496
|
+
);
|
|
304497
|
+
}
|
|
304498
|
+
const parent = stack[stack.length - 1];
|
|
304499
|
+
if (!parent) {
|
|
304500
|
+
throw unsupportedSyntaxError("block stack underflow while closing tag");
|
|
304501
|
+
}
|
|
304502
|
+
parent.children.push({
|
|
304503
|
+
kind: "block",
|
|
304504
|
+
conditional: frame.closer.conditional,
|
|
304505
|
+
negated: frame.closer.negated,
|
|
304506
|
+
children: frame.children
|
|
304507
|
+
});
|
|
304508
|
+
} else {
|
|
304509
|
+
const top = stack[stack.length - 1];
|
|
304510
|
+
if (top) {
|
|
304511
|
+
top.children.push({ kind: "literal", text: fullTag });
|
|
304512
|
+
}
|
|
304513
|
+
}
|
|
304514
|
+
cursor = tagStart + fullTag.length;
|
|
304515
|
+
}
|
|
304516
|
+
if (cursor < template.length) {
|
|
304517
|
+
const top = stack[stack.length - 1];
|
|
304518
|
+
if (top) {
|
|
304519
|
+
top.children.push({ kind: "literal", text: template.slice(cursor) });
|
|
304520
|
+
}
|
|
304521
|
+
}
|
|
304522
|
+
if (stack.length !== 1) {
|
|
304523
|
+
const open = stack[stack.length - 1]?.closer?.conditional;
|
|
304524
|
+
throw unsupportedSyntaxError(`unclosed conditional block ${open ? `{{#${open}}}` : ""}`);
|
|
304525
|
+
}
|
|
304526
|
+
const root = stack[0];
|
|
304527
|
+
if (!root) {
|
|
304528
|
+
throw unsupportedSyntaxError("parser produced no root frame");
|
|
304529
|
+
}
|
|
304530
|
+
return root.children;
|
|
304531
|
+
}
|
|
304532
|
+
function renderBlocks(tokens, conditionals, resolved) {
|
|
304533
|
+
let out = "";
|
|
304534
|
+
for (const token of tokens) {
|
|
304535
|
+
if (token.kind === "literal") {
|
|
304536
|
+
out += token.text;
|
|
304537
|
+
continue;
|
|
304538
|
+
}
|
|
304539
|
+
const flag = conditionals[token.conditional] ?? false;
|
|
304540
|
+
resolved[token.conditional] = flag;
|
|
304541
|
+
const include = token.negated ? !flag : flag;
|
|
304542
|
+
if (include) {
|
|
304543
|
+
out += renderBlocks(token.children, conditionals, resolved);
|
|
304544
|
+
} else {
|
|
304545
|
+
renderBlocks(token.children, conditionals, resolved);
|
|
304546
|
+
}
|
|
304547
|
+
}
|
|
304548
|
+
return out;
|
|
304549
|
+
}
|
|
304550
|
+
var VARIABLE_TAG_REGEX = /\{\{\s*([^#^/>\s][^}]*?)\s*\}\}/g;
|
|
304551
|
+
function substituteVariables(text, variables) {
|
|
304552
|
+
const usedSet = /* @__PURE__ */ new Set();
|
|
304553
|
+
const missingSet = /* @__PURE__ */ new Set();
|
|
304554
|
+
const rendered = text.replace(VARIABLE_TAG_REGEX, (_match, captured) => {
|
|
304555
|
+
const name = captured.trim();
|
|
304556
|
+
const value = lookupVariable(name, variables);
|
|
304557
|
+
if (value === void 0) {
|
|
304558
|
+
missingSet.add(name);
|
|
304559
|
+
return `{{undefined:${name}}}`;
|
|
304560
|
+
}
|
|
304561
|
+
usedSet.add(name);
|
|
304562
|
+
return value;
|
|
304563
|
+
});
|
|
304564
|
+
return {
|
|
304565
|
+
rendered,
|
|
304566
|
+
used: [...usedSet],
|
|
304567
|
+
missing: [...missingSet]
|
|
304568
|
+
};
|
|
304569
|
+
}
|
|
304570
|
+
function renderMonitorTemplate(template, context) {
|
|
304571
|
+
const variables = context.variables ?? {};
|
|
304572
|
+
const conditionals = context.conditionals ?? {};
|
|
304573
|
+
const tree = parseBlocks(template);
|
|
304574
|
+
const conditionalsResolved = {};
|
|
304575
|
+
const afterConditionals = renderBlocks(tree, conditionals, conditionalsResolved);
|
|
304576
|
+
const { rendered, used, missing } = substituteVariables(afterConditionals, variables);
|
|
304577
|
+
return {
|
|
304578
|
+
rendered,
|
|
304579
|
+
variablesUsed: used,
|
|
304580
|
+
variablesMissing: missing,
|
|
304581
|
+
conditionalsResolved
|
|
304582
|
+
};
|
|
304583
|
+
}
|
|
304584
|
+
|
|
303918
304585
|
// src/tools/monitors.ts
|
|
303919
304586
|
var ActionSchema2 = external_exports.enum([
|
|
303920
304587
|
"list",
|
|
@@ -303926,8 +304593,14 @@ var ActionSchema2 = external_exports.enum([
|
|
|
303926
304593
|
"mute",
|
|
303927
304594
|
"unmute",
|
|
303928
304595
|
"top",
|
|
303929
|
-
"history"
|
|
304596
|
+
"history",
|
|
304597
|
+
"preview",
|
|
304598
|
+
"test_notification"
|
|
303930
304599
|
]);
|
|
304600
|
+
var PreviewContextSchema = external_exports.object({
|
|
304601
|
+
variables: external_exports.record(external_exports.unknown()).optional(),
|
|
304602
|
+
conditionals: external_exports.record(external_exports.enum(SUPPORTED_CONDITIONALS), external_exports.boolean()).optional()
|
|
304603
|
+
}).optional().describe("Substitution context for monitors.preview (variables + conditionals).");
|
|
303931
304604
|
var InputSchema2 = {
|
|
303932
304605
|
action: ActionSchema2.describe("Action to perform"),
|
|
303933
304606
|
id: external_exports.string().optional().describe("Monitor ID (required for get/update/delete/mute/unmute)"),
|
|
@@ -303939,8 +304612,14 @@ var InputSchema2 = {
|
|
|
303939
304612
|
),
|
|
303940
304613
|
limit: external_exports.number().min(1).optional().describe("Maximum number of monitors to return (default: 50)"),
|
|
303941
304614
|
config: external_exports.record(external_exports.unknown()).optional().describe("Monitor configuration (for create/update)"),
|
|
303942
|
-
message: external_exports.string().optional().describe(
|
|
304615
|
+
message: external_exports.string().optional().describe(
|
|
304616
|
+
"Mute message (for mute action) OR inline template source for the preview action. For preview, supply either this inline string or `monitor_id` (or the existing `id` field) so the action can load the monitor message via getMonitor."
|
|
304617
|
+
),
|
|
303943
304618
|
end: external_exports.number().optional().describe("Mute end timestamp (for mute action)"),
|
|
304619
|
+
monitor_id: external_exports.number().optional().describe(
|
|
304620
|
+
"Numeric monitor ID used by the preview action when no inline `message` is supplied. Equivalent to passing the existing `id` field as a numeric string."
|
|
304621
|
+
),
|
|
304622
|
+
context: PreviewContextSchema,
|
|
303944
304623
|
// Top action parameters
|
|
303945
304624
|
from: external_exports.string().optional().describe('Start time (ISO 8601, relative like "1h", or Unix timestamp)'),
|
|
303946
304625
|
to: external_exports.string().optional().describe('End time (ISO 8601, relative like "1h", or Unix timestamp)'),
|
|
@@ -303964,6 +304643,12 @@ var InputSchema2 = {
|
|
|
303964
304643
|
),
|
|
303965
304644
|
group: external_exports.string().optional().describe(
|
|
303966
304645
|
'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.'
|
|
304646
|
+
),
|
|
304647
|
+
dry_run: external_exports.boolean().optional().describe(
|
|
304648
|
+
"When create + dry_run=true, validate the monitor body via POST /api/v1/monitor/validate without creating it. Allowed under --read-only because no monitor is created. Returns { valid, dryRun, monitor }. 400 responses surface verbatim like a failed create."
|
|
304649
|
+
),
|
|
304650
|
+
timezone: external_exports.string().optional().describe(
|
|
304651
|
+
'Optional IANA timezone (e.g. "UTC", "Europe/Paris"). When supplied on get/list, the response adds sibling createdLocal/modifiedLocal ISO 8601 strings next to created/modified. Omit for byte-identical legacy shape. Invalid zones return EINVALID_TIMEZONE.'
|
|
303967
304652
|
)
|
|
303968
304653
|
};
|
|
303969
304654
|
var MonitorThresholdsSchema = external_exports.object({
|
|
@@ -304058,6 +304743,18 @@ function summarizeZodIssue(error2) {
|
|
|
304058
304743
|
const expected = issue2.code === "invalid_type" && "expected" in issue2 ? `expected ${String(issue2.expected)}` : issue2.message;
|
|
304059
304744
|
return `${path}: ${expected}`;
|
|
304060
304745
|
}
|
|
304746
|
+
function annotateMonitorTimezone(monitor, tz) {
|
|
304747
|
+
const annotated = { ...monitor };
|
|
304748
|
+
if (monitor.created) {
|
|
304749
|
+
const ms = new Date(monitor.created).getTime();
|
|
304750
|
+
if (Number.isFinite(ms)) annotated.createdLocal = formatLocal(ms, tz);
|
|
304751
|
+
}
|
|
304752
|
+
if (monitor.modified) {
|
|
304753
|
+
const ms = new Date(monitor.modified).getTime();
|
|
304754
|
+
if (Number.isFinite(ms)) annotated.modifiedLocal = formatLocal(ms, tz);
|
|
304755
|
+
}
|
|
304756
|
+
return annotated;
|
|
304757
|
+
}
|
|
304061
304758
|
function formatMonitor(m, site = "datadoghq.com") {
|
|
304062
304759
|
const monitorId = m.id ?? 0;
|
|
304063
304760
|
return {
|
|
@@ -304201,9 +304898,9 @@ async function historyMonitor(eventsApi, monitorId, params, limits, site) {
|
|
|
304201
304898
|
while (pageCount < maxPages && eventCount < maxEventsToProcess) {
|
|
304202
304899
|
const pageBody = { ...body, page: { ...body.page, cursor } };
|
|
304203
304900
|
const response = await eventsApi.searchEvents({ body: pageBody });
|
|
304204
|
-
const
|
|
304205
|
-
if (
|
|
304206
|
-
for (const event of
|
|
304901
|
+
const events2 = response.data ?? [];
|
|
304902
|
+
if (events2.length === 0) break;
|
|
304903
|
+
for (const event of events2) {
|
|
304207
304904
|
const transition = formatMonitorTransition(event);
|
|
304208
304905
|
if (transition !== null) {
|
|
304209
304906
|
transitions.push(transition);
|
|
@@ -304236,14 +304933,18 @@ async function historyMonitor(eventsApi, monitorId, params, limits, site) {
|
|
|
304236
304933
|
meta
|
|
304237
304934
|
};
|
|
304238
304935
|
}
|
|
304239
|
-
async function listMonitors(api, params, limits, site) {
|
|
304936
|
+
async function listMonitors(api, params, limits, site, timezone) {
|
|
304937
|
+
if (timezone !== void 0) {
|
|
304938
|
+
validateIanaZone(timezone);
|
|
304939
|
+
}
|
|
304240
304940
|
const effectiveLimit = params.limit ?? limits.defaultLimit;
|
|
304241
304941
|
const response = await api.listMonitors({
|
|
304242
304942
|
name: params.name,
|
|
304243
304943
|
tags: params.tags?.join(","),
|
|
304244
304944
|
groupStates: params.groupStates?.join(",")
|
|
304245
304945
|
});
|
|
304246
|
-
const
|
|
304946
|
+
const baseMonitors = response.slice(0, effectiveLimit).map((m) => formatMonitor(m, site));
|
|
304947
|
+
const monitors2 = timezone !== void 0 ? baseMonitors.map((m) => annotateMonitorTimezone(m, timezone)) : baseMonitors;
|
|
304247
304948
|
const statusCounts = {
|
|
304248
304949
|
total: response.length,
|
|
304249
304950
|
alert: response.filter((m) => m.overallState === "Alert").length,
|
|
@@ -304260,14 +304961,19 @@ async function listMonitors(api, params, limits, site) {
|
|
|
304260
304961
|
)
|
|
304261
304962
|
};
|
|
304262
304963
|
}
|
|
304263
|
-
async function getMonitor(api, id, site) {
|
|
304964
|
+
async function getMonitor(api, id, site, timezone) {
|
|
304965
|
+
if (timezone !== void 0) {
|
|
304966
|
+
validateIanaZone(timezone);
|
|
304967
|
+
}
|
|
304264
304968
|
const monitorId = Number.parseInt(id, 10);
|
|
304265
304969
|
if (Number.isNaN(monitorId)) {
|
|
304266
304970
|
throw new Error(`Invalid monitor ID: ${id}`);
|
|
304267
304971
|
}
|
|
304268
304972
|
const monitor = await api.getMonitor({ monitorId });
|
|
304973
|
+
const baseDetail = formatMonitorDetail(monitor, site);
|
|
304974
|
+
const detail = timezone !== void 0 ? annotateMonitorTimezone(baseDetail, timezone) : baseDetail;
|
|
304269
304975
|
return {
|
|
304270
|
-
monitor:
|
|
304976
|
+
monitor: detail,
|
|
304271
304977
|
datadog_url: buildMonitorUrl(monitorId, site)
|
|
304272
304978
|
};
|
|
304273
304979
|
}
|
|
@@ -304369,6 +305075,37 @@ async function createMonitor(api, config2, site = "datadoghq.com") {
|
|
|
304369
305075
|
}
|
|
304370
305076
|
return result;
|
|
304371
305077
|
}
|
|
305078
|
+
async function dryRunMonitor(api, config2) {
|
|
305079
|
+
const normalized = normalizeMonitorConfig(config2);
|
|
305080
|
+
const body = normalized;
|
|
305081
|
+
await api.validateMonitor({ body });
|
|
305082
|
+
return {
|
|
305083
|
+
valid: true,
|
|
305084
|
+
dryRun: true,
|
|
305085
|
+
monitor: normalized
|
|
305086
|
+
};
|
|
305087
|
+
}
|
|
305088
|
+
async function previewMonitor(api, args, site = "datadoghq.com") {
|
|
305089
|
+
let template;
|
|
305090
|
+
let monitorId;
|
|
305091
|
+
if (args.inlineMessage !== void 0 && args.inlineMessage !== "") {
|
|
305092
|
+
template = args.inlineMessage;
|
|
305093
|
+
} else if (args.monitorIdSource !== void 0 && args.monitorIdSource !== "") {
|
|
305094
|
+
const loaded = await getMonitor(api, args.monitorIdSource, site);
|
|
305095
|
+
template = loaded.monitor.message ?? "";
|
|
305096
|
+
monitorId = loaded.monitor.id;
|
|
305097
|
+
} else {
|
|
305098
|
+
throw new Error(
|
|
305099
|
+
"Action 'preview' requires either an inline 'message' or a 'monitor_id' (or 'id') to load the template from."
|
|
305100
|
+
);
|
|
305101
|
+
}
|
|
305102
|
+
const result = renderMonitorTemplate(template, args.context ?? {});
|
|
305103
|
+
return monitorId !== void 0 ? { ...result, monitorId } : result;
|
|
305104
|
+
}
|
|
305105
|
+
var TEST_NOTIFICATION_NOT_SUPPORTED_MESSAGE = "ENOT_SUPPORTED: action 'test_notification' is not implemented \u2014 the Datadog public REST API exposes no monitor test-notification endpoint at v1 or v2 as of the events-dx-improvements spec. Use the Datadog UI's 'Test Notifications' button on the monitor page, or open an issue if Datadog publishes such an endpoint. Reference: https://docs.datadoghq.com/api/latest/monitors/";
|
|
305106
|
+
function testNotificationMonitor(_args) {
|
|
305107
|
+
throw new McpError(ErrorCode.InvalidRequest, TEST_NOTIFICATION_NOT_SUPPORTED_MESSAGE);
|
|
305108
|
+
}
|
|
304372
305109
|
async function updateMonitor(api, id, config2, site = "datadoghq.com") {
|
|
304373
305110
|
const monitorId = Number.parseInt(id, 10);
|
|
304374
305111
|
const normalized = normalizeMonitorConfig(config2, true);
|
|
@@ -304453,7 +305190,7 @@ async function topMonitors(eventsApi, monitorsApi, params, limits, site) {
|
|
|
304453
305190
|
}
|
|
304454
305191
|
});
|
|
304455
305192
|
const rawEvents = searchResponse.data ?? [];
|
|
304456
|
-
const
|
|
305193
|
+
const events2 = rawEvents.map(formatEventV2);
|
|
304457
305194
|
const contextPrefixes = new Set(
|
|
304458
305195
|
params.contextTags ?? [
|
|
304459
305196
|
"queue",
|
|
@@ -304465,7 +305202,7 @@ async function topMonitors(eventsApi, monitorsApi, params, limits, site) {
|
|
|
304465
305202
|
]
|
|
304466
305203
|
);
|
|
304467
305204
|
const monitorGroups = /* @__PURE__ */ new Map();
|
|
304468
|
-
for (const event of
|
|
305205
|
+
for (const event of events2) {
|
|
304469
305206
|
const monitorId = event.monitorId;
|
|
304470
305207
|
if (typeof monitorId !== "number") continue;
|
|
304471
305208
|
let group = monitorGroups.get(monitorId);
|
|
@@ -304534,7 +305271,7 @@ async function topMonitors(eventsApi, monitorsApi, params, limits, site) {
|
|
|
304534
305271
|
from: fromTime,
|
|
304535
305272
|
to: toTime,
|
|
304536
305273
|
totalMonitors: monitorGroups.size,
|
|
304537
|
-
totalEvents:
|
|
305274
|
+
totalEvents: events2.length,
|
|
304538
305275
|
contextPrefixes: Array.from(contextPrefixes),
|
|
304539
305276
|
datadog_url: buildEventsUrl(query, validFrom, validTo, site)
|
|
304540
305277
|
}
|
|
@@ -304543,7 +305280,7 @@ async function topMonitors(eventsApi, monitorsApi, params, limits, site) {
|
|
|
304543
305280
|
function registerMonitorsTool(server, api, eventsApi, limits, readOnly = false, site = "datadoghq.com") {
|
|
304544
305281
|
server.tool(
|
|
304545
305282
|
"monitors",
|
|
304546
|
-
`Manage Datadog monitors. Actions: list, get, search, create, update, delete, mute, unmute, top, history.
|
|
305283
|
+
`Manage Datadog monitors. Actions: list, get, search, create, update, delete, mute, unmute, top, history, preview, test_notification.
|
|
304547
305284
|
Filters: name, tags, groupStates (alert/warn/ok/no data).
|
|
304548
305285
|
get/create/update return the full options object so callers can safely read-then-patch.
|
|
304549
305286
|
|
|
@@ -304581,6 +305318,21 @@ history: Count and list real state transitions for one monitor over a time windo
|
|
|
304581
305318
|
transition_type filter that excludes renotifies by default. To include renotifies, pass
|
|
304582
305319
|
transitionType including "renotify".
|
|
304583
305320
|
|
|
305321
|
+
preview: Render a Datadog monitor message template against a context (read-only safe).
|
|
305322
|
+
- Inputs: either inline 'message' OR 'monitor_id' (or existing 'id'); plus optional 'context' { variables, conditionals }.
|
|
305323
|
+
- Supported syntax: {{variable.name}} substitution and conditional blocks {{#name}}...{{/name}} / {{^name}}...{{/name}}
|
|
305324
|
+
where name is one of: ${SUPPORTED_CONDITIONALS.join(", ")}.
|
|
305325
|
+
- Missing variables render as {{undefined:name}} markers and are reported in 'variablesMissing'.
|
|
305326
|
+
- Loops ({{#each ...}}) and partials ({{> ...}}) return EUNSUPPORTED_TEMPLATE_SYNTAX.
|
|
305327
|
+
- Allowed under --read-only (no mutation; at most a getMonitor load).
|
|
305328
|
+
|
|
305329
|
+
test_notification: KNOWN LIMITATION \u2014 always returns ENOT_SUPPORTED.
|
|
305330
|
+
- Datadog's public REST API exposes no monitor test-notification endpoint at v1 or v2
|
|
305331
|
+
(audited against the official OpenAPI specs). The v1 SDK has no notifyMonitor / testMonitor method.
|
|
305332
|
+
- Allowed under --read-only because no Datadog HTTP call is attempted.
|
|
305333
|
+
- If Datadog publishes such an endpoint in future, this action will be reimplemented to invoke it.
|
|
305334
|
+
- Workaround: use the 'Test Notifications' button in the Datadog monitor UI.
|
|
305335
|
+
|
|
304584
305336
|
For generic event grouping (deployments, configs), use events tool instead. Note that the
|
|
304585
305337
|
events tool's action=search with source:alert ALSO includes renotifies; use its
|
|
304586
305338
|
transitionType filter (or this action=history) for fires-only counts.`,
|
|
@@ -304594,24 +305346,32 @@ transitionType filter (or this action=history) for fires-only counts.`,
|
|
|
304594
305346
|
groupStates,
|
|
304595
305347
|
limit,
|
|
304596
305348
|
config: config2,
|
|
305349
|
+
message,
|
|
304597
305350
|
end,
|
|
304598
305351
|
from,
|
|
304599
305352
|
to,
|
|
304600
305353
|
contextTags,
|
|
304601
305354
|
maxEvents,
|
|
304602
305355
|
transitionType,
|
|
304603
|
-
group
|
|
305356
|
+
group,
|
|
305357
|
+
dry_run: dryRun,
|
|
305358
|
+
monitor_id: monitorIdNum,
|
|
305359
|
+
context,
|
|
305360
|
+
timezone
|
|
304604
305361
|
}) => {
|
|
304605
305362
|
try {
|
|
304606
|
-
|
|
305363
|
+
const isDryRunCreate = action === "create" && dryRun === true;
|
|
305364
|
+
if (!isDryRunCreate) {
|
|
305365
|
+
checkReadOnly(action, readOnly);
|
|
305366
|
+
}
|
|
304607
305367
|
switch (action) {
|
|
304608
305368
|
case "list":
|
|
304609
305369
|
return toolResult(
|
|
304610
|
-
await listMonitors(api, { name, tags, groupStates, limit }, limits, site)
|
|
305370
|
+
await listMonitors(api, { name, tags, groupStates, limit }, limits, site, timezone)
|
|
304611
305371
|
);
|
|
304612
305372
|
case "get": {
|
|
304613
305373
|
const monitorId = requireParam(id, "id", "get");
|
|
304614
|
-
return toolResult(await getMonitor(api, monitorId, site));
|
|
305374
|
+
return toolResult(await getMonitor(api, monitorId, site, timezone));
|
|
304615
305375
|
}
|
|
304616
305376
|
case "search": {
|
|
304617
305377
|
const searchQuery = requireParam(query, "query", "search");
|
|
@@ -304619,6 +305379,9 @@ transitionType filter (or this action=history) for fires-only counts.`,
|
|
|
304619
305379
|
}
|
|
304620
305380
|
case "create": {
|
|
304621
305381
|
const monitorConfig = requireParam(config2, "config", "create");
|
|
305382
|
+
if (dryRun) {
|
|
305383
|
+
return toolResult(await dryRunMonitor(api, monitorConfig));
|
|
305384
|
+
}
|
|
304622
305385
|
return toolResult(await createMonitor(api, monitorConfig, site));
|
|
304623
305386
|
}
|
|
304624
305387
|
case "update": {
|
|
@@ -304671,6 +305434,24 @@ transitionType filter (or this action=history) for fires-only counts.`,
|
|
|
304671
305434
|
)
|
|
304672
305435
|
);
|
|
304673
305436
|
}
|
|
305437
|
+
case "preview": {
|
|
305438
|
+
const monitorIdSource = monitorIdNum !== void 0 ? String(monitorIdNum) : id ?? void 0;
|
|
305439
|
+
return toolResult(
|
|
305440
|
+
await previewMonitor(
|
|
305441
|
+
api,
|
|
305442
|
+
{
|
|
305443
|
+
inlineMessage: message,
|
|
305444
|
+
monitorIdSource,
|
|
305445
|
+
context
|
|
305446
|
+
},
|
|
305447
|
+
site
|
|
305448
|
+
)
|
|
305449
|
+
);
|
|
305450
|
+
}
|
|
305451
|
+
case "test_notification": {
|
|
305452
|
+
const monitorIdSource = monitorIdNum !== void 0 ? String(monitorIdNum) : id ?? void 0;
|
|
305453
|
+
return testNotificationMonitor({ monitorId: monitorIdSource });
|
|
305454
|
+
}
|
|
304674
305455
|
default:
|
|
304675
305456
|
throw new Error(`Unknown action: ${action}`);
|
|
304676
305457
|
}
|
|
@@ -305287,6 +306068,56 @@ var InputSchema5 = {
|
|
|
305287
306068
|
"Maximum data points per timeseries (for query action). AI controls resolution vs token usage (default: 1000)."
|
|
305288
306069
|
)
|
|
305289
306070
|
};
|
|
306071
|
+
var ROLLUP_METHODS = /* @__PURE__ */ new Set(["avg", "max", "min", "sum", "count"]);
|
|
306072
|
+
var DEFAULT_ROLLUP_METHOD = "avg";
|
|
306073
|
+
function parseRollupFromQuery(query) {
|
|
306074
|
+
if (typeof query !== "string") return null;
|
|
306075
|
+
const matches = [...query.matchAll(/\.rollup\(\s*([^)]*?)\s*\)/g)];
|
|
306076
|
+
const lastMatch = matches[matches.length - 1];
|
|
306077
|
+
if (lastMatch === void 0) return null;
|
|
306078
|
+
const inner = lastMatch[1];
|
|
306079
|
+
if (inner === void 0 || inner.length === 0) return null;
|
|
306080
|
+
const parts = inner.split(",").map((p) => p.trim());
|
|
306081
|
+
if (parts.length === 1) {
|
|
306082
|
+
const raw = parts[0] ?? "";
|
|
306083
|
+
const interval = Number.parseInt(raw, 10);
|
|
306084
|
+
if (!Number.isFinite(interval) || interval <= 0 || String(interval) !== raw) {
|
|
306085
|
+
return null;
|
|
306086
|
+
}
|
|
306087
|
+
return { interval, method: DEFAULT_ROLLUP_METHOD, methodInferred: true };
|
|
306088
|
+
}
|
|
306089
|
+
if (parts.length === 2) {
|
|
306090
|
+
const method = parts[0] ?? "";
|
|
306091
|
+
const secondsRaw = parts[1] ?? "";
|
|
306092
|
+
if (!ROLLUP_METHODS.has(method)) return null;
|
|
306093
|
+
const interval = Number.parseInt(secondsRaw, 10);
|
|
306094
|
+
if (!Number.isFinite(interval) || interval <= 0 || String(interval) !== secondsRaw) {
|
|
306095
|
+
return null;
|
|
306096
|
+
}
|
|
306097
|
+
return { interval, method, methodInferred: false };
|
|
306098
|
+
}
|
|
306099
|
+
return null;
|
|
306100
|
+
}
|
|
306101
|
+
function computeEffectiveRollup(series) {
|
|
306102
|
+
const intervals = [];
|
|
306103
|
+
for (const s of series) {
|
|
306104
|
+
const pts = s.pointlist ?? [];
|
|
306105
|
+
if (pts.length < 2) continue;
|
|
306106
|
+
const first = pts[0];
|
|
306107
|
+
const second = pts[1];
|
|
306108
|
+
if (first === void 0 || second === void 0) continue;
|
|
306109
|
+
const deltaMs = (second[0] ?? 0) - (first[0] ?? 0);
|
|
306110
|
+
if (deltaMs <= 0) continue;
|
|
306111
|
+
intervals.push(Math.round(deltaMs / 1e3));
|
|
306112
|
+
}
|
|
306113
|
+
const primary = intervals[0];
|
|
306114
|
+
if (primary === void 0) return null;
|
|
306115
|
+
const unique = Array.from(new Set(intervals)).sort((a, b) => a - b);
|
|
306116
|
+
if (unique.length === 1) {
|
|
306117
|
+
return { interval: primary };
|
|
306118
|
+
}
|
|
306119
|
+
return { interval: primary, intervalsObserved: unique };
|
|
306120
|
+
}
|
|
305290
306121
|
async function queryMetrics(api, params, limits, site) {
|
|
305291
306122
|
const defaultFrom = hoursAgo(limits.defaultTimeRangeHours);
|
|
305292
306123
|
const defaultTo = now();
|
|
@@ -305299,7 +306130,8 @@ async function queryMetrics(api, params, limits, site) {
|
|
|
305299
306130
|
to: toTs,
|
|
305300
306131
|
query: params.query
|
|
305301
306132
|
});
|
|
305302
|
-
const
|
|
306133
|
+
const rawSeries = response.series ?? [];
|
|
306134
|
+
const series = rawSeries.map((s) => ({
|
|
305303
306135
|
metric: s.metric ?? "",
|
|
305304
306136
|
points: (s.pointlist ?? []).slice(0, params.pointLimit ?? limits.defaultMetricDataPoints).map((p) => ({
|
|
305305
306137
|
timestamp: p[0] ?? 0,
|
|
@@ -305308,6 +306140,13 @@ async function queryMetrics(api, params, limits, site) {
|
|
|
305308
306140
|
scope: s.scope ?? "",
|
|
305309
306141
|
tags: s.tagSet ?? []
|
|
305310
306142
|
}));
|
|
306143
|
+
const rollupRequested = parseRollupFromQuery(params.query);
|
|
306144
|
+
const rollupEffective = computeEffectiveRollup(
|
|
306145
|
+
rawSeries.map((s) => ({
|
|
306146
|
+
pointlist: s.pointlist ?? []
|
|
306147
|
+
}))
|
|
306148
|
+
);
|
|
306149
|
+
const rollupOverridden = rollupRequested !== null && rollupEffective !== null && (rollupEffective.interval !== rollupRequested.interval || (rollupEffective.intervalsObserved?.some((i) => i !== rollupRequested.interval) ?? false));
|
|
305311
306150
|
return {
|
|
305312
306151
|
series,
|
|
305313
306152
|
meta: {
|
|
@@ -305315,7 +306154,10 @@ async function queryMetrics(api, params, limits, site) {
|
|
|
305315
306154
|
from: new Date(fromTs * 1e3).toISOString(),
|
|
305316
306155
|
to: new Date(toTs * 1e3).toISOString(),
|
|
305317
306156
|
seriesCount: series.length,
|
|
305318
|
-
datadog_url: buildMetricsUrl(params.query, fromTs, toTs, site)
|
|
306157
|
+
datadog_url: buildMetricsUrl(params.query, fromTs, toTs, site),
|
|
306158
|
+
rollupRequested,
|
|
306159
|
+
rollupEffective,
|
|
306160
|
+
rollupOverridden
|
|
305319
306161
|
}
|
|
305320
306162
|
};
|
|
305321
306163
|
}
|
|
@@ -305995,10 +306837,11 @@ var InputSchema8 = {
|
|
|
305995
306837
|
from: external_exports.string().optional().describe('Start time for history (ISO 8601 or relative like "7d", "1w")'),
|
|
305996
306838
|
to: external_exports.string().optional().describe("End time for history (ISO 8601 or relative, default: now)")
|
|
305997
306839
|
};
|
|
305998
|
-
function formatSlo(s) {
|
|
306840
|
+
function formatSlo(s, site = "datadoghq.com") {
|
|
305999
306841
|
const primaryThreshold = s.thresholds?.[0];
|
|
306842
|
+
const id = s.id ?? "";
|
|
306000
306843
|
const summary = {
|
|
306001
|
-
id
|
|
306844
|
+
id,
|
|
306002
306845
|
name: s.name ?? "",
|
|
306003
306846
|
description: s.description ?? null,
|
|
306004
306847
|
type: String(s.type ?? "unknown"),
|
|
@@ -306013,7 +306856,8 @@ function formatSlo(s) {
|
|
|
306013
306856
|
},
|
|
306014
306857
|
overallStatus: [],
|
|
306015
306858
|
createdAt: s.createdAt ? new Date(s.createdAt * 1e3).toISOString() : "",
|
|
306016
|
-
modifiedAt: s.modifiedAt ? new Date(s.modifiedAt * 1e3).toISOString() : ""
|
|
306859
|
+
modifiedAt: s.modifiedAt ? new Date(s.modifiedAt * 1e3).toISOString() : "",
|
|
306860
|
+
url: id ? buildSloUrl(id, site) : ""
|
|
306017
306861
|
};
|
|
306018
306862
|
if (s.query?.numerator && s.query.denominator) {
|
|
306019
306863
|
summary.query = { numerator: s.query.numerator, denominator: s.query.denominator };
|
|
@@ -306029,11 +306873,12 @@ function formatSlo(s) {
|
|
|
306029
306873
|
}
|
|
306030
306874
|
return summary;
|
|
306031
306875
|
}
|
|
306032
|
-
function formatSearchSlo(slo) {
|
|
306876
|
+
function formatSearchSlo(slo, site = "datadoghq.com") {
|
|
306033
306877
|
const attrs = slo.data?.attributes;
|
|
306034
306878
|
const primaryThreshold = attrs?.thresholds?.[0];
|
|
306879
|
+
const id = slo.data?.id ?? "";
|
|
306035
306880
|
const summary = {
|
|
306036
|
-
id
|
|
306881
|
+
id,
|
|
306037
306882
|
name: attrs?.name ?? "",
|
|
306038
306883
|
description: attrs?.description ?? null,
|
|
306039
306884
|
type: String(attrs?.sloType ?? "unknown"),
|
|
@@ -306054,7 +306899,8 @@ function formatSearchSlo(slo) {
|
|
|
306054
306899
|
timeframe: String(os.timeframe ?? "")
|
|
306055
306900
|
})),
|
|
306056
306901
|
createdAt: attrs?.createdAt ? new Date(attrs.createdAt * 1e3).toISOString() : "",
|
|
306057
|
-
modifiedAt: attrs?.modifiedAt ? new Date(attrs.modifiedAt * 1e3).toISOString() : ""
|
|
306902
|
+
modifiedAt: attrs?.modifiedAt ? new Date(attrs.modifiedAt * 1e3).toISOString() : "",
|
|
306903
|
+
url: id ? buildSloUrl(id, site) : ""
|
|
306058
306904
|
};
|
|
306059
306905
|
if (attrs?.query?.numerator && attrs.query.denominator) {
|
|
306060
306906
|
summary.query = { numerator: attrs.query.numerator, denominator: attrs.query.denominator };
|
|
@@ -306073,14 +306919,14 @@ function buildSearchQuery(query, tags) {
|
|
|
306073
306919
|
if (tags?.length) parts.push(...tags);
|
|
306074
306920
|
return parts.join(" ");
|
|
306075
306921
|
}
|
|
306076
|
-
async function listSlos(api, params, limits) {
|
|
306922
|
+
async function listSlos(api, params, limits, site = "datadoghq.com") {
|
|
306077
306923
|
const effectiveLimit = params.limit ?? limits.defaultLimit;
|
|
306078
306924
|
if (params.ids?.length) {
|
|
306079
306925
|
const response2 = await api.listSLOs({
|
|
306080
306926
|
ids: params.ids.join(","),
|
|
306081
306927
|
limit: effectiveLimit
|
|
306082
306928
|
});
|
|
306083
|
-
const slos3 = (response2.data ?? []).map(formatSlo);
|
|
306929
|
+
const slos3 = (response2.data ?? []).map((s) => formatSlo(s, site));
|
|
306084
306930
|
return { slos: slos3, total: slos3.length };
|
|
306085
306931
|
}
|
|
306086
306932
|
const searchQuery = buildSearchQuery(params.query, params.tags);
|
|
@@ -306089,16 +306935,16 @@ async function listSlos(api, params, limits) {
|
|
|
306089
306935
|
pageSize: effectiveLimit
|
|
306090
306936
|
});
|
|
306091
306937
|
const searchSlos = response.data?.attributes?.slos ?? [];
|
|
306092
|
-
const slos2 = searchSlos.map(formatSearchSlo);
|
|
306938
|
+
const slos2 = searchSlos.map((s) => formatSearchSlo(s, site));
|
|
306093
306939
|
return {
|
|
306094
306940
|
slos: slos2,
|
|
306095
306941
|
total: slos2.length
|
|
306096
306942
|
};
|
|
306097
306943
|
}
|
|
306098
|
-
async function getSlo(api, id) {
|
|
306944
|
+
async function getSlo(api, id, site = "datadoghq.com") {
|
|
306099
306945
|
const response = await api.getSLO({ sloId: id });
|
|
306100
306946
|
return {
|
|
306101
|
-
slo: response.data ? formatSlo(response.data) : null
|
|
306947
|
+
slo: response.data ? formatSlo(response.data, site) : null
|
|
306102
306948
|
};
|
|
306103
306949
|
}
|
|
306104
306950
|
function normalizeSloConfig(config2) {
|
|
@@ -306114,20 +306960,20 @@ function normalizeSloConfig(config2) {
|
|
|
306114
306960
|
}
|
|
306115
306961
|
return normalized;
|
|
306116
306962
|
}
|
|
306117
|
-
async function createSlo(api, config2) {
|
|
306963
|
+
async function createSlo(api, config2, site = "datadoghq.com") {
|
|
306118
306964
|
const body = normalizeSloConfig(config2);
|
|
306119
306965
|
const response = await api.createSLO({ body });
|
|
306120
306966
|
return {
|
|
306121
306967
|
success: true,
|
|
306122
|
-
slo: response.data?.[0] ? formatSlo(response.data[0]) : null
|
|
306968
|
+
slo: response.data?.[0] ? formatSlo(response.data[0], site) : null
|
|
306123
306969
|
};
|
|
306124
306970
|
}
|
|
306125
|
-
async function updateSlo(api, id, config2) {
|
|
306971
|
+
async function updateSlo(api, id, config2, site = "datadoghq.com") {
|
|
306126
306972
|
const body = normalizeConfigKeys(config2);
|
|
306127
306973
|
const response = await api.updateSLO({ sloId: id, body });
|
|
306128
306974
|
return {
|
|
306129
306975
|
success: true,
|
|
306130
|
-
slo: response.data?.[0] ? formatSlo(response.data[0]) : null
|
|
306976
|
+
slo: response.data?.[0] ? formatSlo(response.data[0], site) : null
|
|
306131
306977
|
};
|
|
306132
306978
|
}
|
|
306133
306979
|
async function deleteSlo(api, id) {
|
|
@@ -306167,29 +307013,29 @@ async function getSloHistory(api, id, params) {
|
|
|
306167
307013
|
}
|
|
306168
307014
|
};
|
|
306169
307015
|
}
|
|
306170
|
-
function registerSlosTool(server, api, limits, readOnly = false,
|
|
307016
|
+
function registerSlosTool(server, api, limits, readOnly = false, site = "datadoghq.com") {
|
|
306171
307017
|
server.tool(
|
|
306172
307018
|
"slos",
|
|
306173
|
-
"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.",
|
|
307019
|
+
"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.",
|
|
306174
307020
|
InputSchema8,
|
|
306175
307021
|
async ({ action, id, ids, query, tags, limit, config: config2, from, to }) => {
|
|
306176
307022
|
try {
|
|
306177
307023
|
checkReadOnly(action, readOnly);
|
|
306178
307024
|
switch (action) {
|
|
306179
307025
|
case "list":
|
|
306180
|
-
return toolResult(await listSlos(api, { ids, query, tags, limit }, limits));
|
|
307026
|
+
return toolResult(await listSlos(api, { ids, query, tags, limit }, limits, site));
|
|
306181
307027
|
case "get": {
|
|
306182
307028
|
const sloId = requireParam(id, "id", "get");
|
|
306183
|
-
return toolResult(await getSlo(api, sloId));
|
|
307029
|
+
return toolResult(await getSlo(api, sloId, site));
|
|
306184
307030
|
}
|
|
306185
307031
|
case "create": {
|
|
306186
307032
|
const sloConfig = requireParam(config2, "config", "create");
|
|
306187
|
-
return toolResult(await createSlo(api, sloConfig));
|
|
307033
|
+
return toolResult(await createSlo(api, sloConfig, site));
|
|
306188
307034
|
}
|
|
306189
307035
|
case "update": {
|
|
306190
307036
|
const sloId = requireParam(id, "id", "update");
|
|
306191
307037
|
const sloConfig = requireParam(config2, "config", "update");
|
|
306192
|
-
return toolResult(await updateSlo(api, sloId, sloConfig));
|
|
307038
|
+
return toolResult(await updateSlo(api, sloId, sloConfig, site));
|
|
306193
307039
|
}
|
|
306194
307040
|
case "delete": {
|
|
306195
307041
|
const sloId = requireParam(id, "id", "delete");
|
|
@@ -306856,11 +307702,11 @@ async function searchEvents(api, params, limits, site) {
|
|
|
306856
307702
|
sort: params.sort === "timestamp" ? "timestamp" : "-timestamp",
|
|
306857
307703
|
pageLimit: params.limit ?? limits.defaultLimit
|
|
306858
307704
|
});
|
|
306859
|
-
const
|
|
307705
|
+
const events2 = (response.data ?? []).map(formatEvent);
|
|
306860
307706
|
return {
|
|
306861
|
-
events,
|
|
307707
|
+
events: events2,
|
|
306862
307708
|
meta: {
|
|
306863
|
-
totalCount:
|
|
307709
|
+
totalCount: events2.length,
|
|
306864
307710
|
timeRange: {
|
|
306865
307711
|
from: new Date(fromTime * 1e3).toISOString(),
|
|
306866
307712
|
to: new Date(toTime * 1e3).toISOString()
|
|
@@ -307073,19 +307919,19 @@ async function getSessionWaterfall(api, params, limits, site) {
|
|
|
307073
307919
|
sort: "timestamp",
|
|
307074
307920
|
pageLimit: limits.defaultLimit
|
|
307075
307921
|
});
|
|
307076
|
-
const
|
|
307922
|
+
const events2 = (response.data ?? []).map(formatWaterfallEvent);
|
|
307077
307923
|
const summary = {
|
|
307078
|
-
views:
|
|
307079
|
-
resources:
|
|
307080
|
-
actions:
|
|
307081
|
-
errors:
|
|
307082
|
-
longTasks:
|
|
307924
|
+
views: events2.filter((e) => e.type === "view").length,
|
|
307925
|
+
resources: events2.filter((e) => e.type === "resource").length,
|
|
307926
|
+
actions: events2.filter((e) => e.type === "action").length,
|
|
307927
|
+
errors: events2.filter((e) => e.type === "error").length,
|
|
307928
|
+
longTasks: events2.filter((e) => e.type === "long_task").length
|
|
307083
307929
|
};
|
|
307084
307930
|
return {
|
|
307085
|
-
events,
|
|
307931
|
+
events: events2,
|
|
307086
307932
|
summary,
|
|
307087
307933
|
meta: {
|
|
307088
|
-
totalCount:
|
|
307934
|
+
totalCount: events2.length,
|
|
307089
307935
|
applicationId: params.applicationId,
|
|
307090
307936
|
sessionId: params.sessionId,
|
|
307091
307937
|
viewId: params.viewId ?? null,
|
|
@@ -308265,6 +309111,17 @@ var dashboards = {
|
|
|
308265
309111
|
docsUrl: "https://docs.datadoghq.com/api/latest/dashboards/"
|
|
308266
309112
|
};
|
|
308267
309113
|
|
|
309114
|
+
// src/schema/events.ts
|
|
309115
|
+
var events = {
|
|
309116
|
+
/**
|
|
309117
|
+
* Diagnostic codes emitted on zero-result `events.search` responses.
|
|
309118
|
+
* Each code is also documented inline in `src/tools/events.ts` next to the
|
|
309119
|
+
* heuristic that produces it.
|
|
309120
|
+
*/
|
|
309121
|
+
diagnosticCodes: ["UNINDEXED_TAG_PREFIX", "NARROW_TIME_RANGE", "RESTRICTIVE_SOURCE_FILTER"],
|
|
309122
|
+
docsUrl: "https://docs.datadoghq.com/api/latest/events/"
|
|
309123
|
+
};
|
|
309124
|
+
|
|
308268
309125
|
// src/schema/metrics.ts
|
|
308269
309126
|
var metrics = {
|
|
308270
309127
|
aggregators: ["avg", "max", "min", "sum", "count"],
|
|
@@ -308325,14 +309182,14 @@ var slos = {
|
|
|
308325
309182
|
};
|
|
308326
309183
|
|
|
308327
309184
|
// src/schema/index.ts
|
|
308328
|
-
var schemas = { dashboards, metrics, monitors, slos };
|
|
309185
|
+
var schemas = { dashboards, events, metrics, monitors, slos };
|
|
308329
309186
|
var schemaResources = Object.keys(schemas);
|
|
308330
309187
|
|
|
308331
309188
|
// src/tools/schema.ts
|
|
308332
309189
|
var ResourceSchema2 = external_exports.enum(schemaResources);
|
|
308333
309190
|
var InputSchema20 = {
|
|
308334
309191
|
resource: ResourceSchema2.describe(
|
|
308335
|
-
"Datadog resource type to get schema for: dashboards, metrics, monitors, slos"
|
|
309192
|
+
"Datadog resource type to get schema for: dashboards, events, metrics, monitors, slos"
|
|
308336
309193
|
)
|
|
308337
309194
|
};
|
|
308338
309195
|
function getSchema(resource) {
|