DEV Community

Vivek Kumar
Vivek Kumar

Posted on

The MCP Security Model: How a Broker Keeps Your DB Credentials Away From the AI

There's a moment that makes every backend engineer wince: you're pairing with an AI assistant, it needs to see your data to help, and the fastest way to make that happen is to paste your DATABASE_URL into the chat. It works. It also just leaked a credential that grants full read/write access to production into a prompt, a chat log, and probably a vendor's retention window.

The Model Context Protocol (MCP) exists partly to make that shortcut unnecessary. But "MCP is more secure" gets repeated a lot without anyone explaining why. The interesting part isn't the protocol's wire format — it's the architecture it encourages: a broker sits between the AI and your database, holds the credentials itself, and only ever hands the model results. The AI can ask questions all day and never learn your password.

This post walks through that model: what the broker actually does, why credential isolation matters, and the properties (read-only enforcement, OAuth, least privilege, auditing) that turn "an AI can touch my database" from a scary sentence into a controlled one.

The core idea: the AI talks to a broker, not to the database

In a naive setup, the AI tool holds a connection string and opens a socket straight to your database. Every machine running that assistant is now a database client, and your credentials live wherever that config lives.

MCP inserts a server in the middle. The mental model looks like this:

Component Knows the DB credentials? Role
AI assistant (the model/host) No Sends natural-language intent and receives results
MCP client (Claude, Cursor, an IDE) No Speaks the protocol, forwards tool calls
MCP server (the broker) Yes Holds the connection, runs SQL, returns rows
Your SQL database Only ever talks to the broker

The AI never gets a socket to Postgres. It gets a set of tools the broker exposes — things like "list databases," "get schema," "run this query" — and the broker decides what actually reaches the database. That indirection is the whole security story. Everything below is a consequence of it.

Property 1: Credential isolation

Because the broker holds the connection, your credentials never enter the model's context. They aren't in the prompt, aren't in the chat transcript, and aren't sitting in a config file on every developer's laptop. They live in one place — the broker's environment — where you can rotate them without touching a single AI client.

This matters more than it first appears. Credentials that pass through an LLM are effectively public: they land in logs, get cached, and may be retained by whatever service processes the conversation. A broker breaks that chain. The model can be fully compromised and still not know how to connect to your database directly.

Property 2: Read-only by design

A well-built database MCP server exposes read paths and refuses everything else. When the AI generates a query, the broker can enforce that it's a SELECT before it ever executes:

-- The AI proposes this. The broker runs it.
SELECT status, COUNT(*) AS orders
FROM orders
WHERE created_at >= NOW() - INTERVAL '30 days'
GROUP BY status;
Enter fullscreen mode Exit fullscreen mode
-- The AI proposes this. The broker rejects it — no writes, no DDL.
DELETE FROM orders WHERE created_at < '2024-01-01';
Enter fullscreen mode Exit fullscreen mode

You get this at two layers, and you should use both:

  1. At the broker — parse or gate incoming SQL so anything that isn't a read is refused.
  2. At the database — connect the broker with a role that literally cannot write:
CREATE ROLE ai_readonly LOGIN PASSWORD '...';
GRANT CONNECT ON DATABASE app TO ai_readonly;
GRANT USAGE ON SCHEMA public TO ai_readonly;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO ai_readonly;
-- Note: no INSERT, UPDATE, DELETE, or DDL granted.
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO ai_readonly;
Enter fullscreen mode Exit fullscreen mode

Now even a hallucinated DROP TABLE is a no-op: the broker won't forward it, and the database wouldn't honor it anyway. The AI gets to explore your data freely without any path to modify it.

Property 3: OAuth instead of long-lived secrets

There are two ways to give the broker access, and the difference is significant.

Static credentials OAuth-based access
Lifetime Long-lived, often never rotated Short-lived tokens, auto-refreshed
Revocation Change the secret, redeploy everything Revoke centrally, effective immediately
Scope Usually all-or-nothing Granular scopes per action
Where it lives Configs, env files, sometimes prompts Issued on demand, not stored in clients

