Griffin logo

AI agent tool permissions: give each caller only the tools it needs

An agent that can send email, run SQL or call an API is safe only if you control who can make it do those things. This guide explains the problem, the pattern that solves it, and how to set it up in Griffin, an open-source agent platform.

Griffin on GitHub Quick start

The problem: the agent is a confused deputy

Most agent setups give an agent one list of tools. That list is fine when you are the only one talking to it. It stops being fine the moment anyone else can reach the agent: a colleague on a chat app, another agent that delegates work to it, a scheduled job that runs at night, or an external tool such as Claude Code.

Each of those callers can ask the agent for anything, and the agent will happily use every tool it has. A colleague who should only read a report can ask the agent to send a message in your name; another agent that only needed a summary can trigger a database write. Security people call this a confused deputy: the agent has the authority, and whoever talks to it borrows that authority.

Writing “only do X for Y” in the prompt does not fix it. A prompt is a request, not a control; a persuasive enough message gets past it.

The pattern: a tool grant per (agent, caller)

Treat permissions as a table, not as an instruction. For every agent, list who may call it and which of its tools each caller gets:

Then enforce the table where tools are handed to the model. If a caller wasn't granted a tool, the tool is not in the model's tool list for that run, so there is nothing to talk the model into.

Irreversible actions go to a human

Some actions should never be decided by whoever happens to be asking: merging to production, deleting data, changing access, sending money. Mark those tools as irreversible in code. When a chain that you didn't start reaches one of them, the agent asks you — with a button — and waits. When nobody is watching (a nightly job), it refuses instead of guessing. Record every approval.

Doing it in Griffin

Griffin is built around this table. Every agent is a profile with its instructions, model, tools and callers:

{
  "id": "assistant",
  "label": "Assistant",
  "tools": ["ask_owner", "knowledge_write", "knowledge_list", "jobs_create", "telegram_read", "telegram_send"],
  "callers": {
    "owner":     { "tools": "*" },
    "griffin":   { "tools": ["knowledge_list", "telegram_read"] },
    "scheduler": { "tools": ["knowledge_list", "telegram_read"] }
  }
}

Here the owner gets everything, the orchestrator agent (griffin) can read messages and notes but never send as you, and scheduled jobs can read but not write. You can edit the same thing in the web UI: Agents → your agent → who can call it, then pick the tools for each caller.

Under the hood, the tool list for each run is filtered by the caller before it reaches the model, and a separate guard asks the owner before any tool marked irreversible runs in a chain the owner didn't start. Credentials live in a separate broker process, so the process running the model never sees a secret even if it is tricked into trying.

Checklist

Griffin is open source (MIT). Try it with one command:

docker run -d --name griffin -p 3100:3100 -v griffin-data:/data \
  -e ANTHROPIC_API_KEY=sk-ant-… ghcr.io/paziresh24/griffin:latest
docker exec griffin cat /data/owner.token   # the token you sign in with

Source and docs: github.com/paziresh24/Griffin · how agents and callers work.

More guides