·

Datadog MCP With Cursor

Set up Datadog MCP in Cursor so your AI agent can query metrics, logs, and monitors right from your editor.

Cursor's pitch is an AI-native IDE, and Agent Mode with MCP support is where that pitch actually pays off for observability work — you get the trace-to-code pivot from the Claude Code + VS Code topic, but built on Cursor's own agent loop and composer UI. This topic covers the Cursor-specific setup, the trace-to-code and instrumentation-gap workflows that Cursor's tighter code-context integration makes especially strong, and the honest limitations you'll hit.

Connecting Datadog MCP to Cursor Agent Mode

Cursor reads MCP configuration from .cursor/mcp.json at the project root, or a global ~/.cursor/mcp.json for servers you want available across all projects. The format:

{
  "mcpServers": {
    "datadog": {
      "command": "npx",
      "args": ["-y", "@datadog/datadog-mcp-server"],
      "env": {
        "DD_API_KEY": "your-api-key",
        "DD_APP_KEY": "your-app-key",
        "DD_SITE": "datadoghq.com"
      }
    }
  }
}

Cursor's MCP config, as of current versions, does not reliably support shell environment variable interpolation inside mcp.json the way Claude Code or Gemini CLI do — check your Cursor version's release notes before assuming ${VAR} syntax works, since this has been inconsistent across releases. The safer default: keep .cursor/mcp.json out of version control entirely (add it to .gitignore) and accept that the keys live in that file in plaintext locally, or use an OS-level secrets tool that injects the file at IDE startup via a pre-launch script if your organization requires it.

echo ".cursor/mcp.json" >> .gitignore

Enable and verify the server from Cursor's settings: Cursor Settings → MCP shows all configured servers with a green/red status indicator and the tool count each one exposes. A green dot confirms the process is running; it doesn't confirm the API/app keys are valid — same caveat as every other client in this module.

