Sandboxing and least privilege
You cannot make an agent incapable of mistakes. You can decide in advance how much a mistake is allowed to cost.
The default is worse than people think
An agent running on your laptop, launched from your shell, inherits everything you have: your SSH keys, your cloud credentials, your production access, your entire filesystem, unrestricted network.
That is the default configuration for most local agent setups. It is fine for reading a project. It is a poor place to run anything that reads untrusted input.
Containers
The most practical isolation available. Run the agent inside a container with only the project mounted:
docker run --rm -it \ -v "$(pwd)":/work \ -w /work \ --network none \ node:22 bash
Two things are doing the work. The volume mount limits the filesystem to this project. --network none removes the exfiltration route entirely.
Drop --network none when the task genuinely needs the network, and understand that you have reopened that door.
Scope every credential
Give the agent its own credentials, never your personal ones.
- A GitHub token for one repository, not your account
- A database user with read only access to the tables needed
- A cloud role with the minimum policy, not administrator
- Separate keys per service, so one leak is one revocation
This limits blast radius, and it makes revocation possible. You cannot rotate your own account credentials without disrupting yourself, so you will hesitate, and hesitation is how a small incident becomes a large one.
Read only wherever it fits
Most agent work is reading. Many tools offer a read only mode and it is underused:
"args": ["-y", "mongodb-mcp-server@latest", "--readOnly"]
A read only connection cannot be persuaded to delete anything, whatever ends up in the context. That is a stronger guarantee than any instruction.
Separate environments properly
The agent should not be able to reach production. Not "should be careful with", cannot reach.
Different credentials, different network, different everything. If the only thing standing between an agent and your production database is a line in a prompt saying not to touch it, that is not a control.
Filesystem scope is an argument, not a suggestion
When a server takes a path, that path is the boundary:
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/you/projects/thisone"]
Pointed at your home directory, it can read your SSH keys and your browser profile. There is no second layer behind it.
Log enough to reconstruct
If something goes wrong, you need to know what the agent did. Log every tool call with arguments, timestamped.
The most useful question after an incident is what exactly it touched, and that is unanswerable without a record.
The trade
Heavy sandboxing is friction, and friction gets abandoned. A setup so restrictive you bypass it daily is worse than a moderate one you keep.
A reasonable middle: containerised with the project mounted, scoped credentials, no production access, approval on writes and installs. That is survivable without being unusable.