Skip to main content
Glama
clabrado

dtctl

by clabrado

dtctl for VS Code

Zero-install Dynatrace Platform tooling. Everything dtctl does — DQL, documents, workflows, SLOs, settings, entities — reimplemented in pure TypeScript and shipped inside the extension. No CLI to download, no binary to unquarantine, no admin rights, no OS permission prompts.

  • 16 tools exposed to GitHub Copilot Chat (VS Code Language Model Tools API)

  • The same 16 tools exposed to Claude / any MCP client via a bundled stdio MCP server that VS Code auto-discovers — zero config

  • DQL query runner webview (table + JSON + CSV export), tenant explorer tree, status-bar tenant indicator, and command-palette verbs

  • Credentials never touch settings.json and never appear in logs. Browser-login tokens are stored as 0600 files in the extension's own storage directory (not the OS keychain, so nothing prompts for a keychain password); API tokens and OAuth client secrets continue to use VS Code SecretStorage


Install (locked-down managed machine)

You need VS Code and nothing else. No admin rights are required at any step.

  1. Copy dtctl-vscode-0.2.0.vsix to the machine (email, share drive, USB — it is a single file, ~2 MB, no installer).

  2. In VS Code: Extensions view → overflow menu → Install from VSIX… → pick the file. Or from a terminal: code --install-extension dtctl-vscode-0.2.0.vsix

  3. Reload the window when prompted. Done.

There is no post-install step. Nothing is written outside VS Code's own extension folder in your user profile, no service is registered, and nothing asks for elevation, Gatekeeper approval, or SmartScreen approval — because no native binary ships in the package.

Option B — Marketplace

Search the Extensions view for dtctl — Dynatrace Platform Tools and click Install.

Where it runs

Environment

Extension UI

Copilot Chat tools

MCP server

VS Code desktop (Win/macOS/Linux)

yes

yes

yes

GitHub Codespaces / dev containers

yes

yes

yes

Remote-SSH / WSL

yes

yes

yes

vscode.dev / github.dev (browser)

yes

yes

no (browser hosts cannot spawn a child process)

The browser build is the same TypeScript, compiled to a web worker bundle. Only the stdio MCP server — which by definition needs a process — is unavailable there.


Related MCP server: dynatrace-mcp

Login

