Skip to content
essayarchitecturetechnical foundersagent builders

How Agents Act: The Tool Gateway Pattern

By James Han

·

Apr 1, 2026

·

3 min read

agent architecturetool governanceproduction reliabilityClayRamp

Every agent tutorial starts the same way: give your LLM a list of tools, let it call them, and watch the magic happen. I tried that. The magic was my agent running rm -rf on a test directory and dumping 50MB of stdout into its own context window.

The fix wasn't smarter prompting. It was architecture.

Tools are an API, not a free-for-all

The pattern that changed how I build agents is treating tools as a typed gateway — every action the agent takes passes through a single chokepoint that validates, executes, and observes.

Instead of handing the model raw shell access, you define high-level tools with schemas:

tool_spec = {
  name: "edit_file",
  description: "Apply a targeted edit to a file",
  input_schema: { path: string, old_text: string, new_text: string },
  risk: "medium",
  side_effects: true,
  scopes: ["filesystem:write"]
}

Each tool declares what it does, what it takes, how risky it is, and whether it has side effects. This isn't just documentation — it's metadata that downstream systems (policy engines, loggers) use to make decisions.

The loop that makes it work

The agent never touches the filesystem directly. Every action follows the same path:

Model emits tool call
  → Gateway validates against schema
  → Policy engine checks permissions
  → Executor runs in sandbox with timeout
  → Result captured as typed observation
  → Observation returned to model for next turn

This is deliberately boring. One path. No shortcuts. Every tool call logged with arguments, result, duration, and session context.

Why high-level tools beat raw commands

The temptation is to expose run_command and let the model do whatever it wants. The problem: a policy engine can reason about edit_file and run_tests much more precisely than it can reason about arbitrary bash.

When I switched from raw shell to typed tools, three things happened immediately:

  1. Validation caught errors early. The model tried to edit a file outside the workspace root. The gateway rejected it before execution — not after.
  2. Output became manageable. Instead of raw stdout dumps, results came back as structured objects with summaries, diffs, and metadata. The model stopped drowning in its own observations.
  3. Logging became useful. Every action was traceable: which tool, what arguments, what happened, how long it took. When something went wrong, I could replay the session.

The anti-pattern: raw shell as the only tool

I've seen agent setups where the only tool is bash. The model generates arbitrary commands, the runtime executes them, and the raw output goes back into the prompt.

This breaks in every way: no validation (the model can run anything), no output control (a find / fills the context window), no policy (you can't distinguish a safe read from a destructive delete), and no useful logs (you just have a list of bash commands and their stdout).

The takeaway

Don't let your agent talk directly to the machine. Put a gateway between them. It's the single architectural decision that makes everything else — permissions, safety, observability — possible.

Next in the series: The Loop: Plan, Act, Observe


I write about this when I have something worth saying.