Give an AI agent governed access to PostgreSQL¶
Enterprise. About 15 minutes. Checked end to end against enterprise 0.21.0 and PostgreSQL 16.
By the end, an agent such as Claude Code can query your PostgreSQL database through TDB with a key that can only read, can only use two tools, gets compact (Lean) results, and leaves an audit entry for every call — including the ones TDB refuses.
1. Create a read-only database role¶
TDB reads with the credentials you register, so give it a role that can do nothing else.
In psql, as the database owner:
CREATE ROLE tdb_reader LOGIN PASSWORD 'choose-a-strong-password';
GRANT USAGE ON SCHEMA public TO tdb_reader;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO tdb_reader;
TDB refuses roles that can reach the server's files or programs (a superuser, for example). More: least-privileged role.
2. Register the database¶
With an admin key (TDB_API_KEYS holds one):
export TDB=http://localhost:8000
export ADMIN_KEY=... # a key from TDB_API_KEYS
curl -s -X POST $TDB/v1/sources \
-H "Authorization: Bearer $ADMIN_KEY" -H "Content-Type: application/json" \
-d '{"name":"shop","source_type":"postgres",
"connection":{"host":"db.internal","port":5432,"dbname":"shop",
"user":"tdb_reader","password":"choose-a-strong-password","schema":"public"}}'
Leaving out table registers the whole database: the agent can see and join every table
the role can read.
3. Create a key for the agent¶
curl -s -X POST $TDB/v1/auth/keys \
-H "Authorization: Bearer $ADMIN_KEY" -H "Content-Type: application/json" \
-d '{"name":"claude-code-agent","role":"read","expires_in_days":90}'
The response holds the key's details under key (keep key.id) and the key itself as
raw_key, shown once. Save both:
4. Limit it to two tools, and turn on Lean Mode¶
curl -s -X PUT $TDB/v1/auth/keys/$AGENT_ID/tools \
-H "Authorization: Bearer $ADMIN_KEY" -H "Content-Type: application/json" \
-d '{"tools":["schema_source","query_source"]}'
curl -s -X PUT $TDB/v1/auth/keys/$AGENT_ID/lean-mode \
-H "Authorization: Bearer $ADMIN_KEY" -H "Content-Type: application/json" \
-d '{"lean": true}'
The agent is now offered only schema_source and query_source, and every result comes
back in the compact table format unless a call asks otherwise.
See Saving tokens.
5. Check what the agent will see¶
curl -s -X POST $TDB/v1/mcp \
-H "Authorization: Bearer $AGENT_KEY" -H "Content-Type: application/json" \
-H "MCP-Protocol-Version: 2025-11-25" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"query_source",
"arguments":{"source_name":"shop","sql":"SELECT region, COUNT(*) AS orders, SUM(amount) AS revenue FROM orders GROUP BY region ORDER BY revenue DESC"}}}'
The result text, in the Lean format:
{"source":"shop","columns":["region","orders","revenue"],
"rows":[["APAC",100,"55617.83"],["AMER",100,"54536.79"],["EMEA",100,"53028.48"]],
"rows_returned":3,"truncated":false,"lean":{"format":"table"}}
Two refusals worth seeing once:
| The agent tries | It gets | The audit log records |
|---|---|---|
preview_source |
Tool 'preview_source' is not permitted for this API key. |
denied, tool_not_permitted_preview_source |
UPDATE orders SET amount = 0 |
SQL validation error: Blocked keyword: UPDATE |
denied, sql_validation_failed |
6. Connect the agent¶
Give the agent $TDB and $AGENT_KEY. For Claude Code, the TDB plugin asks for both;
for VS Code, Cursor and others, see Connect an IDE / AI tool.
Next¶
- Serve TDB over HTTPS before anyone outside your machine uses it: HTTPS with Caddy.
- Rotate the agent's key before it expires:
POST /v1/auth/keys/{id}/rotate. - Run through the production checklist.