cd /news/ai-agents/let-an-ai-agent-query-your-prodution… · home › topics › ai-agents › article
[ARTICLE · art-169030] src=railyard.run ↗ pub= topic=ai-agents verified=true sentiment=↑ positive

Let an AI agent query your prodution database safely with read-only SQL over MCP

Railyard published a tutorial showing how to give an AI agent read-only SQL access to a production database through its `list_tables` and `query_database` MCP tools, with five guard rails: a write-scoped connection requirement, an admin/owner-only role cap, a read-only transaction that is always rolled back on Postgres (read-only mode on MySQL and ClickHouse), a statement timeout, and a 200-row cap flagged as `truncated` when exceeded. The tutorial also states that SQL text is never written into the MCP audit entry, so queries containing an email address are not copied into the activity log. The setup matters because it lets agents answer plain-language business, data-quality and performance questions without production credentials that can modify data.

by read6 min views4 publishedNov 13, 2026
Let an AI agent query your prodution database safely with read-only SQL over MCP
Image: Railyard (auto-discovered)

← BlogGuides & tutorials • 4 min read

Follow this on your own server. Start free, no card needed

“How many teams signed up this week?” “Which customers have more than three failed payments?” “Is the orders table actually using that index?” These questions come up every day, and every answer means opening a database console with production credentials. An agent can answer them in plain language, as long as it can’t change anything while it looks.

This tutorial sets that up on Railyard with the list_tables and query_database MCP tools, and explains each guard rail so you know exactly what an agent can and can’t do.

The guard rails, in order #

Before the how-to, here’s why it’s safe. Every query passes through five layers.

  1. Scope.query_database andlist_tables need a** write** -scoped connection, even though they only read. Your data is the most sensitive thing in the account, so a read-only dashboard connection doesn’t get it. Agents that only check deploys never see rows.
  2. Role cap. Only admins and owners can grantwrite . A viewer’s or deployer’s agent can’t query data, whatever scope it asked for. Members limited to some apps can’t reach other apps’ databases.
  3. Read-only transaction. On Postgres the query runs inside aREAD ONLY transaction that is always rolled back. On MySQL it runs in a read-only transaction, and ClickHouse runs it in read-only mode. AnINSERT ,UPDATE ,DELETE or DDL statement fails at the database, not just in a filter that could be bypassed.
  4. Time limit. A statement timeout stops runaway queries, so an accidental cross join can’t pin your primary.
  5. Row cap. At most 200 rows come back, flagged astruncated when there were more. Aggregate in SQL and the agent gets a precise answer instead of a sample.

And one rule outside the database: the SQL text is never written into the MCP audit entry, so a query that happens to contain an email address doesn’t get copied into your activity log.

Step 1: Connect with write access #

Write access is for admins and owners. In Claude, go to Customize → Connectors → Railyard → Connect and choose Full (write) on the consent page. In Claude Code:

claude mcp add --transport http railyard https://app.railyard.run/api/mcp

Then run /mcp → railyard → Authenticate, and pick write.

If you’d rather give a specific agent data access without dashboard sign-in, create a write API token under Settings → Access & tokens, set an expiry, and pass it as a Bearer header. The same rules apply.

Step 2: Let it learn the schema #

> What tables does the shop app's database have?

list_tables returns each table with approximate row counts and sizes, largest first. That’s usually enough for the agent to orient itself:

orders        1.2M rows   840 MB
order_items   3.9M rows   610 MB
customers     210K rows    95 MB
payments      1.3M rows   120 MB

When it needs columns, it asks the database the standard way:

SELECT column_name, data_type
FROM information_schema.columns
WHERE table_name = 'orders'
ORDER BY ordinal_position;

If an app has more than one database (say Postgres and ClickHouse), name it with engine: "clickhouse" or just say “in ClickHouse”.

Step 3: Ask real questions #

Business questions

> How many new customers signed up each day this week?
SELECT date_trunc('day', created_at) AS day, count(*)
FROM customers
WHERE created_at >= now() - interval '7 days'
GROUP BY 1 ORDER BY 1;

Data-quality questions, often while debugging a failed deploy:

> Are there duplicate emails in orders? A unique index migration just failed.
SELECT email, count(*) FROM orders GROUP BY email HAVING count(*) > 1 ORDER BY 2 DESC LIMIT 50;

Performance questions

> Is the orders lookup by customer_id using an index?
EXPLAIN SELECT * FROM orders WHERE customer_id = 42;

Plain EXPLAIN is fine. EXPLAIN ANALYZE actually executes the query, so on a big table the time limit may stop it, which is the point.

Step 4: Write queries that fit the limits #

The 200-row cap shapes good habits, and Claude follows them quickly when you say so once:

  • Aggregate in SQL.count ,sum ,avg ,percentile_cont andGROUP BY return a handful of rows that answer the question exactly.
  • Filter early. Put a date range on large tables. It’s faster and stays under the time limit.
  • Order and limit on purpose. “Top 20 by revenue” beats “all customers”.
  • Look at the truncated flag. When it’strue , the answer is a sample. Ask for an aggregate instead.

What it can’t do, and what to do instead #

  • Change data. No writes, ever, through this tool. To fix bad data, have Claude draft theUPDATE and run it yourself in the dashboard’s database console, or ship it as a migration so it’s reviewed and repeatable.
  • Restore a backup.restore_backup exists, but it always waits for an admin’s approval in Railyard, because it replaces today’s data.
  • Export everything. Bounded results are deliberate. For a full export, download a backup from the dashboard.

Backups, from the same conversation #

The agent can also see and start backups:

> When was the shop database last backed up? Take one now before I run the migration.

list_backups shows recent backups with status and size. create_backup starts one immediately (it needs write, and it’s additive, so no approval). It’s a good habit before any risky migration, and now it’s one sentence.

Questions people ask #

Does the agent see my database password? Not through queries; they run through Railyard, and no connection string is needed or returned. But a write connection can read variable values with get_env, and that can include a DATABASE_URL. Seal sensitive variables and they stay masked.

Can a prompt injection make it write? No. The read-only transaction is enforced by the database itself, not by checking the SQL text. A malicious instruction hidden in a row the agent reads can’t turn a read into a write.

Which databases are supported? Postgres, MySQL and ClickHouse that Railyard manages for your app.

The full tool reference is in the MCP docs.

Why people pick Railyard for this #

  • Your own cloud account. Deploy to DigitalOcean, Hetzner, Vultr, AWS, OVHcloud, or any Linux server over SSH, billed directly by your provider with no markup.
  • One flat price per team. Free is free forever: one server plus a database server, three apps, no card. Pro is $19 a month for up to five servers and unlimited apps. Team is $99 a month with unlimited servers. No per-app, per-deploy, per-seat or per-gigabyte charges.
  • One-click templates. Deploy open-source apps, like databases, analytics and dev tools, to your own server in one click.
  • Managed databases. Postgres, MySQL and Redis with scheduled, restore-verified backups and point-in-time recovery.
  • Push-to-deploy. Deploy from GitHub, GitLab or the CLI, with automatic HTTPS and zero-downtime releases.
── more in #ai-agents 4 stories · sorted by recency
── more on @railyard 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
→ Live at https://your-agent.zahid.host ✓
Get free account → Pricing
from €0/mo · no card required
LIVE [news/let-an-ai-agent-quer…] indexed:0 read:6min 2026-11-13 · —