Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
.env
dist/*
node_modules/
.vscode/
/.vscode/*
!/.vscode/mcp.json

tsconfig.tsbuildinfo
10 changes: 10 additions & 0 deletions .vscode/mcp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"servers": {
"my-dynatrace-mcp-server": {
"command": "node",
"cwd": "${workspaceFolder}",
"args": ["${workspaceFolder}/dist/index.js"],
"envFile": "${workspaceFolder}/.env"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,36 +5,24 @@ import {
RequestBodyTypes,
} from '@dynatrace-sdk/http-client';
import { getSSOUrl } from 'dt-app';
import { version as VERSION } from '../package.json';

// Define the OAuthTokenResponse interface to match the expected structure of the response
export interface OAuthTokenResponse {
scope?: string;
token_type?: string;
expires_in?: number;
access_token?: string;
errorCode?: number;
message?: string;
issueId?: string;
error?: string;
error_description?: string;
}
import { version as VERSION } from '../../package.json';
import { OAuthTokenResponse } from './types';

/**
* Uses the provided oauth Client ID and Secret and requests a token
* Uses the provided oauth Client ID and Secret and requests a token via client-credentials flow
* @param clientId - OAuth Client ID for Dynatrace
* @param clientSecret - Oauth Client Secret for Dynatrace
* @param authUrl - SSO Authentication URL
* @param ssoAuthUrl - SSO Authentication URL
* @param scopes - List of requested scopes
* @returns
*/
const requestToken = async (
clientId: string,
clientSecret: string,
authUrl: string,
ssoAuthUrl: string,
scopes: string[],
): Promise<OAuthTokenResponse> => {
const res = await fetch(authUrl, {
const res = await fetch(ssoAuthUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
Expand Down Expand Up @@ -83,7 +71,9 @@ export class ExtendedOauthClient extends _OAuthHttpClient {
}
}

/** Create an Oauth Client based on clientId, clientSecret, environmentUrl and scopes */
/** Create an Oauth Client based on clientId, clientSecret, environmentUrl and scopes
* This uses a client-credentials flow to request a token from the SSO endpoint.
*/
export const createOAuthClient = async (
clientId: string,
clientSecret: string,
Expand Down Expand Up @@ -130,28 +120,3 @@ export const createOAuthClient = async (
userAgent,
);
};

/** Helper function to call an app-function via platform-api */
export const callAppFunction = async (
dtClient: _OAuthHttpClient,
appId: string,
functionName: string,
payload: any,
) => {
console.error(`Sending payload ${JSON.stringify(payload)}`);

const response = await dtClient.send({
url: `/platform/app-engine/app-functions/v1/apps/${appId}/api/${functionName}`,
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: payload,
statusValidator: (status: number) => {
return [200].includes(status);
},
});

return await response.body('json');
};
12 changes: 12 additions & 0 deletions src/authentication/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Define the OAuthTokenResponse interface to match the expected structure of the response
export interface OAuthTokenResponse {
scope?: string;
token_type?: string;
expires_in?: number;
access_token?: string;
errorCode?: number;
message?: string;
issueId?: string;
error?: string;
error_description?: string;
}
26 changes: 26 additions & 0 deletions src/capabilities/call-app-function.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { _OAuthHttpClient } from '@dynatrace-sdk/http-client';

/** Helper function to call an app-function via platform-api */
export const callAppFunction = async (
dtClient: _OAuthHttpClient,
appId: string,
functionName: string,
payload: any,
) => {
console.error(`Sending payload ${JSON.stringify(payload)}`);

const response = await dtClient.send({
url: `/platform/app-engine/app-functions/v1/apps/${appId}/api/${functionName}`,
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: payload,
statusValidator: (status: number) => {
return [200].includes(status);
},
});

return await response.body('json');
};
2 changes: 1 addition & 1 deletion src/capabilities/get-ownership-information.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { _OAuthHttpClient } from '@dynatrace-sdk/http-client';
import { callAppFunction } from '../dynatrace-clients';
import { callAppFunction } from './call-app-function';

export const getOwnershipInformation = async (dtClient: _OAuthHttpClient, entityIds: string) => {
const ownershipResponse = await callAppFunction(dtClient, 'dynatrace.ownership', 'get-ownership-from-entity', {
Expand Down
2 changes: 1 addition & 1 deletion src/capabilities/send-slack-message.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { _OAuthHttpClient } from '@dynatrace-sdk/http-client';
import { callAppFunction } from '../dynatrace-clients';
import { callAppFunction } from './call-app-function';

export const sendSlackMessage = async (
dtClient: _OAuthHttpClient,
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { config } from 'dotenv';
import { z, ZodRawShape, ZodTypeAny } from 'zod';

import { version as VERSION } from '../package.json';
import { createOAuthClient } from './dynatrace-clients';
import { createOAuthClient } from './authentication/dynatrace-clients';
import { listVulnerabilities } from './capabilities/list-vulnerabilities';
import { listProblems } from './capabilities/list-problems';
import { getProblemDetails } from './capabilities/get-problem-details';
Expand Down