Modern MCP treats the server as an OAuth 2.1 resource server and the client as an OAuth client acting on behalf of a user. Practically, that means access is a short-lived token you can revoke from one place the moment someone leaves the team — no hunting through repos for a leaked connection string. Scopes let you say "this client may read contacts but not send anything," enforcing least privilege at the token level so a stolen token is far less useful than a stolen password.

If OAuth is overkill for a purely internal, service-to-service setup, the fallback is short-lived JWTs (an hour or less) with scope claims and rotating refresh tokens — still revocable, still not a permanent secret.

Property 4: Schema-awareness reduces hallucinated SQL

A subtle security-adjacent benefit: because the broker can expose the schema as a tool, the AI writes queries against real tables and columns instead of guessing. Ask a schema-blind model for "monthly active users" and you might get a query referencing a last_login column that doesn't exist. Give it the schema first and it grounds the SQL in reality.

A realistic exchange looks like this:

You:  How many trial users converted to paid last month?

AI:   [calls get_schema → sees `subscriptions(user_id, plan, status, started_at)`]
      [proposes SELECT below → broker validates it's read-only → runs it]
Enter fullscreen mode Exit fullscreen mode
SELECT COUNT(*) AS conversions
FROM subscriptions
WHERE plan = 'paid'
  AND status = 'active'
  AND started_at >= date_trunc('month', NOW() - INTERVAL '1 month')
  AND started_at <  date_trunc('month', NOW());
Enter fullscreen mode Exit fullscreen mode

Fewer hallucinated columns means fewer failed queries, fewer retries, and less time spent second-guessing what the AI produced. Sharing schema — not credentials — is the trade you want.

Property 5: A smaller, auditable attack surface

Two more properties fall out of the broker model almost for free.

Auditability. Every query flows through one point, so you can log who asked what, when, and which SQL ran. Instead of scattered credentials on a dozen laptops, you have one gateway with a clear access record — the difference between "we think a few people can query prod" and "here's exactly what ran last Tuesday."

Reduced exposure. Your database isn't network-reachable from every machine running an AI client. Only the broker connects to it. That shrinks the attack surface to a single, hardenable service instead of a fan-out of direct connections you have to secure individually.

Common mistakes and gotchas

Even with the right architecture, teams trip over the same things:

  • Assuming the protocol enforces auth for you. It doesn't. MCP leaves access control to the implementor. If you stand up a server with no authentication, you've built a convenient, unguarded door to every database it connects to. Require a valid token on every request.
  • Giving the broker a superuser role. The read-only guarantee only holds if the database role is also read-only. A broker connected as an admin can still be talked into damage. Grant SELECT and nothing else.
  • Aggregating every database behind one unauthenticated broker. A single server wired to prod, staging, and analytics with no access control is a single point of catastrophic failure. Segment access and scope tokens.
  • Trusting AI-written SQL blindly on large tables. Read-only doesn't mean cost-free. An unbounded scan can still hammer your database. Consider query timeouts, row limits, and pointing the broker at a replica.
  • Leaving tokens long-lived "just for now." The temporary secret always outlives the sprint. Prefer short lifetimes and central revocation from day one.

If you'd rather not build and harden all of this yourself, managed database MCP servers exist that implement these patterns out of the box — Draxlr's MCP server, for example, connects over OAuth, is read-only (SELECT only), and exposes schema, query, and dashboard tools rather than raw credentials. The point isn't the specific tool, though — it's that whatever you use, it should hold the connection so the AI never has to.

Key takeaways

The security value of MCP for databases isn't magic in the protocol — it's the broker pattern the protocol makes natural. Put a server between the AI and your database and you get credential isolation (the model never learns your password), read-only enforcement (explore freely, modify never), revocable OAuth access instead of forever-secrets, schema-grounded SQL with fewer hallucinations, and a single auditable, low-exposure gateway.

None of it is automatic. You still have to require authentication, hand the broker a genuinely read-only role, keep tokens short-lived, and put guardrails on expensive queries. Do that, and "let the AI query the database" stops being a gamble and becomes a controlled, reviewable capability.

How are you handling AI access to your database today — direct connections, a broker, or still copy-pasting connection strings? I'd love to hear what's working (and what's bitten you) in the comments.

Top comments (0)