@dynatrace-oss/dynatrace-mcp-server 0.5.0-rc.4 → 0.5.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
@@ -27,6 +27,20 @@ Bring real-time observability data directly into your development workflow.
27
27
  - Get more information about a monitored entity
28
28
  - Get Ownership of an entity
29
29
 
30
+ ## Costs
31
+
32
+ **Important:** While this local MCP server is provided for free, using it to access data in Dynatrace Grail may incur additional costs based
33
+ on your Dynatrace consumption model. This affects `execute_dql` tool and other capabilities that **query** Dynatrace Grail storage, and costs
34
+ depend on the volume (GB scanned/billed).
35
+
36
+ **Before using this MCP server extensively, please:**
37
+
38
+ 1. Review your current Dynatrace consumption model and pricing
39
+ 2. Understand the cost implications of the specific data you plan to query (logs, events, metrics) - see [Dynatrace Pricing and Rate Card](https://www.dynatrace.com/pricing/)
40
+ 3. Start with smaller timeframes (e.g., 12h-24h) and make use of [buckets](https://docs.dynatrace.com/docs/discover-dynatrace/platform/grail/data-model#built-in-grail-buckets) to reduce the cost impact
41
+
42
+ **Note**: We will be providing a way to monitor Query Usage of the dynatrace-mcp-server in the future.
43
+
30
44
  ### AI-Powered Assistance (Preview)
31
45
 
32
46
  - **Natural Language to DQL** - Convert plain English queries to Dynatrace Query Language
@@ -554,6 +568,20 @@ Third, create a `.env` file in this repository (you can copy from `.env.template
554
568
 
555
569
  Finally, make changes to your code and compile it with `npm run build` or just run `npm run watch` and it auto-compiles.
556
570
 
571
+ ## Releasing
572
+
573
+ When you are preparing for a release, you can use GitHub Copilot to guide you through the preparations.
574
+
575
+ In Visual Studio Code, you can use `/release` in the chat with Copilot in Agent Mode, which will execute [release.prompt.md](.github/prompts/release.prompt.md).
576
+
577
+ You may include additional information such as the version number. If not specified, you will be asked.
578
+
579
+ This will
580
+
581
+ - prepare the [changelog](CHANGELOG.md),
582
+ - update the version number in [package.json](package.json),
583
+ - commit the changes.
584
+
557
585
  ## Notes
558
586
 
559
587
  This product is not officially supported by Dynatrace.
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createDtHttpClient = 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
+ const user_agent_1 = require("../utils/user-agent");
7
7
  /**
8
8
  * Uses the provided oauth Client ID and Secret and requests a token via client-credentials flow
9
9
  * @param clientId - OAuth Client ID for Dynatrace
@@ -60,7 +60,7 @@ const createBearerTokenHttpClient = async (environmentUrl, dtPlatformToken) => {
60
60
  baseUrl: environmentUrl,
61
61
  defaultHeaders: {
62
62
  'Authorization': `Bearer ${dtPlatformToken}`,
63
- 'User-Agent': `dynatrace-mcp-server/v${package_json_1.version} (${process.platform}-${process.arch})`,
63
+ 'User-Agent': (0, user_agent_1.getUserAgent)(),
64
64
  },
65
65
  });
66
66
  };
@@ -62,7 +62,7 @@ describe('dynatrace-clients', () => {
62
62
  baseUrl: environmentUrl,
63
63
  defaultHeaders: {
64
64
  'Authorization': 'Bearer test-access-token',
65
- 'User-Agent': 'dynatrace-mcp-server/v1.0.0-test (linux-x64)',
65
+ 'User-Agent': expect.stringMatching(/^dynatrace-mcp-server\/v1\.0\.0-test \(\w+-\w+\)$/),
66
66
  },
67
67
  });
68
68
  expect(result).toBeInstanceOf(http_client_1.PlatformHttpClient);
@@ -131,7 +131,7 @@ describe('dynatrace-clients', () => {
131
131
  baseUrl: environmentUrl,
132
132
  defaultHeaders: {
133
133
  'Authorization': `Bearer ${dtPlatformToken}`,
134
- 'User-Agent': 'dynatrace-mcp-server/v1.0.0-test (linux-x64)',
134
+ 'User-Agent': expect.stringMatching(/^dynatrace-mcp-server\/v1\.0\.0-test \(\w+-\w+\)$/),
135
135
  },
136
136
  });
137
137
  expect(result).toBeInstanceOf(http_client_1.PlatformHttpClient);
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.executeDql = exports.verifyDqlStatement = void 0;
4
4
  const client_query_1 = require("@dynatrace-sdk/client-query");
5
+ const user_agent_1 = require("../utils/user-agent");
5
6
  const verifyDqlStatement = async (dtClient, dqlStatement) => {
6
7
  const queryAssistanceClient = new client_query_1.QueryAssistanceClient(dtClient);
7
8
  const response = await queryAssistanceClient.queryVerify({
@@ -22,7 +23,10 @@ exports.verifyDqlStatement = verifyDqlStatement;
22
23
  */
23
24
  const executeDql = async (dtClient, body) => {
24
25
  const queryExecutionClient = new client_query_1.QueryExecutionClient(dtClient);
25
- const response = await queryExecutionClient.queryExecute({ body });
26
+ const response = await queryExecutionClient.queryExecute({
27
+ body,
28
+ dtClientContext: (0, user_agent_1.getUserAgent)(),
29
+ });
26
30
  if (response.result) {
27
31
  // return response result immediately
28
32
  return response.result.records;
@@ -36,6 +40,7 @@ const executeDql = async (dtClient, body) => {
36
40
  await new Promise((resolve) => setTimeout(resolve, 2000));
37
41
  pollResponse = await queryExecutionClient.queryPoll({
38
42
  requestToken: response.requestToken,
43
+ dtClientContext: (0, user_agent_1.getUserAgent)(),
39
44
  });
40
45
  // done - let's return it
41
46
  if (pollResponse.result) {
package/dist/index.js CHANGED
@@ -17,7 +17,6 @@ const list_vulnerabilities_1 = require("./capabilities/list-vulnerabilities");
17
17
  const list_problems_1 = require("./capabilities/list-problems");
18
18
  const get_monitored_entity_details_1 = require("./capabilities/get-monitored-entity-details");
19
19
  const get_ownership_information_1 = require("./capabilities/get-ownership-information");
20
- const get_logs_for_entity_1 = require("./capabilities/get-logs-for-entity");
21
20
  const get_events_for_cluster_1 = require("./capabilities/get-events-for-cluster");
22
21
  const create_workflow_for_problem_notification_1 = require("./capabilities/create-workflow-for-problem-notification");
23
22
  const update_workflow_1 = require("./capabilities/update-workflow");
@@ -261,13 +260,6 @@ const main = async () => {
261
260
  const response = await (0, send_slack_message_1.sendSlackMessage)(dtClient, slackConnectionId, channel, message);
262
261
  return `Message sent to Slack channel: ${JSON.stringify(response)}`;
263
262
  });
264
- tool('get_logs_for_entity', 'Get Logs for a monitored entity based on name of the entity on Dynatrace', {
265
- entityName: zod_1.z.string().optional(),
266
- }, async ({ entityName }) => {
267
- const dtClient = await (0, dynatrace_clients_1.createDtHttpClient)(dtEnvironment, scopesBase.concat('storage:logs:read'), oauthClientId, oauthClientSecret, dtPlatformToken);
268
- const logs = await (0, get_logs_for_entity_1.getLogsForEntity)(dtClient, entityName);
269
- return `Logs:\n${JSON.stringify(logs?.map((logLine) => (logLine ? logLine.content : 'Empty log')))}`;
270
- });
271
263
  tool('verify_dql', 'Verify a Dynatrace Query Language (DQL) statement on Dynatrace GRAIL before executing it. This step is recommended for DQL statements that have been dynamically created by non-expert tools. For statements coming from the `generate_dql_from_natural_language` tool as well as from documentation, this step can be omitted.', {
272
264
  dqlStatement: zod_1.z.string(),
273
265
  }, async ({ dqlStatement }) => {
@@ -288,9 +280,14 @@ const main = async () => {
288
280
  }
289
281
  return resp;
290
282
  });
291
- tool('execute_dql', 'Get Logs, Metrics, Spans or Events from Dynatrace GRAIL by executing a Dynatrace Query Language (DQL) statement. It\'s recommended to use "verify_dql" tool before you execute a DQL statement. A valid statement looks like this: "fetch [logs, spans, events] | filter <some-filter> | summarize count(), by:{some-fields}. Adapt filters for certain attributes: `traceId` could be `trace_id` or `trace.id`.', {
292
- dqlStatement: zod_1.z.string(),
283
+ tool('execute_dql', 'Get Logs, Metrics, Spans or Events from Dynatrace GRAIL by executing a Dynatrace Query Language (DQL) statement. ' +
284
+ 'You can also use "generate_dql_from_natural_language" to generate a DQL statement based on your request. ' +
285
+ 'Note: For more information about available fields for filters and aggregation, use the query "fetch dt.semantic_dictionary.models | filter data_object == \"logs\""', {
286
+ dqlStatement: zod_1.z
287
+ .string()
288
+ .describe('DQL Statement (Ex: "fetch [logs, spans, events] | filter <some-filter> | summarize count(), by:{some-fields}.", "timeseries { avg(<metric-name>), value.A = avg(<metric-name>, scalar: true) }")'),
293
289
  }, async ({ dqlStatement }) => {
290
+ // Create a HTTP Client that has all storage:*:read scopes
294
291
  const dtClient = await (0, dynatrace_clients_1.createDtHttpClient)(dtEnvironment, scopesBase.concat('storage:buckets:read', // Read all system data stored on Grail
295
292
  'storage:logs:read', // Read logs for reliability guardian validations
296
293
  'storage:metrics:read', // Read metrics for reliability guardian validations
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getUserAgent = void 0;
4
+ const package_json_1 = require("../../package.json");
5
+ /**
6
+ * Generate a user agent string for Dynatrace MCP Server
7
+ * @returns User agent string in format: dynatrace-mcp-server/vX.X.X (platform-arch)
8
+ */
9
+ const getUserAgent = () => {
10
+ return `dynatrace-mcp-server/v${package_json_1.version} (${process.platform}-${process.arch})`;
11
+ };
12
+ exports.getUserAgent = getUserAgent;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dynatrace-oss/dynatrace-mcp-server",
3
- "version": "0.5.0-rc.4",
3
+ "version": "0.5.0",
4
4
  "description": "Model Context Protocol (MCP) server for Dynatrace",
5
5
  "keywords": [
6
6
  "Dynatrace",
@@ -52,7 +52,7 @@
52
52
  "@dynatrace-sdk/shared-errors": "^1.0.0",
53
53
  "@modelcontextprotocol/sdk": "^1.8.0",
54
54
  "commander": "^14.0.0",
55
- "dotenv": "^16.4.7",
55
+ "dotenv": "^17.2.1",
56
56
  "dt-app": "^0.148.1",
57
57
  "zod-to-json-schema": "^3.24.5"
58
58
  },
@@ -1,9 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getLogsForEntity = void 0;
4
- const execute_dql_1 = require("./execute-dql");
5
- const getLogsForEntity = async (dtClient, entityId) => {
6
- const dql = `fetch logs | filter dt.source_entity == "${entityId}"`;
7
- return (0, execute_dql_1.executeDql)(dtClient, { query: dql });
8
- };
9
- exports.getLogsForEntity = getLogsForEntity;