Read-only database access for agents

Last checked · 3 min read

Read-only database access for an AI agent is a database role that can SELECT the tables the agent needs and holds no privilege to change anything. The database enforces it with grants, so no prompt, tool setting or session command can turn it into write access. It is the safest place to start.

Privileges are the boundary, not a setting

There are two ways to make a Postgres connection “read-only”, and only one holds against an agent that writes its own SQL.

The first is a setting. default_transaction_read_only sets the default read-only status of each new transaction, and it is off by default.2 It is a default, though, and a session can change its own defaults: in our test, a session of a role turned statement_timeout off with a plain SET, even though the setting came from ALTER ROLE … SET.7 A read-only transaction can also be ended, and the next one may not be read-only.

That isn’t hypothetical. The reference Postgres MCP server was archived on 28 May 2025.4 Datadog Security Labs showed that its read-only mode ran each query in a read-only transaction that a stacked COMMIT; ends, after which later statements run with the role’s full rights.3

The second is privileges. In Postgres, only an object’s owner or a superuser can do anything with it until privileges are granted, and the INSERT privilege is what allows an INSERT. In our test, a role with SELECT only was refused an INSERT with permission denied, including after its session ran SET default_transaction_read_only = off.1 No statement the agent sends can grant it a privilege it doesn’t hold.

Setting it up

  • One role per agent, so the database log and any audit can tell agents apart.
  • Grant table by table. SELECT on the tables the job needs, USAGE on their schema and CONNECT on the database. Postgres grants no privileges on tables to PUBLIC by default,1 so anything you don’t grant stays out of reach.
  • Avoid the shortcuts. The predefined role pg_read_all_data reads every table, view and sequence, as if it had SELECT on all of them.5 That is everything, including tables the agent has no business seeing.
  • Check the role’s attributes. A superuser ignores grants entirely, and BYPASSRLS skips row-level security.
  • Cap the cost. statement_timeout aborts any statement that runs longer than its limit.2 A connection limit stops one agent from taking every connection.

What read-only still allows

  • Reading sensitive data. An agent that can read customer records and also send email or fetch URLs can leak them. Grant columns and rows, not whole tables, where it matters; see row-level security.
  • Running functions. Postgres grants EXECUTE on new functions to PUBLIC by default,1 and a SECURITY DEFINER function runs with the privileges of its owner.6 A read-only role that can call one that writes can write through it. Revoke EXECUTE from PUBLIC on those functions.
  • Expensive queries. A full scan of a large table during peak hours is a read, and it can still hurt.

When the agent has to write, keep the read-only role for reads and add a second, narrow role for the writes. The guide to safe write access goes through it step by step.

An example

PostgreSQL 18.6: a read-only role for one agent, and what it does with a writetext
-- as an admin
CREATE ROLE agent_ro LOGIN PASSWORD '…' CONNECTION LIMIT 3;
GRANT CONNECT ON DATABASE shop TO agent_ro;
GRANT USAGE ON SCHEMA public TO agent_ro;
GRANT SELECT ON public.orders TO agent_ro;
ALTER ROLE agent_ro SET statement_timeout = '5s';

-- as agent_ro
shop=> INSERT INTO public.orders (id, status) VALUES (9, 'paid');
ERROR:  permission denied for table orders
shop=> SET default_transaction_read_only = off;
shop=> INSERT INTO public.orders (id, status) VALUES (9, 'paid');
ERROR:  permission denied for table orders

How Deedglass uses it

deedglass scan reads your agent's database role in one read-only transaction and lists what it can change, table by table, and whether the role is a superuser or bypasses row-level security. If your agent only reads, a read-only role is the right tool and you don't need Deedglass; it helps once the agent has to write.

Related on this site

Sources

  1. 1PostgreSQL 18 documentation: Privileges · postgresql.org · checked 18 October 2026
  2. 2PostgreSQL 18 documentation: Client connection defaults · postgresql.org · checked 18 October 2026
  3. 3Datadog Security Labs: SQL injection in the PostgreSQL MCP server · securitylabs.datadoghq.com · checked 5 October 2026
  4. 4modelcontextprotocol/servers-archived · github.com · checked 5 October 2026
  5. 5PostgreSQL 18 documentation: Predefined roles · postgresql.org · checked 18 October 2026
  6. 6PostgreSQL 18 documentation: CREATE FUNCTION · postgresql.org · checked 18 October 2026
  7. 7PostgreSQL 18 documentation: ALTER ROLE · postgresql.org · checked 14 October 2026
  8. 8Deedglass source: the scan's read-only database pass · github.com · checked 18 October 2026

Last checked 18 October 2026. We re-check the glossary every month. If something here is wrong, write to hello@deedglass.com.