datadog-mcp 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -72,7 +72,7 @@ function parseArgs() {
72
72
  const nextArg = argv[i + 1];
73
73
  if (nextArg && !nextArg.startsWith("--")) {
74
74
  strings[argName] = nextArg;
75
- i++;
75
+ i += 1;
76
76
  } else {
77
77
  booleans.add(argName);
78
78
  }
@@ -98,18 +98,20 @@ function loadConfig() {
98
98
  name: "datadog-mcp",
99
99
  version: "1.0.0",
100
100
  transport: args.strings.transport ?? process.env.MCP_TRANSPORT ?? "stdio",
101
- port: parseInt(args.strings.port ?? process.env.MCP_PORT ?? "3000", 10),
101
+ port: Number.parseInt(args.strings.port ?? process.env.MCP_PORT ?? "3000", 10),
102
102
  host: args.strings.host ?? process.env.MCP_HOST ?? "localhost"
103
103
  },
104
104
  limits: {
105
- maxResults: parseInt(process.env.MCP_MAX_RESULTS ?? "100", 10),
106
- maxLogLines: parseInt(process.env.MCP_MAX_LOG_LINES ?? "500", 10),
107
- maxMetricDataPoints: parseInt(process.env.MCP_MAX_METRIC_POINTS ?? "1000", 10),
108
- defaultTimeRangeHours: parseInt(process.env.MCP_DEFAULT_TIME_RANGE ?? "24", 10)
105
+ maxResults: Number.parseInt(process.env.MCP_MAX_RESULTS ?? "100", 10),
106
+ maxLogLines: Number.parseInt(process.env.MCP_MAX_LOG_LINES ?? "500", 10),
107
+ maxMetricDataPoints: Number.parseInt(process.env.MCP_MAX_METRIC_POINTS ?? "1000", 10),
108
+ defaultTimeRangeHours: Number.parseInt(process.env.MCP_DEFAULT_TIME_RANGE ?? "24", 10)
109
109
  },
110
110
  features: {
111
111
  readOnly: args.booleans.has("read-only") || process.env.MCP_READ_ONLY === "true",
112
- disabledTools: parseDisabledTools(args.strings["disable-tools"] ?? process.env.MCP_DISABLE_TOOLS)
112
+ disabledTools: parseDisabledTools(
113
+ args.strings["disable-tools"] ?? process.env.MCP_DISABLE_TOOLS
114
+ )
113
115
  }
114
116
  };
115
117
  return configSchema.parse(raw);
@@ -194,19 +196,31 @@ function handleDatadogError(error) {
194
196
  case 400:
195
197
  throw new McpError(ErrorCode.InvalidRequest, `Invalid request: ${message}`);
196
198
  case 401:
197
- throw new McpError(DatadogErrorCode.Unauthorized, `Authentication failed: Invalid Datadog API key or APP key`);
199
+ throw new McpError(
200
+ DatadogErrorCode.Unauthorized,
201
+ `Authentication failed: Invalid Datadog API key or APP key`
202
+ );
198
203
  case 403:
199
204
  throw new McpError(DatadogErrorCode.Forbidden, `Authorization denied: ${message}`);
200
205
  case 404:
201
206
  throw new McpError(DatadogErrorCode.NotFound, `Resource not found: ${message}`);
202
207
  case 429:
203
- throw new McpError(DatadogErrorCode.RateLimited, "Rate limit exceeded. Retry after a short delay.");
208
+ throw new McpError(
209
+ DatadogErrorCode.RateLimited,
210
+ "Rate limit exceeded. Retry after a short delay."
211
+ );
204
212
  case 500:
205
213
  case 502:
206
214
  case 503:
207
- throw new McpError(DatadogErrorCode.ServiceUnavailable, "Datadog service temporarily unavailable. Retry later.");
215
+ throw new McpError(
216
+ DatadogErrorCode.ServiceUnavailable,
217
+ "Datadog service temporarily unavailable. Retry later."
218
+ );
208
219
  default:
209
- throw new McpError(ErrorCode.InternalError, `Datadog API error (${apiError.code}): ${message}`);
220
+ throw new McpError(
221
+ ErrorCode.InternalError,
222
+ `Datadog API error (${apiError.code}): ${message}`
223
+ );
210
224
  }
211
225
  }
