> ## Documentation Index
> Fetch the complete documentation index at: https://agno-v2.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Querying your data

> Give an agent read-only SQL access and schema introspection.

`SQLTools` connects an agent to a database. Point it at a read-only connection and the agent can introspect the schema and run queries.

```bash theme={null}
uv pip install "agno[openai,psycopg,sql]"
```

```python theme={null}
from agno.agent import Agent
from agno.db.postgres import PostgresDb
from agno.models.openai import OpenAIResponses
from agno.tools.sql import SQLTools
from sqlalchemy import create_engine

warehouse_url = "postgresql+psycopg://readonly@warehouse/analytics"
readonly_engine = create_engine(
    warehouse_url,
    connect_args={"options": "-c default_transaction_read_only=on"},
)

agent = Agent(
    model=OpenAIResponses(id="gpt-5.5"),
    db=PostgresDb(db_url="postgresql+psycopg://ai:ai@localhost:5532/ai"),
    tools=[SQLTools(db_engine=readonly_engine)],
    instructions=(
        "Introspect the schema before writing SQL. Answer with the numbers "
        "and the exact query you ran. Never guess a column name. Pass "
        "limit=None to run_sql_query when the answer needs every row."
    ),
)

agent.print_response("How many active subscriptions are on the Pro plan?")
```

The agent's `db` and the `SQLTools` connection are separate. `db` stores the agent's sessions. `SQLTools` points at the warehouse you are answering questions about. Keep them distinct.

## Introspect before generating

`SQLTools` provides `list_tables` and `describe_table` so the agent can read the current schema before generating SQL. The instruction above makes schema introspection part of each request.

| Tool | Use |
| - | - |
| `list_tables` | Discover what exists before querying |
| `describe_table` | Get exact column names and types |
| `run_sql_query` | Execute the generated SQL |

`run_sql_query(query, limit=10)` returns at most 10 rows unless the agent passes a different `limit`. Pass `limit=None` for the full result set. Otherwise a `GROUP BY` over more than 10 groups comes back truncated with no sign that rows were dropped, so tell the agent when an answer needs every row.

## Scope the connection

`SQLTools` executes any SQL the engine permits. It does not classify statements as read-only. This engine starts transactions with `default_transaction_read_only=on`, which blocks ordinary write statements. Because that setting is a configurable session default, use a non-owner database role with no write grants as the hard boundary. See [Safe data access](https://agno-v2.mintlify.site/use-cases/data-agents/safe-data-access.md) for the full read and write split.

## Next steps

| Task | Guide |
| - | - |
| Ground SQL in business rules | [Grounding in context](https://agno-v2.mintlify.site/use-cases/data-agents/grounding-in-context.md) |
| Stop repeating query errors | [Self-correcting agents](https://agno-v2.mintlify.site/use-cases/data-agents/self-correcting-agents.md) |
| Allow controlled writes | [Safe data access](https://agno-v2.mintlify.site/use-cases/data-agents/safe-data-access.md) |

## Developer Resources

* [SQL tools cookbook](https://github.com/agno-agi/agno/blob/main/cookbook/91_tools/sql_tools.py)
* [Dash: the multi-agent data team](https://agno-v2.mintlify.site/deploy/templates/dash/overview.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.