Core concepts: human in the loop MCP approvals
flowifai is a human in the loop MCP approval layer: it intercepts an agent’s MCP tool calls, adjudicates each one against your policy, and pauses high-impact calls until a human approves them. This page explains the moving parts and the guarantees behind them.
The three processes
flowifai(CLI) — the operator tool. It enrolls agents, manages the broker service, prints client config, and manages policies and approvals.flowifai-broker— the trusted local process on the tools host. It owns the enrollment credential, launches the real MCP server, asks the backend to adjudicate every call, and performs the actual tool execution.flowifai-wrapper— the untrusted stdio shim your MCP client (Claude Code, VS Code, Cursor, …) launches instead of the real server. It presents the wrapped server’s tools to the agent and forwards every call to the broker over loopback. It holds no flowifai credential, by design: bypassing it just reaches the broker, which still gates.
The credential boundary
Credentials configured in your local MCP server — API keys, service tokens — stay in the local tools sandbox with that server. The flowifai enrollment credential is separate: the broker uses it to authenticate outbound calls to the backend.
The cloud backend is an approval authority, never a credential holder. It stores policy, approval tickets, audit records, and registered tool metadata. It has no downstream credential columns by construction. The backend also never initiates a connection into your machine — the model is pull-only: the broker asks the backend for decisions through outbound requests, and execution happens locally.
One honest caveat: tool arguments and execution results are sent to the backend for policy, approval, and audit. Any value embedded in them — including an accidental secret — may be stored. See the security model.
Adjudication: default-deny policy
Every tool call the broker receives is adjudicated against your org’s policy rules.
A rule matches on the tool name (exact, or * for any tool) plus an optional CEL
predicate over the call’s arguments. Among matching rules, the most restrictive effect
wins (deny > needs_approval > allow), and if no rule matches, the call is denied.
The engine fails closed: unparseable arguments and predicate evaluation errors are
denials, never silent allows. Full semantics in the
policy reference.
The approval ticket lifecycle
A call whose decision is needs_approval becomes an approval ticket:
- pending — the agent’s tool call returns a pending result carrying an
approval_ref. The human sees the request in the console inbox (and, in supported clients, gets the approval link delivered protocol-natively). - approved or denied — an eligible human decides in the console. Approving requires a user identity distinct from the broker’s machine identity, so an agent can never approve its own call. Policies can require n-of-m approvals across approver groups; one eligible deny vetoes.
- executing → executed — when the agent retries with the
approval_ref, the broker claims the ticket and runs the real tool. The result returns to the agent. - Terminal failures — expired (nobody decided in time, or approved-but-unexecuted lapsed), revoked, or execution_failed. Terminal states never silently re-arm: re-running the action requires a fresh call and a fresh human approval.
Two-phase execution: exactly once
Approved actions execute through a two-phase claim/complete protocol. The backend
performs the atomic approved → executing → executed transition with a claimant token
and a lease, so concurrent retries of the same ticket yield exactly one execution.
If the broker crashes between invoking the tool and completing the ticket, the ticket
becomes execution_failed and any re-run needs a new approval — the failure is
reported honestly, never silently retried.
Schema drift blocks calls
At enrollment, flowifai connect inspects the MCP server and registers its tool
schema. If the server’s live schema later drifts from the registered one, the broker
refuses all calls to it (fail closed) until you re-run the app-generated
flowifai connect command. Policy rules that reference arguments of a changed schema
are disabled until re-confirmed, so a renamed field can never quietly widen an allow
rule.
Audit
Adjudications, approvals, denials, execution outcomes, and lifecycle events are written to an append-only audit log (updates and deletes are rejected at the database level). Security-critical transitions write their audit row in the same transaction as the state change, so a committed execution can never lack its audit record.
What flowifai is not
flowifai is a cooperative approval gate, not a sandbox. It gates the MCP path routed through the wrapper and broker. If an agent also holds direct API keys, shell access, or another ungated tool path, those paths remain outside flowifai’s control. The security model spells out the boundary.