Enough to plan. OpenTofu reaches state backends and module sources with the credentials in the server's environment.
.mcp.json
{
"mcpServers": {
"terragrunt": {
"command": "terragrunt",
"args": [
"mcp",
"--working-dir", "/path/to/estate",
"--allow=exec",
"--allow=http",
"--allow=env"
],
"env": {
"TG_EXPERIMENT": "mcp-command"
}
}
}
}
A throwaway environment the agent may change, with each run waiting on you to accept it. Point `--working-dir` at the environment, not the repository root. Tools cannot reach above it.
.mcp.json
{
"mcpServers": {
"terragrunt-throwaway": {
"command": "terragrunt",
"args": [
"mcp",
"--working-dir", "/path/to/estate/throwaway",
"--allow=exec",
"--allow=http",
"--allow=env",
"--dangerously-allow-apply"
],
"env": {
"TG_EXPERIMENT": "mcp-command"
}
}
}
}
Tools
Name
What it does
Needs
discover
Lists the units and stacks under a directory
None
render_config
Evaluates one unit’s terragrunt.hcl and returns the resolved configuration
exec to read dependency outputs
validate
Reports syntax and evaluation errors in the configurations under a directory
None
run_order
Reports the order units run in, and what blocks each one
None
get_outputs
Reads the applied outputs of one unit
exec, or http for s3, gcs, and azurerm state
plan
Runs run --all plan across the units and returns the per-unit result
exec
catalog
Lists the components in the catalog the configuration names
exec to clone a remote repository
scaffold
Writes new configuration from a module, unit, or stack source
http, or exec for git, to fetch a remote source
fmt
Rewrites .hcl files into canonical format, as hcl fmt does
None
A server started with no --allow flag can still write under its root: scaffold generates files from a local source,
and fmt rewrites .hcl files.
Runs run --all destroy across the units, dependents first
exec
Neither acts when the agent calls it. Each first returns an elicitation (the protocol’s way for a server to ask the
client’s user a question) naming the units it would run, and runs only once the person operating the client accepts.
A client that cannot ask anyone is rejected.
--working-dir sets the server root, and defaults to the directory the
server is launched in. Every tool takes a working_dir relative to the root. A value resolving outside it is refused,
symlinks included, and a filter following dependencies or dependents cannot return a unit from outside it.
working_dir is optional and defaults to the root, except on render_config and get_outputs, which act on a single
unit and require it. Where a tool takes filter, it accepts filter queries and narrows that tool
the way --filter narrows a run.
The root does not limit file access. file() reads anything the server’s user can read, and resolving a dependency
downloads its source into a .terragrunt-cache.
The tools that run OpenTofu/Terraform use the binary the server was started with. Set it with
--tf-path, as a program name found in PATH or as a filepath. It defaults to
tofu, or to terraform when tofu is not installed. A unit’s terraform_binary and engine block are ignored.
scaffold writes new configuration the way scaffold does, without prompting. The agent
passes the values the form would ask for, and anything it leaves out is written as a TODO placeholder.
Files go in output_folder, which defaults to working_dir, must resolve under the server root, and must be empty or
not exist yet. A local source or template is read from wherever it is, outside the root included.
A template’s shell helpers and hooks never run, even under --allow=exec. The tool refuses a template that declares
dependencies, which boilerplate would download past the server’s capabilities, or that uses template syntax in a file or
directory name, which can render to a path outside output_folder. It also refuses a source holding a symlink that
leads out of it.
Under --allow=docs, the server also relays the tools and resources of the
Terragrunt docs MCP server, so an agent connected to
terragrunt mcp can search the Terragrunt documentation without a second server configured. Each tool keeps its name
on the docs server with a docs_ prefix, e.g. search_docs is served as docs_search_docs.
discover
Lists the units and stacks under a directory.
filter narrows what is returned.
dependencies adds each unit’s dependency edges, and whether an exclude block excluded it.
as is the command those exclude blocks are evaluated against: plan (the default), apply, or destroy.
hidden includes units in hidden directories.
Units and stacks come back as separate lists of paths, with unit_count.
render_config
Evaluates one unit’s terragrunt.hcl the way a run would, resolving locals, includes, functions, and dependency
outputs.
format chooses how much comes back:
summary, the default, returns the unit’s source, its remote state backend, the IAM role it assumes, the names of
its inputs, its dependencies, and its generate blocks.
full returns the whole resolved configuration.
render_metadata tags each value in a format=full result with where it was defined, so you can see which include
a value came from.
Without --allow=exec, dependency outputs fall back to mock_outputs where the configuration declares them, and the
call fails where it does not.
validate
Checks the configurations under a directory for syntax and evaluation errors. Each diagnostic gives a file, line,
severity, summary, and detail. The result also counts errors and warnings, and sets valid.
unit validates a single unit or stack, relative to working_dir. Discovery is skipped, and filter must be empty.
filter narrows which units are validated.
run_order
Reports the order units run in, based on the DAG. Each unit
lists the units that block it in blocked_by.
command is the command the order is for: plan (the default), apply, or destroy.
format selects the shape. edges, the default, returns the blocking relation as data. tree and dot return the
graph as list prints it.
filter narrows the set of units.
The units array is sorted by path.
get_outputs
Reads one unit’s applied outputs, what a dependency block on that unit receives.
include_sensitive returns the values of outputs marked sensitive, which are otherwise replaced with (sensitive).
The agent sets it itself, and nobody is asked.
Each output comes back with its value and whether it is sensitive. redacted lists the names whose values were
replaced.
Under --allow=exec, Terragrunt runs OpenTofu/Terraform to read the outputs. Without it, Terragrunt reads the state
itself, which needs --allow=http and works for the s3, gcs, and azurerm backends. Any other unit reports that
it fetched nothing, and says why.
plan
Runs run --all plan across the units under a directory. Each unit comes back with its result, how long it took, and
the cause when it failed. The result also counts what succeeded, failed, was excluded, and exited early.
filter narrows which units are planned.
parallelism caps how many units run at once, the way --parallelism does. By default there is no limit.
include_sensitive keeps each unit’s plan files and returns plan_json. The plan JSON holds every value in the plan
in cleartext, sensitive ones included. Without it, Terragrunt deletes the plan files once it has counted their
changes. The agent sets it itself, and nobody is asked.
Each unit result can have these fields:
Field
What it holds
Returned by
changes
Counts of resources the plan would add, change, destroy, import, and forget, and of outputs that would change. A plan that changes only outputs counts them under outputs.
plan
plan_json
Path of the plan’s JSON representation, the document tofu show -json prints for the plan file. It contains every value in the plan, sensitive ones included. Only with include_sensitive.
plan
stdout
Path of a file with what OpenTofu/Terraform printed to stdout, color codes stripped, including the output of an init Terragrunt ran first. Sensitive values are redacted as the CLI redacts them.
plan, apply, destroy
stdout_truncated
true when the file keeps only the last 8 MiB of the unit’s stdout.
plan, apply, destroy
A unit that printed nothing has no stdout, and a unit whose plan failed has no changes or plan_json.
The files go in a directory under the system temp directory, readable only by the server’s user, and are deleted when
the server stops.
fmt rewrites the .hcl files under a directory into canonical format. It skips .terragrunt-cache,
.terragrunt-stack, and .boilerplate directories, as hcl fmt does.
check reports the files that need formatting without changing any, as hcl fmt --check does.
diff adds a unified diff for each of those files.
file formats a single .hcl file, relative to working_dir. The directory is not walked, and filter must be
empty.
filter narrows which files are formatted, with the path filters
hcl fmt --filter takes, e.g. ./stage/** or !./modules/**. A filter
that needs unit discovery, such as type=unit, is refused.
changed lists the files whose formatting differed. Without check, fmt rewrote them. A file that does not parse is
left as it is and reported in parse_errors. formatted is true when changed and parse_errors are both empty,
and file_count is the number of files examined.
fmt skips symlinks. A symlinked file is listed in skipped, and a symlinked directory is not walked. A file whose
directory resolves outside the server root is refused. The check comes before the read, so a symlink that another
process swaps in after the check, before the read, is followed.
apply and destroy
Run run --all apply and run --all destroy across the units under a directory, destroy with dependents first. Both
answer in the shape plan does, with stdout and stdout_truncated and without changes or plan_json. An apply’s
stdout has the apply summary and the unit’s outputs, sensitive values redacted.
filter narrows which units run.
parallelism works as it does for plan.
The approval prompt shows filter and parallelism as flags on the command it asks about.
Connecting a client
Clients read a .mcp.json like the examples above. Set TG_EXPERIMENT in its env, since there is no shell to export
it from. Each client decides where the file lives and how much of this shape it reads, so check its own documentation:
docs lets the server relay the tools of the Terragrunt docs MCP server at https://mcp.docs.terragrunt.com/mcp, as
Tools describes. It is a separate capability from http, although both grant network connectivity. If the
docs server does not answer at startup, the server starts without its tools and logs a warning, and only a restart
tries again. Every argument an agent passes to a docs_ tool leaves the machine, including any configuration it pastes
in.
Wherever a denied capability stops a tool, the result includes a degraded list naming what it did instead, such as
substituting mock_outputs for a dependency output.
exec
Lets the tools run the tofu, terraform, and git that Terragrunt starts for its own work: a plan, a source
download, a git-range filter. It does not let a configuration run those programs itself. A program a configuration
names, through run_cmd(), a hook, or --auth-provider-cmd, is refused like any other.
A refused program is reported in the call’s degraded list and the call continues, with run_cmd() yielding an empty
string. apply and destroy fail the unit instead, so a hook written to block the run still blocks it.
To run a program a configuration names, allow it with an --allow-cmd
pattern, or accept it when a tool asks. A pattern naming git matches the git on the server’s PATH, not a ./git
in the tree.
discover, render_config, validate, run_order, and catalog ask. When a configuration names a refused program, the call
returns an elicitation naming the programs, then runs again with the ones the person operating the client accepts. An
acceptance covers those programs for that call only. get_outputs, plan, scaffold, apply, and destroy never ask.
http
Covers what Terragrunt itself fetches:
Downloading a unit’s terraform { source } when it is remote, such as a tfr:// registry module, an https
archive, or an oci:// image. Without this, such a unit cannot be planned.
Fetching a stack’s sources during discovery and generation.
Reading remote state directly for get_outputs.
Cloud API calls Terragrunt makes itself, including assuming a role through STS and reading S3, GCS, or Azure Storage.
Provider downloads made by the provider cache server.
sops
Without it, a configuration calling sops_decrypt_file fails, so a unit whose inputs come from an encrypted file
cannot be rendered. A refused decrypt fails the call. No value is substituted.
env
Without it, a tool call starts from an empty environment. A configuration’s get_env() returns its default, and every
command the tools run, tofu included, sees none of the server’s variables. Terragrunt still finds tofu and git on
the server’s own PATH.
Granting it passes every one of the server’s environment variables to configurations and the commands the tools run, as
every other Terragrunt command does. That can include cloud credentials.
Without it, the server also points HOME at an empty directory, so nothing reads ~/.aws/credentials,
~/.gitconfig, or an age key file. A direct state read under http and a decrypt under sops both need env to find
credentials in the environment or your home directory.
Logs
Logs, and any output from the commands the tools run, go to standard error, where MCP clients typically surface them
as server logs. Standard output carries the protocol itself.
Flags
--allowoptional(list(string))
Grant a capability the server otherwise denies. Repeatable.
Turns on one capability the server otherwise denies. Repeat it for each one you want:
Terminal window
terragruntmcp--allowexec--allowhttp
The capabilities, and what granting each one costs you, are documented with the mcp command.
Environment Variables:
TG_ALLOW
--allow-cmdoptional(list(string))
Allow MCP tools to run a command a configuration names, such as one a run_cmd or a hook calls, given as a pattern like 'jq **'. Repeatable. Requires --allow=exec.
--allow=exec runs tofu, terraform, and git, and refuses every other program a configuration names. This flag allows more, one pattern at a time:
A pattern is split into words the way --auth-provider-cmd is, so quoting works as it does in a shell. Quote the whole pattern as well, so your own shell passes it through as one value.
The first word names the program and cannot contain glob syntax. A bare name, e.g. jq, matches a configuration that calls jq, or that calls the path where the server’s PATH finds jq. A path, e.g. ./scripts/version.sh, matches a configuration that calls the same path once . and .. are resolved on both sides, so scripts/version.sh matches too. A bare name does not match a file the tree ships, so git ** does not allow ./git.
Each later word matches one argument. Glob syntax applies within that argument, e.g. --profile=*.
A word that is ** matches any number of arguments, none included.
Pattern
Allows
Refuses
git rev-parse --show-toplevel
git rev-parse --show-toplevel
git rev-parse --show-toplevel --quiet
jq *
jq .
jq -r .version
jq **
jq, jq -r .version version.json
yq .
aws ** --profile=*
aws sts get-caller-identity --profile=dev
aws sts get-caller-identity
./scripts/version.sh **
scripts/version.sh --short
version.sh
An argument containing spaces stays one word, so sh -c 'echo hello' allows run_cmd("sh", "-c", "echo hello") and nothing else. A pattern that cannot be parsed, or that contains a shell operator such as ; or |, is refused at startup.
Finding out what to allow
You do not have to guess. Call a tool once and read the degraded list, which names every command that was refused:
"degraded": [
"stubbed \"jq -r .version\": --allow=exec runs Terragrunt's own commands, not programs a configuration names"
]
If your MCP client supports elicitation (the protocol’s way for a server to ask the client’s user a question), the server offers the refused programs for approval instead, and you can decide per call rather than up front. An approval covers the program as the configuration called it, by name or by path, with any arguments, for that call. This flag is the answer once you know which commands a tree needs and are tired of approving them.
Environment Variables:
TG_ALLOW_CMD
--dangerously-allow-applyoptional(bool)
Serve apply and destroy tools, which change and tear down real infrastructure. Each run is gated on a person accepting it in your MCP client. Requires --allow=exec.
Without this flag the server has no apply or destroy tool at all. The tools are not registered, so a client is never told they exist.
The flag requires --allow=exec, since applying is running OpenTofu/Terraform. Passing it alone is refused at startup.
What the tools do
Both take the same arguments as plan, a working_dir, a filter, and a parallelism, and return the same per-unit summary.
apply runs terragrunt run --all apply across the units under the working directory.
destroy runs terragrunt run --all destroy, in dependency order.
Runs are serialized with the other real-run tools, so concurrent calls queue rather than racing on the shared cache directories.
Approval
Neither tool acts when the agent calls it. The first call answers with an elicitation (the protocol’s way for a server to ask the client’s user a question) describing what the run would do, and the run starts only when the person operating the client accepts it. A decline or a dismissal ends the call, and calling again asks again.
An acceptance counts only for the arguments the elicitation asked about. One sent with different arguments, or with no elicitation from the server behind it, is refused, and nothing runs.
The description names the units the run would touch, worked out the way terragrunt find --as=apply and terragrunt find --as=destroy work them out, so the exclude blocks belonging to the action being approved are honored.
Approve running `terragrunt run --all apply`?
This APPLIES the following units (3):
- app
- db
- vpc
This names the units, not the changes: nothing was planned to build this list. Call the plan tool first to see what would change.
The apply runs with -auto-approve, so accepting here is the only confirmation.
A call that sets filter or parallelism shows them as flags on the first line, e.g. Approve running `terragrunt run --all apply --filter=./app --parallelism=2`?. An invalid value fails the call before anyone is asked.
Call the plan tool first when you want the changes in front of you before accepting.
A client with no way to put the question in front of a person is refused, and the run does not happen.
Narrowing what a call can touch
The server root already bounds every tool: a working_dir resolving outside the directory the server was launched in is refused. That is a coarse bound, and for these tools it is worth making it tight. Launch the server in the environment directory you are willing to have changed, not at the repository root:
Beyond that, scope the credentials the server process is given to what those units need. Terragrunt cannot bound what a spawned tofu does with the credentials it inherits, so the credentials are the real limit on blast radius.
Environment Variables:
TG_DANGEROUSLY_ALLOW_APPLY
--tf-pathoptional(string)
Path to the OpenTofu/Terraform binary. Default is tofu (on PATH).
Specifies the path to the OpenTofu/Terraform binary that Terragrunt should use. By default, Terragrunt will look for a binary named tofu in your system’s PATH.
This is useful when:
You have multiple versions of OpenTofu/Terraform installed.
The binary is not in your PATH.
You want to use a specific version for certain operations.
You want to switch between OpenTofu and Terraform binaries.
Example:
Terminal window
terragruntrunplan--tf-path=/usr/local/bin/tofu
Note that if you only have terraform installed, and it is available in your PATH, Terragrunt will automatically use that binary.
NOTE: This will override the terraform binary that is used by terragrunt in all instances, including dependency lookups. This setting will also override any terraform_binary configuration values specified in the terragrunt.hcl config for both the top level, and dependency lookups.