@@ -34,43 +34,11 @@ import { DynatraceEnv, getDynatraceEnv } from "./getDynatraceEnv";
34
34
35
35
config ( ) ;
36
36
37
- let scopes = [
37
+ let scopesBase = [
38
38
'app-engine:apps:run' , // needed for environmentInformationClient
39
39
'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
61
40
] ;
62
41
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
-
74
42
const main = async ( ) => {
75
43
// read Environment variables
76
44
let dynatraceEnv : DynatraceEnv ;
@@ -82,9 +50,6 @@ const main = async () => {
82
50
}
83
51
const { oauthClient, oauthClientSecret, dtEnvironment, slackConnectionId } = dynatraceEnv ;
84
52
85
- // create an oauth-client
86
- const dtClient = await createOAuthClient ( oauthClient , oauthClientSecret , dtEnvironment , scopes ) ;
87
-
88
53
console . error ( `Starting Dynatrace MCP Server v${ VERSION } ...` ) ;
89
54
const server = new McpServer (
90
55
{
@@ -139,6 +104,8 @@ const main = async () => {
139
104
"Get information about the connected Dynatrace Environment (Tenant)" ,
140
105
{ } ,
141
106
async ( { } ) => {
107
+ // create an oauth-client
108
+ const dtClient = await createOAuthClient ( oauthClient , oauthClientSecret , dtEnvironment , scopesBase ) ;
142
109
const environmentInformationClient = new EnvironmentInformationClient ( dtClient ) ;
143
110
144
111
const environmentInfo = await environmentInformationClient . getEnvironmentInformation ( ) ;
@@ -156,6 +123,7 @@ const main = async () => {
156
123
"List all vulnerabilities from Dynatrace ",
157
124
{ } ,
158
125
async ( { } ) => {
126
+ const dtClient = await createOAuthClient ( oauthClient , oauthClientSecret , dtEnvironment , scopesBase . concat ( "environment-api:security-problems:read" ) ) ;
159
127
const result = await listVulnerabilities ( dtClient ) ;
160
128
if ( ! result || result . length === 0 ) {
161
129
return "No vulnerabilities found" ;
@@ -181,6 +149,7 @@ const main = async () => {
181
149
securityProblemId : z . string ( ) . optional ( )
182
150
} ,
183
151
async ( { securityProblemId} ) => {
152
+ const dtClient = await createOAuthClient ( oauthClient , oauthClientSecret , dtEnvironment , scopesBase . concat ( "environment-api:security-problems:read" ) ) ;
184
153
const result = await getVulnerabilityDetails ( dtClient , securityProblemId ) ;
185
154
186
155
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 () => {
249
218
{
250
219
} ,
251
220
async ( { } ) => {
221
+ const dtClient = await createOAuthClient ( oauthClient , oauthClientSecret , dtEnvironment , scopesBase . concat ( "environment-api:problems:read" ) ) ;
252
222
const result = await listProblems ( dtClient ) ;
253
223
if ( ! result || result . length === 0 ) {
254
224
return "No problems found" ;
@@ -264,6 +234,7 @@ const main = async () => {
264
234
problemId : z . string ( ) . optional ( )
265
235
} ,
266
236
async ( { problemId} ) => {
237
+ const dtClient = await createOAuthClient ( oauthClient , oauthClientSecret , dtEnvironment , scopesBase . concat ( "environment-api:problems:read" ) ) ;
267
238
const result = await getProblemDetails ( dtClient , problemId ) ;
268
239
269
240
let resp = `The problem ${ result . displayId } with the title ${ result . title } (ID: ${ result . problemId } ).` +
@@ -303,6 +274,7 @@ const main = async () => {
303
274
entityName : z . string ( )
304
275
} ,
305
276
async ( { entityName} ) => {
277
+ const dtClient = await createOAuthClient ( oauthClient , oauthClientSecret , dtEnvironment , scopesBase . concat ( "environment-api:entities:read" , "storage:entities:read" ) ) ;
306
278
const entityResponse = await findMonitoredEntityByName ( dtClient , entityName ) ;
307
279
return entityResponse ;
308
280
}
@@ -315,6 +287,7 @@ const main = async () => {
315
287
entityId : z . string ( ) . optional ( )
316
288
} ,
317
289
async ( { entityId} ) => {
290
+ const dtClient = await createOAuthClient ( oauthClient , oauthClientSecret , dtEnvironment , scopesBase . concat ( "environment-api:entities:read" ) ) ;
318
291
const entityDetails = await getMonitoredEntityDetails ( dtClient , entityId ) ;
319
292
320
293
let resp = `Entity ${ entityDetails . displayName } of type ${ entityDetails . type } with \`entityId\` ${ entityDetails . entityId } \n` +
@@ -342,6 +315,7 @@ const main = async () => {
342
315
message : z . string ( ) . optional ( )
343
316
} ,
344
317
async ( { channel, message} ) => {
318
+ const dtClient = await createOAuthClient ( oauthClient , oauthClientSecret , dtEnvironment , scopesBase . concat ( "app-settings:objects:read" ) ) ;
345
319
const response = await sendSlackMessage ( dtClient , slackConnectionId , channel , message ) ;
346
320
347
321
return `Message sent to Slack channel: ${ JSON . stringify ( response ) } ` ;
@@ -355,6 +329,7 @@ const main = async () => {
355
329
entityName : z . string ( ) . optional ( )
356
330
} ,
357
331
async ( { entityName} ) => {
332
+ const dtClient = await createOAuthClient ( oauthClient , oauthClientSecret , dtEnvironment , scopesBase . concat ( "storage:logs:read" ) ) ;
358
333
const logs = await getLogsForEntity ( dtClient , entityName ) ;
359
334
360
335
return `Logs:\n${ JSON . stringify ( logs ?. map ( logLine => logLine ?logLine . content :'Empty log' ) ) } ` ;
@@ -368,6 +343,7 @@ const main = async () => {
368
343
dqlStatement : z . string ( )
369
344
} ,
370
345
async ( { dqlStatement} ) => {
346
+ const dtClient = await createOAuthClient ( oauthClient , oauthClientSecret , dtEnvironment , scopesBase ) ;
371
347
const response = await verifyDqlStatement ( dtClient , dqlStatement ) ;
372
348
373
349
let resp = 'DQL Statement Verification:\n' ;
@@ -398,6 +374,23 @@ const main = async () => {
398
374
dqlStatement : z . string ( )
399
375
} ,
400
376
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
+ ) ;
401
394
const response = await executeDql ( dtClient , dqlStatement ) ;
402
395
403
396
return `DQL Response: ${ JSON . stringify ( response ) } ` ;
@@ -415,6 +408,11 @@ const main = async () => {
415
408
isPrivate : z . boolean ( ) . optional ( ) . default ( false )
416
409
} ,
417
410
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
+ ) ) ;
418
416
const response = await createWorkflowForProblemNotification ( dtClient , teamName , channel , problemType , isPrivate ) ;
419
417
420
418
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 () => {
440
438
workflowId : z . string ( ) . optional ( )
441
439
} ,
442
440
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
+ ) ) ;
443
446
const response = await updateWorkflow ( dtClient , workflowId , {
444
447
isPrivate : false ,
445
448
} ) ;
@@ -455,6 +458,7 @@ const main = async () => {
455
458
clusterId : z . string ( ) . optional ( ) . describe ( `The Kubernetes (K8s) Cluster Id, referred to as k8s.cluster.uid (this is NOT the Dynatrace environment)` )
456
459
} ,
457
460
async ( { clusterId} ) => {
461
+ const dtClient = await createOAuthClient ( oauthClient , oauthClientSecret , dtEnvironment , scopesBase . concat ( "storage:events:read" ) ) ;
458
462
const events = await getEventsForCluster ( dtClient , clusterId ) ;
459
463
460
464
return `Kubernetes Events:\n${ JSON . stringify ( events ) } ` ;
@@ -468,6 +472,7 @@ const main = async () => {
468
472
entityIds : z . string ( ) . optional ( ) . describe ( "Comma separated list of entityIds" ) ,
469
473
} ,
470
474
async ( { entityIds} ) => {
475
+ const dtClient = await createOAuthClient ( oauthClient , oauthClientSecret , dtEnvironment , scopesBase . concat ( "environment-api:entities:read" , "settings:objects:read" ) ) ;
471
476
console . error ( `Fetching ownership for ${ entityIds } ` ) ;
472
477
const ownershipInformation = await getOwnershipInformation ( dtClient , entityIds ) ;
473
478
console . error ( `Done!` ) ;
0 commit comments