{"slug": "don-t-give-your-agent-the-production-database", "title": "Don't give your agent the production database", "summary": "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.", "body_md": "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:\n\n```\nSELECT o.id, o.amount, u.last_login_at\nFROM biz_order o\nJOIN sys_user u ON u.id = o.user_id\nORDER BY u.last_login_at DESC;\n```\n\nPaste it into your client. Enter:\n\n```\nERROR: column \"last_login_at\" does not exist\nLINE 2: SELECT o.id, o.amount, u.last_login_at\n```\n\nThere is no `last_login_at`\n\ncolumn. There never was. The model did not know — it just decided the column \"should\" exist.\n\nThis 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.\n\nInvented columns are dangerous because they **do not look like errors**. `last_login_at`\n\nappears 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.\n\n**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.\n\n** @schema.sql.** Export DDL and drop it into context. The most honest approach today — but two holes: it\n\n**Live catalog MCP.** Let the Agent query `information_schema`\n\ndirectly. 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.\n\nWire production into the IDE and you hit four walls:\n\n**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%.\n\n**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.\n\n**Production credentials in the IDE are a red line.** Your `.cursor/mcp.json`\n\nends up in git, screenshots, and devices you forgot you synced. Read-only is still production.\n\n**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`\n\n, just harder to spot.\n\nAnd something more fundamental: **structure is not semantics.** `information_schema`\n\ntells you `status`\n\nis `CHAR(1)`\n\n; it does not tell you `'1'`\n\nis valid and `'9'`\n\nis dirty data. It tells you `del_flag`\n\nexists; it does not tell you every query must include `del_flag = '0'`\n\n. Live catalogs are solving structure; semantics live in people's heads and review records — not in the database.\n\nOur approach moves the fact source from \"production database\" to **\"approved version.\"**\n\nIn ERD Online, schema lives in the designer. Each change saves as a named version humans can diff and roll back. That `projectJSON`\n\nis the contract: human-reviewed, no database passwords (`profile.dbs`\n\nstripped at the API layer), and naturally semantic — column labels and notes are where tribal knowledge like \"`del_flag = '0'`\n\n\" gets written down.\n\nThe Agent reads the contract, not production. Three direct consequences:\n\nContracts can be large too, so how you read matters. We added two MCP tools deliberately shaped as \"list first, expand on demand\":\n\n`list_tables`\n\nreturns 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`\n\nfor fields and FK neighborhood (who references me, whom do I reference).\n\nWhen the Agent guesses a wrong table name, the response is not silence or fabrication:\n\n```\n{\n  \"found\": false,\n  \"query\": \"user_id\",\n  \"suggestions\": [\"sys_user\", \"sys_user_role\"],\n  \"hint\": \"Table not in the approved contract. Retry with one of the suggestions; do not invent columns.\"\n}\n```\n\n`found:false`\n\nplus suggestions — invented columns get blocked at the tool layer, not by \"please do not hallucinate\" in the prompt.\n\nBoth tools read an approved version snapshot (pass `versionId`\n\nto 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).\n\nThe next step from this article is not \"go install an MCP first.\" It is more basic: **save a version for your model.**\n\nOpen 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.\n\n👉\n\nTry 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):\n\n[https://github.com/erd-online/erd-online?utm_source=hashnode&utm_campaign=mcp-agent&utm_content=dont-give-agent-prod-db]", "url": "https://wpnews.pro/news/don-t-give-your-agent-the-production-database", "canonical_source": "https://dev.to/erdonline/dont-give-your-agent-the-production-database-354j", "published_at": "2026-08-29 15:25:04+00:00", "updated_at": "2026-08-29 15:49:11.429119+00:00", "lang": "en", "topics": ["ai-agents", "developer-tools", "ai-safety", "ai-tools"], "entities": ["ERD Online", "Cursor", "postgres-mcp"], "alternates": {"html": "https://wpnews.pro/news/don-t-give-your-agent-the-production-database", "markdown": "https://wpnews.pro/news/don-t-give-your-agent-the-production-database.md", "text": "https://wpnews.pro/news/don-t-give-your-agent-the-production-database.txt", "jsonld": "https://wpnews.pro/news/don-t-give-your-agent-the-production-database.jsonld"}}