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

# Context Providers

> Three ways to give your agent the right context on every run.

An agent is only as good as the context it has access to. Even a perfect system prompt fails without the right context at runtime.

AgentOS gives you three primitives for getting context to the agent:

| Primitive             | Shape                 | When it loads                                           |
| --------------------- | --------------------- | ------------------------------------------------------- |
| **Knowledge**         | Indexed content (RAG) | Auto-searched before each run, or via tool              |
| **Dependencies**      | A dict you pass in    | Injected into the prompt and available to tools         |
| **Context providers** | Live external systems | A clean tool surface backed by source-scoped sub-agents |

Knowledge is durable and pre-loaded. Dependencies are ephemeral and request-scoped. Context providers are dynamic and authenticated, and they're the primary architectural move for any agent that touches more than one external system. Most production agents use some combination of the three.

## Knowledge

Knowledge is what the agent looks up: documentation, policies, code conventions, table schemas. It lives in your database with embeddings and surfaces via similarity search.

```python theme={null}
from agno.knowledge import Knowledge
from agno.vector_db.pgvector import PgVector

agent = Agent(
    model=...,
    db=db,
    knowledge=Knowledge(
        vector_db=PgVector(table_name="my_kb", db_url="postgresql://..."),
    ),
    search_knowledge=True,              # recommended: expose as a callable tool
    add_knowledge_to_context=True,      # traditional RAG: auto-search before each run
)

agent.knowledge.add_content_from_path("docs/")
agent.knowledge.add_content_from_url("https://example.com/policies")
```

| Flag                            | Behavior                                                                                                |
| ------------------------------- | ------------------------------------------------------------------------------------------------------- |
| `search_knowledge=True`         | The agent gets a `search_knowledge_base(query)` tool. Known as Agentic RAG.                             |
| `add_knowledge_to_context=True` | AgentOS runs `knowledge.search(message)` before each turn and injects the top-k chunks into the prompt. |

Knowledge supports hybrid search, reranking, and chunking.

## Dependencies

Sometimes an agent needs runtime values that aren't in knowledge: a feature flag, a tenant ID, a per-request DB connection, an API key scoped to the caller.

```python theme={null}
from agno.agent import Agent
from agno.run import RunContext
from agno.tools import tool

@tool
def get_config(run_context: RunContext, key: str) -> str:
    return str(run_context.dependencies["config"].get(key, "not set"))

agent = Agent(
    model=...,
    dependencies={
        "config": {"region": "us-east-1", "max_retries": 3},
        "feature_flags": {"beta_search": True},
        "tenant_id": "acme-corp",
    },
    add_dependencies_to_context=True,
    tools=[get_config],
)
```

`add_dependencies_to_context=True` injects them into the system prompt. Tools that take a `RunContext` parameter get access via `run_context.dependencies`.

Per-request dependencies override the agent-level ones for a single run:

```python theme={null}
agent.run(
    "What region am I in?",
    dependencies={"region": "eu-west-1"},   # request-scoped
    user_id="user-123",
)
```

For a worked example, see the [Injector agent](/demo-os/dependencies).

## Context providers

If you've built an agent with a real number of tools, you've hit three walls:

1. **Context pollution.** Every tool description, schema, and example lands in the system prompt. Slack alone is 8 to 12 tools. Add Drive, GitHub, your CRM, and you're at 50 tools before adding anything custom. Past 20, models start hallucinating tools, calling them with the wrong shape, or skipping the right one.
2. **Scope collisions.** `search` in one toolkit collides with `search` in another. `send_message` could be Slack, email, or your CRM. The agent picks wrong half the time, and no naming convention fixes it.
3. **System-prompt sprawl.** Using Slack well requires Slack-specific guidance: look up user IDs before DMing, resolve channel names, prefer `conversations.history` for channels and `conversations.replies` for threads. Multiply by every API. The system prompt becomes the union of every source's quirks.

A `ContextProvider` is a thin layer between the agent and the tools that fixes all three:

<div className="text-center text-2xl font-mono my-10">
  <span className="text-zinc-400">Agent</span>
  <span className="mx-3 text-orange-500">↔</span>
  <span className="text-zinc-200">ContextProvider</span>
  <span className="mx-3 text-orange-500">↔</span>
  <span className="text-zinc-400">Tools</span>
</div>

