Securing MCP Server Database Connections: Direct, API, and Beyond
Connecting an MCP server to a database is where AI tooling gets genuinely useful, and genuinely risky. The model decides which tools to call and with what arguments, so whatever your server can do to the database, the mo
Connecting an MCP server to a database is where AI tooling gets genuinely useful, and genuinely risky. The model decides which tools to call and with what arguments, so whatever your server can do to the database, the model can ask it to do. Securing that connection isn't optional.
This article compares three common connection approaches, then covers the principle that matters more than any of them: least privilege.
Approach 1: Direct database connection
The MCP server connects straight to the database with a driver (ODBC, JDBC, psycopg, pyodbc, and so on).
How to do it safely:
- Never hard-code credentials. Load them at runtime from a secret store such as Azure Key Vault, AWS Secrets Manager, or HashiCorp Vault.
- Prefer identity over passwords. On Azure, a managed identity can sign in to Azure SQL without any password stored anywhere.
- Encrypt in transit. Require TLS on every connection.
- Rotate keys. Use short-lived credentials where possible, and rotate the rest on a schedule.
- Use parameterized queries only. Never build SQL by concatenating strings the model produced.
When it fits: small internal tools, prototypes, and read-only analytics where you control the whole environment.
Watch out for: the server holds real database credentials. If it's compromised or does more than intended, the blast radius is the database account's full permissions.
Approach 2: Going through an API layer
The MCP server never talks to the database. It calls an internal REST or GraphQL API, and that API holds the database connection.
Why it's safer:
- Database credentials are never exposed to the MCP server or the model. The server only holds a scoped API token.
-
The API defines exactly what's possible. Instead of "run any query," the model gets operations like
GET /orders/{id}orsearch_customers(name). - You reuse existing controls: authentication, rate limiting, input validation, and logging you already have.
When it fits: production systems, and anything touching customer or financial data.
Watch out for: an API that's too generic (for example, a /query endpoint that accepts raw SQL) gives up most of the benefit.
Approach 3: Connection pool or proxy in between
A proxy sits between the MCP server and the database, such as PgBouncer for PostgreSQL, a cloud database proxy (for example, Amazon RDS Proxy), or a SQL-aware gateway.
What it adds:
- Central access control: which users, roles, and databases the MCP server can reach.
- Auditing: one place that logs every connection and, with SQL-aware proxies, every statement.
- Connection management: pooling protects the database from bursts of agent traffic.
- Credential separation: with some proxies, the server authenticates to the proxy, and only the proxy holds the database credentials.
When it fits: teams that already run a proxy, or several agents sharing one database.
Watch out for: a plain pooler like PgBouncer manages connections, but it doesn't understand what a query does. Pair it with database roles and logging; don't rely on it alone.
Least privilege: the rule that matters most
Whichever approach you choose, the account the MCP server uses should be able to do as little as possible.
-
Default to read-only. Give the MCP account a role that can only
SELECT, ideally only on specific views rather than raw tables. - Separate reads from writes. Writes use a different role, exposed through narrow tools such as one stored procedure per action, never free-form SQL.
- Require approval for writes. Have the client or your server ask a human to confirm before any insert, update, or delete runs.
- Limit the data. Use views or row-level security so the account only sees the rows and columns it needs. Mask sensitive fields.
- Set resource limits. Add query timeouts and row limits so a bad query can't lock tables or dump everything.
- Log every call. Record which tool was called, with what arguments, and what ran, so you can review and audit it later.
Beyond the three approaches
A few other patterns are worth knowing:
- Read replicas. Point the MCP server at a read-only replica so it can never affect the primary database.
- Semantic or data-catalog layers. Expose curated metrics and entities instead of tables, so the model asks for "monthly revenue by region," not raw SQL.
- Search indexes as a buffer. As in my previous Azure AI Search article, sync the data into a search index for retrieval and only fetch full records by ID through a controlled path.
- Private networking. Put the database behind a private endpoint or VNet so only the MCP host can reach it.
Quick checklist
- Credentials come from a secret store or managed identity, never from code.
- TLS is required on every connection.
- The MCP account is read-only by default.
- Writes go through narrow tools and require approval.
- Every tool call and query is logged.
- Timeouts and row limits are in place.
An MCP server is only as safe as the permissions behind it. Choose the connection approach that fits your environment, but in every case give the model the least access it needs, and make every write deliberate.
About the author
I write about Applied AI, MCP, Azure AI Search, and RAG. Follow me on Dev.to and Hashnode for more.
Originally published by Dev.to AI. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.
