@dynatrace-oss/dynatrace-mcp-server 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -25,6 +25,8 @@ bringing real-time observability data directly into your development workflow.
|
|
|
25
25
|
|
|
26
26
|
If you need help, please contact us via [GitHub Issues](https://github.com/dynatrace-oss/dynatrace-mcp/issues) if you have feature requests, questions, or need help.
|
|
27
27
|
|
|
28
|
+
https://github.com/user-attachments/assets/25c05db1-8e09-4a7f-add2-ed486ffd4b5a
|
|
29
|
+
|
|
28
30
|
## Quickstart
|
|
29
31
|
|
|
30
32
|
You can add this MCP server to your MCP Client like VSCode, Claude, Cursor, Amazon Q, Windsurf, ChatGPT, or Github Copilot via the npmjs package `@dynatrace-oss/dynatrace-mcp-server`, and type `stdio`.
|
|
@@ -34,7 +34,7 @@ const requestToken = async (clientId, clientSecret, ssoAuthUrl, scopes) => {
|
|
|
34
34
|
return await res.json();
|
|
35
35
|
};
|
|
36
36
|
/**
|
|
37
|
-
* Create a Dynatrace Http Client (from the http-client SDK) based on the provided authentication
|
|
37
|
+
* Create a Dynatrace Http Client (from the http-client SDK) based on the provided authentication credentials
|
|
38
38
|
* @param environmentUrl
|
|
39
39
|
* @param scopes
|
|
40
40
|
* @param clientId
|
|
@@ -2,12 +2,22 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getEventsForCluster = void 0;
|
|
4
4
|
const execute_dql_1 = require("./execute-dql");
|
|
5
|
-
const getEventsForCluster = async (dtClient, clusterId) => {
|
|
6
|
-
let dql =
|
|
7
|
-
if (!clusterId) {
|
|
8
|
-
//
|
|
9
|
-
dql
|
|
5
|
+
const getEventsForCluster = async (dtClient, clusterId, kubernetesEntityId, eventType) => {
|
|
6
|
+
let dql = 'fetch events';
|
|
7
|
+
if (!clusterId && !kubernetesEntityId) {
|
|
8
|
+
// If no clusterId or kubernetesEntityId is provided, return all kubernetes related events
|
|
9
|
+
dql += ` | filter isNotNull(k8s.cluster.uid)`;
|
|
10
10
|
}
|
|
11
|
+
else if (clusterId || kubernetesEntityId) {
|
|
12
|
+
// filter by clusterId or kubernetesEntityId if provided
|
|
13
|
+
dql += `| filter k8s.cluster.uid == "${clusterId}" or dt.entity.kubernetes_cluster == "${kubernetesEntityId}"`;
|
|
14
|
+
}
|
|
15
|
+
// filter by eventType if provided
|
|
16
|
+
if (eventType) {
|
|
17
|
+
dql += ` | filter eventType == "${eventType}"`;
|
|
18
|
+
}
|
|
19
|
+
// sort by timestamp
|
|
20
|
+
dql += ' | sort timestamp desc';
|
|
11
21
|
return (0, execute_dql_1.executeDql)(dtClient, { query: dql });
|
|
12
22
|
};
|
|
13
23
|
exports.getEventsForCluster = getEventsForCluster;
|
|
@@ -3,8 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.updateWorkflow = void 0;
|
|
4
4
|
const client_automation_1 = require("@dynatrace-sdk/client-automation");
|
|
5
5
|
const updateWorkflow = async (dtClient, workflowId, body) => {
|
|
6
|
-
const
|
|
7
|
-
return await
|
|
6
|
+
const workflowsClient = new client_automation_1.WorkflowsClient(dtClient);
|
|
7
|
+
return await workflowsClient.updateWorkflow({
|
|
8
8
|
id: workflowId,
|
|
9
9
|
body: body,
|
|
10
10
|
});
|
package/dist/index.js
CHANGED
|
@@ -321,7 +321,7 @@ const main = async () => {
|
|
|
321
321
|
return 'No problems found';
|
|
322
322
|
}
|
|
323
323
|
});
|
|
324
|
-
tool('find_entity_by_name', 'Find the entityId and type of a monitored entity (service, host, process-group, application, kubernetes-node, custom-app, ...) within the topology on Dynatrace, based on the name of the entity. Run this before querying data like logs, metrics, problems, events. If no entity name is known, make an educated guess with common identifiers like package.json `id`/`name`, helm chart names, kubernetes
|
|
324
|
+
tool('find_entity_by_name', 'Find the entityId and type of a monitored entity (service, host, process-group, application, kubernetes-node, custom-app, ...) within the topology on Dynatrace, based on the name of the entity. Run this before querying data like logs, metrics, problems, events. If no entity name is known, make an educated guess with common identifiers like package.json `id`/`name`, helm chart names, kubernetes manifest names, and alike.', {
|
|
325
325
|
entityNames: zod_1.z
|
|
326
326
|
.array(zod_1.z.string())
|
|
327
327
|
.describe('Names of the entities to search for - try with one name at first (identifiers like package.json id), and only try with multiple names if the first search was unsuccessful'),
|
|
@@ -637,13 +637,50 @@ const main = async () => {
|
|
|
637
637
|
clusterId: zod_1.z
|
|
638
638
|
.string()
|
|
639
639
|
.optional()
|
|
640
|
-
.describe(`The Kubernetes
|
|
640
|
+
.describe(`The Kubernetes Cluster Id, referred to as k8s.cluster.uid, usually seen when using "kubectl" - this is NOT the Dynatrace environment and not the Dynatrace Kubernetes Entity Id. Leave empty if you don't know the Cluster Id.`),
|
|
641
|
+
kubernetesEntityId: zod_1.z
|
|
642
|
+
.string()
|
|
643
|
+
.optional()
|
|
644
|
+
.describe(`The Dynatrace Kubernetes Entity Id, referred to as dt.entity.kubernetes_cluster. Leave empty if you don't know the Entity Id, or use the "find_entity_by_name" tool to find the cluster by name.`),
|
|
645
|
+
eventType: zod_1.z
|
|
646
|
+
.enum([
|
|
647
|
+
'OMPLIANCE_FINDING',
|
|
648
|
+
'COMPLIANCE_SCAN_COMPLETED',
|
|
649
|
+
'CUSTOM_INFO',
|
|
650
|
+
'DETECTION_FINDING',
|
|
651
|
+
'ERROR_EVENT',
|
|
652
|
+
'OSI_UNEXPECTEDLY_UNAVAILABLE',
|
|
653
|
+
'PROCESS_RESTART',
|
|
654
|
+
'RESOURCE_CONTENTION_EVENT',
|
|
655
|
+
'SERVICE_CLIENT_ERROR_RATE_INCREASED',
|
|
656
|
+
'SERVICE_CLIENT_SLOWDOWN',
|
|
657
|
+
'SERVICE_ERROR_RATE_INCREASED',
|
|
658
|
+
'SERVICE_SLOWDOWN',
|
|
659
|
+
'SERVICE_UNEXPECTED_HIGH_LOAD',
|
|
660
|
+
'SERVICE_UNEXPECTED_LOW_LOAD',
|
|
661
|
+
])
|
|
662
|
+
.optional(),
|
|
663
|
+
maxEventsToDisplay: zod_1.z.number().default(10).describe('Maximum number of events to display in the response.'),
|
|
641
664
|
}, {
|
|
642
665
|
readOnlyHint: true,
|
|
643
|
-
}, async ({ clusterId }) => {
|
|
666
|
+
}, async ({ clusterId, kubernetesEntityId, eventType, maxEventsToDisplay }) => {
|
|
644
667
|
const dtClient = await (0, dynatrace_clients_1.createDtHttpClient)(dtEnvironment, scopesBase.concat('storage:events:read'), oauthClientId, oauthClientSecret, dtPlatformToken);
|
|
645
|
-
const
|
|
646
|
-
|
|
668
|
+
const result = await (0, get_events_for_cluster_1.getEventsForCluster)(dtClient, clusterId, kubernetesEntityId, eventType);
|
|
669
|
+
if (result && result.records && result.records.length > 0) {
|
|
670
|
+
let resp = `Found ${result.records.length} events! Displaying the top ${maxEventsToDisplay} events:\n`;
|
|
671
|
+
// iterate over dqlResponse and create a string with the problem details, but only show the top maxEntitiesToDisplay problems
|
|
672
|
+
result.records.slice(0, maxEventsToDisplay).forEach((event) => {
|
|
673
|
+
if (event) {
|
|
674
|
+
resp += `- Event ${event['event.id']} (${event['event.type']}) on Kubernetes Entity ID ${event['dt.entity.kubernetes_cluster']} with status ${event['event.status']}: ${event['event.name']} - started at ${event['event.start']}, ended at ${event['event.end']}, duration: ${event['duration']}\n`;
|
|
675
|
+
}
|
|
676
|
+
});
|
|
677
|
+
resp +=
|
|
678
|
+
`\nNext Steps:` +
|
|
679
|
+
`\n1. Consider filtering by \`eventType\` to find specific events of interest.` +
|
|
680
|
+
`\n2. Use "execute_dql" tool with the following query to get more details about a specific event: "fetch events | filter event.id == \"<event-id>\""`;
|
|
681
|
+
return resp;
|
|
682
|
+
}
|
|
683
|
+
return 'No events found for the specified Kubernetes cluster. Try to leave clusterId and kubernetesEntityId empty to get events from all clusters.';
|
|
647
684
|
});
|
|
648
685
|
tool('get_ownership', 'Get detailed Ownership information for one or multiple entities on Dynatrace', {
|
|
649
686
|
entityIds: zod_1.z.string().optional().describe('Comma separated list of entityIds'),
|
|
@@ -774,7 +811,8 @@ You can now execute new Grail queries (DQL, etc.) again. If this happens more of
|
|
|
774
811
|
}
|
|
775
812
|
catch (error) {
|
|
776
813
|
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
777
|
-
|
|
814
|
+
// Respond with a JSON-RPC Parse error
|
|
815
|
+
res.end(JSON.stringify({ jsonrpc: '2.0', id: null, error: { code: -32700, message: 'Parse error' } }));
|
|
778
816
|
return;
|
|
779
817
|
}
|
|
780
818
|
}
|