Why Read-Only MCP Is Not Read-Only Until the Database Agrees Read-only access for AI agents connected to production databases must be enforced at both the MCP layer and the database layer, according to a new security guide. The guide recommends creating a dedicated non-owner database role with minimal permissions, testing that prohibited writes fail, and regularly reviewing effective privileges to prevent policy mistakes from becoming production changes. TL;DR — Key Takeaways - Enforce read-only access at both the MCP layer and the database. - Use a dedicated, non-owner database role with only the permissions the AI agent needs. - Test that approved reads succeed and prohibited writes fail. - Review inherited roles, public grants and effective privileges regularly. - Keep MCP credential revocation and database access revocation independent. - Correlate MCP request logs with database execution logs for stronger auditing. Teams connecting AI agents to production data often start with a reassuring control: Mark the MCP server or tool as read-only. The label is useful, but it is not the final security boundary. A database connection with broad privileges can still turn a policy mistake into a production change. The safer model is simple: Read-only access should be enforced twice. The MCP layer narrows what the client is allowed to request. The database independently narrows what the execution identity is allowed to do. Read-Only is a Chain, Not a Switch There are at least three permission layers in a typical MCP database workflow: - The AI client proves its identity to the MCP server. - The MCP server decides which tools and operations the client may use. - The database authorizes the role used to execute the final query. The effective permission is the intersection of all three layers. Authentication answers who is calling. MCP policy answers which operation the caller may request. Database grants answer what the resulting session can actually execute. Why the Database Must Have the Final Say Tool-level restrictions are application controls. They depend on the server correctly classifying operations and applying policy to every path that can reach the database. That control can fail in ordinary ways. A new tool may bypass an older permission check. A parser may classify a statement differently than expected. A stored procedure exposed as a read operation may produce side effects. None of these possibilities means the MCP layer is useless. It means the database should not delegate its final authorization decision to another component. If the database role can only connect, use a specific schema and select from approved tables, then an attempted write fails even if the request reaches the database. Create a Dedicated Execution Identity Do not connect an AI agent with an owner, migration or administrator account. Create a separate login role for the workload and grant it access to only the objects needed. For PostgreSQL, a minimal pattern can look like this: CREATE ROLE ai reader LOGIN PASSWORD 'replace-with-a-secret'; GRANT CONNECT ON DATABASE app production TO ai reader; GRANT USAGE ON SCHEMA reporting TO ai reader; GRANT SELECT ON TABLE reporting.daily metrics, reporting.customer health TO ai reader; Granting access to selected tables makes the review surface smaller and prevents a newly created sensitive table from becoming readable by accident. Broader schema access may be appropriate, but it should be a documented decision. Test Denial, Not Only Success Most connection checks prove that a query works. A security check must also prove that a forbidden query fails. After configuring the execution identity, run one expected read and one harmless write attempt in a controlled test object or within a transaction: SELECT FROM reporting.daily metrics LIMIT 1; BEGIN; UPDATE reporting.daily metrics SET metric value = metric value WHERE false; ROLLBACK; The read should succeed. The update should be rejected because the role lacks the UPDATE , even though the statement would not change a row. This test catches a common configuration error: The MCP interface says read-only, but the database credential belongs to a role with write access. Repeat it after permission changes, migrations, credential rotation or MCP server upgrades. Check Effective Privileges Security reviews should inspect effective access, not only the grants someone remembers creating. Roles can inherit permissions through memberships, ownership, default privileges or public grants. In PostgreSQL, teams can review role memberships and object privileges with catalog queries and information schema views. The review should answer: - Can the role create objects in any reachable schema? - Can it insert, update, delete, truncate or execute privileged functions? - Does it inherit access from another role? - Can it read tables outside the intended scope? - Can it change its own role or session authorization? Run these checks using the same login identity and connection path used by the MCP server. An administrator session can hide differences in inherited privileges and row-level security behavior. Keep Revocation Independent Incident response should not depend on a single control plane. Teams should be able to disable the MCP credential and revoke the database role separately. At the MCP layer, revoke the client key, OAuth grant or server link. At the database layer, block new sessions and terminate existing ones if the situation requires immediate containment. For PostgreSQL, a basic emergency action can start with: ALTER ROLE ai reader NOLOGIN; That prevents new connections. Existing sessions may need to be terminated separately. Document and test the procedure before an incident. Log Both the Request and the Execution MCP activity logs and database logs answer different questions. The MCP layer can record the authenticated client, requested tool, policy decision, connection and response status. The database can record the execution identity, statement, duration and server-side error. Correlation identifiers, timestamps and stable connection identifiers help connect the two views. A Practical Production Checklist Before giving an AI agent access to a production database, verify the following: - The MCP client has its own revocable identity. - The MCP policy exposes only the required tools and operations. - The database uses a dedicated non-owner login role. - The role has access only to the required schemas, tables and operations. - One allowed read succeeds through the real MCP path. - One forbidden write fails at the database boundary. - Effective privileges and inherited role memberships are reviewed. - MCP and database activity can be correlated. - Gateway revocation and database revocation are documented separately. A read-only label is useful because it communicates intent and reduces the operations available to the client. Production safety comes from turning that intent into independent enforcement. When both the MCP layer and the database agree, a mistake in one layer is less likely to become a change in production data.