Claude Code is the strongest fit for Datadog MCP among the agents in this course, mainly because it can hold both your codebase and the observability tool calls in the same context window and run multi-step tool chains without hand-holding. This topic walks through wiring the server into Claude Code — both the CLI and the VS Code extension — and then runs a realistic incident investigation end to end: from a vague symptom report to a traced root cause in application code.
Installing and Connecting Datadog MCP to Claude Code
Claude Code reads MCP server definitions from .claude/settings.json (project-shared) or .claude/settings.local.json (per-developer, gitignored by default — the right place for API keys). You can also register the server imperatively with the CLI, which writes to the same config for you.
claude mcp add datadog -- npx -y @datadog/datadog-mcp-server
Then set the required environment variables. Since claude mcp add doesn't take env vars directly in every version, edit the generated entry in .claude/settings.local.json:
{
"mcpServers": {
"datadog": {
"command": "npx",
"args": ["-y", "@datadog/datadog-mcp-server"],
"env": {
"DD_API_KEY": "${DD_API_KEY}",
"DD_APP_KEY": "${DD_APP_KEY}",
"DD_SITE": "datadoghq.com"
}
}
}
}
Using ${DD_API_KEY} syntax means Claude Code substitutes from your actual shell environment at launch time rather than storing the literal secret in the file — export these in your shell profile (~/.zshrc or equivalent) beforehand:
export DD_API_KEY="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
export DD_APP_KEY="yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy"
Verify the connection:
claude mcp list
You should see datadog listed as connected. If it shows a connection error, run claude mcp get datadog to see the last error message — most often it's a missing env var, not a code problem.
For VS Code, install the Claude Code extension from the marketplace, open the command palette, and run Claude Code: Open MCP Settings — this points at the same .claude/settings.json used by the CLI, so a server configured in one surface is immediately available in the other. No separate VS Code-specific Datadog config exists; the extension just hosts the same Claude Code agent inside the editor's chat panel, which matters for the trace-to-code workflow below since you get file navigation for free.
Once connected, sanity-check with a trivial query before anything real:
List the Datadog monitors currently in an Alert state.
If that returns real monitor names from your org, keys and site are correct.
Tips
- KeepDD_API_KEYandDD_APP_KEYout of.claude/settings.json(the shared, committed file) — use.claude/settings.local.jsonor shell-substituted env vars only.
-claude mcp get datadogsurfaces the actual connection error; don't guess, read it.
- Run the trivial "list alerting monitors" check every time you rotate keys or switch Datadog sites — it's the fastest way to catch aDD_SITEmismatch.
Building Log and Metric Queries from Plain-English Questions
The practical skill here is learning to phrase prompts so Claude Code builds the query you actually want, rather than a plausible-but-wrong one. Claude Code shows you the tool call it's about to make (or the result, depending on your permission settings), so treat the first few queries in any session as a chance to check its syntax translation.
Plain-English to log query:
You: Show me error logs from the payment-service in the last 2 hours,
excluding anything from the staging environment.
Claude builds: service:payment-service status:error -env:staging
Time range: now-2h to now
Plain-English to metric query:
You: What's the average request duration for the checkout API over
the last 6 hours, broken down by endpoint?
Claude builds: avg:trace.express.request.duration{service:checkout-api} by {resource_name}
Time range: now-6h to now
Vague or ambiguous prompts produce vague or wrong queries. "Show me recent errors" with no service scope will either time out on a high-volume account or return a firehose that isn't useful. Anchor every prompt with at minimum a service name and a time bound:
Show me errors from service:order-service in the last 30 minutes,
grouped by error message.
For metric aggregations across multiple services, be explicit about the grouping dimension — "by service" vs "by host" vs "by resource_name" produces materially different tables, and Claude will pick one interpretation silently if you don't specify:
Compare p95 latency for checkout-api and payment-service over the
last hour, grouped by service.
Claude Code will typically issue two separate metric queries (one per service) rather than a single combined query, since Datadog's query language scopes a single query to one filter expression at a time unless you explicitly construct a multi-series query — worth knowing so you're not surprised by two tool calls where you expected one.
Tips
- Always give a service tag and a time window in the same sentence as your question — the two most common causes of a bad first query are missing scope and missing time bound.
- Ask "what query did you run" if the results look off — Claude Code will echo the exact Datadog query string, which is usually enough to spot the mistake yourself.
- For multi-service comparisons, specify the grouping dimension explicitly rather than trusting the default interpretation.
Correlating APM Traces with Application Code in VS Code
This is the workflow that makes Claude Code + Datadog MCP worth the setup effort over just using the Datadog UI: the agent has your repository open in the same session as the trace data, so it can go from a slow span directly to the function responsible.
The connective tissue is the trace ID. Datadog APM tags every trace with a trace_id, and if your app logs it (most Datadog tracing libraries inject dd.trace_id and dd.span_id into structured logs automatically via the language tracer's log-injection feature), you can pivot from an error log straight to the full trace waterfall.
This log line has dd.trace_id=6890423571920484812. Pull the full trace
and show me which service and function the slow span comes from.
Claude Code calls the trace tool with that ID, gets back the span waterfall, identifies the highest-duration span (say, a span named pg.query inside checkout-api taking 1.8s of a 2.1s total request), and — because it's running inside your repo in VS Code — can grep for the corresponding query or call site.
The slow span is a Postgres query on the orders table in checkout-api,
resource name "SELECT * FROM orders WHERE customer_id = ?". Find where
this query originates in our codebase.
From here Claude Code searches your source (using its normal file tools, not MCP) for the matching query or ORM call — often landing on something like an unindexed lookup in src/services/orderService.ts — and can propose the fix (add an index, add a LIMIT, batch the calls) in the same conversation turn. This loop — trace span → resource name → grep → fix — is the single highest-value pattern in this whole module.
If your services don't currently inject dd.trace_id into logs, this is worth fixing before relying on MCP-driven correlation: without it, the agent has to correlate log and trace timestamps approximately (same service, overlapping few-second window), which is meaningfully less reliable than an exact ID match. Datadog's tracing libraries (dd-trace for Node, ddtrace for Python, dd-trace-java) support automatic log injection with a config flag — for Node it's DD_LOGS_INJECTION=true.
Tips
- EnableDD_LOGS_INJECTION=true(or the equivalent for your tracer) in every service before you lean on AI-assisted correlation — exact trace ID matching beats timestamp-window guessing every time.
- Ask for the resource name and service, not just "the slow part," when pivoting from trace to code — resource names map far more directly to a grep target than a generic span name does.
- Keep VS Code's workspace scoped to the actual monorepo/service root so Claude Code's code search after a trace lookup covers the right files without noise from unrelated repos.
Prompting AI to Produce an Incident Timeline from Telemetry
Once you've pulled monitor state, logs, and traces, the last step of an investigation is almost always "write this up." Claude Code is genuinely good at this specific task — turning timestamped, disparate tool outputs into a coherent narrative — provided you give it all the source data in the same conversation rather than asking it to remember from an earlier session.
A structured prompt gets a structured, useful result:
Using the monitor alert data, logs, and traces we've pulled in this
conversation, write an incident timeline with:
1. Timestamp of first anomaly
2. Timestamp monitor fired
3. Root cause (service, code path, specific query/function)
4. Timestamp of mitigation (if we've discussed one)
5. Customer impact estimate based on error log volume
Format as a markdown table followed by a one-paragraph summary.
Typical output resembles:
| Time (UTC) | Event |
|---|---|
| 14:15 | p99 latency on checkout-api begins climbing (trace.express.request.duration) |
| 14:32 | checkout-latency-p99 monitor fires (Alert) |
| 14:34 | Error rate on /checkout/complete rises from 0.1% to 4.2% |
| 14:41 | Root cause identified: unindexed query on orders.customer_id |
| 14:58 | Index added, deployed via hotfix-orders-index |
| 15:04 | p99 latency returns to baseline |
Summary: A missing index on orders.customer_id caused query time to
degrade under load starting ~14:15 UTC, tripping the checkout-latency-p99
monitor at 14:32. Approximately 380 checkout attempts errored during the
43-minute window based on log volume for status:error scoped to
checkout-api.
Verify every number before this goes into a shared postmortem doc — the row timestamps come from real tool output, but a "customer impact estimate" derived from log volume is an inference, and inferences from an LLM should be spot-checked against the raw log count yourself, not taken as authoritative. A good habit: ask Claude Code to cite which tool call each row came from, so you (or a reviewer) can trace every claim back to source data in thirty seconds.
Add a "source" column citing which query or trace ID backs each row.
Tips
- Keep the whole investigation in one conversation so the timeline-generation prompt has every tool result available — starting a fresh session loses that context.
- Ask for a "source" column citing the exact query or trace ID behind each timeline row; it turns an unverifiable narrative into an auditable one.
- Treat the "customer impact estimate" as a rough order of magnitude from log volume, not a precise figure — cross-check against your actual error-budget or business metrics before publishing.
Tips
Tips
- Configure keys via.claude/settings.local.jsonor shell env substitution, never in the shared.claude/settings.json.
- Anchor every log/metric prompt with a service name and time window to avoid slow, unscoped queries.
- Enable trace-log correlation (DD_LOGS_INJECTION=trueor equivalent) across your services before relying on AI-assisted trace-to-code pivots.
- Require a source citation on every row of an AI-generated incident timeline before it goes into a postmortem doc.