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

# Database

> Route SQL database reads and writes through separate SQLAlchemy engines.

**DatabaseContextProvider** routes database reads and writes through separate SQLAlchemy engines. It exposes `query_database` for reads and `update_database` for writes.

## Prerequisites

```shell theme={null}
uv pip install -U agno sqlalchemy openai
# Plus your database driver:
uv pip install -U psycopg2-binary  # PostgreSQL
uv pip install -U pymysql          # MySQL
```

<CodeGroup>
  ```bash Mac/Linux theme={null}
  export OPENAI_API_KEY="your_openai_api_key_here"
  ```

  ```bash Windows theme={null}
  $Env:OPENAI_API_KEY="your_openai_api_key_here"
  ```
</CodeGroup>

## Example

```python theme={null}
import asyncio

from sqlalchemy import create_engine

from agno.agent import Agent
from agno.context.database import DatabaseContextProvider
from agno.models.openai import OpenAIResponses

# Database roles enforce permissions for each engine.
readonly_engine = create_engine("postgresql://reader:pass@localhost/mydb")
sql_engine = create_engine("postgresql://writer:pass@localhost/mydb")

db = DatabaseContextProvider(
    sql_engine=sql_engine,
    readonly_engine=readonly_engine,
    schema="public",
)

agent = Agent(
    model=OpenAIResponses(id="gpt-5.4"),
    tools=db.get_tools(),
    instructions=db.instructions(),
)


async def main() -> None:
    await agent.aprint_response("How many orders were placed last month?")


if __name__ == "__main__":
    asyncio.run(main())
```

<Warning>
  **Both engines are required.** The provider routes read requests to `readonly_engine` and write requests to `sql_engine`. Configure `readonly_engine` with database credentials restricted to SELECT operations and the allowed schemas. The `schema` parameter controls table discovery, not authorization for arbitrary SQL.
</Warning>

## Provider Params

| Parameter            | Type            | Default      | Description                                                                                                  |
| -------------------- | --------------- | ------------ | ------------------------------------------------------------------------------------------------------------ |
| `sql_engine`         | `Engine`        | required     | SQLAlchemy engine used by the write sub-agent. Its database credentials determine write permissions.         |
| `readonly_engine`    | `Engine`        | required     | SQLAlchemy engine used by the read sub-agent. Use SELECT-only credentials restricted to the allowed schemas. |
| `schema`             | `str \| None`   | `None`       | Schema used when listing tables and describing columns. It does not authorize or restrict arbitrary SQL.     |
| `id`                 | `str`           | `"database"` | Provider ID. Tools become `query_<id>` and `update_<id>`.                                                    |
| `name`               | `str \| None`   | `None`       | Display name. Defaults to `id` ("database").                                                                 |
| `read_instructions`  | `str \| None`   | `None`       | Custom instructions for the read sub-agent.                                                                  |
| `write_instructions` | `str \| None`   | `None`       | Custom instructions for the write sub-agent.                                                                 |
| `mode`               | `ContextMode`   | `default`    | Tool exposure mode. See [Architecture](/context-providers/overview#mode).                                    |
| `model`              | `Model \| None` | `None`       | Model for the sub-agents. Defaults to Agno's default model.                                                  |
| `read`               | `bool`          | `True`       | Expose `query_database` tool.                                                                                |
| `write`              | `bool`          | `True`       | Expose `update_database` tool.                                                                               |

## Tools Exposed

| Tool              | Description                                                                 |
| ----------------- | --------------------------------------------------------------------------- |
| `query_database`  | Answer database questions through the read sub-agent and `readonly_engine`. |
| `update_database` | Apply database changes through the write sub-agent and `sql_engine`.        |

## Privilege Separation

The read and write sub-agents use separate database connections:

```
query_database  → readonly_engine → read replica / SELECT-only user
update_database → sql_engine      → writable connection
```

The separate engines route each tool to the intended connection. Database roles enforce the boundary. Grant the reader role SELECT-only access to the required schemas, and grant the writer role only the write permissions it needs. The `schema` parameter helps the agent discover tables and columns but does not block cross-schema SQL.

## Multiple Databases

Use different `id` values to expose multiple databases:

```python theme={null}
orders_db = DatabaseContextProvider(
    id="orders",
    sql_engine=orders_write_engine,
    readonly_engine=orders_read_engine,
)

inventory_db = DatabaseContextProvider(
    id="inventory",
    sql_engine=inventory_write_engine,
    readonly_engine=inventory_read_engine,
)

agent = Agent(
    model=...,
    tools=[*orders_db.get_tools(), *inventory_db.get_tools()],
)
# Agent sees: query_orders, update_orders, query_inventory, update_inventory
```

## Read-only Tool Surface

```python theme={null}
db = DatabaseContextProvider(
    sql_engine=engine,
    readonly_engine=readonly_engine,
    write=False,
)
# Agent only sees query_database
```

`write=False` removes `update_database` from the agent's tools. It does not change the permissions of `readonly_engine`, so keep that engine restricted at the database level.

## Cookbook

<Card title="Database Context Provider" icon="database" href="https://github.com/agno-agi/agno/blob/main/cookbook/12_context/04_database_read_write.py">
  Read/write with separate engines
</Card>
