What are Context Providers?

Context Providers give agents clean access to external systems without tool sprawl.

A context provider gives an agent a small tool surface for a data source. Most built-in providers expose query_<id> and, where supported, update_<id>. A source-specific sub-agent chooses the underlying tools; custom providers can answer directly.

Agent ↔ ContextProvider ↔ Source tools

This keeps source-specific tool descriptions and instructions out of the calling agent’s tool list. Each provider still needs the dependencies, credentials and resources required by its source.

Create a virtual environment using SDK setup, then install the model dependency and set your key:

uv pip install -U agno openai
export OPENAI_API_KEY="your-openai-api-key"
context_demo.py
import asyncio
from pathlib import Path

from agno.agent import Agent
from agno.context.fs import FilesystemContextProvider
from agno.models.openai import OpenAIResponses

root = Path(__file__).resolve().parent / "context-demo-docs"
root.mkdir(exist_ok=True)
policy = root / "refunds.md"
if not policy.exists():
    policy.write_text("# Refunds\nRefund requests are accepted within 30 days.\n")

fs = FilesystemContextProvider(
    id="docs", root=root, model=OpenAIResponses(id="gpt-5.4-mini")
)
agent = Agent(
    model=OpenAIResponses(id="gpt-5.4"),
    tools=fs.get_tools(),
    instructions=fs.instructions(),
)

if __name__ == "__main__":
    asyncio.run(agent.aprint_response("What is the refund policy? Cite the file."))

Save as context_demo.py and run python context_demo.py. The agent gets query_docs; its sub-agent searches the prepared directory.

How it Works

Sub-agent Architecture

Most built-in providers run an internal sub-agent. Configure its model independently of the calling agent. This fragment continues the local example:

fs = FilesystemContextProvider(
    id="docs", root=root, model=OpenAIResponses(id="gpt-5.4-mini")
)
agent = Agent(model=OpenAIResponses(id="gpt-5.4"), tools=fs.get_tools())

A smaller sub-agent can reduce per-call cost, but the extra model calls also add work. Measure cost, latency and answer quality for your workload.

Read/Write Separation

Providers such as Database and Slack separate read and write workflows:

ProviderRead sub-agentWrite sub-agent
DatabaseUses readonly_engineUses sql_engine
SlackHistory, thread, and lookup tools (search with a user token)send_message + lookups

Slack’s default read sub-agent has no send/upload tools. Database access is enforced by the database permissions of the credentials behind each engine; naming an engine readonly_engine does not make it read-only. MCP providers can expose server write tools through their query wrapper. Consult each provider’s access contract.

Mode

The following are Slack configuration fragments. Complete Slack setup before using them; the local filesystem dependencies above do not install Slack’s SDK.

Provider's default exposure. For Slack, this means query_<id> + update_<id> with separate sub-agents.

from agno.context.slack import SlackContextProvider

slack = SlackContextProvider()
# Agent sees: query_slack, update_slack

When to use: Most cases. You get clean read/write separation with privilege isolation. The read sub-agent cannot call write tools.

How it works: Two sub-agents handle the underlying toolkit. Reads go through the read sub-agent (history, threads, lookups, plus search when a user token or Slack-interface token is configured). Writes go through the write sub-agent (send_message + lookups for channel resolution).

Multi-Provider Composition

Use distinct provider IDs when combining sources. This fragment continues the local example, exposing the same prepared directory through two separately named tools:

policies = FilesystemContextProvider(id="policies", root=root)
archive = FilesystemContextProvider(id="archive", root=root)
agent = Agent(
    model=OpenAIResponses(id="gpt-5.4"),
    tools=[*policies.get_tools(), *archive.get_tools()],
    instructions="\n".join([policies.instructions(), archive.instructions()]),
)

The tools are query_policies and query_archive. IDs are normalized to lowercase letters, digits and underscores, so my-source and my_source collide. Raw tool mode exposes backend names directly and needs its own collision checks. See each provider guide before adding external sources.

Guides

Resources