To the calling agent, each provider exposes exactly two tools: `query_<id>` for natural-language reads, and `update_<id>` for writes (or a clean read-only error). Behind the tool is a sub-agent scoped to that one source. The sub-agent owns the source's tools, the source's quirks, the lookup-before-write patterns, the pagination weirdness. It runs in its own context, returns an answer, and the calling agent gets a clean result.

```python theme={null}
from agno.agent import Agent
from agno.context.slack import SlackContextProvider
from agno.context.gdrive import GDriveContextProvider
from agno.context.database import DatabaseContextProvider

slack = SlackContextProvider(id="slack")
drive = GDriveContextProvider(id="drive")
crm = DatabaseContextProvider(id="crm", sql_engine=engine, readonly_engine=ro_engine)

agent = Agent(
    model=...,
    tools=[*slack.get_tools(), *drive.get_tools(), *crm.get_tools()],
)
```

The agent sees four tools: `query_slack`, `query_drive`, `query_crm`, `update_crm`. Add ten more sources and the surface stays linear at 2N. The agent's prompt doesn't grow with the number of integrations.

### Built-in providers

| Provider                        | Source                                             | Read         | Write         |
| ------------------------------- | -------------------------------------------------- | ------------ | ------------- |
| `FilesystemContextProvider`     | A scoped local directory                           | `query_<id>` | —             |
| `WebContextProvider`            | Web search and fetch (Exa or Parallel; SDK or MCP) | `query_<id>` | —             |
| `DatabaseContextProvider`       | Any SQL database via SQLAlchemy                    | `query_<id>` | `update_<id>` |
| `SlackContextProvider`          | A Slack workspace                                  | `query_<id>` | `update_<id>` |
| `GmailContextProvider`          | Gmail via OAuth or service account                 | `query_<id>` | `update_<id>` |
| `GoogleCalendarContextProvider` | Google Calendar via OAuth or service account       | `query_<id>` | `update_<id>` |
| `GoogleDriveContextProvider`    | Google Drive; all-drives + Office extraction       | `query_<id>` | `update_<id>` |
| `WikiContextProvider`           | Git-backed markdown wiki (FS or Git backend)       | `query_<id>` | `update_<id>` |
| `WorkspaceContextProvider`      | Composite: Drive + Gmail + Calendar                | `query_<id>` | `update_<id>` |
| `MCPContextProvider`            | Any MCP server (stdio, SSE, streamable-HTTP)       | `query_<id>` | —             |

#### Web backends

`WebContextProvider` takes a backend, so you can swap providers without touching the agent:

| Backend              | What                                                                   | When                                                                       |
| -------------------- | ---------------------------------------------------------------------- | -------------------------------------------------------------------------- |
| `ExaBackend`         | Exa direct SDK. `web_search` + `web_extract`.                          | You have an `EXA_API_KEY` and want full extraction payloads.               |
| `ExaMCPBackend`      | Exa public MCP server. Keyless, rate-limited.                          | First experiment with no signup. Keyed for higher throughput.              |
| `ParallelBackend`    | Parallel direct SDK. `web_search` + `web_extract`.                     | You prefer Parallel's ranking and excerpt shape. Needs `PARALLEL_API_KEY`. |
| `ParallelMCPBackend` | Parallel public MCP. `web_search` + `web_fetch` (compressed markdown). | Token-efficient output. Keyless for trial; keyed for higher limits.        |

```python theme={null}
from agno.context.web import WebContextProvider, ExaBackend

web = WebContextProvider(backend=ExaBackend(), id="web")
```

#### Read/write separation

Writable providers (Database, Slack, GitHub) run two sub-agents under the hood with minimum privilege per role:

* **Database**: read sub-agent uses the readonly engine; write sub-agent uses the writable engine. The read path can't mutate even if the model tries.
* **Slack**: read sub-agent gets `search_workspace`, `get_channel_history`, `get_thread`, lookup tools. Write sub-agent gets `send_message` and the lookup tools it needs to resolve names. The reader never sees `send_message`. The writer never sees search.
* **GitHub**: read sub-agent operates on a clone with read-only Workspace + git tools. Write sub-agent operates on a per-session worktree on a `<prefix>/<task>` branch and ends in a PR. The agent cannot push to the default branch.

These are infrastructure-level guarantees, not prompt instructions. They hold even if the model goes off-script.

### Configuration

#### Lifecycle

