Skip to content

Commit 311910a

Browse files
fix: Do not require all oauth scopes to start the mcp server (#54)
* fix: Do not require all oauth scopes to start the mcp server * fix: Removing outdated README parts
1 parent fc0677f commit 311910a

File tree

2 files changed

+41
-40
lines changed

2 files changed

+41
-40
lines changed

README.md

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -139,10 +139,6 @@ and set up the following environment variables in order for this MCP to work:
139139
In addition, depending on the features you use, the following variables can be configured:
140140

141141
* `SLACK_CONNECTION_ID` (string) - connection ID of a [Slack Connection](https://docs.dynatrace.com/docs/analyze-explore-automate/workflows/actions/slack)
142-
* `USE_APP_SETTINGS` (boolean, `true` or `false`; default: `false`)
143-
* Requires scope `app-settings:objects:read` to read settings-objects from app settings
144-
* `USE_WORKFLOWS` (boolean, `true` or `false`; default: `false`)
145-
* Requires scopes `automation:workflows:read`, `automation:workflows:write` and `automation:workflows:run` to read, write and execute Workflows
146142

147143
## ✨ Example prompts ✨
148144

src/index.ts

Lines changed: 41 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -34,43 +34,11 @@ import { DynatraceEnv, getDynatraceEnv } from "./getDynatraceEnv";
3434

3535
config();
3636

37-
let scopes = [
37+
let scopesBase = [
3838
'app-engine:apps:run', // needed for environmentInformationClient
3939
'app-engine:functions:run', // needed for environmentInformationClient
40-
'hub:catalog:read', // get details about installed Apps on Dynatrace Environment
41-
42-
'environment-api:security-problems:read', // needed for reading security problems
43-
'environment-api:entities:read', // read monitored entities
44-
'environment-api:problems:read', // get problems
45-
'environment-api:metrics:read', // read metrics
46-
'environment-api:slo:read', // read SLOs
47-
'settings:objects:read', // needed for reading settings objects, like ownership information and Guardians (SRG) from settings
48-
// 'settings:objects:write', // [OPTIONAL] not used right now
49-
50-
// Grail related permissions: https://docs.dynatrace.com/docs/discover-dynatrace/platform/grail/data-model/assign-permissions-in-grail
51-
'storage:buckets:read', // Read all system data stored on Grail
52-
'storage:logs:read', // Read logs for reliability guardian validations
53-
'storage:metrics:read', // Read metrics for reliability guardian validations
54-
'storage:bizevents:read', // Read bizevents for reliability guardian validations
55-
'storage:spans:read', // Read spans from Grail
56-
'storage:entities:read', // Read Entities from Grail
57-
'storage:events:read', // Read events from Grail
58-
'storage:system:read', // Read System Data from Grail
59-
'storage:user.events:read', // Read User events from Grail
60-
'storage:user.sessions:read', // Read User sessions from Grail
6140
];
6241

63-
// configurable call for app settings scope (not available on all environments)
64-
if (process.env.USE_APP_SETTINGS) {
65-
scopes.push('app-settings:objects:read'); // needed when using app settings in Workflows, see below
66-
}
67-
68-
if (process.env.USE_WORKFLOWS) {
69-
scopes.push('automation:workflows:read'); // read workflows
70-
scopes.push('automation:workflows:write'); // write workflows
71-
scopes.push('automation:workflows:run'); // execute workflows
72-
}
73-
7442
const main = async () => {
7543
// read Environment variables
7644
let dynatraceEnv: DynatraceEnv;
@@ -82,9 +50,6 @@ const main = async () => {
8250
}
8351
const { oauthClient, oauthClientSecret, dtEnvironment, slackConnectionId } = dynatraceEnv;
8452

85-
// create an oauth-client
86-
const dtClient = await createOAuthClient(oauthClient, oauthClientSecret, dtEnvironment, scopes);
87-
8853
console.error(`Starting Dynatrace MCP Server v${VERSION}...`);
8954
const server = new McpServer(
9055
{
@@ -139,6 +104,8 @@ const main = async () => {
139104
"Get information about the connected Dynatrace Environment (Tenant)",
140105
{},
141106
async({}) => {
107+
// create an oauth-client
108+
const dtClient = await createOAuthClient(oauthClient, oauthClientSecret, dtEnvironment, scopesBase);
142109
const environmentInformationClient = new EnvironmentInformationClient(dtClient);
143110

144111
const environmentInfo = await environmentInformationClient.getEnvironmentInformation();
@@ -156,6 +123,7 @@ const main = async () => {
156123
"List all vulnerabilities from Dynatrace",
157124
{},
158125
async ({}) => {
126+
const dtClient = await createOAuthClient(oauthClient, oauthClientSecret, dtEnvironment, scopesBase.concat("environment-api:security-problems:read"));
159127
const result = await listVulnerabilities(dtClient);
160128
if (!result || result.length === 0) {
161129
return "No vulnerabilities found";
@@ -181,6 +149,7 @@ const main = async () => {
181149
securityProblemId: z.string().optional()
182150
},
183151
async ({securityProblemId}) => {
152+
const dtClient = await createOAuthClient(oauthClient, oauthClientSecret, dtEnvironment, scopesBase.concat("environment-api:security-problems:read"));
184153
const result = await getVulnerabilityDetails(dtClient, securityProblemId);
185154

186155
let resp = `The Security Problem (Vulnerability) ${result.displayId} with securityProblemId ${result.securityProblemId} has the title ${result.title}.\n`;
@@ -249,6 +218,7 @@ const main = async () => {
249218
{
250219
},
251220
async ({}) => {
221+
const dtClient = await createOAuthClient(oauthClient, oauthClientSecret, dtEnvironment, scopesBase.concat("environment-api:problems:read"));
252222
const result = await listProblems(dtClient);
253223
if (!result || result.length === 0) {
254224
return "No problems found";
@@ -264,6 +234,7 @@ const main = async () => {
264234
problemId: z.string().optional()
265235
},
266236
async ({problemId}) => {
237+
const dtClient = await createOAuthClient(oauthClient, oauthClientSecret, dtEnvironment, scopesBase.concat("environment-api:problems:read"));
267238
const result = await getProblemDetails(dtClient, problemId);
268239

269240
let resp = `The problem ${result.displayId} with the title ${result.title} (ID: ${result.problemId}).` +
@@ -303,6 +274,7 @@ const main = async () => {
303274
entityName: z.string()
304275
},
305276
async ({entityName}) => {
277+
const dtClient = await createOAuthClient(oauthClient, oauthClientSecret, dtEnvironment, scopesBase.concat("environment-api:entities:read", "storage:entities:read"));
306278
const entityResponse = await findMonitoredEntityByName(dtClient, entityName);
307279
return entityResponse;
308280
}
@@ -315,6 +287,7 @@ const main = async () => {
315287
entityId: z.string().optional()
316288
},
317289
async ({entityId}) => {
290+
const dtClient = await createOAuthClient(oauthClient, oauthClientSecret, dtEnvironment, scopesBase.concat("environment-api:entities:read"));
318291
const entityDetails = await getMonitoredEntityDetails(dtClient, entityId);
319292

320293
let resp = `Entity ${entityDetails.displayName} of type ${entityDetails.type} with \`entityId\` ${entityDetails.entityId}\n` +
@@ -342,6 +315,7 @@ const main = async () => {
342315
message: z.string().optional()
343316
},
344317
async ({channel, message}) => {
318+
const dtClient = await createOAuthClient(oauthClient, oauthClientSecret, dtEnvironment, scopesBase.concat("app-settings:objects:read"));
345319
const response = await sendSlackMessage(dtClient, slackConnectionId, channel, message);
346320

347321
return `Message sent to Slack channel: ${JSON.stringify(response)}`;
@@ -355,6 +329,7 @@ const main = async () => {
355329
entityName: z.string().optional()
356330
},
357331
async ({entityName}) => {
332+
const dtClient = await createOAuthClient(oauthClient, oauthClientSecret, dtEnvironment, scopesBase.concat("storage:logs:read"));
358333
const logs = await getLogsForEntity(dtClient, entityName);
359334

360335
return `Logs:\n${JSON.stringify(logs?.map(logLine => logLine?logLine.content:'Empty log'))}`;
@@ -368,6 +343,7 @@ const main = async () => {
368343
dqlStatement: z.string()
369344
},
370345
async ({dqlStatement}) => {
346+
const dtClient = await createOAuthClient(oauthClient, oauthClientSecret, dtEnvironment, scopesBase);
371347
const response = await verifyDqlStatement(dtClient, dqlStatement);
372348

373349
let resp = 'DQL Statement Verification:\n';
@@ -398,6 +374,23 @@ const main = async () => {
398374
dqlStatement: z.string()
399375
},
400376
async ({dqlStatement}) => {
377+
const dtClient = await createOAuthClient(
378+
oauthClient,
379+
oauthClientSecret,
380+
dtEnvironment,
381+
scopesBase.concat(
382+
'storage:buckets:read', // Read all system data stored on Grail
383+
'storage:logs:read', // Read logs for reliability guardian validations
384+
'storage:metrics:read', // Read metrics for reliability guardian validations
385+
'storage:bizevents:read', // Read bizevents for reliability guardian validations
386+
'storage:spans:read', // Read spans from Grail
387+
'storage:entities:read', // Read Entities from Grail
388+
'storage:events:read', // Read events from Grail
389+
'storage:system:read', // Read System Data from Grail
390+
'storage:user.events:read', // Read User events from Grail
391+
'storage:user.sessions:read' // Read User sessions from Grail
392+
)
393+
);
401394
const response = await executeDql(dtClient, dqlStatement);
402395

403396
return `DQL Response: ${JSON.stringify(response)}`;
@@ -415,6 +408,11 @@ const main = async () => {
415408
isPrivate: z.boolean().optional().default(false)
416409
},
417410
async ({problemType, teamName, channel, isPrivate}) => {
411+
const dtClient = await createOAuthClient(oauthClient, oauthClientSecret, dtEnvironment, scopesBase.concat(
412+
"automation:workflows:write",
413+
"automation:workflows:read",
414+
"automation:workflows:run"
415+
));
418416
const response = await createWorkflowForProblemNotification(dtClient, teamName, channel, problemType, isPrivate);
419417

420418
let resp = `Workflow Created: ${response?.id} with name ${response?.title}.\nYou can access the Workflow via the following link: ${dtEnvironment}/ui/apps/dynatrace.automations/workflows/${response?.id}.\nTell the user to inspect the Workflow by visiting the link.\n`;
@@ -440,6 +438,11 @@ const main = async () => {
440438
workflowId: z.string().optional()
441439
},
442440
async ({workflowId}) => {
441+
const dtClient = await createOAuthClient(oauthClient, oauthClientSecret, dtEnvironment, scopesBase.concat(
442+
"automation:workflows:write",
443+
"automation:workflows:read",
444+
"automation:workflows:run"
445+
));
443446
const response = await updateWorkflow(dtClient, workflowId, {
444447
isPrivate: false,
445448
});
@@ -455,6 +458,7 @@ const main = async () => {
455458
clusterId: z.string().optional().describe(`The Kubernetes (K8s) Cluster Id, referred to as k8s.cluster.uid (this is NOT the Dynatrace environment)`)
456459
},
457460
async ({clusterId}) => {
461+
const dtClient = await createOAuthClient(oauthClient, oauthClientSecret, dtEnvironment, scopesBase.concat("storage:events:read"));
458462
const events = await getEventsForCluster(dtClient, clusterId);
459463

460464
return `Kubernetes Events:\n${JSON.stringify(events)}`;
@@ -468,6 +472,7 @@ const main = async () => {
468472
entityIds: z.string().optional().describe("Comma separated list of entityIds"),
469473
},
470474
async ({entityIds}) => {
475+
const dtClient = await createOAuthClient(oauthClient, oauthClientSecret, dtEnvironment, scopesBase.concat("environment-api:entities:read", "settings:objects:read"));
471476
console.error(`Fetching ownership for ${entityIds}`);
472477
const ownershipInformation = await getOwnershipInformation(dtClient, entityIds);
473478
console.error(`Done!`);

0 commit comments

Comments
 (0)