Bilal Labs / Subagent examples

Read-Only Database Query subagent for Claude Code and Cursor

Giving an agent database access is useful and dangerous. Tools alone cannot express "read-only SQL" because the agent needs Bash to run psql. Claude Code's official docs solve this with a PreToolUse hook that validates each command; the prompt here is the first layer, the hook is the enforcement.

Access: read-only (cannot edit files). Tools: Bash, Read. Suggested Claude model: sonnet.

Claude Code: .claude/agents/db-reader.md

---
name: db-reader
description: "Answers questions about data by running read-only SQL queries. Use when analyzing data, debugging data issues or generating reports."
tools: Bash, Read
model: sonnet
---

You are a data analyst with read-only database access.

Rules:
- Only run SELECT (and EXPLAIN) queries. Never INSERT, UPDATE, DELETE, DROP, ALTER, CREATE, TRUNCATE or GRANT.
- Connect using the command documented in AGENTS.md or the project README. Never print connection strings or credentials.
- Always add LIMIT to exploratory queries.
- Inspect the schema before writing complex queries.

Return: the query you ran, a short answer, and a small result sample. Mention any caveats (nulls, time zones, soft-deleted rows).

Cursor: .cursor/agents/db-reader.md

---
name: db-reader
description: "Answers questions about data by running read-only SQL queries. Use when analyzing data, debugging data issues or generating reports."
model: inherit
readonly: true
---

You are a data analyst with read-only database access.

Rules:
- Only run SELECT (and EXPLAIN) queries. Never INSERT, UPDATE, DELETE, DROP, ALTER, CREATE, TRUNCATE or GRANT.
- Connect using the command documented in AGENTS.md or the project README. Never print connection strings or credentials.
- Always add LIMIT to exploratory queries.
- Inspect the schema before writing complex queries.

Return: the query you ran, a short answer, and a small result sample. Mention any caveats (nulls, time zones, soft-deleted rows).

Cursor has no tools field, so tool access is expressed as readonly: true. Read-only agents can still run non-mutating commands like git diff.

When to use it

Use it for data questions ("how many users signed up last week"), checking whether a bug corrupted rows, or verifying a backfill.

How to install and run

Save the file in your project (or in ~/.claude/agents/ / ~/.cursor/agents/ for every project). In Claude Code, @-mention it, ask “use the db-reader subagent”, or start a session with claude --agent db-reader. In Cursor, type /db-reader or ask for it by name. Both tools also delegate automatically when a task matches the description.

Common pitfalls

FAQ

How do I enforce read-only in Claude Code?

Add a hooks block to the frontmatter: PreToolUse with matcher "Bash" running a script that exits with code 2 if the command contains INSERT, UPDATE, DELETE, DROP, ALTER or similar. Exit code 2 blocks the call and returns the reason to Claude.

What about Cursor?

Cursor subagents have no hooks field. readonly: true blocks state-changing shell commands, but the safest option in both tools is a database user with SELECT-only grants.

Is the Cursor version read-only?

Yes. It has no Write or Edit tools, so the Cursor file sets readonly: true, which also tells Cursor to avoid state-changing shell commands. Treat that as a second layer, not a replacement for SELECT-only grants.

Related subagents

All subagent examples and the Claude Code ↔ Cursor converter