I’ve been seeing versions of the same question all over the place lately, Reddit, Discord, X, LinkedIn, and in random chats: how do you actually control these coding agents?

Not how do you ask nicely.

Not how do you write a better system prompt.

I mean: how do you make the control real, programmatic, and tied to the actual tool call that is about to happen?

That is a big part of why we’ve been leaning so hard into hooks in DollhouseMCP.

And, to be very honest, I wanted this for myself too. I wanted a way to make a “careful reviewer” actually careful. I wanted a read only investigator to actually be read only. I did not want to rely on vibes.

Prompts Are Not Permission Systems

I like good prompts. We use a lot of personas, skills, agents, and ensembles in Dollhouse. They are incredibly useful.

But, a prompt is still a prompt.

If the model has broad tool access, or the host is in some kind of session wide allow state, your careful wording is not the thing in control anymore.

A real permission system has to run before the tool runs.

With DollhouseMCP, that happens on PreToolUse. The host is about to call a tool. The hook sees the exact tool name and input. It sends that to DollhouseMCP. DollhouseMCP evaluates it and returns a decision: allow, deny, or confirm.

That is not post hoc logging. It is not “please behave.” It is the permission check that happens before execution.

That difference matters… a lot.

What The Hook Actually Does

The flow is pretty simple:

  1. A host like Claude Code, Codex, or Cursor emits a pre tool event.
  2. pretooluse-dollhouse.sh normalizes the incoming payload and sends it to the evaluate_permission endpoint.
  3. DollhouseMCP evaluates the pending tool call in three stages:
    • rate limiting
    • static classification for obviously safe or obviously dangerous behavior
    • active element policy evaluation
  4. DollhouseMCP returns a host specific response.
  5. The host uses that response to allow, block, or ask for approval.

That third step is the interesting one.

Before the tool is used, the hook looks at what the tool is going to do, compares it against the rules we have in Dollhouse, and if it matches one of those rules it gets allowed, denied, or sent for confirmation.

So if the pending call is Bash:git push --force, Edit:/some/file, or Read:/some/path, that comparison is happening mechanically, every single time, before the host runs anything.

Why This Is So Useful With Personas, Skills, Agents, And Ensembles

This is one of the reasons I keep saying Dollhouse elements are more than prompts.

A persona can carry permissioning. A skill can carry permissioning. An agent can carry permissioning. An ensemble can carry permissioning.

So if you ask the LLM to create a careful reviewer, a read only investigator, a release manager, or a deployment agent, you are not just generating tone or workflow. You can attach actual rules to that thing.

That gives you another layer of protection, and it is a meaningful one.

You can even ask the LLM to draft the rules for you. Something like this is already useful:

gatekeeper:
  externalRestrictions:
    description: "Careful code reviewer"
    allowPatterns:
      - "Read:*"
      - "Grep:*"
      - "Glob:*"
      - "Bash:git status*"
      - "Bash:git diff*"
      - "Bash:npm test*"
    confirmPatterns:
      - "Bash:npm install*"
      - "Bash:gh pr merge*"
      - "Edit:*"
    denyPatterns:
      - "Bash:rm -rf *"
      - "Bash:git push --force*"
      - "Bash:sudo *"
      - "Bash:curl * | *"

The important part is not that the LLM wrote the YAML.

The important part is that the runtime enforces it.

What I Think Are Generally Safer Defaults

If people want a practical starting point, these are the patterns I trust a lot more:

  • Default to read only personas or ensembles for research, debugging, and exploration
  • Use confirmPatterns for installs, merges, deploys, edits, and anything that changes state
  • Use denyPatterns for obviously dangerous commands like rm -rf, force pushes, sudo, and pipe to shell patterns
  • Keep allowlists tight for specialized agents
  • Split responsibilities so your researcher, reviewer, and deployer do not all have the same tool surface

