·

Terraform MCP With Cursor

Set up Terraform MCP in Cursor so your AI agent can plan, review, and manage infrastructure as code right from your editor.

Cursor's advantage for Terraform work is proximity — Agent mode sees the exact .tf files open in your editor, so a plan review or a module edit happens right next to the code that produced it, no copy-pasting plan output into a separate chat window. The trade-off is that Cursor's MCP integration is scoped per-workspace and its confirmation UI for tool calls is less configurable than Claude Code's permission system, so the discipline of keeping apply out of reach falls more on server configuration than IDE settings. This topic covers connecting the server to Agent mode, authoring HCL with live schema awareness, reviewing plans next to code, and where Cursor's Terraform support has rough edges.

Connecting Terraform MCP to Cursor Agent Mode

Cursor reads MCP servers from .cursor/mcp.json at the workspace root, or ~/.cursor/mcp.json globally:

{
  "mcpServers": {
    "terraform-registry": {
      "command": "docker",
      "args": ["run", "-i", "--rm", "hashicorp/terraform-mcp-server"]
    },
    "terraform-cli": {
      "command": "terraform-mcp-server",
      "args": [
        "--allowed-dirs", "${workspaceFolder}/infra",
        "--deny-glob", "**/*.tfstate",
        "--tools", "init,validate,plan,state_list,state_show"
      ],
      "env": {
        "TF_IN_AUTOMATION": "true",
        "TF_WORKSPACE": "staging"
      }
    }
  }
}

Open Cursor Settings → MCP to confirm both show green and expand the tool list — this is also where you'll notice if a server crashed silently, which happens more often with the Docker-based registry server than the native binary if Docker Desktop isn't already running when Cursor starts:

docker info >/dev/null 2>&1 && echo "Docker is running" || echo "Start Docker Desktop first"

Cursor's per-call confirmation dialog shows the tool name and arguments before execution, but unlike Claude Code, there's no persistent per-tool allow/ask policy file you commit to the repo — each teammate's Cursor instance manages its own confirmation preferences locally. That's exactly why the --tools flag excluding apply on the server side matters more here than in Claude Code: it's the one control that's consistent across every developer's machine regardless of their individual Cursor settings.