Some providers hold async resources: MCP sessions, watched inboxes, cloned repos. They implement `asetup()` and `aclose()`. Bracket their use so the resource is owned by a single task:

```python theme={null}
await provider.asetup()
try:
    # provider.get_tools() is now ready
    ...
finally:
    await provider.aclose()
```

In an AgentOS app, register providers with the runtime and they're set up and torn down on the FastAPI lifespan automatically. Manual bracketing is for scripts and tests.

Providers without async resources (`FilesystemContextProvider`, `DatabaseContextProvider` with sync engines, the SDK-based web backends) work without `asetup` / `aclose`.

#### Mode

Every provider has a `mode` parameter that controls how it exposes tools to the calling agent:

| Mode                  | What happens                                                                                                                                                  |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ContextMode.default` | Provider's recommended exposure. Read/write providers typically expose two tools: `query_<id>` + `update_<id>`. Read-only providers expose `query_<id>` only. |
| `ContextMode.agent`   | Single `query_<id>` tool wrapping a sub-agent. Good for complex sources that need internal orchestration.                                                     |
| `ContextMode.tools`   | Expose the underlying tools directly (flat). Calling agent orchestrates raw tools itself. Use for fine-grained control.                                       |

```python theme={null}
from agno.context.mode import ContextMode
from agno.context.web import WebContextProvider, ExaBackend

# Default: provider decides optimal exposure
web = WebContextProvider(backend=ExaBackend(), id="web")

# Tools mode: expose raw web_search + web_fetch
web = WebContextProvider(backend=ExaBackend(), id="web", mode=ContextMode.tools)
```

Why this matters: In `default` mode, `WebContextProvider` wraps its backend in a sub-agent that synthesizes cited answers. In `tools` mode, your agent calls `web_search` and `web_fetch` directly and does its own synthesis. The tradeoff is control vs. complexity.

#### RunContext propagation

When a provider runs a sub-agent, it extracts auth and framework state from the caller's `RunContext` and forwards it:

```
Calling Agent
  ↓ tool call with run_context
_query_tool() / _update_tool()
  ↓ passes run_context to aquery() / aupdate()
Provider.aquery()
  ↓ extracts via _run_kwargs_for_sub_agent(run_context)
Sub-agent arun(question, user_id=..., session_id=..., metadata=..., dependencies=...)
```

The forwarded fields are `user_id`, `session_id`, `metadata`, and `dependencies`. This ensures:

* **Per-user auth tokens** reach the sub-agent's tools
* **Framework-injected state** (e.g., Slack's `action_token` in `metadata`) survives the hop
* **User/session isolation** works across provider boundaries

Message history and `session_state` stay with the outer agent. Sub-agents run isolated.

#### Read/write flags

Providers with both read and write support can be restricted to one direction:

```python theme={null}
# Read-only: agent can search emails but not send
gmail = GmailContextProvider(write=False)

# Write-only: agent can send but not read inbox
gmail = GmailContextProvider(read=False)
```

### Composition

#### Multi-provider

Three providers on one agent compose cleanly because each provider has its own namespace:

```python theme={null}
fs = FilesystemContextProvider(id="cookbooks", root="./docs")
web = WebContextProvider(backend=ExaMCPBackend(), id="web")
db = DatabaseContextProvider(id="releases", sql_engine=engine, readonly_engine=engine)

agent = Agent(
    model=...,
    tools=[*fs.get_tools(), *web.get_tools(), *db.get_tools()],
    instructions="\n".join([fs.instructions(), web.instructions(), db.instructions()]),
)
```

The agent sees `query_cookbooks`, `query_web`, `query_releases` and picks the right one per question. The compositional payoff lands when one provider's output feeds another's query: pull a topic from Slack, run a web search on it, synthesize the briefing in one turn.

#### Custom providers

When the built-ins don't fit, subclass `ContextProvider`. The base class handles tool wrapping, name derivation, and error shaping.

**Required methods (abstract):**

| Method                                            | Purpose            |
| ------------------------------------------------- | ------------------ |
| `query(question, *, run_context=None) -> Answer`  | Sync read          |
| `aquery(question, *, run_context=None) -> Answer` | Async read         |
| `status() -> Status`                              | Sync health check  |
| `astatus() -> Status`                             | Async health check |

**Optional methods (override to customize):**

| Method                   | Default                      | Override when                                 |
| ------------------------ | ---------------------------- | --------------------------------------------- |
| `update()` / `aupdate()` | Raises `NotImplementedError` | Provider supports writes                      |
| `asetup()`               | No-op                        | Need async init (MCP sessions, cache priming) |
| `aclose()`               | No-op                        | Hold long-lived state (watches, connections)  |
| `instructions()`         | Generic guidance             | Want source-specific usage hints              |
| `_default_tools()`       | `[_query_tool()]`            | Custom tool surface for `mode=default`        |
| `_all_tools()`           | `[_query_tool()]`            | Custom surface for `mode=tools`               |

```python theme={null}
from agno.context import Answer, ContextProvider, Status

