Don't give your agent the production database A developer from ERD Online warns against giving AI coding agents direct access to production databases, citing the risk of 'invented columns' where models fabricate plausible but nonexistent schema elements. The team's solution routes agents through a versioned, human-reviewed schema contract via MCP tools, avoiding credential exposure and context overflow while preserving semantic knowledge. Friday night. You ask Cursor for a query: join orders to users, sort by last login. Three seconds later, an answer arrives with DBA-level confidence: SELECT o.id, o.amount, u.last login at FROM biz order o JOIN sys user u ON u.id = o.user id ORDER BY u.last login at DESC; Paste it into your client. Enter: ERROR: column "last login at" does not exist LINE 2: SELECT o.id, o.amount, u.last login at There is no last login at column. There never was. The model did not know — it just decided the column "should" exist. This is not "AI is not smart enough yet." It has a name — invented column : the model fabricates a plausible column name with no factual source, then writes it into a JOIN with unshakable tone. Invented columns are dangerous because they do not look like errors . last login at appears on 90% of user tables. Syntax is correct. Naming is conventional. Indentation is perfect. Mixed into ten correct JOINs, you will not catch it line by line. You find out in code review — or worse, in production logs. A better prompt. "Do not invent column names; only use the schema I provide" — added to the system prompt. Works day one. By day three, long context and the model forgets. A prompt is a wish, not a constraint. @schema.sql. Export DDL and drop it into context. The most honest approach today — but two holes: it Live catalog MCP. Let the Agent query information schema directly. Directionally correct — give the model a fact source instead of guesses. Tools like postgres-mcp and cloud vendor MCPs do solve half of "stop hallucinating column names." Worth acknowledging. Wire production into the IDE and you hit four walls: Permission-filtered information schema is another kind of hallucination. The database account you give the Agent probably cannot see every table. The "complete schema" it returns is only the slice it is allowed to see. The model will not say "I only see 60% of tables." It keeps inventing columns in the missing 40%. A 240-table full dump blows context. Real projects are not eight-table demos. Dump everything and you either truncate or drown the three relevant tables in noise. Production credentials in the IDE are a red line. Your .cursor/mcp.json ends up in git, screenshots, and devices you forgot you synced. Read-only is still production. Tool-list cache refreshes only on restart. A colleague adds a column at 10 a.m.; your Agent at 3 p.m. still writes SQL from cached schema — the same stale problem as @schema.sql , just harder to spot. And something more fundamental: structure is not semantics. information schema tells you status is CHAR 1 ; it does not tell you '1' is valid and '9' is dirty data. It tells you del flag exists; it does not tell you every query must include del flag = '0' . Live catalogs are solving structure; semantics live in people's heads and review records — not in the database. Our approach moves the fact source from "production database" to "approved version." In ERD Online, schema lives in the designer. Each change saves as a named version humans can diff and roll back. That projectJSON is the contract: human-reviewed, no database passwords profile.dbs stripped at the API layer , and naturally semantic — column labels and notes are where tribal knowledge like " del flag = '0' " gets written down. The Agent reads the contract, not production. Three direct consequences: Contracts can be large too, so how you read matters. We added two MCP tools deliberately shaped as "list first, expand on demand": list tables returns table name, label, and field count — eight rows for an eight-table project, 240 rows for a 240-table project, neither blows context. The Agent picks the two or three it needs, then calls describe table for fields and FK neighborhood who references me, whom do I reference . When the Agent guesses a wrong table name, the response is not silence or fabrication: { "found": false, "query": "user id", "suggestions": "sys user", "sys user role" , "hint": "Table not in the approved contract. Retry with one of the suggestions; do not invent columns." } found:false plus suggestions — invented columns get blocked at the tool layer, not by "please do not hallucinate" in the prompt. Both tools read an approved version snapshot pass versionId to pin a revision . No SQL execution. No database connection. Same configuration path as existing tools — see Read ER diagrams from Cursor via MCP https://www.erdonline.com/docs/guide/api-and-mcp?utm source=hashnode&utm campaign=mcp-agent&utm content=dont-give-agent-prod-db . The next step from this article is not "go install an MCP first." It is more basic: save a version for your model. Open the demo, change one table, save a named version, watch the diff — 30 seconds, no signup. The moment you save that first version, you have a contract the Agent can read and you can stand behind. MCP comes after, if you want it. 👉 Try it in 30 seconds without signing up: https://www.erdonline.com/demo?utm source=hashnode&utm campaign=mcp-agent&utm content=dont-give-agent-prod-db Open source MIT — star / issue / PR welcome : https://github.com/erd-online/erd-online?utm source=hashnode&utm campaign=mcp-agent&utm content=dont-give-agent-prod-db