dtctl
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@dtctllist all hosts in my environment"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
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
0600files 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.
Option A — sideload the VSIX (recommended for restricted environments)
Copy
dtctl-vscode-0.2.0.vsixto the machine (email, share drive, USB — it is a single file, ~2 MB, no installer).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.vsixReload 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
Browser login (recommended)
Ctrl/Cmd+Shift+P → dtctl: Login (browser)
Enter your Dynatrace environment URL (
https://<id>.apps.dynatrace.com) and a profile name.VS Code opens your browser at the Dynatrace sign-in page. Sign in as you normally would — SSO, MFA and all.
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
dtctlCLI's public OAuth client, OAuth grants and token activity for this extension appear in Dynatrace audit logs attributed todt0s12.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; setdtctl.oauthClientIdonce 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:
| Requests |
| 40 read scopes |
| read + write/delete of resources you own |
| read + write/delete of all resources |
| 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>.jsonVS 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 staletest/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.
Ctrl/Cmd+Shift+P→ dtctl: Add Tenant ProfileEnter 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
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 |
| browser sign-in with PKCE — no OAuth client needed |
| deletes the stored tokens from disk |
| multi-tenant profile management |
| webview with table/JSON views and CSV export |
| runs the current editor selection as DQL |
| opens the last result set as a CSV document |
|
|
| resource browse and fetch |
| upsert the open JSON document (write-gated) |
| reload the tenant tree |
| 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 Configand 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.jsWhy 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>.vsixnpm 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.
This server cannot be installed
Maintenance
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
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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