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.

uv pip install "agno[openai,psycopg,sql]"

Set OPENAI_API_KEY in the environment before running the Python code. Replace the warehouse URLs with your own PostgreSQL connection strings and create the database roles, schemas, and grants described on this page first. The host warehouse, database analytics, and roles such as readonly and dash_writer are placeholders.

Examples using PostgresDb or PgVector also need a separate writable application database. The sample URL assumes a PostgreSQL service at localhost:5532 with database/user/password ai; knowledge examples need the pgvector extension. See PgVector setup. Keep this application's storage credentials separate from the restricted warehouse role.

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 asks for schema introspection on each request; the model decides whether to call those tools. Fetch schema context in application code before the run when inspection must be guaranteed.

ToolUse
list_tablesDiscover what exists before querying
describe_tableGet exact column names and types
run_sql_queryExecute 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 for the full read and write split.

Next steps

TaskGuide
Ground SQL in business rulesGrounding in context
Stop repeating query errorsSelf-correcting agents
Allow controlled writesSafe data access

Developer Resources