212
226
  throw new McpError(
@@ -341,7 +355,16 @@ function buildRumSessionUrl(applicationId, sessionId, site = "datadoghq.com") {
341
355
  }
342
356
 
343
357
  // src/tools/monitors.ts
344
- var ActionSchema = z2.enum(["list", "get", "search", "create", "update", "delete", "mute", "unmute"]);
358
+ var ActionSchema = z2.enum([
359
+ "list",
360
+ "get",
361
+ "search",
362
+ "create",
363
+ "update",
364
+ "delete",
365
+ "mute",
366
+ "unmute"
367
+ ]);
345
368
  var InputSchema = {
346
369
  action: ActionSchema.describe("Action to perform"),
347
370
  id: z2.string().optional().describe("Monitor ID (required for get/update/delete/mute/unmute)"),
@@ -389,8 +412,8 @@ async function listMonitors(api, params, limits, site) {
389
412
  };
390
413
  }
391
414
  async function getMonitor(api, id, site) {
392
- const monitorId = parseInt(id, 10);
393
- if (isNaN(monitorId)) {
415
+ const monitorId = Number.parseInt(id, 10);
416
+ if (Number.isNaN(monitorId)) {
394
417
  throw new Error(`Invalid monitor ID: ${id}`);
395
418
  }
396
419
  const monitor = await api.getMonitor({ monitorId });
@@ -478,7 +501,7 @@ async function createMonitor(api, config) {
478
501
  };
479
502
  }
480
503
  async function updateMonitor(api, id, config) {
481
- const monitorId = parseInt(id, 10);
504
+ const monitorId = Number.parseInt(id, 10);
482
505
  const body = normalizeMonitorConfig(config);
483
506
  const monitor = await api.updateMonitor({ monitorId, body });
484
507
  return {
@@ -487,12 +510,12 @@ async function updateMonitor(api, id, config) {
487
510
  };
488
511
  }
489
512
  async function deleteMonitor(api, id) {
490
- const monitorId = parseInt(id, 10);
513
+ const monitorId = Number.parseInt(id, 10);
491
514
  await api.deleteMonitor({ monitorId });
492
515
  return { success: true, message: `Monitor ${id} deleted` };
493
516
  }
494
517
  async function muteMonitor(api, id, params) {
495
- const monitorId = parseInt(id, 10);
518
+ const monitorId = Number.parseInt(id, 10);
496
519
  const monitor = await api.getMonitor({ monitorId });
497
520
  await api.updateMonitor({
498
521
  monitorId,
@@ -506,7 +529,7 @@ async function muteMonitor(api, id, params) {
506
529
  return { success: true, message: `Monitor ${id} muted` };
507
530
  }
508
531
  async function unmuteMonitor(api, id) {
509
- const monitorId = parseInt(id, 10);
532
+ const monitorId = Number.parseInt(id, 10);
510
533
  const monitor = await api.getMonitor({ monitorId });
511
534
  await api.updateMonitor({
512
535
  monitorId,
@@ -531,7 +554,9 @@ TIP: For alert HISTORY (which monitors triggered), use the events tool with tags
531
554
  checkReadOnly(action, readOnly);
532
555
  switch (action) {
533
556
  case "list":
534
- return toolResult(await listMonitors(api, { name, tags, groupStates, limit }, limits, site));
557
+ return toolResult(
558
+ await listMonitors(api, { name, tags, groupStates, limit }, limits, site)
559
+ );
535
560
  case "get": {
536
561
  const monitorId = requireParam(id, "id", "get");
537
562
  return toolResult(await getMonitor(api, monitorId, site));
@@ -602,9 +627,7 @@ async function listDashboards(api, params, limits) {
602
627
  let dashboards = response.dashboards ?? [];
603
628
  if (params.name) {
604
629
  const lowerName = params.name.toLowerCase();
605
- dashboards = dashboards.filter(
606
- (d) => d.title?.toLowerCase().includes(lowerName)
607
- );
630
+ dashboards = dashboards.filter((d) => d.title?.toLowerCase().includes(lowerName));
608
631
  }
609
632
  const result = dashboards.slice(0, effectiveLimit).map(formatDashboardSummary);
610
633
  return {
@@ -667,7 +690,7 @@ async function deleteDashboard(api, id) {
667
690
  await api.deleteDashboard({ dashboardId: id });
668
691
  return { success: true, message: `Dashboard ${id} deleted` };
669
692
  }
670
- function registerDashboardsTool(server, api, limits, readOnly = false) {
693
+ function registerDashboardsTool(server, api, limits, readOnly = false, _site = "datadoghq.com") {
671
694
  server.tool(
672
695
  "dashboards",
673
696
  "Access Datadog dashboards and visualizations. Actions: list (filter by name/tags), get, create, update, delete. Use for: finding existing views, team dashboards, understanding what is monitored.",
@@ -729,9 +752,9 @@ function parseTime(input, defaultValue) {
729
752
  return input;
730
753
  }
731
754
  const trimmed = input.trim();
732
- const simpleRelativeMatch = trimmed.match(/^(\d+)(s|m|h|d)$/);
755
+ const simpleRelativeMatch = trimmed.match(/^(\d+)([smhd])$/);
733
756
  if (simpleRelativeMatch) {
734
- const value = parseInt(simpleRelativeMatch[1] ?? "0", 10);
757
+ const value = Number.parseInt(simpleRelativeMatch[1] ?? "0", 10);
735
758
  const unit = simpleRelativeMatch[2];
736
759
  const nowTs = now();
737
760
  switch (unit) {
@@ -747,13 +770,13 @@ function parseTime(input, defaultValue) {
747
770
  return defaultValue;
748
771
  }
749
772
  }
750
- const relativeWithTimeMatch = trimmed.match(/^(\d+)(d|h)[@\s](\d{1,2}):(\d{2})(?::(\d{2}))?$/);
773
+ const relativeWithTimeMatch = trimmed.match(/^(\d+)([dh])[@\s](\d{1,2}):(\d{2})(?::(\d{2}))?$/);
751
774
  if (relativeWithTimeMatch) {
752
- const value = parseInt(relativeWithTimeMatch[1] ?? "0", 10);
775
+ const value = Number.parseInt(relativeWithTimeMatch[1] ?? "0", 10);
753
776
  const unit = relativeWithTimeMatch[2];
754
- const hours = parseInt(relativeWithTimeMatch[3] ?? "0", 10);
755
- const minutes = parseInt(relativeWithTimeMatch[4] ?? "0", 10);
756
- const seconds = parseInt(relativeWithTimeMatch[5] ?? "0", 10);
777
+ const hours = Number.parseInt(relativeWithTimeMatch[3] ?? "0", 10);
778
+ const minutes = Number.parseInt(relativeWithTimeMatch[4] ?? "0", 10);
779
+ const seconds = Number.parseInt(relativeWithTimeMatch[5] ?? "0", 10);
757
780
  if (unit === "d") {
758
781
  const date3 = startOfDayAgo(value);
759
782
  date3.setHours(hours, minutes, seconds, 0);
@@ -767,26 +790,27 @@ function parseTime(input, defaultValue) {
767
790
  const keywordMatch = trimmed.match(/^(today|yesterday)[@\s](\d{1,2}):(\d{2})(?::(\d{2}))?$/i);
768
791
  if (keywordMatch) {
769
792
  const keyword = keywordMatch[1]?.toLowerCase();
770
- const hours = parseInt(keywordMatch[2] ?? "0", 10);
771
- const minutes = parseInt(keywordMatch[3] ?? "0", 10);
772
- const seconds = parseInt(keywordMatch[4] ?? "0", 10);
793
+ const hours = Number.parseInt(keywordMatch[2] ?? "0", 10);
794
+ const minutes = Number.parseInt(keywordMatch[3] ?? "0", 10);
795
+ const seconds = Number.parseInt(keywordMatch[4] ?? "0", 10);
773
796
  const daysAgo = keyword === "yesterday" ? 1 : 0;
774
797
  const date2 = startOfDayAgo(daysAgo);
775
798
  date2.setHours(hours, minutes, seconds, 0);
776
799
  return Math.floor(date2.getTime() / 1e3);
777
800
  }
778
801
  const date = new Date(trimmed);
779
- if (!isNaN(date.getTime())) {
802
+ if (!Number.isNaN(date.getTime())) {
780
803
  return Math.floor(date.getTime() / 1e3);
781
804
  }
782
- const ts = parseInt(trimmed, 10);
783
- if (!isNaN(ts)) {
805
+ const ts = Number.parseInt(trimmed, 10);
806
+ if (!Number.isNaN(ts)) {
784
807
  return ts;
785
808
  }
786
809
  return defaultValue;
787
810
  }
788
811
  function ensureValidTimeRange(from, to, minRangeSeconds = 60) {
789
812
  if (from > to) {
813
+ ;
790
814
  [from, to] = [to, from];
791
815
  }
792
816
  if (to - from < minRangeSeconds) {
@@ -804,21 +828,21 @@ function parseDurationToNs(input) {
804
828
  const trimmed = input.trim().toLowerCase();
805
829
  const match = trimmed.match(/^(\d+(?:\.\d+)?)(ns|µs|us|ms|s|m|h|d|w)?$/);
806
830
  if (!match) {
807
- const raw = parseInt(trimmed, 10);
808
- return isNaN(raw) ? void 0 : raw;
831
+ const raw = Number.parseInt(trimmed, 10);
832
+ return Number.isNaN(raw) ? void 0 : raw;
809
833
  }
810
- const value = parseFloat(match[1] ?? "0");
834
+ const value = Number.parseFloat(match[1] ?? "0");
811
835
  const unit = match[2] ?? "ns";
812
836
  const multipliers = {
813
- "ns": 1,
814
- "\xB5s": 1e3,
815
- "us": 1e3,
816
- "ms": 1e6,
817
- "s": 1e9,
818
- "m": 6e10,
819
- "h": 36e11,
820
- "d": 864e11,
821
- "w": 6048e11
837
+ ns: 1,
838
+ \u00B5s: 1e3,
839
+ us: 1e3,
840
+ ms: 1e6,
841
+ s: 1e9,
842
+ m: 6e10,
843
+ h: 36e11,
844
+ d: 864e11,
845
+ w: 6048e11
822
846
  };
823
847
  return Math.floor(value * (multipliers[unit] ?? 1));
824
848
  }
@@ -834,10 +858,18 @@ function formatDurationNs(ns) {
834
858
  var ActionSchema3 = z4.enum(["search", "aggregate"]);
835
859
  var InputSchema3 = {
836
860
  action: ActionSchema3.describe("Action to perform"),
837
- query: z4.string().optional().describe('Log search query (Datadog syntax). Examples: "error", "service:my-service status:error", "error AND timeout"'),
838
- keyword: z4.string().optional().describe("Simple text search - finds logs containing this text (grep-like). Merged with query using AND"),
839
- pattern: z4.string().optional().describe('Regex pattern to match in log message (grep -E style). Example: "ERROR.*timeout|connection refused"'),
840
- from: z4.string().optional().describe("Start time. Formats: ISO 8601, relative (30s, 15m, 2h, 7d), precise (3d@11:45:23, yesterday@14:00)"),
861
+ query: z4.string().optional().describe(
862
+ 'Log search query (Datadog syntax). Examples: "error", "service:my-service status:error", "error AND timeout"'
863
+ ),
864
+ keyword: z4.string().optional().describe(
865
+ "Simple text search - finds logs containing this text (grep-like). Merged with query using AND"
866
+ ),
867
+ pattern: z4.string().optional().describe(
868
+ 'Regex pattern to match in log message (grep -E style). Example: "ERROR.*timeout|connection refused"'
869
+ ),
870
+ from: z4.string().optional().describe(
871
+ "Start time. Formats: ISO 8601, relative (30s, 15m, 2h, 7d), precise (3d@11:45:23, yesterday@14:00)"
872
+ ),
841
873
  to: z4.string().optional().describe('End time. Same formats as "from". Example: from="3d@11:45:23" to="3d@12:55:34"'),
842
874
  service: z4.string().optional().describe("Filter by service name"),
843
875
  host: z4.string().optional().describe("Filter by host"),
@@ -845,8 +877,12 @@ var InputSchema3 = {
845
877
  indexes: z4.array(z4.string()).optional().describe("Log indexes to search"),
846
878
  limit: z4.number().optional().describe("Maximum number of logs to return"),
847
879
  sort: z4.enum(["timestamp", "-timestamp"]).optional().describe("Sort order"),
848
- sample: z4.enum(["first", "spread", "diverse"]).optional().describe("Sampling mode: first (chronological, default), spread (evenly across time range), diverse (distinct message patterns)"),
849
- compact: z4.boolean().optional().describe("Strip custom attributes for token efficiency. Keeps: id, timestamp, service, status, message (truncated), dd.trace_id, error info"),
880
+ sample: z4.enum(["first", "spread", "diverse"]).optional().describe(
881
+ "Sampling mode: first (chronological, default), spread (evenly across time range), diverse (distinct message patterns)"
882
+ ),
883
+ compact: z4.boolean().optional().describe(
884
+ "Strip custom attributes for token efficiency. Keeps: id, timestamp, service, status, message (truncated), dd.trace_id, error info"
885
+ ),
850
886
  groupBy: z4.array(z4.string()).optional().describe("Fields to group by (for aggregate)"),
851
887
  compute: z4.record(z4.unknown()).optional().describe("Compute operations (for aggregate)")
852
888
  };
@@ -876,8 +912,9 @@ function formatLogCompact(log) {
876
912
  const ts = attrs.timestamp;
877
913
  timestamp = ts instanceof Date ? ts.toISOString() : new Date(String(ts)).toISOString();
878
914
  }
879
- const traceId = nestedAttrs["dd.trace_id"] ?? nestedAttrs["trace_id"] ?? attrs["dd.trace_id"] ?? "";
880
- const spanId = nestedAttrs["dd.span_id"] ?? nestedAttrs["span_id"] ?? attrs["dd.span_id"] ?? "";
915
+ const attrsAny = attrs;
916
+ const traceId = nestedAttrs["dd.trace_id"] ?? nestedAttrs["trace_id"] ?? attrsAny["dd.trace_id"] ?? "";
917
+ const spanId = nestedAttrs["dd.span_id"] ?? nestedAttrs["span_id"] ?? attrsAny["dd.span_id"] ?? "";
881
918
  const errorType = nestedAttrs["error.type"] ?? nestedAttrs["error.kind"] ?? "";
882
919
  const errorMessage = nestedAttrs["error.message"] ?? nestedAttrs["error.msg"] ?? "";
883
920
  const fullMessage = attrs.message ?? "";
@@ -1054,35 +1091,66 @@ CORRELATION: Logs contain dd.trace_id in attributes for linking to traces and AP
1054
1091
  SAMPLING: Use sample:"diverse" for error investigation (dedupes by message pattern), sample:"spread" for time distribution.
1055
1092
  TOKEN TIP: Use compact:true to reduce payload size (strips heavy fields) when querying large volumes.`,
1056
1093
  InputSchema3,
1057
- async ({ action, query, keyword, pattern, service, host, status, from, to, indexes, limit, sort, sample, compact, groupBy, compute }) => {
1094
+ async ({
1095
+ action,
1096
+ query,
1097
+ keyword,
1098
+ pattern,
1099
+ service,
1100
+ host,
1101
+ status,
1102
+ from,
1103
+ to,
1104
+ indexes,
1105
+ limit,
1106
+ sort,
1107
+ sample,
1108
+ compact,
1109
+ groupBy,
1110
+ compute
1111
+ }) => {
1058
1112
  try {
1059
1113
  switch (action) {
1060
1114
  case "search": {
1061
- return toolResult(await searchLogs(api, {
1062
- query,
1063
- keyword,
1064
- pattern,
1065
- service,
1066
- host,
1067
- status,
1068
- from,
1069
- to,
1070
- indexes,
1071
- limit,
1072
- sort,
1073
- sample,
1074
- compact
1075
- }, limits, site));
1115
+ return toolResult(
1116
+ await searchLogs(
1117
+ api,
1118
+ {
1119
+ query,
1120
+ keyword,
1121
+ pattern,
1122
+ service,
1123
+ host,
1124
+ status,
1125
+ from,
1126
+ to,
1127
+ indexes,
1128
+ limit,
1129
+ sort,
1130
+ sample,
1131
+ compact
1132
+ },
1133
+ limits,
1134
+ site
1135
+ )
1136
+ );
1076
1137
  }
1077
1138
  case "aggregate": {
1078
1139
  const aggregateQuery = requireParam(query, "query", "aggregate");
1079
- return toolResult(await aggregateLogs(api, {
1080
- query: aggregateQuery,
1081
- from,
1082
- to,
1083
- groupBy,
1084
- compute
1085
- }, limits, site));
1140
+ return toolResult(
1141
+ await aggregateLogs(
1142
+ api,
1143
+ {
1144
+ query: aggregateQuery,
1145
+ from,
1146
+ to,
1147
+ groupBy,
1148
+ compute
1149
+ },
1150
+ limits,
1151
+ site
1152
+ )
1153
+ );
1086
1154
  }
1087
1155
  default:
1088
1156
  throw new Error(`Unknown action: ${action}`);
@@ -1099,8 +1167,12 @@ import { z as z5 } from "zod";
1099
1167
  var ActionSchema4 = z5.enum(["query", "search", "list", "metadata"]);
1100
1168
  var InputSchema4 = {
1101
1169
  action: ActionSchema4.describe("Action to perform"),
1102
- query: z5.string().optional().describe('For query: PromQL expression (e.g., "avg:system.cpu.user{*}"). For search: grep-like filter on metric names. For list: tag filter.'),
1103
- from: z5.string().optional().describe("Start time (ONLY for query action). Formats: ISO 8601, relative (30s, 15m, 2h, 7d), precise (3d@11:45:23)"),
1170
+ query: z5.string().optional().describe(
1171
+ 'For query: PromQL expression (e.g., "avg:system.cpu.user{*}"). For search: grep-like filter on metric names. For list: tag filter.'
1172
+ ),
1173
+ from: z5.string().optional().describe(
1174
+ "Start time (ONLY for query action). Formats: ISO 8601, relative (30s, 15m, 2h, 7d), precise (3d@11:45:23)"
1175
+ ),
1104
1176
  to: z5.string().optional().describe('End time (ONLY for query action). Same formats as "from".'),
1105
1177
  metric: z5.string().optional().describe("Metric name (for metadata action)"),
1106
1178
  tag: z5.string().optional().describe("Filter by tag"),
@@ -1198,18 +1270,31 @@ Example: max:trace.{service}.request.duration{*}`,
1198
1270
  switch (action) {
1199
1271
  case "query": {
1200
1272
  const metricsQuery = requireParam(query, "query", "query");
1201
- return toolResult(await queryMetrics(metricsV1Api, {
1202
- query: metricsQuery,
1203
- from,
1204
- to
1205
- }, limits, site));
1273
+ return toolResult(
1274
+ await queryMetrics(
1275
+ metricsV1Api,
1276
+ {
1277
+ query: metricsQuery,
1278
+ from,
1279
+ to
1280
+ },
1281
+ limits,
1282
+ site
1283
+ )
1284
+ );
1206
1285
  }
1207
1286
  case "search": {
1208
1287
  const searchQuery = requireParam(query, "query", "search");
1209
- return toolResult(await searchMetrics(metricsV1Api, {
1210
- query: searchQuery,
1211
- limit
1212
- }, limits));
1288
+ return toolResult(
1289
+ await searchMetrics(
1290
+ metricsV1Api,
1291
+ {
1292
+ query: searchQuery,
1293
+ limit
1294
+ },
1295
+ limits
1296
+ )
1297
+ );
1213
1298
  }
1214
1299
  case "list":
1215
1300
  return toolResult(await listMetrics(metricsV1Api, { query }, limits));
@@ -1245,17 +1330,25 @@ var RESERVED_SPAN_FACETS = /* @__PURE__ */ new Set([
1245
1330
  ]);
1246
1331
  var InputSchema5 = {
1247
1332
  action: ActionSchema5.describe("Action to perform"),
1248
- query: z6.string().optional().describe('APM trace search query (Datadog syntax). Example: "@http.status_code:500", "service:my-service status:error"'),
1249
- from: z6.string().optional().describe("Start time. Formats: ISO 8601, relative (30s, 15m, 2h, 7d), precise (3d@11:45:23, yesterday@14:00)"),
1333
+ query: z6.string().optional().describe(
1334
+ 'APM trace search query (Datadog syntax). Example: "@http.status_code:500", "service:my-service status:error"'
1335
+ ),
1336
+ from: z6.string().optional().describe(
1337
+ "Start time. Formats: ISO 8601, relative (30s, 15m, 2h, 7d), precise (3d@11:45:23, yesterday@14:00)"
1338
+ ),
1250
1339
  to: z6.string().optional().describe('End time. Same formats as "from". Example: from="3d@11:45" to="3d@12:55"'),
1251
1340
  service: z6.string().optional().describe('Filter by service name. Example: "my-service", "postgres"'),
1252
1341
  operation: z6.string().optional().describe('Filter by operation name. Example: "express.request", "mongodb.query"'),
1253
- resource: z6.string().optional().describe('Filter by resource name (endpoint/query). Supports wildcards. Example: "GET /api/*", "*orders*"'),
1342
+ resource: z6.string().optional().describe(
1343
+ 'Filter by resource name (endpoint/query). Supports wildcards. Example: "GET /api/*", "*orders*"'
1344
+ ),
1254
1345
  status: z6.enum(["ok", "error"]).optional().describe('Filter by span status - "ok" for successful, "error" for failed spans'),
1255
1346
  env: z6.string().optional().describe('Filter by environment. Example: "production", "staging"'),
1256
1347
  minDuration: z6.string().optional().describe('Minimum span duration (find slow spans). Examples: "1s", "500ms", "100ms"'),
1257
1348
  maxDuration: z6.string().optional().describe('Maximum span duration. Examples: "5s", "1000ms"'),
1258
- httpStatus: z6.string().optional().describe('HTTP status code filter. Examples: "500", "5xx" (500-599), "4xx" (400-499), ">=400"'),
1349
+ httpStatus: z6.string().optional().describe(
1350
+ 'HTTP status code filter. Examples: "500", "5xx" (500-599), "4xx" (400-499), ">=400"'
1351
+ ),
1259
1352
  errorType: z6.string().optional().describe('Filter by error type (grep-like). Example: "TimeoutError", "ConnectionRefused"'),
1260
1353
  errorMessage: z6.string().optional().describe('Filter by error message (grep-like). Example: "timeout", "connection refused"'),
1261
1354
  limit: z6.number().optional().describe("Maximum number of results"),
@@ -1318,11 +1411,7 @@ function buildTraceQuery(params) {
1318
1411
  parts.push(`operation_name:${params.operation}`);
1319
1412
  }
1320
1413
  if (params.resource) {
1321
- if (params.resource.includes("*") || params.resource.includes(" ")) {
1322
- parts.push(`resource_name:${params.resource}`);
1323
- } else {
1324
- parts.push(`resource_name:${params.resource}`);
1325
- }
1414
+ parts.push(`resource_name:${params.resource}`);
1326
1415
  }
1327
1416
  if (params.status) {
1328
1417
  parts.push(`status:${params.status}`);
@@ -1345,7 +1434,7 @@ function buildTraceQuery(params) {
1345
1434
  if (params.httpStatus) {
1346
1435
  const status = params.httpStatus.toLowerCase();
1347
1436
  if (status.endsWith("xx")) {
1348
- const base = parseInt(status[0] ?? "0", 10) * 100;
1437
+ const base = Number.parseInt(status[0] ?? "0", 10) * 100;
1349
1438
  parts.push(`@http.status_code:[${base} TO ${base + 99}]`);
1350
1439
  } else if (status.startsWith(">=")) {
1351
1440
  parts.push(`@http.status_code:>=${status.slice(2)}`);
@@ -1491,10 +1580,12 @@ async function listApmServices(api, params, limits) {
1491
1580
  to: toTime
1492
1581
  },
1493
1582
  compute: [{ aggregation: "count", type: "total" }],
1494
- groupBy: [{
1495
- facet: "service",
1496
- limit: limits.maxResults
1497
- }]
1583
+ groupBy: [
1584
+ {
1585
+ facet: "service",
1586
+ limit: limits.maxResults
1587
+ }
1588
+ ]
1498
1589
  }
1499
1590
  }
1500
1591
  };
@@ -1521,45 +1612,77 @@ function registerTracesTool(server, spansApi, _servicesApi, limits, site = "data
1521
1612
  `Analyze APM traces for request flow and latency debugging. Actions: search (find spans), aggregate (group stats), services (list APM services). Key filters: minDuration/maxDuration ("500ms", "2s"), httpStatus ("5xx", ">=400"), status (ok/error), errorMessage (grep).
1522
1613
  APM METRICS: Traces auto-generate metrics in trace.{service}.* namespace. Use metrics tool to query: avg:trace.{service}.request.duration{*}`,
1523
1614
  InputSchema5,
1524
- async ({ action, query, from, to, service, operation, resource, status, env, minDuration, maxDuration, httpStatus, errorType, errorMessage, limit, sort, groupBy }) => {
1615
+ async ({
1616
+ action,
1617
+ query,
1618
+ from,
1619
+ to,
1620
+ service,
1621
+ operation,
1622
+ resource,
1623
+ status,
1624
+ env,
1625
+ minDuration,
1626
+ maxDuration,
1627
+ httpStatus,
1628
+ errorType,
1629
+ errorMessage,
1630
+ limit,
1631
+ sort,
1632
+ groupBy
1633
+ }) => {
1525
1634
  try {
1526
1635
  switch (action) {
1527
1636
  case "search": {
1528
- return toolResult(await searchTraces(spansApi, {
1529
- query,
1530
- from,
1531
- to,
1532
- service,
1533
- operation,
1534
- resource,
1535
- status,
1536
- env,
1537
- minDuration,
1538
- maxDuration,
1539
- httpStatus,
1540
- errorType,
1541
- errorMessage,
1542
- limit,
1543
- sort
1544
- }, limits, site));
1637
+ return toolResult(
1638
+ await searchTraces(
1639
+ spansApi,
1640
+ {
1641
+ query,
1642
+ from,
1643
+ to,
1644
+ service,
1645
+ operation,
1646
+ resource,
1647
+ status,
1648
+ env,
1649
+ minDuration,
1650
+ maxDuration,
1651
+ httpStatus,
1652
+ errorType,
1653
+ errorMessage,
1654
+ limit,
1655
+ sort
1656
+ },
1657
+ limits,
1658
+ site
1659
+ )
1660
+ );
1545
1661
  }
1546
1662
  case "aggregate": {
1547
- return toolResult(await aggregateTraces(spansApi, {
1548
- query,
1549
- from,
1550
- to,
1551
- service,
1552
- operation,
1553
- resource,
1554
- status,
1555
- env,
1556
- minDuration,
1557
- maxDuration,
1558
- httpStatus,
1559
- errorType,
1560
- errorMessage,
1561
- groupBy
1562
- }, limits, site));
1663
+ return toolResult(
1664
+ await aggregateTraces(
1665
+ spansApi,
1666
+ {
1667
+ query,
1668
+ from,
1669
+ to,
1670
+ service,
1671
+ operation,
1672
+ resource,
1673
+ status,
1674
+ env,
1675
+ minDuration,
1676
+ maxDuration,
1677
+ httpStatus,
1678
+ errorType,
1679
+ errorMessage,
1680
+ groupBy
1681
+ },
1682
+ limits,
1683
+ site
1684
+ )
1685
+ );
1563
1686
  }
1564
1687
  case "services":
1565
1688
  return toolResult(await listApmServices(spansApi, { env, from, to }, limits));
@@ -1575,7 +1698,16 @@ APM METRICS: Traces auto-generate metrics in trace.{service}.* namespace. Use me
1575
1698
 
1576
1699
  // src/tools/events.ts
1577
1700
  import { z as z7 } from "zod";
1578
- var ActionSchema6 = z7.enum(["list", "get", "create", "search", "aggregate", "top", "timeseries", "incidents"]);
1701
+ var ActionSchema6 = z7.enum([
1702
+ "list",
1703
+ "get",
1704
+ "create",
1705
+ "search",
1706
+ "aggregate",
1707
+ "top",
1708
+ "timeseries",
1709
+ "incidents"
1710
+ ]);
1579
1711
  var InputSchema6 = {
1580
1712
  action: ActionSchema6.describe("Action to perform"),
1581
1713
  id: z7.string().optional().describe("Event ID (for get action)"),
@@ -1624,9 +1756,9 @@ function extractTitleFromMessage(message) {
1624
1756
  function extractMonitorIdFromMessage(message) {
1625
1757
  if (!message) return void 0;
1626
1758
  const match = message.match(/\/monitors\/(\d+)/);
1627
- if (match && match[1]) {
1628
- const id = parseInt(match[1], 10);
1629
- return isNaN(id) ? void 0 : id;
1759
+ if (match?.[1]) {
1760
+ const id = Number.parseInt(match[1], 10);
1761
+ return Number.isNaN(id) ? void 0 : id;
1630
1762
  }
1631
1763
  return void 0;
1632
1764
  }
@@ -1745,8 +1877,8 @@ async function listEventsV1(api, params, limits) {
1745
1877
  };
1746
1878
  }
1747
1879
  async function getEventV1(api, id) {
1748
- const eventId = parseInt(id, 10);
1749
- if (isNaN(eventId)) {
1880
+ const eventId = Number.parseInt(id, 10);
1881
+ if (Number.isNaN(eventId)) {
1750
1882
  throw new Error(`Invalid event ID: ${id}`);
1751
1883
  }
1752
1884
  const response = await api.getEvent({ eventId });
@@ -1910,13 +2042,18 @@ async function aggregateEventsV2(api, params, limits, site) {
1910
2042
  async function topEventsV2(api, params, limits, site) {
1911
2043
  const effectiveQuery = params.query ?? "source:alert";
1912
2044
  const effectiveTags = params.tags ?? ["source:alert"];
1913
- const result = await aggregateEventsV2(api, {
1914
- ...params,
1915
- query: effectiveQuery,
1916
- tags: effectiveTags,
1917
- groupBy: params.groupBy ?? ["monitor_name"],
1918
- limit: params.limit ?? 10
1919
- }, limits, site);
2045
+ const result = await aggregateEventsV2(
2046
+ api,
2047
+ {
2048
+ ...params,
2049
+ query: effectiveQuery,
2050
+ tags: effectiveTags,
2051
+ groupBy: params.groupBy ?? ["monitor_name"],
2052
+ limit: params.limit ?? 10
2053
+ },
2054
+ limits,
2055
+ site
2056
+ );
1920
2057
  return {
1921
2058
  top: result.buckets.map((bucket, index) => ({
1922
2059
  rank: index + 1,
@@ -2149,7 +2286,9 @@ async function incidentsEventsV2(api, params, limits, site) {
2149
2286
  sample: inc.sample
2150
2287
  };
2151
2288
  });
2152
- incidentList.sort((a, b) => new Date(b.firstTrigger).getTime() - new Date(a.firstTrigger).getTime());
2289
+ incidentList.sort(
2290
+ (a, b) => new Date(b.firstTrigger).getTime() - new Date(a.firstTrigger).getTime()
2291
+ );
2153
2292
  const effectiveLimit = Math.min(params.limit ?? 100, 500);
2154
2293
  return {
2155
2294
  incidents: incidentList.slice(0, effectiveLimit),
@@ -2227,20 +2366,44 @@ Use action:"timeseries" with interval:"1h" to see alert trends over time.
2227
2366
  Use action:"incidents" with dedupeWindow:"5m" to deduplicate alerts into incidents.
2228
2367
  Use enrich:true with search to get monitor metadata (slower).`,
2229
2368
  InputSchema6,
2230
- async ({ action, id, query, from, to, priority, sources, tags, limit, title, text, alertType, groupBy, cursor, interval, dedupeWindow, enrich }) => {
2369
+ async ({
2370
+ action,
2371
+ id,
2372
+ query,
2373
+ from,
2374
+ to,
2375
+ priority,
2376
+ sources,
2377
+ tags,
2378
+ limit,
2379
+ title,
2380
+ text,
2381
+ alertType,
2382
+ groupBy,
2383
+ cursor,
2384
+ interval,
2385
+ dedupeWindow,
2386
+ enrich
2387
+ }) => {
2231
2388
  try {
2232
2389
  checkReadOnly(action, readOnly);
2233
2390
  switch (action) {
2234
2391
  case "list":
2235
- return toolResult(await listEventsV1(apiV1, {
2236
- query,
2237
- from,
2238
- to,
2239
- priority,
2240
- sources,
2241
- tags,
2242
- limit
2243
- }, limits));
2392
+ return toolResult(
2393
+ await listEventsV1(
2394
+ apiV1,
2395
+ {
2396
+ query,
2397
+ from,
2398
+ to,
2399
+ priority,
2400
+ sources,
2401
+ tags,
2402
+ limit
2403
+ },
2404
+ limits
2405
+ )
2406
+ );
2244
2407
  case "get": {
2245
2408
  const eventId = requireParam(id, "id", "get");
2246
2409
  return toolResult(await getEventV1(apiV1, eventId));
@@ -2248,25 +2411,32 @@ Use enrich:true with search to get monitor metadata (slower).`,
2248
2411
  case "create": {
2249
2412
  const eventTitle = requireParam(title, "title", "create");
2250
2413
  const eventText = requireParam(text, "text", "create");
2251
- return toolResult(await createEventV1(apiV1, {
2252
- title: eventTitle,
2253
- text: eventText,
2254
- priority,
2255
- tags,
2256
- alertType
2257
- }));
2414
+ return toolResult(
2415
+ await createEventV1(apiV1, {
2416
+ title: eventTitle,
2417
+ text: eventText,
2418
+ priority,
2419
+ tags,
2420
+ alertType
2421
+ })
2422
+ );
2258
2423
  }
2259
2424
  case "search": {
2260
- const result = await searchEventsV2(apiV2, {
2261
- query,
2262
- from,
2263
- to,
2264
- sources,
2265
- tags,
2266
- priority,
2267
- limit,
2268
- cursor
2269
- }, limits, site);
2425
+ const result = await searchEventsV2(
2426
+ apiV2,
2427
+ {
2428
+ query,
2429
+ from,
2430
+ to,
2431
+ sources,
2432
+ tags,
2433
+ priority,
2434
+ limit,
2435
+ cursor
2436
+ },
2437
+ limits,
2438
+ site
2439
+ );
2270
2440
  if (enrich && result.events.length > 0) {
2271
2441
  const enrichedEvents = await enrichWithMonitorMetadata(result.events, monitorsApi);
2272
2442
  return toolResult({ ...result, events: enrichedEvents });
@@ -2274,46 +2444,74 @@ Use enrich:true with search to get monitor metadata (slower).`,
2274
2444
  return toolResult(result);
2275
2445
  }
2276
2446
  case "aggregate":
2277
- return toolResult(await aggregateEventsV2(apiV2, {
2278
- query,
2279
- from,
2280
- to,
2281
- sources,
2282
- tags,
2283
- groupBy,
2284
- limit
2285
- }, limits, site));
2447
+ return toolResult(
2448
+ await aggregateEventsV2(
2449
+ apiV2,
2450
+ {
2451
+ query,
2452
+ from,
2453
+ to,
2454
+ sources,
2455
+ tags,
2456
+ groupBy,
2457
+ limit
2458
+ },
2459
+ limits,
2460
+ site
2461
+ )
2462
+ );
2286
2463
  case "top":
2287
- return toolResult(await topEventsV2(apiV2, {
2288
- query,
2289
- from,
2290
- to,
2291
- sources,
2292
- tags,
2293
- groupBy,
2294
- limit
2295
- }, limits, site));
2464
+ return toolResult(
2465
+ await topEventsV2(
2466
+ apiV2,
2467
+ {
2468
+ query,
2469
+ from,
2470
+ to,
2471
+ sources,
2472
+ tags,
2473
+ groupBy,
2474
+ limit
2475
+ },
2476
+ limits,
2477
+ site
2478
+ )
2479
+ );
2296
2480
  case "timeseries":
2297
- return toolResult(await timeseriesEventsV2(apiV2, {
2298
- query,
2299
- from,
2300
- to,
2301
- sources,
2302
- tags,
2303
- groupBy,
2304
- interval,
2305
- limit
2306
- }, limits, site));
2481
+ return toolResult(
2482
+ await timeseriesEventsV2(
2483
+ apiV2,
2484
+ {
2485
+ query,
2486
+ from,
2487
+ to,
2488
+ sources,
2489
+ tags,
2490
+ groupBy,
2491
+ interval,
2492
+ limit
2493
+ },
2494
+ limits,
2495
+ site
2496
+ )
2497
+ );
2307
2498
  case "incidents":
2308
- return toolResult(await incidentsEventsV2(apiV2, {
2309
- query,
2310
- from,
2311
- to,
2312
- sources,
2313
- tags,
2314
- dedupeWindow,
2315
- limit
2316
- }, limits, site));
2499
+ return toolResult(
2500
+ await incidentsEventsV2(
2501
+ apiV2,
2502
+ {
2503
+ query,
2504
+ from,
2505
+ to,
2506
+ sources,
2507
+ tags,
2508
+ dedupeWindow,
2509
+ limit
2510
+ },
2511
+ limits,
2512
+ site
2513
+ )
2514
+ );
2317
2515
  default:
2318
2516
  throw new Error(`Unknown action: ${action}`);
2319
2517
  }
@@ -2333,7 +2531,9 @@ var InputSchema7 = {
2333
2531
  query: z8.string().optional().describe("Search query (for search action)"),
2334
2532
  status: z8.enum(["active", "stable", "resolved"]).optional().describe("Filter by status (for list)"),
2335
2533
  limit: z8.number().optional().describe("Maximum number of incidents to return"),
2336
- config: z8.record(z8.unknown()).optional().describe("Incident configuration (for create/update). Create requires: title. Update can modify: title, status, severity, fields.")
2534
+ config: z8.record(z8.unknown()).optional().describe(
2535
+ "Incident configuration (for create/update). Create requires: title. Update can modify: title, status, severity, fields."
2536
+ )
2337
2537
  };
2338
2538
  function formatIncident(i) {
2339
2539
  const attrs = i.attributes;
@@ -2385,11 +2585,13 @@ async function searchIncidents(api, query, limits) {
2385
2585
  query,
2386
2586
  pageSize: limits.maxResults
2387
2587
  });
2388
- const incidents = (response.data?.attributes?.incidents ?? []).map((i) => ({
2389
- id: i.data?.id ?? "",
2390
- title: i.data?.attributes?.title ?? "",
2391
- state: i.data?.attributes?.state ?? "unknown"
2392
- }));
2588
+ const incidents = (response.data?.attributes?.incidents ?? []).map(
2589
+ (i) => ({
2590
+ id: i.data?.id ?? "",
2591
+ title: i.data?.attributes?.title ?? "",
2592
+ state: i.data?.attributes?.state ?? "unknown"
2593
+ })
2594
+ );
2393
2595
  return {
2394
2596
  incidents,
2395
2597
  total: response.data?.attributes?.total ?? incidents.length
@@ -2429,7 +2631,7 @@ async function deleteIncident(api, id) {
2429
2631
  message: `Incident ${id} deleted`
2430
2632
  };
2431
2633
  }
2432
- function registerIncidentsTool(server, api, limits, readOnly = false) {
2634
+ function registerIncidentsTool(server, api, limits, readOnly = false, _site = "datadoghq.com") {
2433
2635
  server.tool(
2434
2636
  "incidents",
2435
2637
  "Manage Datadog incidents for incident response. Actions: list, get, search, create, update, delete. Use for: incident management, on-call response, postmortems, tracking MTTR/MTTD.",
@@ -2606,7 +2808,7 @@ async function getSloHistory(api, id, params) {
2606
2808
  }
2607
2809
  };
2608
2810
  }
2609
- function registerSlosTool(server, api, limits, readOnly = false) {
2811
+ function registerSlosTool(server, api, limits, readOnly = false, _site = "datadoghq.com") {
2610
2812
  server.tool(
2611
2813
  "slos",
2612
2814
  "Manage Datadog Service Level Objectives. Actions: list, get, create, update, delete, history. SLO types: metric-based, monitor-based. Use for: reliability tracking, error budgets, SLA compliance, performance targets.",
@@ -2659,7 +2861,9 @@ var InputSchema9 = {
2659
2861
  locations: z10.array(z10.string()).optional().describe("Filter by locations (for list)"),
2660
2862
  tags: z10.array(z10.string()).optional().describe("Filter by tags (for list)"),
2661
2863
  limit: z10.number().optional().describe("Maximum number of tests to return"),
2662
- config: z10.record(z10.unknown()).optional().describe("Test configuration (for create/update). Includes: name, type, config, options, locations, message.")
2864
+ config: z10.record(z10.unknown()).optional().describe(
2865
+ "Test configuration (for create/update). Includes: name, type, config, options, locations, message."
2866
+ )
2663
2867
  };
2664
2868
  function formatTest(t) {
2665
2869
  return {
@@ -2750,7 +2954,7 @@ async function createTest(api, config, testType) {
2750
2954
  }
2751
2955
  async function updateTest(api, id, config) {
2752
2956
  const normalizedConfig = normalizeConfigKeys2(config);
2753
- let testType = "api";
2957
+ let testType;
2754
2958
  try {
2755
2959
  await api.getAPITest({ publicId: id });
2756
2960
  testType = "api";
@@ -2820,7 +3024,7 @@ async function getTestResults(api, id) {
2820
3024
  return { results, testType: "browser" };
2821
3025
  }
2822
3026
  }
2823
- function registerSyntheticsTool(server, api, limits, readOnly = false) {
3027
+ function registerSyntheticsTool(server, api, limits, readOnly = false, _site = "datadoghq.com") {
2824
3028
  server.tool(
2825
3029
  "synthetics",
2826
3030
  "Manage Datadog Synthetic tests (API and Browser). Actions: list, get, create, update, delete, trigger, results. Use for: uptime monitoring, API testing, user journey testing, performance testing, canary deployments.",
@@ -2933,9 +3137,10 @@ async function muteHost(api, hostName, params) {
2933
3137
  override: params.override
2934
3138
  }
2935
3139
  });
3140
+ const muteEndMessage = params.end ? ` until ${new Date(params.end * 1e3).toISOString()}` : " indefinitely";
2936
3141
  return {
2937
3142
  success: true,
2938
- message: `Host ${hostName} muted${params.end ? ` until ${new Date(params.end * 1e3).toISOString()}` : " indefinitely"}`
3143
+ message: `Host ${hostName} muted${muteEndMessage}`
2939
3144
  };
2940
3145
  }
2941
3146
  async function unmuteHost(api, hostName) {
@@ -2950,12 +3155,25 @@ function registerHostsTool(server, api, limits, readOnly = false) {
2950
3155
  "hosts",
2951
3156
  "Manage Datadog infrastructure hosts. Actions: list (with filters), totals (counts), mute (silence alerts), unmute. Use for: infrastructure inventory, host health, silencing noisy hosts during maintenance.",
2952
3157
  InputSchema10,
2953
- async ({ action, filter, from, count, sortField, sortDir, hostName, message, end, override }) => {
3158
+ async ({
3159
+ action,
3160
+ filter,
3161
+ from,
3162
+ count,
3163
+ sortField,
3164
+ sortDir,
3165
+ hostName,
3166
+ message,
3167
+ end,
3168
+ override
3169
+ }) => {
2954
3170
  try {
2955
3171
  checkReadOnly(action, readOnly);
2956
3172
  switch (action) {
2957
3173
  case "list":
2958
- return toolResult(await listHosts(api, { filter, from, count, sortField, sortDir }, limits));
3174
+ return toolResult(
3175
+ await listHosts(api, { filter, from, count, sortField, sortDir }, limits)
3176
+ );
2959
3177
  case "totals":
2960
3178
  return toolResult(await getHostTotals(api));
2961
3179
  case "mute": {
@@ -3141,7 +3359,9 @@ var InputSchema12 = {
3141
3359
  interval: z13.string().optional()
3142
3360
  }).optional().describe("Compute configuration for aggregation"),
3143
3361
  // Performance action parameters
3144
- metrics: z13.array(z13.enum(["lcp", "fcp", "cls", "fid", "inp", "loading_time"])).optional().describe("Core Web Vitals metrics to retrieve (default: all). lcp=Largest Contentful Paint, fcp=First Contentful Paint, cls=Cumulative Layout Shift, fid=First Input Delay, inp=Interaction to Next Paint, loading_time=View loading time"),
3362
+ metrics: z13.array(z13.enum(["lcp", "fcp", "cls", "fid", "inp", "loading_time"])).optional().describe(
3363
+ "Core Web Vitals metrics to retrieve (default: all). lcp=Largest Contentful Paint, fcp=First Contentful Paint, cls=Cumulative Layout Shift, fid=First Input Delay, inp=Interaction to Next Paint, loading_time=View loading time"
3364
+ ),
3145
3365
  // Waterfall action parameters
3146
3366
  applicationId: z13.string().optional().describe("Application ID for waterfall action"),
3147
3367
  sessionId: z13.string().optional().describe("Session ID for waterfall action"),
@@ -3443,10 +3663,7 @@ function formatWaterfallEvent(event) {
3443
3663
  };
3444
3664
  }
3445
3665
  async function getSessionWaterfall(api, params, limits, site) {
3446
- const queryParts = [
3447
- `@application.id:${params.applicationId}`,
3448
- `@session.id:${params.sessionId}`
3449
- ];
3666
+ const queryParts = [`@application.id:${params.applicationId}`, `@session.id:${params.sessionId}`];
3450
3667
  if (params.viewId) {
3451
3668
  queryParts.push(`@view.id:${params.viewId}`);
3452
3669
  }
@@ -3480,22 +3697,44 @@ function registerRumTool(server, api, limits, site = "datadoghq.com") {
3480
3697
  "rum",
3481
3698
  "Query Datadog Real User Monitoring (RUM) data. Actions: applications (list RUM apps), events (search RUM events), aggregate (group and count events), performance (Core Web Vitals: LCP, FCP, CLS, FID, INP), waterfall (session timeline with resources/actions/errors). Use for: frontend performance, user sessions, page views, errors, resource loading.",
3482
3699
  InputSchema12,
3483
- async ({ action, query, from, to, type, sort, limit, groupBy, compute, metrics, applicationId, sessionId, viewId }) => {
3700
+ async ({
3701
+ action,
3702
+ query,
3703
+ from,
3704
+ to,
3705
+ type,
3706
+ sort,
3707
+ limit,
3708
+ groupBy,
3709
+ compute,
3710
+ metrics,
3711
+ applicationId,
3712
+ sessionId,
3713
+ viewId
3714
+ }) => {
3484
3715
  try {
3485
3716
  switch (action) {
3486
3717
  case "applications":
3487
3718
  return toolResult(await listApplications(api));
3488
3719
  case "events":
3489
- return toolResult(await searchEvents(api, { query, from, to, type, sort, limit }, limits, site));
3720
+ return toolResult(
3721
+ await searchEvents(api, { query, from, to, type, sort, limit }, limits, site)
3722
+ );
3490
3723
  case "aggregate":
3491
- return toolResult(await aggregateEvents(api, { query, from, to, groupBy, compute }, limits, site));
3724
+ return toolResult(
3725
+ await aggregateEvents(api, { query, from, to, groupBy, compute }, limits, site)
3726
+ );
3492
3727
  case "performance":
3493
- return toolResult(await getPerformanceMetrics(api, { query, from, to, groupBy, metrics }, limits, site));
3728
+ return toolResult(
3729
+ await getPerformanceMetrics(api, { query, from, to, groupBy, metrics }, limits, site)
3730
+ );
3494
3731
  case "waterfall":
3495
3732
  if (!applicationId || !sessionId) {
3496
3733
  throw new Error("waterfall action requires applicationId and sessionId parameters");
3497
3734
  }
3498
- return toolResult(await getSessionWaterfall(api, { applicationId, sessionId, viewId }, limits, site));
3735
+ return toolResult(
3736
+ await getSessionWaterfall(api, { applicationId, sessionId, viewId }, limits, site)
3737
+ );
3499
3738
  default:
3500
3739
  throw new Error(`Unknown action: ${action}`);
3501
3740
  }
@@ -3653,7 +3892,13 @@ function registerSecurityTool(server, api, limits) {
3653
3892
  }
3654
3893
  return toolResult(await listRules(api, { pageSize, pageCursor }, limits));
3655
3894
  case "signals":
3656
- return toolResult(await searchSignals(api, { query, from, to, severity, status, pageSize, pageCursor }, limits));
3895
+ return toolResult(
3896
+ await searchSignals(
3897
+ api,
3898
+ { query, from, to, severity, status, pageSize, pageCursor },
3899
+ limits
3900
+ )
3901
+ );
3657
3902
  case "findings":
3658
3903
  return toolResult(await listFindings(api, { query, pageSize, pageCursor }, limits));
3659
3904
  default:
@@ -3677,10 +3922,19 @@ var InputSchema14 = {
3677
3922
  excludeAuthorHandle: z15.string().optional().describe("Exclude notebooks by author handle"),
3678
3923
  includeCells: z15.boolean().optional().describe("Include cell content in response (default: true for get)"),
3679
3924
  name: z15.string().optional().describe("Notebook name (for create/update)"),
3680
- cells: z15.array(z15.object({
3681
- type: z15.enum(["markdown", "timeseries", "toplist", "heatmap", "distribution", "log_stream"]),
3682
- content: z15.unknown()
3683
- })).optional().describe("Notebook cells (for create/update)"),
3925
+ cells: z15.array(
3926
+ z15.object({
3927
+ type: z15.enum([
3928
+ "markdown",
3929
+ "timeseries",
3930
+ "toplist",
3931
+ "heatmap",
3932
+ "distribution",
3933
+ "log_stream"
3934
+ ]),
3935
+ content: z15.unknown()
3936
+ })
3937
+ ).optional().describe("Notebook cells (for create/update)"),
3684
3938
  time: z15.object({
3685
3939
  liveSpan: z15.string().optional(),
3686
3940
  start: z15.number().optional(),
@@ -3845,7 +4099,9 @@ async function updateNotebook(api, notebookId, params) {
3845
4099
  };
3846
4100
  });
3847
4101
  }
3848
- const timeConfig = params.time?.liveSpan ? { liveSpan: params.time.liveSpan } : void 0;
4102
+ const timeConfig = params.time?.liveSpan ? {
4103
+ liveSpan: params.time.liveSpan
4104
+ } : void 0;
3849
4105
  const response = await api.updateNotebook({
3850
4106
  notebookId,
3851
4107
  body: {
@@ -3880,38 +4136,61 @@ async function deleteNotebook(api, notebookId) {
3880
4136
  message: `Notebook ${notebookId} deleted successfully`
3881
4137
  };
3882
4138
  }
3883
- function registerNotebooksTool(server, api, limits, readOnly = false) {
4139
+ function registerNotebooksTool(server, api, limits, readOnly = false, _site = "datadoghq.com") {
3884
4140
  server.tool(
3885
4141
  "notebooks",
3886
4142
  "Manage Datadog Notebooks. Actions: list (search notebooks), get (by ID with cells), create (new notebook), update (modify notebook), delete (remove notebook). Use for: runbooks, incident documentation, investigation notes, dashboards as code.",
3887
4143
  InputSchema14,
3888
- async ({ action, id, query, authorHandle, excludeAuthorHandle, includeCells, name, cells, time, status, pageSize, pageNumber }) => {
4144
+ async ({
4145
+ action,
4146
+ id,
4147
+ query,
4148
+ authorHandle,
4149
+ excludeAuthorHandle,
4150
+ includeCells,
4151
+ name,
4152
+ cells,
4153
+ time,
4154
+ status,
4155
+ pageSize,
4156
+ pageNumber
4157
+ }) => {
3889
4158
  try {
3890
4159
  checkReadOnly(action, readOnly);
3891
4160
  switch (action) {
3892
4161
  case "list":
3893
- return toolResult(await listNotebooks(api, { query, authorHandle, excludeAuthorHandle, includeCells, pageSize, pageNumber }, limits));
4162
+ return toolResult(
4163
+ await listNotebooks(
4164
+ api,
4165
+ { query, authorHandle, excludeAuthorHandle, includeCells, pageSize, pageNumber },
4166
+ limits
4167
+ )
4168
+ );
3894
4169
  case "get": {
3895
4170
  const notebookId = requireParam(id, "id", "get");
3896
4171
  return toolResult(await getNotebook(api, notebookId));
3897
4172
  }
3898
4173
  case "create": {
3899
4174
  const notebookName = requireParam(name, "name", "create");
3900
- return toolResult(await createNotebook(api, {
3901
- name: notebookName,
3902
- cells,
3903
- time,
3904
- status
3905
- }));
4175
+ return toolResult(
4176
+ await createNotebook(api, {
4177
+ name: notebookName,
4178
+ cells,
4179
+ time,
4180
+ status
4181
+ })
4182
+ );
3906
4183
  }
3907
4184
  case "update": {
3908
4185
  const notebookId = requireParam(id, "id", "update");
3909
- return toolResult(await updateNotebook(api, notebookId, {
3910
- name,
3911
- cells,
3912
- time,
3913
- status
3914
- }));
4186
+ return toolResult(
4187
+ await updateNotebook(api, notebookId, {
4188
+ name,
4189
+ cells,
4190
+ time,
4191
+ status
4192
+ })
4193
+ );
3915
4194
  }
3916
4195
  case "delete": {
3917
4196
  const notebookId = requireParam(id, "id", "delete");
@@ -3993,7 +4272,9 @@ function registerUsersTool(server, api, limits) {
3993
4272
  try {
3994
4273
  switch (action) {
3995
4274
  case "list":
3996
- return toolResult(await listUsers(api, { filter, status, pageSize, pageNumber }, limits));
4275
+ return toolResult(
4276
+ await listUsers(api, { filter, status, pageSize, pageNumber }, limits)
4277
+ );
3997
4278
  case "get": {
3998
4279
  const userId = requireParam(id, "id", "get");
3999
4280
  return toolResult(await getUser(api, userId));
@@ -4224,9 +4505,18 @@ function registerTagsTool(server, api, _limits, readOnly = false) {
4224
4505
 
4225
4506
  // src/tools/usage.ts
4226
4507
  import { z as z19 } from "zod";
4227
- var ActionSchema18 = z19.enum(["summary", "hosts", "logs", "custom_metrics", "indexed_spans", "ingested_spans"]);
4508
+ var ActionSchema18 = z19.enum([
4509
+ "summary",
4510
+ "hosts",
4511
+ "logs",
4512
+ "custom_metrics",
4513
+ "indexed_spans",
4514
+ "ingested_spans"
4515
+ ]);
4228
4516
  var InputSchema18 = {
4229
- action: ActionSchema18.describe("Action to perform: summary (overall usage), hosts, logs, custom_metrics, indexed_spans, ingested_spans"),
4517
+ action: ActionSchema18.describe(
4518
+ "Action to perform: summary (overall usage), hosts, logs, custom_metrics, indexed_spans, ingested_spans"
4519
+ ),
4230
4520
  from: z19.string().optional().describe('Start time (ISO 8601 date like "2024-01-01", or relative like "30d")'),
4231
4521
  to: z19.string().optional().describe('End time (ISO 8601 date like "2024-01-31", or relative like "now")'),
4232
4522
  includeOrgDetails: z19.boolean().optional().describe("Include usage breakdown by organization (for multi-org accounts)")
@@ -4238,7 +4528,7 @@ function parseDate(dateStr, defaultDate) {
4238
4528
  return new Date(seconds * 1e3);
4239
4529
  }
4240
4530
  const parsed = new Date(dateStr);
4241
- if (!isNaN(parsed.getTime())) {
4531
+ if (!Number.isNaN(parsed.getTime())) {
4242
4532
  return parsed;
4243
4533
  }
4244
4534
  return defaultDate;
@@ -4395,7 +4685,9 @@ function registerUsageTool(server, api, _limits) {
4395
4685
  import { z as z20 } from "zod";
4396
4686
  var ActionSchema19 = z20.enum(["validate"]);
4397
4687
  var InputSchema19 = {
4398
- action: ActionSchema19.describe("Action to perform: validate - test if API key and App key are valid")
4688
+ action: ActionSchema19.describe(
4689
+ "Action to perform: validate - test if API key and App key are valid"
4690
+ )
4399
4691
  };
4400
4692
  function registerAuthTool(server, clients) {
4401
4693
  server.tool(
@@ -4455,14 +4747,26 @@ function registerAllTools(server, clients, limits, features, site = "datadoghq.c
4455
4747
  const { readOnly, disabledTools } = features;
4456
4748
  const enabled = (tool) => !disabledTools.includes(tool);
4457
4749
  if (enabled("monitors")) registerMonitorsTool(server, clients.monitors, limits, readOnly, site);
4458
- if (enabled("dashboards")) registerDashboardsTool(server, clients.dashboards, limits, readOnly, site);
4750
+ if (enabled("dashboards"))
4751
+ registerDashboardsTool(server, clients.dashboards, limits, readOnly, site);
4459
4752
  if (enabled("logs")) registerLogsTool(server, clients.logs, limits, site);
4460
- if (enabled("metrics")) registerMetricsTool(server, clients.metricsV1, clients.metricsV2, limits, site);
4753
+ if (enabled("metrics"))
4754
+ registerMetricsTool(server, clients.metricsV1, clients.metricsV2, limits, site);
4461
4755
  if (enabled("traces")) registerTracesTool(server, clients.spans, clients.services, limits, site);
4462
- if (enabled("events")) registerEventsTool(server, clients.eventsV1, clients.eventsV2, clients.monitors, limits, readOnly, site);
4756
+ if (enabled("events"))
4757
+ registerEventsTool(
4758
+ server,
4759
+ clients.eventsV1,
4760
+ clients.eventsV2,
4761
+ clients.monitors,
4762
+ limits,
4763
+ readOnly,
4764
+ site
4765
+ );
4463
4766
  if (enabled("incidents")) registerIncidentsTool(server, clients.incidents, limits, readOnly, site);
4464
4767
  if (enabled("slos")) registerSlosTool(server, clients.slo, limits, readOnly, site);
4465
- if (enabled("synthetics")) registerSyntheticsTool(server, clients.synthetics, limits, readOnly, site);
4768
+ if (enabled("synthetics"))
4769
+ registerSyntheticsTool(server, clients.synthetics, limits, readOnly, site);
4466
4770
  if (enabled("hosts")) registerHostsTool(server, clients.hosts, limits, readOnly);
4467
4771
  if (enabled("downtimes")) registerDowntimesTool(server, clients.downtimes, limits, readOnly);
4468
4772
  if (enabled("rum")) registerRumTool(server, clients.rum, limits, site);
@@ -4561,19 +4865,16 @@ async function connectHttp(server, config) {
4561
4865
  }
4562
4866
 
4563
4867
  // src/index.ts
4564
- async function main() {
4565
- try {
4566
- const config = loadConfig();
4567
- const server = createServer(config);
4568
- if (config.server.transport === "http") {
4569
- await connectHttp(server, config.server);
4570
- } else {
4571
- await connectStdio(server);
4572
- }
4573
- } catch (error) {
4574
- console.error("[MCP] Failed to start server:", error);
4575
- process.exit(1);
4868
+ try {
4869
+ const config = loadConfig();
4870
+ const server = createServer(config);
4871
+ if (config.server.transport === "http") {
4872
+ await connectHttp(server, config.server);
4873
+ } else {
4874
+ await connectStdio(server);
4576
4875
  }
4876
+ } catch (error) {
4877
+ console.error("[MCP] Failed to start server:", error);
4878
+ process.exit(1);
4577
4879
  }
4578
- main();
4579
4880
  //# sourceMappingURL=index.js.map