And, these are the patterns I trust a lot less:

  • Relying on prompt wording alone
  • Giving the host broad session wide approval and assuming the model will stay disciplined
  • Building giant permissive allowlists because it is easier in the moment
  • Letting an automated loop approve its own risky behavior
  • Treating a fail open transport path like it is the same thing as strong enforcement

That last one is important. In the current hook flow, if the permission server is unreachable or returns unusable data, the wrapper fails open so it does not brick the workflow. That is better for usability. It is not the strongest possible security posture.

Which is exactly why sharing or syncing rules with the host matters.

There Are Really Two Permission Layers Here

We already had internal Gatekeeper permissioning for MCP AQL operations. Hooks extend the same philosophy to external tool use.

So now there are really two related layers:

  • Internal Gatekeeper rules for MCP AQL operations like create_element, edit, delete, execute_agent, and confirm_operation
  • External tool rules for host tools like Bash, Edit, Read, Grep, Glob, and others

That split matters because one of the less safe patterns in agentic systems is letting the model create its own approval loop.

If you keep the external tool surface narrow, and you also keep internal confirmation power narrow, you reduce the chances that the system can unlock more power for itself.

That is a much better place to start.

One Rule System, Different Hosts

Another thing people underestimate is how different these hosts are.

Claude Code, Codex, Cursor, VS Code Copilot, Gemini CLI, and Windsurf do not all expect the same hook contract. The underlying rule system can stay the same. The response format cannot.

So DollhouseMCP translates the same decision differently per host.

If a host has a real escalation path, confirm can become ask.

If a host does not have a safe escalation path, confirm should become deny.

Codex is a good example. Right now the safest allow response is {}. And, for Codex, confirm is safer when treated like a deny response with a reason, not some fake ask payload the client may ignore.

That is part of the point release we are doing right now, better Codex hook behavior, plus tightening support for other platforms beyond Claude Code.

The Three Authority Modes

This part is especially important to me because I do not want Dollhouse permissioning to feel all or nothing.

We have three authority modes:

  • off: the host controls everything, and Dollhouse stays out of the permission path
  • shared: Dollhouse participates in the decision, but the host can still be more restrictive
  • authoritative: Dollhouse becomes the source of truth and syncs managed allow, ask, and deny rules into the host config

That gives people options.

If you want Claude Code, Codex, or Cursor to handle all of the permissions natively, you can do that.

If you want Dollhouse to add another layer without taking over, you can do that.

And, if you have spent the time to build very granular permissioning inside Dollhouse, you can switch over and let Dollhouse controlled permissioning drive the host side too, instead of recreating everything manually.

That is the part I find especially powerful. You can start with host only permissions. Then, when your Dollhouse rules get good enough, transition over with a click and let that become the real control surface.

Why I Care About This

I keep seeing people ask how to make these systems safer, and a lot of the answers are either too casual or too abstract.

“Just prompt it better.” “Just put it in a sandbox.” “Just be careful.”

I do not think that is enough.

I want layered control:

  • Host permissions
  • Dollhouse hook permissions
  • Persona, skill, agent, or ensemble specific restrictions
  • Confirmation rules for risky operations
  • Clear records of what was allowed, denied, or escalated

That model works for Claude Code. It works for Codex. It can work for Cursor. And, it can sit in front of other agent runners and bridges too, including tools like OpenClaw.

The broader point is simple: permissioning should be data, not vibes.

Where This Is Going

DollhouseMCP already had the internal Gatekeeper model. Hooks bring that same idea to external tool use.

So we get to move from “please do not do anything dangerous” to “this exact pending tool call matched this exact rule, so it was allowed, denied, or sent for confirmation.”

That is a much better control surface.

And, it is not theoretical. We are in the middle of another point release right now with optimized hooks for Codex and improvements for other platforms beyond Claude Code.

That work matters because people are not asking for prettier prompts. They are asking for real control.

I think hooks are one of the ways we get there.