Both are single Python files (3.8+, standard library only), served by the API.
Set up
1
Create a key
In the web app, open Agents → Connect an agent. It creates an API key with only the
agents:authorize scope, which can ask for decisions and nothing else, and generates the config to paste.2
Download the adapter
GET /api/v1/integrations lists the files with their checksums.3
Wire it in
- Claude Code hook
- MCP gateway
Merge into For org-wide enforcement, ship the hooks in Claude Code’s managed settings with
~/.claude/settings.json (per user) or .claude/settings.json (per repo):"allowManagedHooksOnly": true, so developers can’t remove them.What the agent sees
A crashed hook blocks the call in closed mode (Claude Code would otherwise treat a crash as “allow”).
How agent tools are classified
Labels are session-wide for agents: once a session has read somethinginternal and fetched something untrusted, later calls carry both. So “Approve sends that mix web content with internal data” holds a curl -X POST issued after the agent read the repo and fetched a web page.
When unsure, a tool counts as write, never read. MCP annotations only make classification stricter (
openWorldHint: true turns a write into egress). Override any tool per org in the governance settings’ external_tools:
Bash for the member role.
Sessions and the kill switch
The Agents page in the web app shows every live session and revokes one in a click.
A session belongs to the organization it started in (
FIBONACCI_ORG_ID; without it, the key owner’s organization, so set it if you belong to several). The caller’s role is checked in that organization on every call. A call that names a different organization is denied, and so is anyone who has left the organization: start a new session to work elsewhere. These come back as deny decisions, never errors, so FIBONACCI_FAIL_MODE=open can’t turn them into an allow.