Skip to main content
Glama

Dynatrace MCP Server

Official
dynatrace-oauth-auth-code-flow.test.ts3.23 kB
import { randomBytes } from 'crypto'; import { createAuthorizationUrl, startOAuthRedirectServer } from './dynatrace-oauth-auth-code-flow'; import { OAuthAuthorizationConfig } from './types'; describe('OAuth Authorization Code Flow', () => { const mockConfig: OAuthAuthorizationConfig = { clientId: 'dt0s08.mocked-client', redirectUri: 'http://localhost:5343/auth/login', scopes: ['app-engine:apps:run', 'app-engine:functions:run', 'storage:logs:read'], // Basic Example scopes }; test('createAuthorizationUrl generates valid URL with PKCE', () => { const result = createAuthorizationUrl('https://sso.dynatrace.com', mockConfig); // URL needs to match sso.dynatrace.com/oauth2/authorize expect(result.authorizationUrl).toMatch(/^https:\/\/sso\.dynatrace\.com\/oauth2\/authorize\?/); expect(result.codeVerifier).toMatch(/^[A-Za-z0-9_-]{62}$/); // Base64URL without padding (46 bytes = ~62 chars) expect(result.state).toMatch(/^[a-f0-9]{40}$/); // Hex string (20 bytes = 40 hex chars) // Parse the URL and verify query parameters const url = new URL(result.authorizationUrl); expect(url.searchParams.get('response_type')).toBe('code'); expect(url.searchParams.get('client_id')).toBe('dt0s08.mocked-client'); expect(url.searchParams.get('redirect_uri')).toBe('http://localhost:5343/auth/login'); expect(url.searchParams.get('scope')).toBe('app-engine:apps:run app-engine:functions:run storage:logs:read'); expect(url.searchParams.get('code_challenge_method')).toBe('S256'); expect(url.searchParams.get('code_challenge')).toMatch(/^[A-Za-z0-9_-]{43}$/); // SHA256 base64url = 43 chars expect(url.searchParams.get('state')).toBe(result.state); }); test('createAuthorizationUrl encodes scopes with %20 for spaces instead of +', () => { const result = createAuthorizationUrl('https://sso.dynatrace.com', mockConfig); // Check that the raw URL string contains %20 for spaces, not + expect(result.authorizationUrl).toMatch( /scope=app-engine%3Aapps%3Arun%20app-engine%3Afunctions%3Arun%20storage%3Alogs%3Aread/, ); // Verify that + is not used for space encoding in scopes expect(result.authorizationUrl).not.toMatch(/scope=.*\+.*(?=&|$)/); // Verify that colons are properly encoded as %3A expect(result.authorizationUrl).toMatch(/app-engine%3Aapps%3Arun/); expect(result.authorizationUrl).toMatch(/app-engine%3Afunctions%3Arun/); expect(result.authorizationUrl).toMatch(/storage%3Alogs%3Aread/); // Double-check by parsing the URL and verifying the decoded scope const url = new URL(result.authorizationUrl); expect(url.searchParams.get('scope')).toBe('app-engine:apps:run app-engine:functions:run storage:logs:read'); }); test('startOAuthRedirectServer returns server configuration', async () => { const port = (randomBytes(2).readUInt16BE(0) % 10000) + 5000; // Random port between 5000-5999 const result = await startOAuthRedirectServer(port); expect(result.redirectUri).toBe(`http://localhost:${port}/auth/login`); expect(result.server).toBeDefined(); expect(result.waitForAuthorizationCode).toBeDefined(); // Clean up result.server.close(); }); });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/dynatrace-oss/dynatrace-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server