Currently Available: Need a skilled Software Developer for your next project?
Categories
Coding Agents

How to Give a Coding Agent Safe, Read-Only Database Access Through MCP

A coding agent that needs to inspect application data should connect through a narrowly configured database account. The database itself must enforce that account’s permissions; settings in an AI client or MCP server add protection but cannot replace database controls.

MCP (the Model Context Protocol) lets an AI application, such as an IDE, connect to a server that provides tools the agent can call. A database MCP server can offer tools to inspect a database schema and see how its tables and columns are laid out. It can also offer a tool to run a query. The agent decides when to call a tool. The server then connects to the database with its configured credentials. The database account sets the access boundary: grant it access only to the data and operations the coding task needs.

Read-only access limits data changes, but it does not prevent the agent from seeing sensitive records or sharing them through other tools. Safe access therefore combines restricted database permissions with limited tools, protected credentials, and checks on the agent’s activity.

Give the agent a dedicated database role

Create a separate database role for the agent rather than reusing a developer’s account, an application credential, or an administrator account. A database role is an identity whose permissions control what a connection can access. Grant it only the ability to connect and read approved objects.

For PostgreSQL, a starting point might look like this:

CREATE ROLE coding_agent_ro
  LOGIN
  NOINHERIT
  NOSUPERUSER
  NOCREATEDB
  NOCREATEROLE
  NOREPLICATION;

GRANT CONNECT ON DATABASE appdb TO coding_agent_ro;
GRANT USAGE ON SCHEMA app TO coding_agent_ro;

GRANT SELECT ON TABLE
  app.orders_read,
  app.customer_summary
TO coding_agent_ro;

Set the role’s password through a secret manager or another protected credential mechanism. Do not put a live password in source code or a repository. The example grants read access to two named objects; replace them with the tables or views the agent actually needs.

PostgreSQL separates read privileges such as SELECT from write privileges such as INSERT, UPDATE, and DELETE. The role’s access depends on its memberships and other grants, so check which roles it belongs to and what permissions they pass to it. Also check permissions granted to PUBLIC, a built-in role whose permissions can apply to all database users. NOINHERIT does not remove permissions granted directly or through other paths. PostgreSQL’s privilege documentation explains how object permissions work.

A view can limit what the agent sees. For example, it can leave out customer identifiers or internal cost fields while showing the columns needed for analysis. Grant access to the view instead of the underlying table when the database’s view and row-level security rules support that design. Also review default privileges. A broad rule for future tables can expand the agent’s access when someone creates new objects. PostgreSQL explains these rules in its guide to default privileges.

Use database permissions as the write-prevention boundary

An MCP server can hide write tools or reject statements, but the database must still refuse unauthorized changes. A server-side filter that allows statements beginning with SELECT is not a dependable substitute for database permissions. SQL can call functions, and PostgreSQL documents that volatile functions can have side effects. PostgreSQL also grants function execution to PUBLIC by default unless an administrator changes that behavior.

Check which functions and procedures the agent role can execute, especially those that run with higher privileges. A SELECT query can invoke a function, so inspect available functions and restrict unnecessary execution rights. Do not make the agent role an object owner or superuser: ownership and higher privileges can give it powers beyond ordinary grants. PostgreSQL’s function documentation covers higher-privilege function execution and the need to control who can call such functions.

A PostgreSQL read-only transaction adds another restriction. It rejects many write commands and DDL commands. DDL means commands that create or change database objects. PostgreSQL describes read-only transactions as a broad restriction, not a guarantee against every kind of write. Use one as an extra layer, with the role’s permissions as the foundation. See PostgreSQL’s documentation for SET TRANSACTION.

Match the MCP tools to the coding task

Expose only the tools the agent needs. A tool that lists approved tables or queries a small set of views gives the agent less access than a general-purpose tool that accepts any SQL. When the agent needs arbitrary SQL to explore the database, use server controls where available. These include rejecting requests that contain more than one statement, setting query timeouts, and limiting result sizes. These controls reduce risk, but database grants remain essential.

Tool behavior depends on the MCP server you choose. For example, Microsoft’s postgres-mcp documentation describes a read-only query tool and separate tools for data-changing statements. Its setup guide recommends both a read-only access mode and a database role with read-only permissions. Those settings describe that project, not every MCP server. Check the deployed server’s profile and exposed tools rather than assuming its defaults are safe. The project documents its available tools and its usage and security settings.

Limit what the agent can learn and return

A role that can read every table still has broad access, even if it cannot change rows. Restrict access to the schemas and views the coding task needs. Then narrow access to the necessary columns and rows. Column-level grants limit which columns the role can read. Row-level policies limit which rows it can read. Use these controls when the database’s authorization design supports them.

Treat query results and schema descriptions as untrusted input. A database row or comment can contain instructions intended to manipulate the agent. OWASP calls this indirect prompt injection: malicious instructions arrive inside content the model retrieves. Read-only permissions limit direct database changes, but they do not stop the agent from reading additional permitted data or forwarding results through another connected tool. OWASP’s prompt injection guidance describes this risk.

Ask the user to approve sensitive tool actions, especially actions that write data or access files. Approval prompts add a checkpoint, but they do not replace restricted permissions. A user can approve too broadly, and the agent can still expose data through allowed read calls. Review the query and its likely results instead of approving based only on the tool’s name.

Protect credentials, bound queries, and verify access

Store the database credential outside source code and limit who can read it. For a remote MCP server, require authentication and protect the connection with appropriate transport security. For a local server, limit the process’s access to host files and resources. A read-only database role does not prevent an over-permissioned server process from accessing other resources on the machine. OWASP’s MCP security guidance recommends least privilege for servers and authentication for remote endpoints that expose private tools or data.

Read queries can still consume resources or return more sensitive information than expected. Set statement and lock timeouts in the database. When the MCP server supports result limits, configure those too. PostgreSQL’s statement_timeout setting aborts statements that run longer than the configured duration. Choose limits through workload testing; there is no universal timeout or row limit that fits every database.

Use a dedicated database identity so logs can distinguish agent queries from ordinary developer traffic. Check what the MCP server logs as well. Some implementations record tool names, durations, and outcomes without recording query text or returned data; verify the behavior of the server version and configuration you deploy.

Before enabling access, test the connection from the coding-agent client through the MCP server to the database. Confirm that the client connects as the dedicated role. Then check that the database rejects writes and schema changes. Verify that the role sees only approved objects and that the configured limits stop long-running queries and large results. Review the logs to make sure they help with investigations without storing sensitive query results unnecessarily. Finally, test how the agent handles instruction-like text in database rows, and whether other connected tools could forward sensitive results.

What I'm building

Delegate tasks. Get software.

Give Vroni a GitHub issue, bug report, spec, or rough idea. It reads the repo, plans the change, writes code, runs checks, and works toward a review-ready pull request.

Take a look at vroni.com

Email updates

Usually a new article and a few links I found interesting.

No spam. Unsubscribe with one click.

Leave a Reply

Your email address will not be published. Required fields are marked *