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 toolsThis 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"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:
| Provider | Read sub-agent | Write sub-agent |
|---|---|---|
| Database | Uses readonly_engine | Uses sql_engine |
| Slack | History, 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_slackWhen 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).
Single query_<id> tool. For Slack, this routes to the read sub-agent.
from agno.context.mode import ContextMode
from agno.context.slack import SlackContextProvider
slack = SlackContextProvider(mode=ContextMode.agent)
# Agent sees: query_slack onlyWhen to use: A single source-level tool. Verify that provider’s underlying capabilities. Your agent just asks questions; the sub-agent figures out which tools to call.
How it works: All requests route through the read sub-agent. No write access. The sub-agent orchestrates reads internally.
Bypass sub-agents entirely. Your agent sees the raw toolkit methods.
from agno.context.mode import ContextMode
from agno.context.slack import SlackContextProvider
slack = SlackContextProvider(mode=ContextMode.tools)
# Agent sees: get_channel_history, get_thread, list_channels, get_user_info, etc.When to use: Building a source-specific agent, or when you need fine-grained control over individual API calls.
How it works: No sub-agent wrapping. Your agent directly calls read tools like get_channel_history, get_thread, list_channels. Write tools require using mode=default.
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
Using Providers
Attach providers to agents and configure them.
Custom Providers
Create your own provider for any data source.
Provider Catalog
Browse all built-in providers.