FAQ = {"pricing": "See agno.com/pricing", "support": "Email help@agno.com"}

class FAQContextProvider(ContextProvider):
    def status(self) -> Status:
        return Status(ok=True, detail=f"{len(FAQ)} entries")

    async def astatus(self) -> Status:
        return self.status()

    def query(self, question: str, *, run_context=None) -> Answer:
        key = next((k for k in FAQ if k in question.lower()), None)
        return Answer(text=FAQ[key] if key else "No FAQ entry matches that.")

    async def aquery(self, question: str, *, run_context=None) -> Answer:
        return self.query(question, run_context=run_context)

faq = FAQContextProvider(id="faq")
agent = Agent(model=..., tools=faq.get_tools())
```

The agent now has a `query_faq` tool. Same shape as every built-in provider.

#### Sub-agent model

Every provider runs its own sub-agent on a configurable model. Default to a cheap one for the source-specific work and let the calling agent use a stronger model for synthesis:

```python theme={null}
from agno.models.openai import OpenAIResponses

slack = SlackContextProvider(id="slack", model=OpenAIResponses(id="gpt-5.4-mini"))

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

The sub-agent does the tool work; the calling agent does the reasoning. On most workloads this is cheaper *and* faster than putting every source's tools on one big agent.

### Provider catalog

#### Google providers

Agno includes four Google-specific providers. All support two auth methods:

<AccordionGroup>
  <Accordion title="Service Account + domain-wide delegation (headless)">
    For workspace accounts. Set environment variables:

    * `GOOGLE_SERVICE_ACCOUNT_FILE` — path to service account JSON
    * `GOOGLE_DELEGATED_USER` — email to impersonate (required for Gmail since service accounts have no inbox)
  </Accordion>

  <Accordion title="OAuth (interactive)">
    For personal accounts. Set environment variables:

    * `GOOGLE_CLIENT_ID`
    * `GOOGLE_CLIENT_SECRET`
    * `GOOGLE_PROJECT_ID`

    Opens browser on first use and caches token locally.
  </Accordion>
</AccordionGroup>

<Tabs>
  <Tab title="Gmail">
    Search, read, draft, send, reply, and manage labels.

    ```python theme={null}
    from agno.context.gmail import GmailContextProvider

    gmail = GmailContextProvider()

    # Read-only variant
    gmail = GmailContextProvider(write=False)
    ```

    | Sub-agent | Tools                                                                        |
    | --------- | ---------------------------------------------------------------------------- |
    | Read      | `search_emails`, `get_message`, `list_labels`                                |
    | Write     | `draft_email`, `send_email`, `reply_to_email`, `create_label`, `apply_label` |
  </Tab>

  <Tab title="Calendar">
    Query events, check availability, create/update/delete events.

    ```python theme={null}
    from agno.context.calendar import GoogleCalendarContextProvider

    calendar = GoogleCalendarContextProvider()

    # Read-only: agent can check availability but not create events
    calendar = GoogleCalendarContextProvider(write=False)
    ```

    | Sub-agent | Tools                                          |
    | --------- | ---------------------------------------------- |
    | Read      | `list_events`, `get_event`, `list_calendars`   |
    | Write     | `create_event`, `update_event`, `delete_event` |
  </Tab>

  <Tab title="Drive">
    Search files, read content (including Office formats), upload files.

    ```python theme={null}
    from agno.context.gdrive import GoogleDriveContextProvider

    drive = GoogleDriveContextProvider(id="drive")

    # Include shared drives
    drive = GoogleDriveContextProvider(id="drive", include_shared_drives=True)
    ```

    Supports Office file extraction (docx, xlsx, pptx) when the appropriate readers are installed.
  </Tab>

  <Tab title="Workspace">
    Composite provider combining Drive, Gmail, and Calendar in one surface.

    ```python theme={null}
    from agno.context.workspace import WorkspaceContextProvider

    workspace = WorkspaceContextProvider()

    agent = Agent(
        model=...,
        tools=workspace.get_tools(),
        instructions=workspace.instructions(),
    )
    ```

    <Tip>
      **Recommended for personal assistants.** Use `WorkspaceContextProvider` when your agent needs full Google Workspace access instead of composing individual providers.
    </Tip>
  </Tab>