To actually use it, switch the chat panel to Agent Mode (not Ask mode — Ask mode in Cursor is read-only over your codebase and won't invoke MCP tools in most current versions) and confirm the Datadog tools are available by checking the tool picker or simply asking:

List Datadog monitors currently in an Alert state.

Tips
- Keep .cursor/mcp.json out of git — Cursor's env-var interpolation support in this file has been inconsistent across versions, so plaintext keys are the common real-world state; don't let that state end up committed.
- MCP tools are only invoked from Agent Mode, not Ask mode — if the agent isn't calling Datadog tools at all, check which mode you're in first.
- The green status dot in Cursor Settings → MCP confirms the process connected, not that your credentials are valid against Datadog's API.


Jumping from a Trace Span to the Exact Code Path in Cursor

This is Cursor's strongest use case for Datadog MCP, and it works essentially the same way as the Claude Code + VS Code pattern, but Cursor's codebase indexing (its own embedding-based semantic search over your repo, separate from MCP) makes the "find where this originates" step faster on large, unfamiliar codebases specifically.

Start from a trace ID, same as before:

Pull the full trace for dd.trace_id=1839472651038291744 and identify
the span with the highest self-time (not just total duration).

Asking for self-time rather than total duration matters — a parent span's total duration includes all its children, so the "slowest" span by total duration is often just the outermost wrapper, not the actual bottleneck. Self-time isolates the span that's actually doing the slow work.

Once you have the offending span — say a redis.command span with high self-time and resource name HGETALL user:session:* — hand off to Cursor's code search:

Find where we call Redis HGETALL for user sessions in this codebase,
and check if there's a way this ends up scanning a large hash rather
than fetching a specific field.

Cursor's semantic codebase index (built automatically on open, refreshed incrementally) tends to find the relevant call site even when the trace's resource name doesn't literally match a string in your code — e.g., matching an ORM/cache-wrapper abstraction like sessionStore.getAll() to the underlying HGETALL call, which a plain grep would miss entirely. That's the concrete advantage over a pure grep-based approach: semantic matching bridges the gap between Datadog's resource-name vocabulary (raw command/query text) and your codebase's abstraction vocabulary (wrapper function names).

From there, ask directly for the fix:

Suggest a fix — we only need the "expires_at" field, not the whole hash.

A reasonable Cursor response proposes swapping HGETALL for HGET user:session:{id} expires_at, with the actual diff against the real file open in the editor, ready to review inline rather than pasted as a code block you have to manually apply.

Tips
- Ask explicitly for the highest self-time span, not just "the slowest span" — total duration on a parent span is misleading when it includes child span time.
- Lean on Cursor's semantic codebase index for the trace-to-code pivot when the Datadog resource name (raw command/query text) doesn't literally appear in your code — it bridges abstraction layers a plain grep can't.
- Review the proposed diff against the actual open file before accepting — Cursor applies suggested fixes as real inline edits, so treat "suggest a fix" prompts with the same scrutiny as any AI-generated code change.


Adding Missing Instrumentation Based on Observability Gaps

A less obvious but genuinely valuable workflow: use Datadog MCP to find where your telemetry has holes, then use Cursor's code-editing strength to fill them — in the same session, without switching tools.

Start by asking the agent to look for a suspiciously quiet span or missing breakdown:

Pull traces for service:order-service resource:"POST /orders" from
the last hour. Is there a span for the inventory-check step, or does
that logic seem to happen inside a single unlabeled span?

If the trace waterfall shows one big span (say, the top-level express.request span) with no child spans breaking out inventory check, payment authorization, and order persistence as distinct steps, that's a real instrumentation gap — you can't tell which of those three sub-steps is slow when they're all bundled invisibly inside one measurement.

Show me the handler function for POST /orders in this codebase, and
add manual APM spans around the inventory check, payment authorization,
and order persistence steps using dd-trace's tracer.trace() API.

Cursor, working directly in your open file, can generate the instrumentation using the correct tracer API for your language — for Node with dd-trace, that's typically:

const tracer = require('dd-trace');

async function handleCreateOrder(req, res) {
  const inventoryResult = await tracer.trace('order.inventory_check', async () => {
    return checkInventory(req.body.items);
  });

  const paymentResult = await tracer.trace('order.payment_authorization', async () => {
    return authorizePayment(req.body.payment);
  });

  const order = await tracer.trace('order.persistence', async () => {
    return persistOrder(req.body, paymentResult);
  });

  res.json({ order });
}

Verify the actual tracer API signature against your installed dd-trace version before accepting this wholesale — the API has been stable for the common tracer.trace(name, fn) pattern across recent major versions, but always confirm against your package.json-pinned version's docs, since minor signature differences (callback style vs promise style) do exist across versions.

After deploying, close the loop by confirming the new spans actually show up:

Pull a fresh trace for POST /orders and confirm the three new child
spans appear with reasonable durations.

Tips
- A single large, unbroken span covering multiple logical steps is the clearest signal of an instrumentation gap — ask the agent to flag traces that look like this rather than waiting to notice it yourself.
- Verify the generated tracer API call against your actual installed dd-trace (or equivalent) version — signature details do shift across major versions.
- Always confirm new instrumentation actually appears in a real trace after deploying — a manual span that's misplaced (wrong scope, wrong async boundary) can silently fail to record without throwing an error.


Known Limitations and Workarounds for Datadog MCP in Cursor

Inconsistent env var support in mcp.json. Already flagged above — treat this as your baseline assumption until you've confirmed otherwise on your specific Cursor version, and default to keeping the config file out of git.

Ask mode doesn't invoke MCP tools. New users regularly get confused when a Datadog question in Ask mode returns a generic, non-data-backed answer. Always confirm you're in Agent Mode before an observability query — this trips up even experienced Cursor users who are used to switching modes for different coding tasks.

No persistent "investigation session" concept. Like most current agent IDEs, closing a Cursor chat/composer loses the tool-call history for that thread unless you keep it open. For a multi-hour incident, keep a single composer thread alive rather than starting fresh ones, or manually paste a running summary into a new thread if you must restart.

Codebase indexing lag on very large monorepos. Cursor's semantic index needs to build (and periodically rebuild) before the trace-to-code semantic matching described above works well. On a very large monorepo, right after opening the project or after a large refactor, the index may be stale — if code search results seem oddly generic or miss an obvious match, check indexing status before assuming the trace-to-code pivot has failed.

No native rate-limit backoff visibility. Similar to other clients, if you hit Datadog API throttling mid-investigation, Cursor surfaces the raw error rather than a friendly retry-with-backoff message in every version — pace your own queries during a heavy session rather than firing many rapid-fire trace pulls back to back.

Instrumentation-gap suggestions are advisory, not validated. The manual-span-instrumentation workflow above produces code Cursor believes is correct, but it hasn't actually run and confirmed the span records properly until you deploy and check — always close that loop, as shown, rather than treating the generated code as done.

Tips
- Default to Agent Mode for any Datadog-related question; Ask mode silently won't use MCP tools in most current versions.
- Keep one composer thread open for the duration of a multi-hour investigation rather than restarting — tool-call history doesn't persist across new threads.
- Check Cursor's indexing status if trace-to-code semantic matching seems to be missing an obvious call site, especially right after opening a large project.


Tips

Tips
- Keep .cursor/mcp.json out of version control by default, since reliable env-var interpolation for secrets in this file isn't guaranteed across versions.
- Ask for the highest self-time span, not the highest total-duration span, when pivoting from a trace to a code fix — total duration on parent spans is misleading.
- Use the instrumentation-gap workflow (spot a suspiciously large unbroken span, add manual tracer.trace() calls, verify in a fresh trace after deploy) as a standing habit, not a one-off exercise.
- Stay in Agent Mode and keep a single composer thread alive for the length of an investigation — both are easy to get wrong and both silently degrade the workflow.