datadog-mcp 1.0.0 → 1.0.4
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 +22 -0
- package/dist/index.js +637 -320
- package/dist/index.js.map +1 -1
- package/package.json +4 -1
package/dist/index.js
CHANGED
|
@@ -35,7 +35,7 @@ var configSchema = z.object({
|
|
|
35
35
|
transport: z.enum(["stdio", "http"]).default("stdio"),
|
|
36
36
|
port: z.number().default(3e3),
|
|
37
37
|
host: z.string().default("localhost")
|
|
38
|
-
}),
|
|
38
|
+
}).default({}),
|
|
39
39
|
limits: z.object({
|
|
40
40
|
maxResults: z.number().default(100),
|
|
41
41
|
maxLogLines: z.number().default(100),
|
|
@@ -44,7 +44,7 @@ var configSchema = z.object({
|
|
|
44
44
|
// Default limit for initial queries
|
|
45
45
|
maxMetricDataPoints: z.number().default(1e3),
|
|
46
46
|
defaultTimeRangeHours: z.number().default(24)
|
|
47
|
-
}),
|
|
47
|
+
}).default({}),
|
|
48
48
|
features: z.object({
|
|
49
49
|
readOnly: z.boolean().default(false),
|
|
50
50
|
disabledTools: z.array(z.string()).default([])
|
|
@@ -52,6 +52,22 @@ var configSchema = z.object({
|
|
|
52
52
|
});
|
|
53
53
|
|
|
54
54
|
// src/config/index.ts
|
|
55
|
+
function parseEqualsFormat(arg) {
|
|
56
|
+
if (!arg.includes("=")) return null;
|
|
57
|
+
const parts = arg.slice(2).split("=");
|
|
58
|
+
const key = parts[0];
|
|
59
|
+
const value = parts.slice(1).join("=");
|
|
60
|
+
return key && value !== void 0 ? [key, value] : null;
|
|
61
|
+
}
|
|
62
|
+
function parseSpacedFormat(arg, nextArg) {
|
|
63
|
+
if (nextArg && !nextArg.startsWith("--")) {
|
|
64
|
+
return [arg.slice(2), nextArg];
|
|
65
|
+
}
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
function parseBooleanFlag(arg) {
|
|
69
|
+
return arg.slice(2);
|
|
70
|
+
}
|
|
55
71
|
function parseArgs() {
|
|
56
72
|
const strings = {};
|
|
57
73
|
const booleans = /* @__PURE__ */ new Set();
|
|
@@ -60,23 +76,20 @@ function parseArgs() {
|
|
|
60
76
|
const arg = argv[i];
|
|
61
77
|
if (!arg) continue;
|
|
62
78
|
if (arg.startsWith("--")) {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
const key =
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
i++;
|
|
76
|
-
} else {
|
|
77
|
-
booleans.add(argName);
|
|
78
|
-
}
|
|
79
|
+
const equalsResult = parseEqualsFormat(arg);
|
|
80
|
+
if (equalsResult) {
|
|
81
|
+
const [key, value] = equalsResult;
|
|
82
|
+
strings[key] = value;
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
const spacedResult = parseSpacedFormat(arg, argv[i + 1]);
|
|
86
|
+
if (spacedResult) {
|
|
87
|
+
const [key, value] = spacedResult;
|
|
88
|
+
strings[key] = value;
|
|
89
|
+
i += 1;
|
|
90
|
+
continue;
|
|
79
91
|
}
|
|
92
|
+
booleans.add(parseBooleanFlag(arg));
|
|
80
93
|
}
|
|
81
94
|
}
|
|
82
95
|
return { strings, booleans };
|
|
@@ -98,18 +111,20 @@ function loadConfig() {
|
|
|
98
111
|
name: "datadog-mcp",
|
|
99
112
|
version: "1.0.0",
|
|
100
113
|
transport: args.strings.transport ?? process.env.MCP_TRANSPORT ?? "stdio",
|
|
101
|
-
port: parseInt(args.strings.port ?? process.env.MCP_PORT ?? "3000", 10),
|
|
114
|
+
port: Number.parseInt(args.strings.port ?? process.env.MCP_PORT ?? "3000", 10),
|
|
102
115
|
host: args.strings.host ?? process.env.MCP_HOST ?? "localhost"
|
|
103
116
|
},
|
|
104
117
|
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)
|
|
118
|
+
maxResults: Number.parseInt(process.env.MCP_MAX_RESULTS ?? "100", 10),
|
|
119
|
+
maxLogLines: Number.parseInt(process.env.MCP_MAX_LOG_LINES ?? "500", 10),
|
|
120
|
+
maxMetricDataPoints: Number.parseInt(process.env.MCP_MAX_METRIC_POINTS ?? "1000", 10),
|
|
121
|
+
defaultTimeRangeHours: Number.parseInt(process.env.MCP_DEFAULT_TIME_RANGE ?? "24", 10)
|
|
109
122
|
},
|
|
110
123
|
features: {
|
|
111
124
|
readOnly: args.booleans.has("read-only") || process.env.MCP_READ_ONLY === "true",
|
|
112
|
-
disabledTools: parseDisabledTools(
|
|
125
|
+
disabledTools: parseDisabledTools(
|
|
126
|
+
args.strings["disable-tools"] ?? process.env.MCP_DISABLE_TOOLS
|
|
127
|
+
)
|
|
113
128
|
}
|
|
114
129
|
};
|
|
115
130
|
return configSchema.parse(raw);
|
|
@@ -194,19 +209,31 @@ function handleDatadogError(error) {
|
|
|
194
209
|
case 400:
|
|
195
210
|
throw new McpError(ErrorCode.InvalidRequest, `Invalid request: ${message}`);
|
|
196
211
|
case 401:
|
|
197
|
-
throw new McpError(
|
|
212
|
+
throw new McpError(
|
|
213
|
+
DatadogErrorCode.Unauthorized,
|
|
214
|
+
`Authentication failed: Invalid Datadog API key or APP key`
|
|
215
|
+
);
|
|
198
216
|
case 403:
|
|
199
217
|
throw new McpError(DatadogErrorCode.Forbidden, `Authorization denied: ${message}`);
|
|
200
218
|
case 404:
|
|
201
219
|
throw new McpError(DatadogErrorCode.NotFound, `Resource not found: ${message}`);
|
|
202
220
|
case 429:
|
|
203
|
-
throw new McpError(
|
|
221
|
+
throw new McpError(
|
|
222
|
+
DatadogErrorCode.RateLimited,
|
|
223
|
+
"Rate limit exceeded. Retry after a short delay."
|
|
224
|
+
);
|
|
204
225
|
case 500:
|
|
205
226
|
case 502:
|
|
206
227
|
case 503:
|
|
207
|
-
throw new McpError(
|
|
228
|
+
throw new McpError(
|
|
229
|
+
DatadogErrorCode.ServiceUnavailable,
|
|
230
|
+
"Datadog service temporarily unavailable. Retry later."
|
|
231
|
+
);
|
|
208
232
|
default:
|
|
209
|
-
throw new McpError(
|
|
233
|
+
throw new McpError(
|
|
234
|
+
ErrorCode.InternalError,
|
|
235
|
+
`Datadog API error (${apiError.code}): ${message}`
|
|
236
|
+
);
|
|
210
237
|
}
|
|
211
238
|
}
|
|
212
239
|
throw new McpError(
|
|
@@ -247,7 +274,7 @@ function formatResponse(data) {
|
|
|
247
274
|
return [
|
|
248
275
|
{
|
|
249
276
|
type: "text",
|
|
250
|
-
text: JSON.stringify(data, null, 2)
|
|
277
|
+
text: JSON.stringify(data, null, 2) ?? "null"
|
|
251
278
|
}
|
|
252
279
|
];
|
|
253
280
|
}
|
|
@@ -341,7 +368,16 @@ function buildRumSessionUrl(applicationId, sessionId, site = "datadoghq.com") {
|
|
|
341
368
|
}
|
|
342
369
|
|
|
343
370
|
// src/tools/monitors.ts
|
|
344
|
-
var ActionSchema = z2.enum([
|
|
371
|
+
var ActionSchema = z2.enum([
|
|
372
|
+
"list",
|
|
373
|
+
"get",
|
|
374
|
+
"search",
|
|
375
|
+
"create",
|
|
376
|
+
"update",
|
|
377
|
+
"delete",
|
|
378
|
+
"mute",
|
|
379
|
+
"unmute"
|
|
380
|
+
]);
|
|
345
381
|
var InputSchema = {
|
|
346
382
|
action: ActionSchema.describe("Action to perform"),
|
|
347
383
|
id: z2.string().optional().describe("Monitor ID (required for get/update/delete/mute/unmute)"),
|
|
@@ -389,8 +425,8 @@ async function listMonitors(api, params, limits, site) {
|
|
|
389
425
|
};
|
|
390
426
|
}
|
|
391
427
|
async function getMonitor(api, id, site) {
|
|
392
|
-
const monitorId = parseInt(id, 10);
|
|
393
|
-
if (isNaN(monitorId)) {
|
|
428
|
+
const monitorId = Number.parseInt(id, 10);
|
|
429
|
+
if (Number.isNaN(monitorId)) {
|
|
394
430
|
throw new Error(`Invalid monitor ID: ${id}`);
|
|
395
431
|
}
|
|
396
432
|
const monitor = await api.getMonitor({ monitorId });
|
|
@@ -478,7 +514,7 @@ async function createMonitor(api, config) {
|
|
|
478
514
|
};
|
|
479
515
|
}
|
|
480
516
|
async function updateMonitor(api, id, config) {
|
|
481
|
-
const monitorId = parseInt(id, 10);
|
|
517
|
+
const monitorId = Number.parseInt(id, 10);
|
|
482
518
|
const body = normalizeMonitorConfig(config);
|
|
483
519
|
const monitor = await api.updateMonitor({ monitorId, body });
|
|
484
520
|
return {
|
|
@@ -487,12 +523,12 @@ async function updateMonitor(api, id, config) {
|
|
|
487
523
|
};
|
|
488
524
|
}
|
|
489
525
|
async function deleteMonitor(api, id) {
|
|
490
|
-
const monitorId = parseInt(id, 10);
|
|
526
|
+
const monitorId = Number.parseInt(id, 10);
|
|
491
527
|
await api.deleteMonitor({ monitorId });
|
|
492
528
|
return { success: true, message: `Monitor ${id} deleted` };
|
|
493
529
|
}
|
|
494
530
|
async function muteMonitor(api, id, params) {
|
|
495
|
-
const monitorId = parseInt(id, 10);
|
|
531
|
+
const monitorId = Number.parseInt(id, 10);
|
|
496
532
|
const monitor = await api.getMonitor({ monitorId });
|
|
497
533
|
await api.updateMonitor({
|
|
498
534
|
monitorId,
|
|
@@ -506,7 +542,7 @@ async function muteMonitor(api, id, params) {
|
|
|
506
542
|
return { success: true, message: `Monitor ${id} muted` };
|
|
507
543
|
}
|
|
508
544
|
async function unmuteMonitor(api, id) {
|
|
509
|
-
const monitorId = parseInt(id, 10);
|
|
545
|
+
const monitorId = Number.parseInt(id, 10);
|
|
510
546
|
const monitor = await api.getMonitor({ monitorId });
|
|
511
547
|
await api.updateMonitor({
|
|
512
548
|
monitorId,
|
|
@@ -531,7 +567,9 @@ TIP: For alert HISTORY (which monitors triggered), use the events tool with tags
|
|
|
531
567
|
checkReadOnly(action, readOnly);
|
|
532
568
|
switch (action) {
|
|
533
569
|
case "list":
|
|
534
|
-
return toolResult(
|
|
570
|
+
return toolResult(
|
|
571
|
+
await listMonitors(api, { name, tags, groupStates, limit }, limits, site)
|
|
572
|
+
);
|
|
535
573
|
case "get": {
|
|
536
574
|
const monitorId = requireParam(id, "id", "get");
|
|
537
575
|
return toolResult(await getMonitor(api, monitorId, site));
|
|
@@ -602,9 +640,7 @@ async function listDashboards(api, params, limits) {
|
|
|
602
640
|
let dashboards = response.dashboards ?? [];
|
|
603
641
|
if (params.name) {
|
|
604
642
|
const lowerName = params.name.toLowerCase();
|
|
605
|
-
dashboards = dashboards.filter(
|
|
606
|
-
(d) => d.title?.toLowerCase().includes(lowerName)
|
|
607
|
-
);
|
|
643
|
+
dashboards = dashboards.filter((d) => d.title?.toLowerCase().includes(lowerName));
|
|
608
644
|
}
|
|
609
645
|
const result = dashboards.slice(0, effectiveLimit).map(formatDashboardSummary);
|
|
610
646
|
return {
|
|
@@ -667,7 +703,7 @@ async function deleteDashboard(api, id) {
|
|
|
667
703
|
await api.deleteDashboard({ dashboardId: id });
|
|
668
704
|
return { success: true, message: `Dashboard ${id} deleted` };
|
|
669
705
|
}
|
|
670
|
-
function registerDashboardsTool(server, api, limits, readOnly = false) {
|
|
706
|
+
function registerDashboardsTool(server, api, limits, readOnly = false, _site = "datadoghq.com") {
|
|
671
707
|
server.tool(
|
|
672
708
|
"dashboards",
|
|
673
709
|
"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 +765,9 @@ function parseTime(input, defaultValue) {
|
|
|
729
765
|
return input;
|
|
730
766
|
}
|
|
731
767
|
const trimmed = input.trim();
|
|
732
|
-
const simpleRelativeMatch = trimmed.match(/^(\d+)(
|
|
768
|
+
const simpleRelativeMatch = trimmed.match(/^(\d+)([smhd])$/);
|
|
733
769
|
if (simpleRelativeMatch) {
|
|
734
|
-
const value = parseInt(simpleRelativeMatch[1] ?? "0", 10);
|
|
770
|
+
const value = Number.parseInt(simpleRelativeMatch[1] ?? "0", 10);
|
|
735
771
|
const unit = simpleRelativeMatch[2];
|
|
736
772
|
const nowTs = now();
|
|
737
773
|
switch (unit) {
|
|
@@ -747,13 +783,13 @@ function parseTime(input, defaultValue) {
|
|
|
747
783
|
return defaultValue;
|
|
748
784
|
}
|
|
749
785
|
}
|
|
750
|
-
const relativeWithTimeMatch = trimmed.match(/^(\d+)(
|
|
786
|
+
const relativeWithTimeMatch = trimmed.match(/^(\d+)([dh])[@\s](\d{1,2}):(\d{2})(?::(\d{2}))?$/);
|
|
751
787
|
if (relativeWithTimeMatch) {
|
|
752
|
-
const value = parseInt(relativeWithTimeMatch[1] ?? "0", 10);
|
|
788
|
+
const value = Number.parseInt(relativeWithTimeMatch[1] ?? "0", 10);
|
|
753
789
|
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);
|
|
790
|
+
const hours = Number.parseInt(relativeWithTimeMatch[3] ?? "0", 10);
|
|
791
|
+
const minutes = Number.parseInt(relativeWithTimeMatch[4] ?? "0", 10);
|
|
792
|
+
const seconds = Number.parseInt(relativeWithTimeMatch[5] ?? "0", 10);
|
|
757
793
|
if (unit === "d") {
|
|
758
794
|
const date3 = startOfDayAgo(value);
|
|
759
795
|
date3.setHours(hours, minutes, seconds, 0);
|
|
@@ -767,26 +803,27 @@ function parseTime(input, defaultValue) {
|
|
|
767
803
|
const keywordMatch = trimmed.match(/^(today|yesterday)[@\s](\d{1,2}):(\d{2})(?::(\d{2}))?$/i);
|
|
768
804
|
if (keywordMatch) {
|
|
769
805
|
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);
|
|
806
|
+
const hours = Number.parseInt(keywordMatch[2] ?? "0", 10);
|
|
807
|
+
const minutes = Number.parseInt(keywordMatch[3] ?? "0", 10);
|
|
808
|
+
const seconds = Number.parseInt(keywordMatch[4] ?? "0", 10);
|
|
773
809
|
const daysAgo = keyword === "yesterday" ? 1 : 0;
|
|
774
810
|
const date2 = startOfDayAgo(daysAgo);
|
|
775
811
|
date2.setHours(hours, minutes, seconds, 0);
|
|
776
812
|
return Math.floor(date2.getTime() / 1e3);
|
|
777
813
|
}
|
|
778
814
|
const date = new Date(trimmed);
|
|
779
|
-
if (!isNaN(date.getTime())) {
|
|
815
|
+
if (!Number.isNaN(date.getTime())) {
|
|
780
816
|
return Math.floor(date.getTime() / 1e3);
|
|
781
817
|
}
|
|
782
|
-
const ts = parseInt(trimmed, 10);
|
|
783
|
-
if (!isNaN(ts)) {
|
|
818
|
+
const ts = Number.parseInt(trimmed, 10);
|
|
819
|
+
if (!Number.isNaN(ts)) {
|
|
784
820
|
return ts;
|
|
785
821
|
}
|
|
786
822
|
return defaultValue;
|
|
787
823
|
}
|
|
788
824
|
function ensureValidTimeRange(from, to, minRangeSeconds = 60) {
|
|
789
825
|
if (from > to) {
|
|
826
|
+
;
|
|
790
827
|
[from, to] = [to, from];
|
|
791
828
|
}
|
|
792
829
|
if (to - from < minRangeSeconds) {
|
|
@@ -804,21 +841,21 @@ function parseDurationToNs(input) {
|
|
|
804
841
|
const trimmed = input.trim().toLowerCase();
|
|
805
842
|
const match = trimmed.match(/^(\d+(?:\.\d+)?)(ns|µs|us|ms|s|m|h|d|w)?$/);
|
|
806
843
|
if (!match) {
|
|
807
|
-
const raw = parseInt(trimmed, 10);
|
|
808
|
-
return isNaN(raw) ? void 0 : raw;
|
|
844
|
+
const raw = Number.parseInt(trimmed, 10);
|
|
845
|
+
return Number.isNaN(raw) ? void 0 : raw;
|
|
809
846
|
}
|
|
810
|
-
const value = parseFloat(match[1] ?? "0");
|
|
847
|
+
const value = Number.parseFloat(match[1] ?? "0");
|
|
811
848
|
const unit = match[2] ?? "ns";
|
|
812
849
|
const multipliers = {
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
850
|
+
ns: 1,
|
|
851
|
+
\u00B5s: 1e3,
|
|
852
|
+
us: 1e3,
|
|
853
|
+
ms: 1e6,
|
|
854
|
+
s: 1e9,
|
|
855
|
+
m: 6e10,
|
|
856
|
+
h: 36e11,
|
|
857
|
+
d: 864e11,
|
|
858
|
+
w: 6048e11
|
|
822
859
|
};
|
|
823
860
|
return Math.floor(value * (multipliers[unit] ?? 1));
|
|
824
861
|
}
|
|
@@ -834,10 +871,18 @@ function formatDurationNs(ns) {
|
|
|
834
871
|
var ActionSchema3 = z4.enum(["search", "aggregate"]);
|
|
835
872
|
var InputSchema3 = {
|
|
836
873
|
action: ActionSchema3.describe("Action to perform"),
|
|
837
|
-
query: z4.string().optional().describe(
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
874
|
+
query: z4.string().optional().describe(
|
|
875
|
+
'Log search query (Datadog syntax). Examples: "error", "service:my-service status:error", "error AND timeout"'
|
|
876
|
+
),
|
|
877
|
+
keyword: z4.string().optional().describe(
|
|
878
|
+
"Simple text search - finds logs containing this text (grep-like). Merged with query using AND"
|
|
879
|
+
),
|
|
880
|
+
pattern: z4.string().optional().describe(
|
|
881
|
+
'Regex pattern to match in log message (grep -E style). Example: "ERROR.*timeout|connection refused"'
|
|
882
|
+
),
|
|
883
|
+
from: z4.string().optional().describe(
|
|
884
|
+
"Start time. Formats: ISO 8601, relative (30s, 15m, 2h, 7d), precise (3d@11:45:23, yesterday@14:00)"
|
|
885
|
+
),
|
|
841
886
|
to: z4.string().optional().describe('End time. Same formats as "from". Example: from="3d@11:45:23" to="3d@12:55:34"'),
|
|
842
887
|
service: z4.string().optional().describe("Filter by service name"),
|
|
843
888
|
host: z4.string().optional().describe("Filter by host"),
|
|
@@ -845,8 +890,12 @@ var InputSchema3 = {
|
|
|
845
890
|
indexes: z4.array(z4.string()).optional().describe("Log indexes to search"),
|
|
846
891
|
limit: z4.number().optional().describe("Maximum number of logs to return"),
|
|
847
892
|
sort: z4.enum(["timestamp", "-timestamp"]).optional().describe("Sort order"),
|
|
848
|
-
sample: z4.enum(["first", "spread", "diverse"]).optional().describe(
|
|
849
|
-
|
|
893
|
+
sample: z4.enum(["first", "spread", "diverse"]).optional().describe(
|
|
894
|
+
"Sampling mode: first (chronological, default), spread (evenly across time range), diverse (distinct message patterns)"
|
|
895
|
+
),
|
|
896
|
+
compact: z4.boolean().optional().describe(
|
|
897
|
+
"Strip custom attributes for token efficiency. Keeps: id, timestamp, service, status, message (truncated), dd.trace_id, error info"
|
|
898
|
+
),
|
|
850
899
|
groupBy: z4.array(z4.string()).optional().describe("Fields to group by (for aggregate)"),
|
|
851
900
|
compute: z4.record(z4.unknown()).optional().describe("Compute operations (for aggregate)")
|
|
852
901
|
};
|
|
@@ -876,8 +925,9 @@ function formatLogCompact(log) {
|
|
|
876
925
|
const ts = attrs.timestamp;
|
|
877
926
|
timestamp = ts instanceof Date ? ts.toISOString() : new Date(String(ts)).toISOString();
|
|
878
927
|
}
|
|
879
|
-
const
|
|
880
|
-
const
|
|
928
|
+
const attrsAny = attrs;
|
|
929
|
+
const traceId = nestedAttrs["dd.trace_id"] ?? nestedAttrs["trace_id"] ?? attrsAny["dd.trace_id"] ?? "";
|
|
930
|
+
const spanId = nestedAttrs["dd.span_id"] ?? nestedAttrs["span_id"] ?? attrsAny["dd.span_id"] ?? "";
|
|
881
931
|
const errorType = nestedAttrs["error.type"] ?? nestedAttrs["error.kind"] ?? "";
|
|
882
932
|
const errorMessage = nestedAttrs["error.message"] ?? nestedAttrs["error.msg"] ?? "";
|
|
883
933
|
const fullMessage = attrs.message ?? "";
|
|
@@ -1054,35 +1104,66 @@ CORRELATION: Logs contain dd.trace_id in attributes for linking to traces and AP
|
|
|
1054
1104
|
SAMPLING: Use sample:"diverse" for error investigation (dedupes by message pattern), sample:"spread" for time distribution.
|
|
1055
1105
|
TOKEN TIP: Use compact:true to reduce payload size (strips heavy fields) when querying large volumes.`,
|
|
1056
1106
|
InputSchema3,
|
|
1057
|
-
async ({
|
|
1107
|
+
async ({
|
|
1108
|
+
action,
|
|
1109
|
+
query,
|
|
1110
|
+
keyword,
|
|
1111
|
+
pattern,
|
|
1112
|
+
service,
|
|
1113
|
+
host,
|
|
1114
|
+
status,
|
|
1115
|
+
from,
|
|
1116
|
+
to,
|
|
1117
|
+
indexes,
|
|
1118
|
+
limit,
|
|
1119
|
+
sort,
|
|
1120
|
+
sample,
|
|
1121
|
+
compact,
|
|
1122
|
+
groupBy,
|
|
1123
|
+
compute
|
|
1124
|
+
}) => {
|
|
1058
1125
|
try {
|
|
1059
1126
|
switch (action) {
|
|
1060
1127
|
case "search": {
|
|
1061
|
-
return toolResult(
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1128
|
+
return toolResult(
|
|
1129
|
+
await searchLogs(
|
|
1130
|
+
api,
|
|
1131
|
+
{
|
|
1132
|
+
query,
|
|
1133
|
+
keyword,
|
|
1134
|
+
pattern,
|
|
1135
|
+
service,
|
|
1136
|
+
host,
|
|
1137
|
+
status,
|
|
1138
|
+
from,
|
|
1139
|
+
to,
|
|
1140
|
+
indexes,
|
|
1141
|
+
limit,
|
|
1142
|
+
sort,
|
|
1143
|
+
sample,
|
|
1144
|
+
compact
|
|
1145
|
+
},
|
|
1146
|
+
limits,
|
|
1147
|
+
site
|
|
1148
|
+
)
|
|
1149
|
+
);
|
|
1076
1150
|
}
|
|
1077
1151
|
case "aggregate": {
|
|
1078
|
-
const aggregateQuery =
|
|
1079
|
-
return toolResult(
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1152
|
+
const aggregateQuery = query ?? "*";
|
|
1153
|
+
return toolResult(
|
|
1154
|
+
await aggregateLogs(
|
|
1155
|
+
api,
|
|
1156
|
+
{
|
|
1157
|
+
query: aggregateQuery,
|
|
1158
|
+
from,
|
|
1159
|
+
to,
|
|
1160
|
+
groupBy,
|
|
1161
|
+
compute
|
|
1162
|
+
},
|
|
1163
|
+
limits,
|
|
1164
|
+
site
|
|
1165
|
+
)
|
|
1166
|
+
);
|
|
1086
1167
|
}
|
|
1087
1168
|
default:
|
|
1088
1169
|
throw new Error(`Unknown action: ${action}`);
|
|
@@ -1099,8 +1180,12 @@ import { z as z5 } from "zod";
|
|
|
1099
1180
|
var ActionSchema4 = z5.enum(["query", "search", "list", "metadata"]);
|
|
1100
1181
|
var InputSchema4 = {
|
|
1101
1182
|
action: ActionSchema4.describe("Action to perform"),
|
|
1102
|
-
query: z5.string().optional().describe(
|
|
1103
|
-
|
|
1183
|
+
query: z5.string().optional().describe(
|
|
1184
|
+
'For query: PromQL expression (e.g., "avg:system.cpu.user{*}"). For search: grep-like filter on metric names. For list: tag filter.'
|
|
1185
|
+
),
|
|
1186
|
+
from: z5.string().optional().describe(
|
|
1187
|
+
"Start time (ONLY for query action). Formats: ISO 8601, relative (30s, 15m, 2h, 7d), precise (3d@11:45:23)"
|
|
1188
|
+
),
|
|
1104
1189
|
to: z5.string().optional().describe('End time (ONLY for query action). Same formats as "from".'),
|
|
1105
1190
|
metric: z5.string().optional().describe("Metric name (for metadata action)"),
|
|
1106
1191
|
tag: z5.string().optional().describe("Filter by tag"),
|
|
@@ -1198,18 +1283,31 @@ Example: max:trace.{service}.request.duration{*}`,
|
|
|
1198
1283
|
switch (action) {
|
|
1199
1284
|
case "query": {
|
|
1200
1285
|
const metricsQuery = requireParam(query, "query", "query");
|
|
1201
|
-
return toolResult(
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1286
|
+
return toolResult(
|
|
1287
|
+
await queryMetrics(
|
|
1288
|
+
metricsV1Api,
|
|
1289
|
+
{
|
|
1290
|
+
query: metricsQuery,
|
|
1291
|
+
from,
|
|
1292
|
+
to
|
|
1293
|
+
},
|
|
1294
|
+
limits,
|
|
1295
|
+
site
|
|
1296
|
+
)
|
|
1297
|
+
);
|
|
1206
1298
|
}
|
|
1207
1299
|
case "search": {
|
|
1208
1300
|
const searchQuery = requireParam(query, "query", "search");
|
|
1209
|
-
return toolResult(
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1301
|
+
return toolResult(
|
|
1302
|
+
await searchMetrics(
|
|
1303
|
+
metricsV1Api,
|
|
1304
|
+
{
|
|
1305
|
+
query: searchQuery,
|
|
1306
|
+
limit
|
|
1307
|
+
},
|
|
1308
|
+
limits
|
|
1309
|
+
)
|
|
1310
|
+
);
|
|
1213
1311
|
}
|
|
1214
1312
|
case "list":
|
|
1215
1313
|
return toolResult(await listMetrics(metricsV1Api, { query }, limits));
|
|
@@ -1245,17 +1343,25 @@ var RESERVED_SPAN_FACETS = /* @__PURE__ */ new Set([
|
|
|
1245
1343
|
]);
|
|
1246
1344
|
var InputSchema5 = {
|
|
1247
1345
|
action: ActionSchema5.describe("Action to perform"),
|
|
1248
|
-
query: z6.string().optional().describe(
|
|
1249
|
-
|
|
1346
|
+
query: z6.string().optional().describe(
|
|
1347
|
+
'APM trace search query (Datadog syntax). Example: "@http.status_code:500", "service:my-service status:error"'
|
|
1348
|
+
),
|
|
1349
|
+
from: z6.string().optional().describe(
|
|
1350
|
+
"Start time. Formats: ISO 8601, relative (30s, 15m, 2h, 7d), precise (3d@11:45:23, yesterday@14:00)"
|
|
1351
|
+
),
|
|
1250
1352
|
to: z6.string().optional().describe('End time. Same formats as "from". Example: from="3d@11:45" to="3d@12:55"'),
|
|
1251
1353
|
service: z6.string().optional().describe('Filter by service name. Example: "my-service", "postgres"'),
|
|
1252
1354
|
operation: z6.string().optional().describe('Filter by operation name. Example: "express.request", "mongodb.query"'),
|
|
1253
|
-
resource: z6.string().optional().describe(
|
|
1355
|
+
resource: z6.string().optional().describe(
|
|
1356
|
+
'Filter by resource name (endpoint/query). Supports wildcards. Example: "GET /api/*", "*orders*"'
|
|
1357
|
+
),
|
|
1254
1358
|
status: z6.enum(["ok", "error"]).optional().describe('Filter by span status - "ok" for successful, "error" for failed spans'),
|
|
1255
1359
|
env: z6.string().optional().describe('Filter by environment. Example: "production", "staging"'),
|
|
1256
1360
|
minDuration: z6.string().optional().describe('Minimum span duration (find slow spans). Examples: "1s", "500ms", "100ms"'),
|
|
1257
1361
|
maxDuration: z6.string().optional().describe('Maximum span duration. Examples: "5s", "1000ms"'),
|
|
1258
|
-
httpStatus: z6.string().optional().describe(
|
|
1362
|
+
httpStatus: z6.string().optional().describe(
|
|
1363
|
+
'HTTP status code filter. Examples: "500", "5xx" (500-599), "4xx" (400-499), ">=400"'
|
|
1364
|
+
),
|
|
1259
1365
|
errorType: z6.string().optional().describe('Filter by error type (grep-like). Example: "TimeoutError", "ConnectionRefused"'),
|
|
1260
1366
|
errorMessage: z6.string().optional().describe('Filter by error message (grep-like). Example: "timeout", "connection refused"'),
|
|
1261
1367
|
limit: z6.number().optional().describe("Maximum number of results"),
|
|
@@ -1306,6 +1412,18 @@ function formatSpan(span) {
|
|
|
1306
1412
|
tags
|
|
1307
1413
|
};
|
|
1308
1414
|
}
|
|
1415
|
+
function buildHttpStatusFilter(httpStatus) {
|
|
1416
|
+
const status = httpStatus.toLowerCase();
|
|
1417
|
+
if (status.endsWith("xx")) {
|
|
1418
|
+
const base = Number.parseInt(status[0] ?? "0", 10) * 100;
|
|
1419
|
+
return `@http.status_code:[${base} TO ${base + 99}]`;
|
|
1420
|
+
}
|
|
1421
|
+
if (status.startsWith(">=")) return `@http.status_code:>=${status.slice(2)}`;
|
|
1422
|
+
if (status.startsWith(">")) return `@http.status_code:>${status.slice(1)}`;
|
|
1423
|
+
if (status.startsWith("<=")) return `@http.status_code:<=${status.slice(2)}`;
|
|
1424
|
+
if (status.startsWith("<")) return `@http.status_code:<${status.slice(1)}`;
|
|
1425
|
+
return `@http.status_code:${httpStatus}`;
|
|
1426
|
+
}
|
|
1309
1427
|
function buildTraceQuery(params) {
|
|
1310
1428
|
const parts = [];
|
|
1311
1429
|
if (params.query) {
|
|
@@ -1318,11 +1436,7 @@ function buildTraceQuery(params) {
|
|
|
1318
1436
|
parts.push(`operation_name:${params.operation}`);
|
|
1319
1437
|
}
|
|
1320
1438
|
if (params.resource) {
|
|
1321
|
-
|
|
1322
|
-
parts.push(`resource_name:${params.resource}`);
|
|
1323
|
-
} else {
|
|
1324
|
-
parts.push(`resource_name:${params.resource}`);
|
|
1325
|
-
}
|
|
1439
|
+
parts.push(`resource_name:${params.resource}`);
|
|
1326
1440
|
}
|
|
1327
1441
|
if (params.status) {
|
|
1328
1442
|
parts.push(`status:${params.status}`);
|
|
@@ -1343,21 +1457,7 @@ function buildTraceQuery(params) {
|
|
|
1343
1457
|
}
|
|
1344
1458
|
}
|
|
1345
1459
|
if (params.httpStatus) {
|
|
1346
|
-
|
|
1347
|
-
if (status.endsWith("xx")) {
|
|
1348
|
-
const base = parseInt(status[0] ?? "0", 10) * 100;
|
|
1349
|
-
parts.push(`@http.status_code:[${base} TO ${base + 99}]`);
|
|
1350
|
-
} else if (status.startsWith(">=")) {
|
|
1351
|
-
parts.push(`@http.status_code:>=${status.slice(2)}`);
|
|
1352
|
-
} else if (status.startsWith(">")) {
|
|
1353
|
-
parts.push(`@http.status_code:>${status.slice(1)}`);
|
|
1354
|
-
} else if (status.startsWith("<=")) {
|
|
1355
|
-
parts.push(`@http.status_code:<=${status.slice(2)}`);
|
|
1356
|
-
} else if (status.startsWith("<")) {
|
|
1357
|
-
parts.push(`@http.status_code:<${status.slice(1)}`);
|
|
1358
|
-
} else {
|
|
1359
|
-
parts.push(`@http.status_code:${params.httpStatus}`);
|
|
1360
|
-
}
|
|
1460
|
+
parts.push(buildHttpStatusFilter(params.httpStatus));
|
|
1361
1461
|
}
|
|
1362
1462
|
if (params.errorType) {
|
|
1363
1463
|
const escaped = params.errorType.replace(/"/g, '\\"');
|
|
@@ -1491,10 +1591,12 @@ async function listApmServices(api, params, limits) {
|
|
|
1491
1591
|
to: toTime
|
|
1492
1592
|
},
|
|
1493
1593
|
compute: [{ aggregation: "count", type: "total" }],
|
|
1494
|
-
groupBy: [
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1594
|
+
groupBy: [
|
|
1595
|
+
{
|
|
1596
|
+
facet: "service",
|
|
1597
|
+
limit: limits.maxResults
|
|
1598
|
+
}
|
|
1599
|
+
]
|
|
1498
1600
|
}
|
|
1499
1601
|
}
|
|
1500
1602
|
};
|
|
@@ -1521,45 +1623,77 @@ function registerTracesTool(server, spansApi, _servicesApi, limits, site = "data
|
|
|
1521
1623
|
`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
1624
|
APM METRICS: Traces auto-generate metrics in trace.{service}.* namespace. Use metrics tool to query: avg:trace.{service}.request.duration{*}`,
|
|
1523
1625
|
InputSchema5,
|
|
1524
|
-
async ({
|
|
1626
|
+
async ({
|
|
1627
|
+
action,
|
|
1628
|
+
query,
|
|
1629
|
+
from,
|
|
1630
|
+
to,
|
|
1631
|
+
service,
|
|
1632
|
+
operation,
|
|
1633
|
+
resource,
|
|
1634
|
+
status,
|
|
1635
|
+
env,
|
|
1636
|
+
minDuration,
|
|
1637
|
+
maxDuration,
|
|
1638
|
+
httpStatus,
|
|
1639
|
+
errorType,
|
|
1640
|
+
errorMessage,
|
|
1641
|
+
limit,
|
|
1642
|
+
sort,
|
|
1643
|
+
groupBy
|
|
1644
|
+
}) => {
|
|
1525
1645
|
try {
|
|
1526
1646
|
switch (action) {
|
|
1527
1647
|
case "search": {
|
|
1528
|
-
return toolResult(
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1648
|
+
return toolResult(
|
|
1649
|
+
await searchTraces(
|
|
1650
|
+
spansApi,
|
|
1651
|
+
{
|
|
1652
|
+
query,
|
|
1653
|
+
from,
|
|
1654
|
+
to,
|
|
1655
|
+
service,
|
|
1656
|
+
operation,
|
|
1657
|
+
resource,
|
|
1658
|
+
status,
|
|
1659
|
+
env,
|
|
1660
|
+
minDuration,
|
|
1661
|
+
maxDuration,
|
|
1662
|
+
httpStatus,
|
|
1663
|
+
errorType,
|
|
1664
|
+
errorMessage,
|
|
1665
|
+
limit,
|
|
1666
|
+
sort
|
|
1667
|
+
},
|
|
1668
|
+
limits,
|
|
1669
|
+
site
|
|
1670
|
+
)
|
|
1671
|
+
);
|
|
1545
1672
|
}
|
|
1546
1673
|
case "aggregate": {
|
|
1547
|
-
return toolResult(
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1674
|
+
return toolResult(
|
|
1675
|
+
await aggregateTraces(
|
|
1676
|
+
spansApi,
|
|
1677
|
+
{
|
|
1678
|
+
query,
|
|
1679
|
+
from,
|
|
1680
|
+
to,
|
|
1681
|
+
service,
|
|
1682
|
+
operation,
|
|
1683
|
+
resource,
|
|
1684
|
+
status,
|
|
1685
|
+
env,
|
|
1686
|
+
minDuration,
|
|
1687
|
+
maxDuration,
|
|
1688
|
+
httpStatus,
|
|
1689
|
+
errorType,
|
|
1690
|
+
errorMessage,
|
|
1691
|
+
groupBy
|
|
1692
|
+
},
|
|
1693
|
+
limits,
|
|
1694
|
+
site
|
|
1695
|
+
)
|
|
1696
|
+
);
|
|
1563
1697
|
}
|
|
1564
1698
|
case "services":
|
|
1565
1699
|
return toolResult(await listApmServices(spansApi, { env, from, to }, limits));
|
|
@@ -1575,7 +1709,16 @@ APM METRICS: Traces auto-generate metrics in trace.{service}.* namespace. Use me
|
|
|
1575
1709
|
|
|
1576
1710
|
// src/tools/events.ts
|
|
1577
1711
|
import { z as z7 } from "zod";
|
|
1578
|
-
var ActionSchema6 = z7.enum([
|
|
1712
|
+
var ActionSchema6 = z7.enum([
|
|
1713
|
+
"list",
|
|
1714
|
+
"get",
|
|
1715
|
+
"create",
|
|
1716
|
+
"search",
|
|
1717
|
+
"aggregate",
|
|
1718
|
+
"top",
|
|
1719
|
+
"timeseries",
|
|
1720
|
+
"incidents"
|
|
1721
|
+
]);
|
|
1579
1722
|
var InputSchema6 = {
|
|
1580
1723
|
action: ActionSchema6.describe("Action to perform"),
|
|
1581
1724
|
id: z7.string().optional().describe("Event ID (for get action)"),
|
|
@@ -1619,14 +1762,14 @@ function extractTitleFromMessage(message) {
|
|
|
1619
1762
|
if (!message) return "";
|
|
1620
1763
|
const content = message.replace(/^%%%\s*\n?/, "").trim();
|
|
1621
1764
|
const firstLine = content.split("\n")[0]?.trim() ?? "";
|
|
1622
|
-
return firstLine.replace(/\s
|
|
1765
|
+
return firstLine.replace(/\s*!?\s*$/, "").trim();
|
|
1623
1766
|
}
|
|
1624
1767
|
function extractMonitorIdFromMessage(message) {
|
|
1625
1768
|
if (!message) return void 0;
|
|
1626
1769
|
const match = message.match(/\/monitors\/(\d+)/);
|
|
1627
|
-
if (match
|
|
1628
|
-
const id = parseInt(match[1], 10);
|
|
1629
|
-
return isNaN(id) ? void 0 : id;
|
|
1770
|
+
if (match?.[1]) {
|
|
1771
|
+
const id = Number.parseInt(match[1], 10);
|
|
1772
|
+
return Number.isNaN(id) ? void 0 : id;
|
|
1630
1773
|
}
|
|
1631
1774
|
return void 0;
|
|
1632
1775
|
}
|
|
@@ -1745,8 +1888,8 @@ async function listEventsV1(api, params, limits) {
|
|
|
1745
1888
|
};
|
|
1746
1889
|
}
|
|
1747
1890
|
async function getEventV1(api, id) {
|
|
1748
|
-
const eventId = parseInt(id, 10);
|
|
1749
|
-
if (isNaN(eventId)) {
|
|
1891
|
+
const eventId = Number.parseInt(id, 10);
|
|
1892
|
+
if (Number.isNaN(eventId)) {
|
|
1750
1893
|
throw new Error(`Invalid event ID: ${id}`);
|
|
1751
1894
|
}
|
|
1752
1895
|
const response = await api.getEvent({ eventId });
|
|
@@ -1910,13 +2053,18 @@ async function aggregateEventsV2(api, params, limits, site) {
|
|
|
1910
2053
|
async function topEventsV2(api, params, limits, site) {
|
|
1911
2054
|
const effectiveQuery = params.query ?? "source:alert";
|
|
1912
2055
|
const effectiveTags = params.tags ?? ["source:alert"];
|
|
1913
|
-
const result = await aggregateEventsV2(
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
2056
|
+
const result = await aggregateEventsV2(
|
|
2057
|
+
api,
|
|
2058
|
+
{
|
|
2059
|
+
...params,
|
|
2060
|
+
query: effectiveQuery,
|
|
2061
|
+
tags: effectiveTags,
|
|
2062
|
+
groupBy: params.groupBy ?? ["monitor_name"],
|
|
2063
|
+
limit: params.limit ?? 10
|
|
2064
|
+
},
|
|
2065
|
+
limits,
|
|
2066
|
+
site
|
|
2067
|
+
);
|
|
1920
2068
|
return {
|
|
1921
2069
|
top: result.buckets.map((bucket, index) => ({
|
|
1922
2070
|
rank: index + 1,
|
|
@@ -1949,7 +2097,7 @@ async function timeseriesEventsV2(api, params, limits, site) {
|
|
|
1949
2097
|
const fullQuery = buildEventQuery({
|
|
1950
2098
|
query: params.query ?? "source:alert",
|
|
1951
2099
|
sources: params.sources,
|
|
1952
|
-
tags: params.tags
|
|
2100
|
+
tags: params.tags
|
|
1953
2101
|
});
|
|
1954
2102
|
const intervalMs = parseIntervalToMs(params.interval);
|
|
1955
2103
|
const groupByFields = params.groupBy ?? ["monitor_name"];
|
|
@@ -2034,7 +2182,7 @@ async function incidentsEventsV2(api, params, limits, site) {
|
|
|
2034
2182
|
const fullQuery = buildEventQuery({
|
|
2035
2183
|
query: params.query ?? "source:alert",
|
|
2036
2184
|
sources: params.sources,
|
|
2037
|
-
tags: params.tags
|
|
2185
|
+
tags: params.tags
|
|
2038
2186
|
});
|
|
2039
2187
|
const dedupeWindowNs = parseDurationToNs(params.dedupeWindow ?? "5m");
|
|
2040
2188
|
const dedupeWindowMs = dedupeWindowNs ? Math.floor(dedupeWindowNs / 1e6) : 3e5;
|
|
@@ -2149,7 +2297,9 @@ async function incidentsEventsV2(api, params, limits, site) {
|
|
|
2149
2297
|
sample: inc.sample
|
|
2150
2298
|
};
|
|
2151
2299
|
});
|
|
2152
|
-
incidentList.sort(
|
|
2300
|
+
incidentList.sort(
|
|
2301
|
+
(a, b) => new Date(b.firstTrigger).getTime() - new Date(a.firstTrigger).getTime()
|
|
2302
|
+
);
|
|
2153
2303
|
const effectiveLimit = Math.min(params.limit ?? 100, 500);
|
|
2154
2304
|
return {
|
|
2155
2305
|
incidents: incidentList.slice(0, effectiveLimit),
|
|
@@ -2227,20 +2377,44 @@ Use action:"timeseries" with interval:"1h" to see alert trends over time.
|
|
|
2227
2377
|
Use action:"incidents" with dedupeWindow:"5m" to deduplicate alerts into incidents.
|
|
2228
2378
|
Use enrich:true with search to get monitor metadata (slower).`,
|
|
2229
2379
|
InputSchema6,
|
|
2230
|
-
async ({
|
|
2380
|
+
async ({
|
|
2381
|
+
action,
|
|
2382
|
+
id,
|
|
2383
|
+
query,
|
|
2384
|
+
from,
|
|
2385
|
+
to,
|
|
2386
|
+
priority,
|
|
2387
|
+
sources,
|
|
2388
|
+
tags,
|
|
2389
|
+
limit,
|
|
2390
|
+
title,
|
|
2391
|
+
text,
|
|
2392
|
+
alertType,
|
|
2393
|
+
groupBy,
|
|
2394
|
+
cursor,
|
|
2395
|
+
interval,
|
|
2396
|
+
dedupeWindow,
|
|
2397
|
+
enrich
|
|
2398
|
+
}) => {
|
|
2231
2399
|
try {
|
|
2232
2400
|
checkReadOnly(action, readOnly);
|
|
2233
2401
|
switch (action) {
|
|
2234
2402
|
case "list":
|
|
2235
|
-
return toolResult(
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2403
|
+
return toolResult(
|
|
2404
|
+
await listEventsV1(
|
|
2405
|
+
apiV1,
|
|
2406
|
+
{
|
|
2407
|
+
query,
|
|
2408
|
+
from,
|
|
2409
|
+
to,
|
|
2410
|
+
priority,
|
|
2411
|
+
sources,
|
|
2412
|
+
tags,
|
|
2413
|
+
limit
|
|
2414
|
+
},
|
|
2415
|
+
limits
|
|
2416
|
+
)
|
|
2417
|
+
);
|
|
2244
2418
|
case "get": {
|
|
2245
2419
|
const eventId = requireParam(id, "id", "get");
|
|
2246
2420
|
return toolResult(await getEventV1(apiV1, eventId));
|
|
@@ -2248,25 +2422,32 @@ Use enrich:true with search to get monitor metadata (slower).`,
|
|
|
2248
2422
|
case "create": {
|
|
2249
2423
|
const eventTitle = requireParam(title, "title", "create");
|
|
2250
2424
|
const eventText = requireParam(text, "text", "create");
|
|
2251
|
-
return toolResult(
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2425
|
+
return toolResult(
|
|
2426
|
+
await createEventV1(apiV1, {
|
|
2427
|
+
title: eventTitle,
|
|
2428
|
+
text: eventText,
|
|
2429
|
+
priority,
|
|
2430
|
+
tags,
|
|
2431
|
+
alertType
|
|
2432
|
+
})
|
|
2433
|
+
);
|
|
2258
2434
|
}
|
|
2259
2435
|
case "search": {
|
|
2260
|
-
const result = await searchEventsV2(
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2436
|
+
const result = await searchEventsV2(
|
|
2437
|
+
apiV2,
|
|
2438
|
+
{
|
|
2439
|
+
query,
|
|
2440
|
+
from,
|
|
2441
|
+
to,
|
|
2442
|
+
sources,
|
|
2443
|
+
tags,
|
|
2444
|
+
priority,
|
|
2445
|
+
limit,
|
|
2446
|
+
cursor
|
|
2447
|
+
},
|
|
2448
|
+
limits,
|
|
2449
|
+
site
|
|
2450
|
+
);
|
|
2270
2451
|
if (enrich && result.events.length > 0) {
|
|
2271
2452
|
const enrichedEvents = await enrichWithMonitorMetadata(result.events, monitorsApi);
|
|
2272
2453
|
return toolResult({ ...result, events: enrichedEvents });
|
|
@@ -2274,46 +2455,74 @@ Use enrich:true with search to get monitor metadata (slower).`,
|
|
|
2274
2455
|
return toolResult(result);
|
|
2275
2456
|
}
|
|
2276
2457
|
case "aggregate":
|
|
2277
|
-
return toolResult(
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2458
|
+
return toolResult(
|
|
2459
|
+
await aggregateEventsV2(
|
|
2460
|
+
apiV2,
|
|
2461
|
+
{
|
|
2462
|
+
query,
|
|
2463
|
+
from,
|
|
2464
|
+
to,
|
|
2465
|
+
sources,
|
|
2466
|
+
tags,
|
|
2467
|
+
groupBy,
|
|
2468
|
+
limit
|
|
2469
|
+
},
|
|
2470
|
+
limits,
|
|
2471
|
+
site
|
|
2472
|
+
)
|
|
2473
|
+
);
|
|
2286
2474
|
case "top":
|
|
2287
|
-
return toolResult(
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2475
|
+
return toolResult(
|
|
2476
|
+
await topEventsV2(
|
|
2477
|
+
apiV2,
|
|
2478
|
+
{
|
|
2479
|
+
query,
|
|
2480
|
+
from,
|
|
2481
|
+
to,
|
|
2482
|
+
sources,
|
|
2483
|
+
tags,
|
|
2484
|
+
groupBy,
|
|
2485
|
+
limit
|
|
2486
|
+
},
|
|
2487
|
+
limits,
|
|
2488
|
+
site
|
|
2489
|
+
)
|
|
2490
|
+
);
|
|
2296
2491
|
case "timeseries":
|
|
2297
|
-
return toolResult(
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2492
|
+
return toolResult(
|
|
2493
|
+
await timeseriesEventsV2(
|
|
2494
|
+
apiV2,
|
|
2495
|
+
{
|
|
2496
|
+
query,
|
|
2497
|
+
from,
|
|
2498
|
+
to,
|
|
2499
|
+
sources,
|
|
2500
|
+
tags,
|
|
2501
|
+
groupBy,
|
|
2502
|
+
interval,
|
|
2503
|
+
limit
|
|
2504
|
+
},
|
|
2505
|
+
limits,
|
|
2506
|
+
site
|
|
2507
|
+
)
|
|
2508
|
+
);
|
|
2307
2509
|
case "incidents":
|
|
2308
|
-
return toolResult(
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2510
|
+
return toolResult(
|
|
2511
|
+
await incidentsEventsV2(
|
|
2512
|
+
apiV2,
|
|
2513
|
+
{
|
|
2514
|
+
query,
|
|
2515
|
+
from,
|
|
2516
|
+
to,
|
|
2517
|
+
sources,
|
|
2518
|
+
tags,
|
|
2519
|
+
dedupeWindow,
|
|
2520
|
+
limit
|
|
2521
|
+
},
|
|
2522
|
+
limits,
|
|
2523
|
+
site
|
|
2524
|
+
)
|
|
2525
|
+
);
|
|
2317
2526
|
default:
|
|
2318
2527
|
throw new Error(`Unknown action: ${action}`);
|
|
2319
2528
|
}
|
|
@@ -2333,7 +2542,9 @@ var InputSchema7 = {
|
|
|
2333
2542
|
query: z8.string().optional().describe("Search query (for search action)"),
|
|
2334
2543
|
status: z8.enum(["active", "stable", "resolved"]).optional().describe("Filter by status (for list)"),
|
|
2335
2544
|
limit: z8.number().optional().describe("Maximum number of incidents to return"),
|
|
2336
|
-
config: z8.record(z8.unknown()).optional().describe(
|
|
2545
|
+
config: z8.record(z8.unknown()).optional().describe(
|
|
2546
|
+
"Incident configuration (for create/update). Create requires: title. Update can modify: title, status, severity, fields."
|
|
2547
|
+
)
|
|
2337
2548
|
};
|
|
2338
2549
|
function formatIncident(i) {
|
|
2339
2550
|
const attrs = i.attributes;
|
|
@@ -2385,11 +2596,13 @@ async function searchIncidents(api, query, limits) {
|
|
|
2385
2596
|
query,
|
|
2386
2597
|
pageSize: limits.maxResults
|
|
2387
2598
|
});
|
|
2388
|
-
const incidents = (response.data?.attributes?.incidents ?? []).map(
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2599
|
+
const incidents = (response.data?.attributes?.incidents ?? []).map(
|
|
2600
|
+
(i) => ({
|
|
2601
|
+
id: i.data?.id ?? "",
|
|
2602
|
+
title: i.data?.attributes?.title ?? "",
|
|
2603
|
+
state: i.data?.attributes?.state ?? "unknown"
|
|
2604
|
+
})
|
|
2605
|
+
);
|
|
2393
2606
|
return {
|
|
2394
2607
|
incidents,
|
|
2395
2608
|
total: response.data?.attributes?.total ?? incidents.length
|
|
@@ -2429,7 +2642,7 @@ async function deleteIncident(api, id) {
|
|
|
2429
2642
|
message: `Incident ${id} deleted`
|
|
2430
2643
|
};
|
|
2431
2644
|
}
|
|
2432
|
-
function registerIncidentsTool(server, api, limits, readOnly = false) {
|
|
2645
|
+
function registerIncidentsTool(server, api, limits, readOnly = false, _site = "datadoghq.com") {
|
|
2433
2646
|
server.tool(
|
|
2434
2647
|
"incidents",
|
|
2435
2648
|
"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 +2819,7 @@ async function getSloHistory(api, id, params) {
|
|
|
2606
2819
|
}
|
|
2607
2820
|
};
|
|
2608
2821
|
}
|
|
2609
|
-
function registerSlosTool(server, api, limits, readOnly = false) {
|
|
2822
|
+
function registerSlosTool(server, api, limits, readOnly = false, _site = "datadoghq.com") {
|
|
2610
2823
|
server.tool(
|
|
2611
2824
|
"slos",
|
|
2612
2825
|
"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 +2872,9 @@ var InputSchema9 = {
|
|
|
2659
2872
|
locations: z10.array(z10.string()).optional().describe("Filter by locations (for list)"),
|
|
2660
2873
|
tags: z10.array(z10.string()).optional().describe("Filter by tags (for list)"),
|
|
2661
2874
|
limit: z10.number().optional().describe("Maximum number of tests to return"),
|
|
2662
|
-
config: z10.record(z10.unknown()).optional().describe(
|
|
2875
|
+
config: z10.record(z10.unknown()).optional().describe(
|
|
2876
|
+
"Test configuration (for create/update). Includes: name, type, config, options, locations, message."
|
|
2877
|
+
)
|
|
2663
2878
|
};
|
|
2664
2879
|
function formatTest(t) {
|
|
2665
2880
|
return {
|
|
@@ -2750,7 +2965,7 @@ async function createTest(api, config, testType) {
|
|
|
2750
2965
|
}
|
|
2751
2966
|
async function updateTest(api, id, config) {
|
|
2752
2967
|
const normalizedConfig = normalizeConfigKeys2(config);
|
|
2753
|
-
let testType
|
|
2968
|
+
let testType;
|
|
2754
2969
|
try {
|
|
2755
2970
|
await api.getAPITest({ publicId: id });
|
|
2756
2971
|
testType = "api";
|
|
@@ -2820,7 +3035,7 @@ async function getTestResults(api, id) {
|
|
|
2820
3035
|
return { results, testType: "browser" };
|
|
2821
3036
|
}
|
|
2822
3037
|
}
|
|
2823
|
-
function registerSyntheticsTool(server, api, limits, readOnly = false) {
|
|
3038
|
+
function registerSyntheticsTool(server, api, limits, readOnly = false, _site = "datadoghq.com") {
|
|
2824
3039
|
server.tool(
|
|
2825
3040
|
"synthetics",
|
|
2826
3041
|
"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 +3148,10 @@ async function muteHost(api, hostName, params) {
|
|
|
2933
3148
|
override: params.override
|
|
2934
3149
|
}
|
|
2935
3150
|
});
|
|
3151
|
+
const muteEndMessage = params.end ? ` until ${new Date(params.end * 1e3).toISOString()}` : " indefinitely";
|
|
2936
3152
|
return {
|
|
2937
3153
|
success: true,
|
|
2938
|
-
message: `Host ${hostName} muted${
|
|
3154
|
+
message: `Host ${hostName} muted${muteEndMessage}`
|
|
2939
3155
|
};
|
|
2940
3156
|
}
|
|
2941
3157
|
async function unmuteHost(api, hostName) {
|
|
@@ -2950,12 +3166,25 @@ function registerHostsTool(server, api, limits, readOnly = false) {
|
|
|
2950
3166
|
"hosts",
|
|
2951
3167
|
"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
3168
|
InputSchema10,
|
|
2953
|
-
async ({
|
|
3169
|
+
async ({
|
|
3170
|
+
action,
|
|
3171
|
+
filter,
|
|
3172
|
+
from,
|
|
3173
|
+
count,
|
|
3174
|
+
sortField,
|
|
3175
|
+
sortDir,
|
|
3176
|
+
hostName,
|
|
3177
|
+
message,
|
|
3178
|
+
end,
|
|
3179
|
+
override
|
|
3180
|
+
}) => {
|
|
2954
3181
|
try {
|
|
2955
3182
|
checkReadOnly(action, readOnly);
|
|
2956
3183
|
switch (action) {
|
|
2957
3184
|
case "list":
|
|
2958
|
-
return toolResult(
|
|
3185
|
+
return toolResult(
|
|
3186
|
+
await listHosts(api, { filter, from, count, sortField, sortDir }, limits)
|
|
3187
|
+
);
|
|
2959
3188
|
case "totals":
|
|
2960
3189
|
return toolResult(await getHostTotals(api));
|
|
2961
3190
|
case "mute": {
|
|
@@ -3141,7 +3370,9 @@ var InputSchema12 = {
|
|
|
3141
3370
|
interval: z13.string().optional()
|
|
3142
3371
|
}).optional().describe("Compute configuration for aggregation"),
|
|
3143
3372
|
// Performance action parameters
|
|
3144
|
-
metrics: z13.array(z13.enum(["lcp", "fcp", "cls", "fid", "inp", "loading_time"])).optional().describe(
|
|
3373
|
+
metrics: z13.array(z13.enum(["lcp", "fcp", "cls", "fid", "inp", "loading_time"])).optional().describe(
|
|
3374
|
+
"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"
|
|
3375
|
+
),
|
|
3145
3376
|
// Waterfall action parameters
|
|
3146
3377
|
applicationId: z13.string().optional().describe("Application ID for waterfall action"),
|
|
3147
3378
|
sessionId: z13.string().optional().describe("Session ID for waterfall action"),
|
|
@@ -3443,10 +3674,7 @@ function formatWaterfallEvent(event) {
|
|
|
3443
3674
|
};
|
|
3444
3675
|
}
|
|
3445
3676
|
async function getSessionWaterfall(api, params, limits, site) {
|
|
3446
|
-
const queryParts = [
|
|
3447
|
-
`@application.id:${params.applicationId}`,
|
|
3448
|
-
`@session.id:${params.sessionId}`
|
|
3449
|
-
];
|
|
3677
|
+
const queryParts = [`@application.id:${params.applicationId}`, `@session.id:${params.sessionId}`];
|
|
3450
3678
|
if (params.viewId) {
|
|
3451
3679
|
queryParts.push(`@view.id:${params.viewId}`);
|
|
3452
3680
|
}
|
|
@@ -3480,22 +3708,44 @@ function registerRumTool(server, api, limits, site = "datadoghq.com") {
|
|
|
3480
3708
|
"rum",
|
|
3481
3709
|
"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
3710
|
InputSchema12,
|
|
3483
|
-
async ({
|
|
3711
|
+
async ({
|
|
3712
|
+
action,
|
|
3713
|
+
query,
|
|
3714
|
+
from,
|
|
3715
|
+
to,
|
|
3716
|
+
type,
|
|
3717
|
+
sort,
|
|
3718
|
+
limit,
|
|
3719
|
+
groupBy,
|
|
3720
|
+
compute,
|
|
3721
|
+
metrics,
|
|
3722
|
+
applicationId,
|
|
3723
|
+
sessionId,
|
|
3724
|
+
viewId
|
|
3725
|
+
}) => {
|
|
3484
3726
|
try {
|
|
3485
3727
|
switch (action) {
|
|
3486
3728
|
case "applications":
|
|
3487
3729
|
return toolResult(await listApplications(api));
|
|
3488
3730
|
case "events":
|
|
3489
|
-
return toolResult(
|
|
3731
|
+
return toolResult(
|
|
3732
|
+
await searchEvents(api, { query, from, to, type, sort, limit }, limits, site)
|
|
3733
|
+
);
|
|
3490
3734
|
case "aggregate":
|
|
3491
|
-
return toolResult(
|
|
3735
|
+
return toolResult(
|
|
3736
|
+
await aggregateEvents(api, { query, from, to, groupBy, compute }, limits, site)
|
|
3737
|
+
);
|
|
3492
3738
|
case "performance":
|
|
3493
|
-
return toolResult(
|
|
3739
|
+
return toolResult(
|
|
3740
|
+
await getPerformanceMetrics(api, { query, from, to, groupBy, metrics }, limits, site)
|
|
3741
|
+
);
|
|
3494
3742
|
case "waterfall":
|
|
3495
3743
|
if (!applicationId || !sessionId) {
|
|
3496
3744
|
throw new Error("waterfall action requires applicationId and sessionId parameters");
|
|
3497
3745
|
}
|
|
3498
|
-
return toolResult(
|
|
3746
|
+
return toolResult(
|
|
3747
|
+
await getSessionWaterfall(api, { applicationId, sessionId, viewId }, limits, site)
|
|
3748
|
+
);
|
|
3499
3749
|
default:
|
|
3500
3750
|
throw new Error(`Unknown action: ${action}`);
|
|
3501
3751
|
}
|
|
@@ -3653,7 +3903,13 @@ function registerSecurityTool(server, api, limits) {
|
|
|
3653
3903
|
}
|
|
3654
3904
|
return toolResult(await listRules(api, { pageSize, pageCursor }, limits));
|
|
3655
3905
|
case "signals":
|
|
3656
|
-
return toolResult(
|
|
3906
|
+
return toolResult(
|
|
3907
|
+
await searchSignals(
|
|
3908
|
+
api,
|
|
3909
|
+
{ query, from, to, severity, status, pageSize, pageCursor },
|
|
3910
|
+
limits
|
|
3911
|
+
)
|
|
3912
|
+
);
|
|
3657
3913
|
case "findings":
|
|
3658
3914
|
return toolResult(await listFindings(api, { query, pageSize, pageCursor }, limits));
|
|
3659
3915
|
default:
|
|
@@ -3677,10 +3933,19 @@ var InputSchema14 = {
|
|
|
3677
3933
|
excludeAuthorHandle: z15.string().optional().describe("Exclude notebooks by author handle"),
|
|
3678
3934
|
includeCells: z15.boolean().optional().describe("Include cell content in response (default: true for get)"),
|
|
3679
3935
|
name: z15.string().optional().describe("Notebook name (for create/update)"),
|
|
3680
|
-
cells: z15.array(
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
|
|
3936
|
+
cells: z15.array(
|
|
3937
|
+
z15.object({
|
|
3938
|
+
type: z15.enum([
|
|
3939
|
+
"markdown",
|
|
3940
|
+
"timeseries",
|
|
3941
|
+
"toplist",
|
|
3942
|
+
"heatmap",
|
|
3943
|
+
"distribution",
|
|
3944
|
+
"log_stream"
|
|
3945
|
+
]),
|
|
3946
|
+
content: z15.unknown()
|
|
3947
|
+
})
|
|
3948
|
+
).optional().describe("Notebook cells (for create/update)"),
|
|
3684
3949
|
time: z15.object({
|
|
3685
3950
|
liveSpan: z15.string().optional(),
|
|
3686
3951
|
start: z15.number().optional(),
|
|
@@ -3845,7 +4110,9 @@ async function updateNotebook(api, notebookId, params) {
|
|
|
3845
4110
|
};
|
|
3846
4111
|
});
|
|
3847
4112
|
}
|
|
3848
|
-
const timeConfig = params.time?.liveSpan ? {
|
|
4113
|
+
const timeConfig = params.time?.liveSpan ? {
|
|
4114
|
+
liveSpan: params.time.liveSpan
|
|
4115
|
+
} : void 0;
|
|
3849
4116
|
const response = await api.updateNotebook({
|
|
3850
4117
|
notebookId,
|
|
3851
4118
|
body: {
|
|
@@ -3880,38 +4147,61 @@ async function deleteNotebook(api, notebookId) {
|
|
|
3880
4147
|
message: `Notebook ${notebookId} deleted successfully`
|
|
3881
4148
|
};
|
|
3882
4149
|
}
|
|
3883
|
-
function registerNotebooksTool(server, api, limits, readOnly = false) {
|
|
4150
|
+
function registerNotebooksTool(server, api, limits, readOnly = false, _site = "datadoghq.com") {
|
|
3884
4151
|
server.tool(
|
|
3885
4152
|
"notebooks",
|
|
3886
4153
|
"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
4154
|
InputSchema14,
|
|
3888
|
-
async ({
|
|
4155
|
+
async ({
|
|
4156
|
+
action,
|
|
4157
|
+
id,
|
|
4158
|
+
query,
|
|
4159
|
+
authorHandle,
|
|
4160
|
+
excludeAuthorHandle,
|
|
4161
|
+
includeCells,
|
|
4162
|
+
name,
|
|
4163
|
+
cells,
|
|
4164
|
+
time,
|
|
4165
|
+
status,
|
|
4166
|
+
pageSize,
|
|
4167
|
+
pageNumber
|
|
4168
|
+
}) => {
|
|
3889
4169
|
try {
|
|
3890
4170
|
checkReadOnly(action, readOnly);
|
|
3891
4171
|
switch (action) {
|
|
3892
4172
|
case "list":
|
|
3893
|
-
return toolResult(
|
|
4173
|
+
return toolResult(
|
|
4174
|
+
await listNotebooks(
|
|
4175
|
+
api,
|
|
4176
|
+
{ query, authorHandle, excludeAuthorHandle, includeCells, pageSize, pageNumber },
|
|
4177
|
+
limits
|
|
4178
|
+
)
|
|
4179
|
+
);
|
|
3894
4180
|
case "get": {
|
|
3895
4181
|
const notebookId = requireParam(id, "id", "get");
|
|
3896
4182
|
return toolResult(await getNotebook(api, notebookId));
|
|
3897
4183
|
}
|
|
3898
4184
|
case "create": {
|
|
3899
4185
|
const notebookName = requireParam(name, "name", "create");
|
|
3900
|
-
return toolResult(
|
|
3901
|
-
|
|
3902
|
-
|
|
3903
|
-
|
|
3904
|
-
|
|
3905
|
-
|
|
4186
|
+
return toolResult(
|
|
4187
|
+
await createNotebook(api, {
|
|
4188
|
+
name: notebookName,
|
|
4189
|
+
cells,
|
|
4190
|
+
time,
|
|
4191
|
+
status
|
|
4192
|
+
})
|
|
4193
|
+
);
|
|
3906
4194
|
}
|
|
3907
4195
|
case "update": {
|
|
3908
4196
|
const notebookId = requireParam(id, "id", "update");
|
|
3909
|
-
return toolResult(
|
|
3910
|
-
|
|
3911
|
-
|
|
3912
|
-
|
|
3913
|
-
|
|
3914
|
-
|
|
4197
|
+
return toolResult(
|
|
4198
|
+
await updateNotebook(api, notebookId, {
|
|
4199
|
+
name,
|
|
4200
|
+
cells,
|
|
4201
|
+
time,
|
|
4202
|
+
status
|
|
4203
|
+
})
|
|
4204
|
+
);
|
|
3915
4205
|
}
|
|
3916
4206
|
case "delete": {
|
|
3917
4207
|
const notebookId = requireParam(id, "id", "delete");
|
|
@@ -3993,7 +4283,9 @@ function registerUsersTool(server, api, limits) {
|
|
|
3993
4283
|
try {
|
|
3994
4284
|
switch (action) {
|
|
3995
4285
|
case "list":
|
|
3996
|
-
return toolResult(
|
|
4286
|
+
return toolResult(
|
|
4287
|
+
await listUsers(api, { filter, status, pageSize, pageNumber }, limits)
|
|
4288
|
+
);
|
|
3997
4289
|
case "get": {
|
|
3998
4290
|
const userId = requireParam(id, "id", "get");
|
|
3999
4291
|
return toolResult(await getUser(api, userId));
|
|
@@ -4224,9 +4516,18 @@ function registerTagsTool(server, api, _limits, readOnly = false) {
|
|
|
4224
4516
|
|
|
4225
4517
|
// src/tools/usage.ts
|
|
4226
4518
|
import { z as z19 } from "zod";
|
|
4227
|
-
var ActionSchema18 = z19.enum([
|
|
4519
|
+
var ActionSchema18 = z19.enum([
|
|
4520
|
+
"summary",
|
|
4521
|
+
"hosts",
|
|
4522
|
+
"logs",
|
|
4523
|
+
"custom_metrics",
|
|
4524
|
+
"indexed_spans",
|
|
4525
|
+
"ingested_spans"
|
|
4526
|
+
]);
|
|
4228
4527
|
var InputSchema18 = {
|
|
4229
|
-
action: ActionSchema18.describe(
|
|
4528
|
+
action: ActionSchema18.describe(
|
|
4529
|
+
"Action to perform: summary (overall usage), hosts, logs, custom_metrics, indexed_spans, ingested_spans"
|
|
4530
|
+
),
|
|
4230
4531
|
from: z19.string().optional().describe('Start time (ISO 8601 date like "2024-01-01", or relative like "30d")'),
|
|
4231
4532
|
to: z19.string().optional().describe('End time (ISO 8601 date like "2024-01-31", or relative like "now")'),
|
|
4232
4533
|
includeOrgDetails: z19.boolean().optional().describe("Include usage breakdown by organization (for multi-org accounts)")
|
|
@@ -4238,7 +4539,7 @@ function parseDate(dateStr, defaultDate) {
|
|
|
4238
4539
|
return new Date(seconds * 1e3);
|
|
4239
4540
|
}
|
|
4240
4541
|
const parsed = new Date(dateStr);
|
|
4241
|
-
if (!isNaN(parsed.getTime())) {
|
|
4542
|
+
if (!Number.isNaN(parsed.getTime())) {
|
|
4242
4543
|
return parsed;
|
|
4243
4544
|
}
|
|
4244
4545
|
return defaultDate;
|
|
@@ -4395,7 +4696,9 @@ function registerUsageTool(server, api, _limits) {
|
|
|
4395
4696
|
import { z as z20 } from "zod";
|
|
4396
4697
|
var ActionSchema19 = z20.enum(["validate"]);
|
|
4397
4698
|
var InputSchema19 = {
|
|
4398
|
-
action: ActionSchema19.describe(
|
|
4699
|
+
action: ActionSchema19.describe(
|
|
4700
|
+
"Action to perform: validate - test if API key and App key are valid"
|
|
4701
|
+
)
|
|
4399
4702
|
};
|
|
4400
4703
|
function registerAuthTool(server, clients) {
|
|
4401
4704
|
server.tool(
|
|
@@ -4455,14 +4758,26 @@ function registerAllTools(server, clients, limits, features, site = "datadoghq.c
|
|
|
4455
4758
|
const { readOnly, disabledTools } = features;
|
|
4456
4759
|
const enabled = (tool) => !disabledTools.includes(tool);
|
|
4457
4760
|
if (enabled("monitors")) registerMonitorsTool(server, clients.monitors, limits, readOnly, site);
|
|
4458
|
-
if (enabled("dashboards"))
|
|
4761
|
+
if (enabled("dashboards"))
|
|
4762
|
+
registerDashboardsTool(server, clients.dashboards, limits, readOnly, site);
|
|
4459
4763
|
if (enabled("logs")) registerLogsTool(server, clients.logs, limits, site);
|
|
4460
|
-
if (enabled("metrics"))
|
|
4764
|
+
if (enabled("metrics"))
|
|
4765
|
+
registerMetricsTool(server, clients.metricsV1, clients.metricsV2, limits, site);
|
|
4461
4766
|
if (enabled("traces")) registerTracesTool(server, clients.spans, clients.services, limits, site);
|
|
4462
|
-
if (enabled("events"))
|
|
4767
|
+
if (enabled("events"))
|
|
4768
|
+
registerEventsTool(
|
|
4769
|
+
server,
|
|
4770
|
+
clients.eventsV1,
|
|
4771
|
+
clients.eventsV2,
|
|
4772
|
+
clients.monitors,
|
|
4773
|
+
limits,
|
|
4774
|
+
readOnly,
|
|
4775
|
+
site
|
|
4776
|
+
);
|
|
4463
4777
|
if (enabled("incidents")) registerIncidentsTool(server, clients.incidents, limits, readOnly, site);
|
|
4464
4778
|
if (enabled("slos")) registerSlosTool(server, clients.slo, limits, readOnly, site);
|
|
4465
|
-
if (enabled("synthetics"))
|
|
4779
|
+
if (enabled("synthetics"))
|
|
4780
|
+
registerSyntheticsTool(server, clients.synthetics, limits, readOnly, site);
|
|
4466
4781
|
if (enabled("hosts")) registerHostsTool(server, clients.hosts, limits, readOnly);
|
|
4467
4782
|
if (enabled("downtimes")) registerDowntimesTool(server, clients.downtimes, limits, readOnly);
|
|
4468
4783
|
if (enabled("rum")) registerRumTool(server, clients.rum, limits, site);
|
|
@@ -4500,8 +4815,9 @@ import { randomUUID } from "crypto";
|
|
|
4500
4815
|
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
4501
4816
|
import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
|
|
4502
4817
|
var transports = {};
|
|
4503
|
-
|
|
4818
|
+
function createExpressApp(server, config) {
|
|
4504
4819
|
const app = express();
|
|
4820
|
+
app.disable("x-powered-by");
|
|
4505
4821
|
app.use(express.json());
|
|
4506
4822
|
app.get("/health", (_req, res) => {
|
|
4507
4823
|
res.json({ status: "ok", name: config.name, version: config.version });
|
|
@@ -4554,6 +4870,10 @@ async function connectHttp(server, config) {
|
|
|
4554
4870
|
res.status(400).json({ error: "Invalid session" });
|
|
4555
4871
|
}
|
|
4556
4872
|
});
|
|
4873
|
+
return app;
|
|
4874
|
+
}
|
|
4875
|
+
async function connectHttp(server, config) {
|
|
4876
|
+
const app = createExpressApp(server, config);
|
|
4557
4877
|
app.listen(config.port, config.host, () => {
|
|
4558
4878
|
console.error(`[MCP] Datadog MCP server running on http://${config.host}:${config.port}/mcp`);
|
|
4559
4879
|
console.error(`[MCP] Health check available at http://${config.host}:${config.port}/health`);
|
|
@@ -4561,19 +4881,16 @@ async function connectHttp(server, config) {
|
|
|
4561
4881
|
}
|
|
4562
4882
|
|
|
4563
4883
|
// src/index.ts
|
|
4564
|
-
|
|
4565
|
-
|
|
4566
|
-
|
|
4567
|
-
|
|
4568
|
-
|
|
4569
|
-
|
|
4570
|
-
|
|
4571
|
-
await connectStdio(server);
|
|
4572
|
-
}
|
|
4573
|
-
} catch (error) {
|
|
4574
|
-
console.error("[MCP] Failed to start server:", error);
|
|
4575
|
-
process.exit(1);
|
|
4884
|
+
try {
|
|
4885
|
+
const config = loadConfig();
|
|
4886
|
+
const server = createServer(config);
|
|
4887
|
+
if (config.server.transport === "http") {
|
|
4888
|
+
await connectHttp(server, config.server);
|
|
4889
|
+
} else {
|
|
4890
|
+
await connectStdio(server);
|
|
4576
4891
|
}
|
|
4892
|
+
} catch (error) {
|
|
4893
|
+
console.error("[MCP] Failed to start server:", error);
|
|
4894
|
+
process.exit(1);
|
|
4577
4895
|
}
|
|
4578
|
-
main();
|
|
4579
4896
|
//# sourceMappingURL=index.js.map
|