</Tabs>

#### WikiContextProvider

Read/write access to a directory of markdown files. Choose a backend based on your versioning needs:

<Tabs>
  <Tab title="Filesystem">
    Local directory, no versioning. Good for prototyping.

    ```python theme={null}
    from agno.context.wiki import WikiContextProvider
    from agno.context.wiki.backend import FileSystemBackend

    wiki = WikiContextProvider(
        backend=FileSystemBackend(root="./docs"),
        id="wiki",
    )
    ```
  </Tab>

  <Tab title="Git">
    Auto-commits after every write. Optionally rebases and pushes to remote.

    ```python theme={null}
    from agno.context.wiki import WikiContextProvider
    from agno.context.wiki.backend import GitBackend

    wiki = WikiContextProvider(
        backend=GitBackend(
            repo_url="git@github.com:org/wiki.git",
            local_path="./wiki-clone",
            branch="main",
        ),
        id="wiki",
    )
    ```

    This turns any markdown directory into a version-controlled wiki the agent can edit.
  </Tab>
</Tabs>

<Accordion title="Optional: Web ingestion">
  Pass a `web` backend to let the write sub-agent pull content from URLs before writing:

  ```python theme={null}
  from agno.context.web import ExaBackend

  wiki = WikiContextProvider(
      backend=FileSystemBackend(root="./docs"),
      web=ExaBackend(),
      id="wiki",
  )
  ```
</Accordion>

### Worked examples

<Card title="Scout" icon="rocket" href="/tutorials/scout/overview">
  Open-source company intelligence agent. Navigates live information sources (web, Slack, Drive, wiki, CRM, MCP servers) to assemble context on demand — and builds its own wiki and CRM as it learns.
</Card>

<CardGroup cols={2}>
  <Card title="Filesystem" icon="folder" href="https://github.com/agno-agi/agno/blob/main/cookbook/12_context/00_filesystem.py">
    Agent explores your codebase or docs folder
  </Card>

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

  <Card title="MCP Server" icon="plug" href="https://github.com/agno-agi/agno/blob/main/cookbook/12_context/06_mcp_server.py">
    Raw MCP tools on the agent, no sub-agent wrapper
  </Card>

  <Card title="Multi-provider" icon="layer-group" href="https://github.com/agno-agi/agno/blob/main/cookbook/12_context/08_multi_provider.py">
    Three sources on one agent, no tool collisions
  </Card>

  <Card title="Web + Slack" icon="arrows-split-up-and-left" href="https://github.com/agno-agi/agno/blob/main/cookbook/12_context/09_web_plus_slack.py">
    Pull topics from Slack, research on web, synthesize
  </Card>

  <Card title="Custom provider" icon="code" href="https://github.com/agno-agi/agno/blob/main/cookbook/12_context/10_custom_provider.py">
    Build your own provider for any data source
  </Card>

  <Card title="Gmail" icon="envelope" href="https://github.com/agno-agi/agno/blob/main/cookbook/12_context/18_gmail.py">
    Search, read, draft, send emails
  </Card>

  <Card title="Calendar" icon="calendar" href="https://github.com/agno-agi/agno/blob/main/cookbook/12_context/19_calendar.py">
    Query events, check availability, create meetings
  </Card>

  <Card title="Wiki (filesystem)" icon="book" href="https://github.com/agno-agi/agno/blob/main/cookbook/12_context/14_wiki_filesystem.py">
    Prose memory backed by local markdown files
  </Card>

  <Card title="Wiki (git)" icon="code-branch" href="https://github.com/agno-agi/agno/blob/main/cookbook/12_context/15_wiki_git.py">
    Prose memory with automatic git commits
  </Card>

  <Card title="Google Workspace" icon="google" href="https://github.com/agno-agi/agno/blob/main/cookbook/12_context/20_google_workspace.py">
    Drive, Gmail, and Calendar in one surface
  </Card>
</CardGroup>

## Next

[Human Approval →](/features/human-approval)