Tips
- Verify Docker Desktop (or whatever runs your registry server's container) is already up before launching Cursor — a silently-crashed MCP server is easy to miss in the settings panel if you're not looking for it.
- Since Cursor's tool confirmation preferences are per-developer and local, put the actual safety boundary in the server's --tools flag, not in per-person Cursor settings that vary by machine.
- Re-open Settings → MCP after every .cursor/mcp.json edit — Cursor doesn't always hot-reload server config changes mid-session.


Authoring and Validating HCL with Live Provider Schema Awareness

The best Cursor-specific workflow is keeping a resource block open in the editor and asking Agent mode to extend it with schema-verified arguments, referencing the open file directly:

@main.tf Add a launch_template block for this autoscaling group using
current aws_launch_template argument syntax. Check get_provider_docs
first — I want the block_device_mappings syntax, not the deprecated
ebs_block_device top-level argument some older examples use.

That distinction matters: aws_instance still supports the older nested ebs_block_device blocks for backward compatibility, but aws_launch_template never did — it always required the block_device_mappings structure, and training-data-only generation frequently confuses the two because they look superficially similar in older tutorials online. A schema-checked result:

resource "aws_launch_template" "app" {
  name_prefix   = "acme-app-"
  image_id      = data.aws_ami.app.id
  instance_type = "m6i.large"

  block_device_mappings {
    device_name = "/dev/xvda"

    ebs {
      volume_size           = 50
      volume_type           = "gp3"
      delete_on_termination = true
    }
  }

  tag_specifications {
    resource_type = "instance"
    tags          = { Name = "acme-app" }
  }
}

resource "aws_autoscaling_group" "app" {
  name                = "acme-app-asg"
  desired_capacity    = 3
  min_size            = 2
  max_size            = 6
  vpc_zone_identifier = var.private_subnet_ids

  launch_template {
    id      = aws_launch_template.app.id
    version = "$Latest"
  }
}

Follow immediately with validate, right from the same chat, before moving on:

terraform validate

The editor-adjacency pays off again here — Cursor can apply the suggested change as an inline diff you review line-by-line in the actual file, rather than a code block you manually copy over, which materially reduces the "did I paste that correctly" class of error.

Tips
- Reference the specific open file (@filename) when asking for schema-verified additions — it grounds the agent in your actual existing resource names and variable references, not a generic example.
- Explicitly ask for docs verification when a resource has a history of deprecated/alternate syntax (block device mappings, IAM policy document formats) — these are exactly the cases where memorized patterns from training data are most likely to be stale.
- Use the inline-diff apply-to-file flow rather than copy-pasting code blocks manually; it's not just convenience, it eliminates a real class of transcription mistakes.


Reviewing Plan Output Alongside the Code That Produced It

Ask for a plan and have Cursor keep the relevant HCL open in a split view while it explains the diff — the value is being able to glance at the source and the plan diff in the same visual field instead of context-switching:

Run terraform_plan for infra/environments/staging using staging.tfvars.
For each change, show me the plan line next to the specific HCL block
in this file that caused it.
main.tf lines 42-58 (aws_autoscaling_group.app):
  desired_capacity changed from 2 to 3

Plan diff:
  ~ resource "aws_autoscaling_group" "app" {
      ~ desired_capacity = 2 -> 3
    }
  Plan: 0 to add, 1 to change, 0 to destroy.

This matches your edit — no surprises. The min_size/max_size bounds
(2/6) still contain the new desired_capacity of 3, so this won't
trigger any bound violation on apply.

That last sentence — checking the new value against the existing min_size/max_size bounds — is a small thing, but it's exactly the kind of adjacent-file cross-check that's easy for a human to skip when reviewing a one-line diff in isolation, and easy for an agent to do reliably when it has both the plan and the surrounding HCL in context at once.

For a genuinely useful side-by-side, ask Cursor to annotate a multi-resource plan block-by-block rather than as one flat summary:

terraform plan -out=tfplan.staging
terraform show tfplan.staging

Tips
- Ask for plan lines matched back to specific file/line ranges — this is Cursor's actual differentiator over a terminal-only agent, and it's wasted if you only ask for a flat text summary.
- Have the agent cross-check adjacent constraints (min/max bounds, uniqueness constraints, required-together arguments) in the same file when reviewing a small change — it catches a class of bug that isolated diff-reading misses.
- Don't skip terraform show tfplan.staging yourself even when the agent's summary looks complete — plan files are cheap to inspect directly and it's a good habit to keep the agent honest.


Known Limitations and Workarounds for Terraform MCP in Cursor

A few things worth knowing before you rely on this setup for anything production-critical, as of the Cursor versions available in mid-2026:

  • No committed, shareable permission policy. Unlike Claude Code's .claude/settings.json, Cursor's tool confirmation behavior isn't a repo-committed file — every developer's local instance decides independently whether to auto-approve a tool call type. Compensate with the server-side --tools allowlist, as covered above; don't assume your teammates have the same confirmation habits you do.
  • Large plan JSON can get silently truncated in the chat context. On very large plans (100+ resources), Cursor's context handling for tool results doesn't always surface the full plan JSON to the model — you may get a summary based on a partial view without an explicit warning that truncation happened. Cross-check resource counts against raw terraform show -json output on big plans.
  • Docker-based registry server startup adds friction inside the IDE loop. Same cold-start cost as in OpenCode, but more noticeable in Cursor because a docs lookup often happens mid-edit, inside a flow where a half-second stall breaks concentration more than it does in a terminal session.
  • No native drift-detection scheduling. Cursor has no built-in way to run a background -refresh-only check — you either trigger it manually in Agent mode or wire it into external CI, the same as any other terminal-based tool; there's no IDE-native scheduling advantage here despite the tighter editor integration elsewhere.

Tips
- Never assume your teammates' Cursor confirmation settings match yours — put the actual safety boundary in the MCP server's tool exposure, not in IDE-level trust settings.
- On plans over roughly 100 resources, spot-check the agent's resource count against terraform show -json tfplan | jq '.resource_changes | length' — silent truncation is hard to detect from the summary alone.
- Run a native terraform-mcp-server binary instead of the Docker-wrapped registry server if IDE-loop latency bothers you during active editing; keep Docker for CI or headless review where cold-start cost matters less.


Tips

Tips
- Put your real safety boundary in the MCP server's --tools allowlist, not in Cursor's per-developer confirmation settings — the latter isn't shared or committed across the team.
- Lean into Cursor's actual differentiator — plan lines matched to specific file/line ranges and adjacent-constraint cross-checks — rather than using it as just another terminal-plan-summarizer.
- Spot-check resource counts against raw plan JSON on large plans; Cursor's context handling can truncate tool results without an explicit warning.