@dynatrace-oss/dynatrace-mcp-server 0.1.4 → 0.3.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
@@ -16,9 +16,10 @@ Bring real-time observability data directly into your development workflow.
16
16
 
17
17
  - List and get [problem](https://www.dynatrace.com/hub/detail/problems/) details from your services (for example Kubernetes)
18
18
  - List and get security problems / [vulnerability](https://www.dynatrace.com/hub/detail/vulnerabilities/) details
19
- - Execute DQL(Dynatrace Query Language) like getting events or logs
19
+ - Execute DQL (Dynatrace Query Language) and retrieve logs, events, spans and metrics
20
20
  - Send Slack messages (via Slack Connector)
21
21
  - Set up notification Workflow (via Dynatrace [AutomationEngine](https://docs.dynatrace.com/docs/discover-dynatrace/platform/automationengine))
22
+ - Get more information about a monitored entity
22
23
  - Get Ownership of an entity
23
24
 
24
25
  ## Quickstart
@@ -27,6 +28,8 @@ Bring real-time observability data directly into your development workflow.
27
28
 
28
29
  You can add this MCP server (using STDIO) to your MCP Client like VS Code, Claude, Cursor, Amazon Q Developer CLI, Windsurf Github Copilot via the package `@dynatrace-oss/dynatrace-mcp-server`.
29
30
 
31
+ We recommend to always set it up for your current workspace instead of using it globally.
32
+
30
33
  **VS Code**
31
34
 
32
35
  ```json
@@ -34,6 +37,7 @@ You can add this MCP server (using STDIO) to your MCP Client like VS Code, Claud
34
37
  "servers": {
35
38
  "npx-dynatrace-mcp-server": {
36
39
  "command": "npx",
40
+ "cwd": "${workspaceFolder}",
37
41
  "args": ["-y", "@dynatrace-oss/dynatrace-mcp-server@latest"],
38
42
  "envFile": "${workspaceFolder}/.env"
39
43
  }
@@ -61,6 +65,7 @@ This only works if the config is stored in the current workspaces, e.g., `<your-
61
65
  ```
62
66
 
63
67
  **Claude Desktop**
68
+
64
69
  ```json
65
70
  {
66
71
  "mcpServers": {
@@ -80,6 +85,7 @@ This only works if the config is stored in the current workspaces, e.g., `<your-
80
85
  **Amazon Q Developer CLI**
81
86
 
82
87
  The [Amazon Q Developer CLI](https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/command-line-mcp-configuration.html) provides an interactive chat experience directly in your terminal. You can ask questions, get help with AWS services, troubleshoot issues, and generate code snippets without leaving your command line environment.
88
+
83
89
  ```json
84
90
  {
85
91
  "mcpServers": {
@@ -96,13 +102,15 @@ The [Amazon Q Developer CLI](https://docs.aws.amazon.com/amazonq/latest/qdevelop
96
102
  }
97
103
  ```
98
104
 
105
+ This configuration should be stored in `<your-repo>/.amazonq/mcp.json`.
106
+
99
107
  ## Environment Variables
100
108
 
101
109
  A **Dynatrace OAuth Client** is needed to communicate with your Dynatrace Environment. Please follow the documentation about
102
110
  [creating an Oauth Client in Dynatrace](https://docs.dynatrace.com/docs/manage/identity-access-management/access-tokens-and-oauth-clients/oauth-clients),
103
111
  and set up the following environment variables in order for this MCP to work:
104
112
 
105
- * `DT_ENVIRONMENT` (string, e.g., https://abcd1234.apps.dynatrace.com) - URL to your Dynatrace Platform
113
+ * `DT_ENVIRONMENT` (string, e.g., https://abc12345.apps.dynatrace.com) - URL to your Dynatrace Platform (do not use Dynatrace classic URLs like `abc12345.live.dynatrace.com`)
106
114
  * `OAUTH_CLIENT_ID` (string, e.g., `dt0s02.SAMPLE`) - Dynatrace OAuth Client ID
107
115
  * `OAUTH_CLIENT_SECRET` (string, e.g., `dt0s02.SAMPLE.abcd1234`) - Dynatrace OAuth Client Secret
108
116
  * OAuth Client Scopes:
@@ -114,7 +122,6 @@ and set up the following environment variables in order for this MCP to work:
114
122
  * `environment-api:problems:read` - get problems
115
123
  * `environment-api:metrics:read` - read metrics
116
124
  * `environment-api:slo:read` - read SLOs
117
- * `settings:objects:read` - needed for reading ownership information and Guardians (SRG) from settings
118
125
  * `storage:buckets:read` - Read all system data stored on Grail
119
126
  * `storage:logs:read` - Read logs for reliability guardian validations
120
127
  * `storage:metrics:read` - Read metrics for reliability guardian validations
@@ -125,6 +132,9 @@ and set up the following environment variables in order for this MCP to work:
125
132
  * `storage:system:read` - Read System Data from Grail
126
133
  * `storage:user.events:read` - Read User events from Grail
127
134
  * `storage:user.sessions:read` - Read User sessions from Grail
135
+ * `settings:objects:read` - needed for reading ownership information and Guardians (SRG) from settings
136
+
137
+ **Note**: Please ensure that `settings:objects:read` is used, and *not* the similarly named scope `app-settings:objects:read`.
128
138
 
129
139
  In addition, depending on the features you use, the following variables can be configured:
130
140
 
@@ -172,9 +182,53 @@ Can you fetch recent events from our "production-cluster"
172
182
  to help identify what might be causing these deployment issues?
173
183
  ```
174
184
 
185
+ ## Troubleshooting
186
+
187
+ ### Authentication Issues
188
+
189
+ In most cases, something is wrong with the OAuth Client. Please ensure that you have added all scopes as requested above.
190
+ In addition, please ensure that your user also has all necessary permissions on your Dynatrace Environment.
191
+
192
+ In case of any problems, you can troubleshoot SSO/OAuth issues based on our [Dynatrace Developer Documentation](https://developer.dynatrace.com/develop/access-platform-apis-from-outside/#get-bearer-token-and-call-app-function) and providing the list of scopes.
193
+
194
+ It is recommended to try access the following API (which requires minimal scopes `app-engine:apps:run` and `app-engine:functions:run`):
195
+
196
+ 1. Use OAuth Client ID and Secret to retrieve a Bearer Token (only valid for a couple of minutes):
197
+ ```bash
198
+ curl --request POST 'https://sso.dynatrace.com/sso/oauth2/token' \
199
+ --header 'Content-Type: application/x-www-form-urlencoded' \
200
+ --data-urlencode 'grant_type=client_credentials' \
201
+ --data-urlencode 'client_id={your-client-id}' \
202
+ --data-urlencode 'client_secret={your-client-secret}' \
203
+ --data-urlencode 'scope=app-engine:apps:run app-engine:functions:run'
204
+ ```
205
+
206
+ 2. Use `access_token` from the response of the above call as the bearer-token in the next call:
207
+ ```bash
208
+ curl -X GET https://abc12345.apps.dynatrace.com/platform/management/v1/environment \
209
+ -H 'accept: application/json' \
210
+ -H 'Authorization: Bearer {your-bearer-token}'
211
+ ```
212
+
213
+ 3. You should retrieve a result like this:
214
+ ```json
215
+ {
216
+ "environmentId": "abc12345",
217
+ "createTime": "2023-01-01T00:10:57.123Z",
218
+ "blockTime": "2025-12-07T00:00:00Z",
219
+ "state": "ACTIVE"
220
+ }
221
+ ```
222
+
223
+
224
+ ### Problem accessing data on Grail
225
+
226
+ Grail has a dedicated section about permissions in the Dynatrace Docs. Please refer to https://docs.dynatrace.com/docs/discover-dynatrace/platform/grail/data-model/assign-permissions-in-grail for more details.
227
+
228
+
175
229
  ## Development
176
230
 
177
- For development purposes, you can use VSCode and GitHub Copilot.
231
+ For local development purposes, you can use VSCode and GitHub Copilot.
178
232
 
179
233
  First, enable Copilot for your Workspace `.vscode/settings.json`:
180
234
  ```json
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.findMonitoredEntityByName = void 0;
4
+ const execute_dql_1 = require("./execute-dql");
5
+ const findMonitoredEntityByName = async (dtClient, entityName) => {
6
+ const dql = `fetch dt.entity.application | search "*${entityName}*" | fieldsAdd entity.type
7
+ | append [fetch dt.entity.service | search "*${entityName}*" | fieldsAdd entity.type]
8
+ | append [fetch dt.entity.host | search "*${entityName}*" | fieldsAdd entity.type]
9
+ | append [fetch dt.entity.process_group | search "*${entityName}*" | fieldsAdd entity.type]
10
+ | append [fetch dt.entity.cloud_application | search "*${entityName}*" | fieldsAdd entity.type]`;
11
+ const dqlResponse = await (0, execute_dql_1.executeDql)(dtClient, dql);
12
+ if (dqlResponse && dqlResponse.length > 0) {
13
+ let resp = 'The following monitored entities were found:\n';
14
+ // iterate over dqlResponse and create a string with the entity names
15
+ dqlResponse.forEach((entity) => {
16
+ if (entity) {
17
+ resp += `- Entity '${entity['entity.name']}' of type '${entity['entity.type']} has entity id '${entity.id}'\n`;
18
+ }
19
+ });
20
+ return resp;
21
+ }
22
+ else {
23
+ return 'No monitored entity found with the specified name.';
24
+ }
25
+ };
26
+ exports.findMonitoredEntityByName = findMonitoredEntityByName;
@@ -1,8 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.callAppFunction = exports.createOAuthClient = void 0;
3
+ exports.callAppFunction = exports.createOAuthClient = exports.ExtendedOauthClient = void 0;
4
4
  const http_client_1 = require("@dynatrace-sdk/http-client");
5
5
  const dt_app_1 = require("dt-app");
6
+ const package_json_1 = require("../package.json");
6
7
  /**
7
8
  * Uses the provided oauth Client ID and Secret and requests a token
8
9
  * @param clientId - OAuth Client ID for Dynatrace
@@ -32,6 +33,26 @@ const requestToken = async (clientId, clientSecret, authUrl, scopes) => {
32
33
  // and return the JSON result, as it contains additional information
33
34
  return await res.json();
34
35
  };
36
+ /**
37
+ * ExtendedOAuthClient that takes parameters for clientId, secret, scopes, environmentUrl, authUrl, and the version of the dynatrace-mcp-server
38
+ */
39
+ class ExtendedOauthClient extends http_client_1._OAuthHttpClient {
40
+ userAgent;
41
+ constructor(config, userAgent) {
42
+ super(config);
43
+ this.userAgent = userAgent;
44
+ }
45
+ send(options) {
46
+ // add the user-agent header to the request
47
+ options.headers = {
48
+ ...options.headers,
49
+ 'User-Agent': this.userAgent,
50
+ };
51
+ // call the parent send method
52
+ return super.send(options);
53
+ }
54
+ }
55
+ exports.ExtendedOauthClient = ExtendedOauthClient;
35
56
  /** Create an Oauth Client based on clientId, clientSecret, environmentUrl and scopes */
36
57
  const createOAuthClient = async (clientId, clientSecret, environmentUrl, scopes) => {
37
58
  if (!clientId) {
@@ -54,13 +75,14 @@ const createOAuthClient = async (clientId, clientSecret, environmentUrl, scopes)
54
75
  throw new Error(`Failed to retrieve OAuth token (IssueId: ${tokenResponse.issueId}): ${tokenResponse.error} - ${tokenResponse.error_description}. Note: Your OAuth client is most likely not configured correctly.`);
55
76
  }
56
77
  console.error(`Successfully retrieved token from SSO!`);
57
- return new http_client_1._OAuthHttpClient({
78
+ const userAgent = `dynatrace-mcp-server/v${package_json_1.version} (${process.platform}-${process.arch})`;
79
+ return new ExtendedOauthClient({
58
80
  scopes,
59
81
  clientId,
60
82
  secret: clientSecret,
61
83
  environmentUrl,
62
84
  authUrl: ssoAuthUrl,
63
- });
85
+ }, userAgent);
64
86
  };
65
87
  exports.createOAuthClient = createOAuthClient;
66
88
  /** Helper function to call an app-function via platform-api */
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getDynatraceEnv = getDynatraceEnv;
4
+ /**
5
+ * Reads and validates required environment variables for Dynatrace MCP.
6
+ * Throws an Error if validation fails.
7
+ */
8
+ function getDynatraceEnv(env = process.env) {
9
+ const oauthClient = env.OAUTH_CLIENT_ID;
10
+ const oauthClientSecret = env.OAUTH_CLIENT_SECRET;
11
+ const dtEnvironment = env.DT_ENVIRONMENT;
12
+ const slackConnectionId = env.SLACK_CONNECTION_ID || "fake-slack-connection-id";
13
+ if (!oauthClient || !oauthClientSecret || !dtEnvironment) {
14
+ throw new Error("Please set OAUTH_CLIENT_ID and OAUTH_CLIENT_SECRET and DT_ENVIRONMENT environment variables");
15
+ }
16
+ if (!dtEnvironment.startsWith("https://")) {
17
+ throw new Error("Please set DT_ENVIRONMENT to a valid Dynatrace Environment URL (e.g., https://<environment-id>.apps.dynatrace.com)");
18
+ }
19
+ if (!dtEnvironment.includes("apps.dynatrace.com") && !dtEnvironment.includes("apps.dynatracelabs.com")) {
20
+ throw new Error("Please set DT_ENVIRONMENT to a valid Dynatrace Platform Environment URL (e.g., https://<environment-id>.apps.dynatrace.com)");
21
+ }
22
+ return { oauthClient, oauthClientSecret, dtEnvironment, slackConnectionId };
23
+ }
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const getDynatraceEnv_1 = require("./getDynatraceEnv");
4
+ describe("getDynatraceEnv", () => {
5
+ const baseEnv = {
6
+ OAUTH_CLIENT_ID: "dt0s02.SAMPLE",
7
+ OAUTH_CLIENT_SECRET: "dt0s02.SAMPLE.abcd1234",
8
+ DT_ENVIRONMENT: "https://abc123.apps.dynatrace.com",
9
+ SLACK_CONNECTION_ID: "slack-conn-id"
10
+ };
11
+ it("returns all required values when environment is valid", () => {
12
+ const env = { ...baseEnv };
13
+ const result = (0, getDynatraceEnv_1.getDynatraceEnv)(env);
14
+ expect(result).toEqual({
15
+ oauthClient: env.OAUTH_CLIENT_ID,
16
+ oauthClientSecret: env.OAUTH_CLIENT_SECRET,
17
+ dtEnvironment: env.DT_ENVIRONMENT,
18
+ slackConnectionId: env.SLACK_CONNECTION_ID
19
+ });
20
+ });
21
+ it("uses default slackConnectionId if not set", () => {
22
+ const env = { ...baseEnv, SLACK_CONNECTION_ID: undefined };
23
+ const result = (0, getDynatraceEnv_1.getDynatraceEnv)(env);
24
+ expect(result.slackConnectionId).toBe("fake-slack-connection-id");
25
+ });
26
+ it("throws if OAUTH_CLIENT_ID is missing", () => {
27
+ const env = { ...baseEnv, OAUTH_CLIENT_ID: undefined };
28
+ expect(() => (0, getDynatraceEnv_1.getDynatraceEnv)(env)).toThrow(/OAUTH_CLIENT_ID/);
29
+ });
30
+ it("throws if OAUTH_CLIENT_SECRET is missing", () => {
31
+ const env = { ...baseEnv, OAUTH_CLIENT_SECRET: undefined };
32
+ expect(() => (0, getDynatraceEnv_1.getDynatraceEnv)(env)).toThrow(/OAUTH_CLIENT_SECRET/);
33
+ });
34
+ it("throws if DT_ENVIRONMENT is missing", () => {
35
+ const env = { ...baseEnv, DT_ENVIRONMENT: undefined };
36
+ expect(() => (0, getDynatraceEnv_1.getDynatraceEnv)(env)).toThrow(/DT_ENVIRONMENT/);
37
+ });
38
+ it("throws if DT_ENVIRONMENT does not start with https://", () => {
39
+ const env = { ...baseEnv, DT_ENVIRONMENT: "http://abc123.apps.dynatrace.com" };
40
+ expect(() => (0, getDynatraceEnv_1.getDynatraceEnv)(env)).toThrow(/https:\/\//);
41
+ });
42
+ it("throws if DT_ENVIRONMENT is not a Dynatrace Platform URL (any URL)", () => {
43
+ const env = { ...baseEnv, DT_ENVIRONMENT: "https://abc123.example.com" };
44
+ expect(() => (0, getDynatraceEnv_1.getDynatraceEnv)(env)).toThrow(/Dynatrace Platform Environment URL/);
45
+ });
46
+ it("throws if DT_ENVIRONMENT is not a Dynatrace Platform URL (contains live)", () => {
47
+ const env = { ...baseEnv, DT_ENVIRONMENT: "https://abc123.live.dynatrace.com" };
48
+ expect(() => (0, getDynatraceEnv_1.getDynatraceEnv)(env)).toThrow(/Dynatrace Platform Environment URL/);
49
+ });
50
+ it("accepts DT_ENVIRONMENT with apps.dynatracelabs.com", () => {
51
+ const env = { ...baseEnv, DT_ENVIRONMENT: "https://xyz789.apps.dynatracelabs.com" };
52
+ expect(() => (0, getDynatraceEnv_1.getDynatraceEnv)(env)).not.toThrow();
53
+ });
54
+ it("accepts DT_ENVIRONMENT with apps.dynatrace.com", () => {
55
+ const env = { ...baseEnv, DT_ENVIRONMENT: "https://env123.apps.dynatrace.com" };
56
+ expect(() => (0, getDynatraceEnv_1.getDynatraceEnv)(env)).not.toThrow();
57
+ });
58
+ });
package/dist/index.js CHANGED
@@ -7,6 +7,7 @@ const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
7
7
  const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
8
8
  const dotenv_1 = require("dotenv");
9
9
  const zod_1 = require("zod");
10
+ const package_json_1 = require("../package.json");
10
11
  const dynatrace_clients_1 = require("./dynatrace-clients");
11
12
  const list_vulnerabilities_1 = require("./capabilities/list-vulnerabilities");
12
13
  const list_problems_1 = require("./capabilities/list-problems");
@@ -20,6 +21,8 @@ const update_workflow_1 = require("./capabilities/update-workflow");
20
21
  const get_vulnerability_details_1 = require("./capabilities/get-vulnerability-details");
21
22
  const execute_dql_1 = require("./capabilities/execute-dql");
22
23
  const send_slack_message_1 = require("./capabilities/send-slack-message");
24
+ const find_monitored_entity_by_name_1 = require("./capabilities/find-monitored-entity-by-name");
25
+ const getDynatraceEnv_1 = require("./getDynatraceEnv");
23
26
  (0, dotenv_1.config)();
24
27
  let scopes = [
25
28
  'app-engine:apps:run', // needed for environmentInformationClient
@@ -55,21 +58,21 @@ if (process.env.USE_WORKFLOWS) {
55
58
  }
56
59
  const main = async () => {
57
60
  // read Environment variables
58
- const oauthClient = process.env.OAUTH_CLIENT_ID;
59
- const oauthClientSecret = process.env.OAUTH_CLIENT_SECRET;
60
- const dtEnvironment = process.env.DT_ENVIRONMENT;
61
- const slackConnectionId = process.env.SLACK_CONNECTION_ID || "fake-slack-connection-id";
62
- // ensure oauthClient and dtEnvironment are set
63
- if (!oauthClient || !oauthClientSecret || !dtEnvironment) {
64
- console.error("Please set OAUTH_CLIENT_ID and OAUTH_CLIENT_SECRET and DT_ENVIRONMENT environment variables");
61
+ let dynatraceEnv;
62
+ try {
63
+ dynatraceEnv = (0, getDynatraceEnv_1.getDynatraceEnv)();
64
+ }
65
+ catch (err) {
66
+ console.error(err.message);
65
67
  process.exit(1);
66
68
  }
69
+ const { oauthClient, oauthClientSecret, dtEnvironment, slackConnectionId } = dynatraceEnv;
67
70
  // create an oauth-client
68
71
  const dtClient = await (0, dynatrace_clients_1.createOAuthClient)(oauthClient, oauthClientSecret, dtEnvironment, scopes);
69
- console.error("Starting Dynatrace MCP Server...");
72
+ console.error(`Starting Dynatrace MCP Server v${package_json_1.version}...`);
70
73
  const server = new mcp_js_1.McpServer({
71
74
  name: "Dynatrace MCP Server",
72
- version: "0.0.1", // ToDo: Read from package.json / hard-code?
75
+ version: package_json_1.version,
73
76
  }, {
74
77
  capabilities: {
75
78
  tools: {},
@@ -208,6 +211,12 @@ const main = async () => {
208
211
  resp += `Tell the user to access the link ${dtEnvironment}/ui/apps/dynatrace.davis.problems/problem/${result.problemId} to get more insights into the problem.\n`;
209
212
  return resp;
210
213
  });
214
+ tool("find_entity_by_name", "Get the entityId of a monitored entity based on the name of the entity on Dynatrace", {
215
+ entityName: zod_1.z.string()
216
+ }, async ({ entityName }) => {
217
+ const entityResponse = await (0, find_monitored_entity_by_name_1.findMonitoredEntityByName)(dtClient, entityName);
218
+ return entityResponse;
219
+ });
211
220
  tool("get_entity_details", "Get details of a monitored entity based on the entityId on Dynatrace", {
212
221
  entityId: zod_1.z.string().optional()
213
222
  }, async ({ entityId }) => {
@@ -241,13 +250,26 @@ const main = async () => {
241
250
  const logs = await (0, get_logs_for_entity_1.getLogsForEntity)(dtClient, entityName);
242
251
  return `Logs:\n${JSON.stringify(logs?.map(logLine => logLine ? logLine.content : 'Empty log'))}`;
243
252
  });
244
- tool("verify_dql", "Verify a DQL statement on Dynatrace", {
253
+ tool("verify_dql", "Verify a Dynatrace Query Language (DQL) statement on Dynatrace GRAIL before executing it. This is useful to ensure that the DQL statement is valid and can be executed without errors.", {
245
254
  dqlStatement: zod_1.z.string()
246
255
  }, async ({ dqlStatement }) => {
247
256
  const response = await (0, execute_dql_1.verifyDqlStatement)(dtClient, dqlStatement);
248
- return `Parsing DQL Statement resulted in: ${JSON.stringify(response)}`;
257
+ let resp = 'DQL Statement Verification:\n';
258
+ if (response.notifications && response.notifications.length > 0) {
259
+ resp += `Please consider the following notifications for adapting the your DQL statement:\n`;
260
+ response.notifications.forEach((notification) => {
261
+ resp += `* ${notification.severity}: ${notification.message}\n`;
262
+ });
263
+ }
264
+ if (response.valid) {
265
+ resp += `The DQL statement is valid - you can use the "execute_dql" tool.\n`;
266
+ }
267
+ else {
268
+ resp += `The DQL statement is invalid. Please adapt your statement.\n`;
269
+ }
270
+ return resp;
249
271
  });
250
- tool("execute_dql", "Get Logs, Metrics, Spans, Events from Dynatrace by executing a DQL statement. Please use verify_dql tool before you execute a DQL statement.", {
272
+ tool("execute_dql", 'Get Logs, Metrics, Spans or Events from Dynatrace GRAIL by executing a Dynatrace Query Language (DQL) statement. Always use "verify_dql" tool before you execute a DQL statement. A valid statement looks like this: "fetch [logs, metrics, spans, events] | filter <some-filter> | summarize count(), by:{some-fields}. Adapt filters for certain attributes: `traceId` could be `trace_id` or `trace.id`.', {
251
273
  dqlStatement: zod_1.z.string()
252
274
  }, async ({ dqlStatement }) => {
253
275
  const response = await (0, execute_dql_1.executeDql)(dtClient, dqlStatement);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dynatrace-oss/dynatrace-mcp-server",
3
- "version": "0.1.4",
3
+ "version": "0.3.0",
4
4
  "description": "Model Context Protocol (MCP) server for Dynatrace",
5
5
  "keywords": [
6
6
  "Dynatrace",
@@ -36,7 +36,8 @@
36
36
  "scripts": {
37
37
  "build": "tsc --build",
38
38
  "prepare": "npm run build",
39
- "watch": "tsc --watch"
39
+ "watch": "tsc --watch",
40
+ "test": "jest"
40
41
  },
41
42
  "author": "Dynatrace",
42
43
  "license": "MIT",
@@ -52,7 +53,10 @@
52
53
  "zod-to-json-schema": "^3.24.5"
53
54
  },
54
55
  "devDependencies": {
56
+ "@types/jest": "^30.0.0",
55
57
  "@types/node": "^22",
58
+ "jest": "^30.0.0",
59
+ "ts-jest": "^29.4.0",
56
60
  "ts-node": "^10.9.2",
57
61
  "typescript": "^5.6.2"
58
62
  }
@@ -1,15 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getMonitoredEntityOwner = void 0;
4
- const get_monitored_entity_details_1 = require("./get-monitored-entity-details");
5
- const getMonitoredEntityOwner = async (dtClient, entityId) => {
6
- const monitoredEntity = await (0, get_monitored_entity_details_1.getMonitoredEntityDetails)(dtClient, entityId);
7
- const owner = monitoredEntity.tags?.find((tag) => {
8
- return tag?.key === "owner";
9
- });
10
- return {
11
- entity: monitoredEntity,
12
- owner: owner?.value
13
- };
14
- };
15
- exports.getMonitoredEntityOwner = getMonitoredEntityOwner;
@@ -1,27 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.executeWorkflow = exports.deleteWorkflow = exports.getWorkflowDetails = exports.listWorkflows = void 0;
4
- const client_automation_1 = require("@dynatrace-sdk/client-automation");
5
- const listWorkflows = async (dtClient) => {
6
- const workflowsClient = new client_automation_1.WorkflowsClient(dtClient);
7
- return await workflowsClient.getWorkflows({});
8
- };
9
- exports.listWorkflows = listWorkflows;
10
- const getWorkflowDetails = async (dtClient, workflowId) => {
11
- const workflowsClient = new client_automation_1.WorkflowsClient(dtClient);
12
- return await workflowsClient.getWorkflow({ id: workflowId });
13
- };
14
- exports.getWorkflowDetails = getWorkflowDetails;
15
- const deleteWorkflow = async (dtClient, workflowId) => {
16
- const workflowsClient = new client_automation_1.WorkflowsClient(dtClient);
17
- return await workflowsClient.deleteWorkflow({ id: workflowId });
18
- };
19
- exports.deleteWorkflow = deleteWorkflow;
20
- const executeWorkflow = async (dtClient, workflowId, input) => {
21
- const workflowsClient = new client_automation_1.WorkflowsClient(dtClient);
22
- return await workflowsClient.runWorkflow({
23
- id: workflowId,
24
- body: input || {}
25
- });
26
- };
27
- exports.executeWorkflow = executeWorkflow;
@@ -1,55 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.OAuthHttpClient = void 0;
4
- class OAuthHttpClient {
5
- config;
6
- oauthToken;
7
- constructor(config) {
8
- this.config = config;
9
- }
10
- async send(options) {
11
- const { statusValidator = defaultStatusValidator } = options;
12
- if (!this.oauthToken || this.oauthToken.isExpired()) {
13
- this.oauthToken = await this.requestToken();
14
- }
15
- const requestBodyType = options.requestBodyType ?? 'json';
16
- const url = new URL(options.url, this.config.environmentUrl);
17
- const body = options.body !== undefined
18
- ? await encodeRequestBody(options.body, requestBodyType)
19
- : undefined;
20
- const headers = {
21
- ...applyContentTypeHeader(options.headers, requestBodyType),
22
- Authorization: `${this.oauthToken.tokenType} ${this.oauthToken.accessToken}`,
23
- };
24
- const response = await send(url.toString(), {
25
- method: options.method,
26
- signal: options.abortSignal,
27
- body,
28
- headers,
29
- });
30
- const ok = statusValidator(response.status);
31
- if (ok) {
32
- return new HttpClientResponse(response);
33
- }
34
- else {
35
- throw new HttpClientResponseError(response, { requestMethod: options.method });
36
- }
37
- }
38
- async requestToken() {
39
- const res = await send(this.config.authUrl, {
40
- method: 'POST',
41
- headers: {
42
- 'Content-Type': 'application/x-www-form-urlencoded',
43
- },
44
- body: new URLSearchParams({
45
- grant_type: 'client_credentials',
46
- client_id: this.config.clientId,
47
- client_secret: this.config.secret,
48
- scope: this.config.scopes.join(' '),
49
- }),
50
- });
51
- const data = await res.json();
52
- return new OAuthToken(data);
53
- }
54
- }
55
- exports.OAuthHttpClient = OAuthHttpClient;
@@ -1 +0,0 @@
1
- {"root":["../src/dynatrace-clients.ts","../src/index.ts","../src/capabilities/create-workflow-for-problem-notification.ts","../src/capabilities/execute-dql.ts","../src/capabilities/get-events-for-cluster.ts","../src/capabilities/get-logs-for-entity.ts","../src/capabilities/get-monitored-entity-details.ts","../src/capabilities/get-ownership-information.ts","../src/capabilities/get-problem-details.ts","../src/capabilities/get-vulnerability-details.ts","../src/capabilities/list-problems.ts","../src/capabilities/list-vulnerabilities.ts","../src/capabilities/send-slack-message.ts","../src/capabilities/update-workflow.ts"],"version":"5.8.2"}