Ctrl/Cmd+Shift+Pdtctl: Login (browser)

  1. Enter your Dynatrace environment URL (https://<id>.apps.dynatrace.com) and a profile name.

  2. VS Code opens your browser at the Dynatrace sign-in page. Sign in as you normally would — SSO, MFA and all.

  3. The tab shows "You can close this tab" and the profile appears in the status bar. Done.

No admin-created OAuth client is needed. Browser login uses Dynatrace's own first-party public OAuth clients (the same ones the dtctl CLI uses), with Authorization Code + PKCE (S256). There is no client secret anywhere — not in the extension, not in your settings, not on disk. You do not have to ask a tenant admin to mint a client ID/secret pair before you can use the extension.

Audit-log note. Because browser login reuses the dtctl CLI's public OAuth client, OAuth grants and token activity for this extension appear in Dynatrace audit logs attributed to dt0s12.dtctl-prod (the dtctl CLI), not to this extension by name. The signed-in user is recorded correctly — everything runs with that user's own permissions — but the extension has no separate application identity, so it cannot be audited or revoked independently of the dtctl CLI. If your security team needs the extension to be distinguishable, it needs its own registered OAuth client; set dtctl.oauthClientId once you have one.

The environment you sign in to is detected from the URL you type (apps.dynatrace.com → prod, dev.apps.dynatracelabs.com → dev, sprint.apps.dynatracelabs.com → sprint/hardening), and the URL is sent as the OAuth resource parameter so the issued token is bound to that tenant.

The loopback callback port

Sign-in completes by redirecting to a local listener on http://localhost:<port>/auth/login, bound explicitly to 127.0.0.1.

The extension tries port 3232 first — the port dtctl uses — and falls back to 3233/3234/3235 and then any free port if 3232 is busy. The Dynatrace OAuth clients accept a loopback redirect on any port, so a port conflict is not a failure mode; the actually-bound port is what goes into redirect_uri. If your machine blocks local loopback sockets entirely (some endpoint-protection policies do), login fails with a message saying exactly that.

Login times out after 5 minutes, and the listener is always closed afterwards — a failed or cancelled login never leaves a port bound.

Scopes (read-only by default)

Browser login requests dtctl's read-only scope set by default. Write scopes are opt-in via dtctl.loginScopeLevel, which mirrors dtctl's safety levels:

dtctl.loginScopeLevel

Requests

readonly (default)

40 read scopes

readwrite-mine

read + write/delete of resources you own

readwrite-all

read + write/delete of all resources

dangerously-unrestricted

every scope dtctl knows about

openid and offline_access are always requested — offline_access is what returns a refresh token. Access tokens are short-lived (~10 minutes) and are refreshed transparently in the background; you are only asked to sign in again if the refresh itself fails. Changing the setting takes effect on the next login. Write tools remain independently gated by dtctl.allowWriteTools.

dtctl.oauthClientId overrides the built-in client ID if your organisation issues a dedicated public OAuth client for this extension.

Where browser-login tokens are stored

Interactive-login tokens are written to disk, not the OS keychain:

<VS Code user data>/User/globalStorage/dtctl-tools.dtctl-vscode/oauth-tokens/<profile>.json

VS Code owns that directory (context.globalStorageUri) and resolves it to the right per-user location on Windows, macOS and Linux. It is outside your repo and workspace, so token files can never be committed or packaged into the VSIX.

This is deliberate. VS Code's SecretStorage is backed by the OS credential vault (macOS Keychain / Windows Credential Manager / libsecret), which can prompt for a password and is frequently unavailable on managed machines. dtctl has the same escape hatch (DTCTL_TOKEN_STORAGE=file).

Security properties:

  • Token files are created and chmoded 0600 (owner read/write only). On POSIX that is real protection. On Windows the POSIX mode is largely a no-op — the practical protection there is that the file lives under your own user profile directory, which is ACL'd to you. We do not claim more than that.

  • Writes are atomic: a temp file in the same directory is renamed over the target, so a crash mid-write cannot corrupt or truncate an existing token file, and no partial token file is left behind.

  • dtctl: Logout (browser login) deletes the token file from disk — it does not merely clear an in-memory cache.

  • Tokens are never logged. All diagnostic output goes through redact().

Keeping OAuth constants in sync with dtctl

The SSO endpoints, client IDs and callback contract are not hand-maintained. They are generated into src/kernel/dtctl-oauth-constants.ts from dtctl's own sdk/session/oauth_flow.go:

npm run sync:dtctl             # fetch upstream and regenerate the constants
npm run sync:dtctl -- --check  # exit 1 if the checked-in constants are stale

test/contract/dtctl-drift.contract.test.ts runs as part of npm test and fails with a per-constant diff if Dynatrace changes any of these upstream:

dtctl OAuth constants have CHANGED UPSTREAM.

  prod.tokenUrl
    checked-in: https://token.dynatrace.com/sso/oauth2/token
    upstream:   https://token.dynatrace.example/sso/oauth2/token

Fix: run `npm run sync:dtctl` to regenerate …

It is network-optional: with no network access it skips with an explanatory message rather than failing, so offline development and egress-restricted CI are unaffected.

This is build-time only. The extension never fetches dtctl source, and never fetches OIDC discovery, at run time — the login path resolves its endpoints entirely offline from the compiled-in constants. There is no code path by which a change upstream could redirect a live OAuth flow without a human running the sync and reviewing the diff.

Token-based profiles (unchanged)

dtctl: Add Tenant Profile still supports OAuth client credentials and classic dt0c01.… API tokens. Those credentials continue to live in VS Code SecretStorage exactly as before.


First-run setup (about 60 seconds)

For most users dtctl: Login (browser) above is the faster path — it needs no admin-created OAuth client. Use the flow below when you already have a client ID/secret or a classic API token.

  1. Ctrl/Cmd+Shift+Pdtctl: Add Tenant Profile

  2. Enter a profile name (e.g. prod), your tenant URL (https://<id>.apps.dynatrace.com), and pick the auth type:

    • OAuth client credentials — enter client ID, optional scopes, then the client secret

    • Classic API token — paste the dt0c01.… token

  3. The credential is stored with vscode.SecretStorage (OS keychain / Keychain Access / libsecret / Credential Manager, managed entirely by VS Code). The extension never writes it to a settings file, workspace file, log, or the VSIX.

Add as many profiles as you have tenants; the status bar shows the active one and clicking it switches profiles. dtctl.defaultProfile sets the startup profile.

Required permissions on the tenant

Read-only usage needs the standard read scopes (storage:*:read, document:documents:read, automation:workflows:read, settings:objects:read, environment-api:entities:read). Write verbs additionally need document:documents:write and the relevant write scopes.


Using it

Command palette

Command

What it does

dtctl: Login (browser)

browser sign-in with PKCE — no OAuth client needed

dtctl: Logout (browser login)

deletes the stored tokens from disk

dtctl: Add / Switch / Remove Tenant Profile

multi-tenant profile management

dtctl: Open DQL Query Runner

webview with table/JSON views and CSV export

dtctl: Run DQL Query

runs the current editor selection as DQL

dtctl: Export Query Results as CSV

opens the last result set as a CSV document

dtctl: Show Current User / List Profiles

whoami / contexts

dtctl: Find Resource / Get Resource by ID

resource browse and fetch

dtctl: Apply Resource from File

upsert the open JSON document (write-gated)

dtctl: Refresh Explorer

reload the tenant tree

dtctl: Copy MCP Server Config

config snippet for an external MCP client

Tenant explorer

The Dynatrace Tenants view in the Explorer sidebar lists profiles → resource types → live resources. Clicking a resource fetches and opens it as JSON.

Write safety

All write and destructive verbs are disabled by default. Set dtctl.allowWriteTools: true to enable them. Even then, destructive tools require an explicit confirm: true argument, agents see a confirmation dialog before execution, and the MCP server does not advertise write tools at all while the setting is off.


Agent access

See docs/SE-ONE-PAGER.md for the customer-facing enablement page. In short:

  • GitHub Copilot Chat — tools register on activation. Type #dtctl_query (or any other tool name) in chat, or just ask a question and let the agent pick.

  • Claude / MCP clients in VS Code — the extension registers an MCP server definition provider, so the server appears automatically with your active profile's credentials injected at start time. Nothing to paste, nothing to configure.

  • Standalone MCP clients — run dtctl: Copy MCP Server Config and paste the snippet into your client's config, then substitute your own token.


Tool surface (16)

Read tier — always available: dtctl_query, dtctl_verify, dtctl_get, dtctl_describe, dtctl_history, dtctl_find, dtctl_logs, dtctl_whoami, dtctl_contexts, dtctl_commands.

Write tier — requires dtctl.allowWriteTools: dtctl_apply, dtctl_share, dtctl_unshare.

Destructive tier — requires dtctl.allowWriteTools and confirm: true: dtctl_delete, dtctl_exec, dtctl_restore.

The registry in src/kernel/tools/index.ts is the single source of truth: the package.json contributions, the MCP tools/list payload, and the runtime handlers are all generated from or driven by it, and npm run gen:contributions -- --check fails the build if they drift.


Architecture

src/kernel/          pure TypeScript, zero vscode imports, zero native code
  auth.ts            OAuth client-credentials + classic token, cache/refresh/retry
  transport.ts       Live / Replay / Record HTTP transports
  dql.ts             DQL execute + poll + paging + CSV
  api/               Platform (gen3) and Classic API clients
  tools/             the tool registry and handlers
src/adapters/
  shared.ts          one executor -> byte-identical envelopes on every surface
  ui/core.ts  ui.ts  tree view, DQL webview, status bar, commands
  lm/core.ts  lm.ts  vscode.lm.registerTool  (Copilot Chat)
  mcp/core.ts mcp.ts vscode.lm.registerMcpServerDefinitionProvider (Claude/MCP)
src/mcp/server.ts    bundled stdio MCP server -> dist/mcp-server.js

Why no binary. A per-platform Go binary would need chmod +x, survive macOS Gatekeeper quarantine and Windows SmartScreen, and would not run in vscode.dev or a browser Codespace at all. Reimplementing the surface over fetch removes every one of those failure modes; the tradeoff is that parity with the Go CLI is maintained by hand (the registry + contract tests are how that is enforced). No fallback binary ships in this VSIX.


Development

npm ci
npm run build          # gen contributions + strict tsc + esbuild (3 bundles)
npm run test           # unit + contract + end-to-end MCP smoke
npm run package        # produces dtctl-vscode-<version>.vsix

npm run test:smoke stands up a fake tenant, spawns the bundled dist/mcp-server.js as a real child process, and drives a real MCP JSON-RPC conversation (initialize → tools/list → tools/call) against it.

License

Apache-2.0.

F
license - not found
-
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
1Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Latest Blog Posts

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/clabrado/dtctl-vscode'

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