Your agent doesn't need your database. It needs an answer.
Point an AI agent at a database over MCP and it inherits whatever the connection string
can do — every table, every row, every DELETE. AccessFlow gives it a
different shape of access: it can ask, and the query goes through the same parse, AI risk
analysis, human approval, and audit trail as one a person submitted.
What a direct database connection actually hands over
The usual way to give an agent data access is a database MCP server holding a connection string. That server is not a permission boundary — it is a pipe. Whatever the credential can do, the agent can do, and three properties follow from that:
- The blast radius is the credential, not the task. An agent asked to summarise last week's signups holds the same access as one asked to drop a table. Scope lives in the prompt, which is not a security control.
- Prompt injection becomes database access. Untrusted text in a support ticket or a web page the agent reads can redirect what it decides to run. The pipe faithfully executes the redirected intent.
- The audit trail loses the human. The database sees one service account. Which person's agent ran what, and why, is not recoverable from the logs afterwards.
The agent joins the queue, like everyone else
AccessFlow exposes a Model Context Protocol server. An agent connects with an API key and gets twelve tools. Submitting a query does not execute it — it enters the same state machine a query from the web editor enters.
Discover, then draft
The agent calls get_datasource_schema to see what exists, and validate_sql to parse-check a draft. Validation never executes and never calls the AI — the agent can iterate for free.
Submit for review
submit_query returns PENDING_AI. Nothing has touched the database yet. Permission and parse failures come back as structured errors, not executions.
AI risk analysis
The query is scored 0–100, checked for missing indexes and anti-patterns, and given a pre-flight cost estimate — the same analysis a human submission gets.
Human approval
The datasource's review plan decides who signs off. Reviewers see the agent's SQL, its risk score, and its justification, and approve or reject from the web UI, Slack, or a mobile push.
Execute, under the same guards
On approval the proxy runs it with the schema allow-list, column masking, row-level security, and row caps applied — then get_query_result returns the rows to the agent.
What holds, specifically
Governance claims are worth only as much as their enforcement point. Each of these is enforced in the service layer, not in the tool wrapper:
Keys inherit the human
An API key carries the owning user's role and datasource permissions exactly. A reviewer's key can review; an analyst's key cannot. An agent never gets more access than the person who created it.
Masking survives sampling
get_column_samples runs the governed read path — row-level security predicates and column masks apply, so a masked column returns the masked value to the agent, never the raw one.
Audit is caller-scoped
get_audit_log forces the actor to the calling user and their organisation. An agent cannot read another user's activity — not even with an admin key.
No self-approval
review_query blocks approving your own submission at the service layer, and list_pending_reviews excludes the caller's own queries. An agent cannot approve what it submitted.
Every action is attributed
MCP submissions and decisions write the same audit rows as the web UI, recording the user id plus the IP and user-agent of the MCP request. There is no separate agent audit path.
Keys are hashed and revocable
The plaintext key is shown exactly once; only a SHA-256 hash is stored, so neither admins nor the system can recover it. Revocation takes effect on the next request.
Connecting an agent
Create a key under Profile → API keys, then point the client at your AccessFlow instance. The server speaks stateless Streamable HTTP at /mcp.
{
"mcpServers": {
"accessflow": {
"type": "http",
"url": "https://accessflow.example.com/mcp",
"headers": {
"X-API-Key": "af_kQ7…"
}
}
}
}
claude mcp add accessflow --transport http \
--url https://accessflow.example.com/mcp \
--header "X-API-Key: af_kQ7…"
Any MCP-compatible client works the same way — Claude Desktop, Claude Code, or a custom pipeline. Full tool reference and error codes are in the MCP documentation →
What this does not solve
A governance layer that claims to solve everything is not one worth trusting. The boundaries are worth stating plainly:
- It does not stop an agent submitting a bad query. A compromised or confused agent can still submit anything its key permits. What it cannot do is get that query executed without passing the review plan. The control is on execution, not on intent.
- Auto-approval is a policy you choose. Routing policies can auto-approve low-risk reads, and a review plan can be configured not to require human approval. Those are deliberate settings — if you grant an agent a path that skips review, it skips review. The default is that it does not.
- Key expiry is not shipped yet. Keys are revocable immediately, but timestamped auto-expiry is still on the roadmap (FE-09). Rotate deliberately in the meantime.
- It governs AccessFlow, not your agent. This is a control on one path — the database. An agent with other tools, other credentials, or a shell is outside its scope entirely.
Run it yourself
AccessFlow is Apache 2.0 and self-hosted — it runs inside your own infrastructure, and no query text or row data leaves it. One docker compose up gets a working instance; the install guide covers Helm and from-source too.