You wired up an AI assistant to your database and it's great. You ask "what was MRR by plan last month?" and get back working SQL and an answer in seconds. Then reality sets in: that was your analytics database. Your user records live in a Postgres replica. Your billing events sit in a separate MySQL box that a previous team stood up and nobody wants to touch. Real teams almost never have one database — they have a small constellation of them.
So the interesting question isn't "can an AI query my database?" It's "how do I give one assistant safe, sane access to all of my databases at once — and make sure it queries the right one, in the right dialect, without me babysitting every request?"
The Model Context Protocol (MCP) is built for exactly this. Let's walk through the two ways to connect multiple databases, how the AI decides which one to use, the dialect trap that quietly breaks multi-database setups, and the mistakes that bite people along the way.
Quick refresher: what MCP actually connects
MCP is an open standard that lets AI clients — Claude, Cursor, ChatGPT, VS Code, and others — call tools through a consistent interface. The AI application is the host. For every MCP server it talks to, it spins up a dedicated client connection. A database MCP server typically exposes a few tools like list_databases, get_schema, and run_query.
The key idea: the AI never holds your credentials or speaks the Postgres wire protocol. It calls a tool by name, and the server does the real database work behind the credentials it holds. That indirection is what makes connecting several databases tractable — you're composing tool calls, not juggling connection strings in a chat window.
Two ways to connect many databases
There are two patterns, and the right one depends on how uniform your databases are.
Pattern A: one MCP server per database
You run a separate server for each database and register them all with your AI client. Configuration looks like a list of servers:
{
"mcpServers": {
"users-postgres": {
"command": "npx",
"args": ["-y", "postgres-mcp-server"],
"env": { "DATABASE_URL": "postgres://readonly:***@replica.internal:5432/users" }
},
"analytics-warehouse": {
"command": "npx",
"args": ["-y", "postgres-mcp-server"],
"env": { "DATABASE_URL": "postgres://readonly:***@warehouse.internal:5432/analytics" }
},
"billing-mysql": {
"command": "npx",
"args": ["-y", "mysql-mcp-server"],
"env": { "DATABASE_URL": "mysql://readonly:***@billing.internal:3306/billing" }
}
}
}
This is flexible — each database can use a purpose-built server — but it has a well-known sharp edge: tool name collisions. If all three servers expose a tool called run_query, the host ends up with three tools of the same name in one aggregated list, and it can call the wrong one. The MCP spec acknowledges this and leaves disambiguation to the host. In practice, hosts and proxies solve it by namespacing tools with the server name — billing-mysql.run_query vs. users-postgres.run_query — so each call routes to the intended server.
Pattern B: one gateway server fronting many databases
The alternative is a single MCP server that knows about all your databases and takes the database as a parameter. Instead of three run_query tools, you get one, plus a list_databases tool the AI calls first:
Tools exposed:
list_databases() -> [ {id, name, engine}, ... ]
get_schema(database_id) -> tables + columns for that DB
run_query(database_id, sql) -> rows
A typical session flows: the AI calls list_databases, sees your three sources, picks the relevant one, pulls its schema, then runs a query scoped to that database_id. No collisions, one connection to manage, and one place to enforce read-only access and auditing. Managed MCP servers tend to use this shape — for example, Draxlr exposes a single read-only (SELECT-only) endpoint over OAuth that lists your connected databases and runs schema/query calls per database id. Whichever you pick, the gateway pattern usually scales better once you're past two or three databases.
Here's the trade-off at a glance:
| Concern | Server per database | One gateway server |
|---|---|---|
| Tool name collisions | Possible; needs namespacing | None — database is a parameter |
| Adding a database | Edit client config, restart | Register it once in the gateway |
| Central audit / read-only | Per server | One enforcement point |
| Mixing DB engines | Easy — different server each | Depends on gateway support |
| Best for | 2–3 heterogeneous databases | Many databases, shared access |
How does the AI pick the right database?
This is where good setups separate from frustrating ones. The AI routes based on names and descriptions, not magic. A database registered as db_47 with no description is a coin flip; one called analytics_warehouse described as "aggregated events and revenue, read replica, updated hourly" gives the model everything it needs to choose correctly.
So when you have a question that could plausibly hit two sources, name and describe your databases like you're onboarding a new analyst. Then a prompt like this resolves cleanly:
"How many trial users from the
usersdatabase converted to paid, using the subscription events in thebillingdatabase?"
The assistant recognizes it needs two sources. Since most databases can't join across a network boundary, it does what a human would: query each separately and combine. First, against the users source:
-- users-postgres
SELECT id
FROM users
WHERE plan = 'trial'
AND created_at >= NOW() - INTERVAL '30 days';
Then, against billing, filtering to those ids:
-- billing-mysql
SELECT DISTINCT user_id
FROM subscription_events
WHERE event_type = 'subscription_started'
AND user_id IN (/* ids from the previous step */);
The assistant stitches the two result sets together and reports the count. The important behavior: it did not try to write one query joining users.users to billing.subscription_events — those live in different engines on different hosts, and no single query spans them.
The dialect trap
Here's the gotcha that surprises people: the same English question produces different SQL depending on which database answers it. Ask "revenue by month" against Postgres and MySQL and the correct queries are not identical.
| Task | PostgreSQL | MySQL |
|---|---|---|
| Extract the month | EXTRACT(MONTH FROM created_at) |
MONTH(created_at) |
| Null fallback | COALESCE(x, 0) |
IFNULL(x, 0) |
| Concatenate group | STRING_AGG(name, ', ') |
GROUP_CONCAT(name) |
| Quote an identifier |
"order" (double quotes) |
`order` (backticks) |
This is exactly why schema-awareness matters so much in a multi-database setup. When the MCP server tells the AI not just the table shapes but which engine each database runs, the model targets the right dialect. When it doesn't, you get Postgres syntax fired at MySQL and a confusing error. If your assistant keeps generating queries in the wrong dialect, that's usually the tell that the engine isn't being surfaced in the schema.
Common mistakes and gotchas
Assuming the AI can join across databases. It can't run a single query across two separate engines. Expect a query-each-then-combine approach, and know that huge intermediate result sets (a million ids in an IN (...) list) will be slow or get truncated.
Vague database names. db1, db2, prod_copy_final — the model can't route what it can't understand. Descriptive names and one-line descriptions are the cheapest accuracy win available.
Pointing at primary databases. Exploratory AI queries can be heavy and unpredictable. Connect read replicas, and keep access read-only so an experimental query can never issue an UPDATE or DROP.
Ignoring tool-name collisions. In the server-per-database pattern, verify your host namespaces tools. Two run_query tools with no prefix is a silent routing bug waiting to happen.
Dumping every schema into context at once. Ten databases with hundreds of tables each can blow past the model's context window. Prefer a list_databases → get_schema(one_db) flow that fetches schema on demand rather than loading everything up front.
Key takeaways
Connecting multiple databases to one AI assistant comes down to a few decisions. Use a server per database for a handful of heterogeneous sources, or a single gateway that takes the database as a parameter once you have many. Name and describe every database clearly so the AI routes correctly. Respect dialect differences by making sure each database's engine is part of what the AI sees. Don't expect cross-engine joins — expect query-and-combine. And keep every connection read-only against a replica so exploration stays safe.
Get those right and you go from "the AI can talk to a database" to "the AI understands our whole data estate" — which is where it actually starts saving your team time.
How many databases would your assistant need to reach to be genuinely useful — and are they named well enough for it to tell them apart? I'd love to hear how you've wired up multi-database access, and what tripped you up. Drop a comment.
Top comments (0)