Using Providers
Attach context providers to agents and configure them for your use case.
Basic Usage
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.
Adding Instructions
Providers can generate usage hints. Include them in your agent's instructions:
agent = Agent(
model=OpenAIResponses(id="gpt-5.4"),
tools=fs.get_tools(),
instructions=fs.instructions(),
)For multiple providers, combine their tool lists and usage hints after configuring each source. See composition.
Read/Write Control
The read and write flags select the outer tools on providers that implement the default two-tool surface. This configuration fragment requires Gmail setup:
from agno.context.gmail import GmailContextProvider
gmail = GmailContextProvider(write=False) # query_gmail only
gmail = GmailContextProvider(read=False, write=True) # update_gmail onlyThe second configuration hides query_gmail, but the write sub-agent can still search and read mail to compose a reply. These flags do not authorize the caller, restrict direct query/update method calls, or determine every mode’s behavior. ContextMode.agent exposes a query tool even with read=False. Enforce resource permissions through source credentials and your application.
Choosing a Mode
The mode parameter controls how the provider exposes itself:
The following are Slack configuration fragments. Complete Slack setup before using them; the local filesystem dependencies above do not install Slack’s SDK.
The provider defines its default exposure. Most read/write providers expose query_<id> + update_<id>.
from agno.context.slack import SlackContextProvider
slack = SlackContextProvider(id="slack")
# Agent sees: query_slack, update_slackSingle query_<id> tool wrapping a sub-agent. Good for complex sources that need internal orchestration.
from agno.context.mode import ContextMode
from agno.context.slack import SlackContextProvider
slack = SlackContextProvider(id="slack", mode=ContextMode.agent)
# Agent sees: query_slack onlyExpose underlying tools directly. Your agent orchestrates raw tools itself.
from agno.context.mode import ContextMode
from agno.context.slack import SlackContextProvider
slack = SlackContextProvider(id="slack", mode=ContextMode.tools)
# Agent sees: get_channel_history, get_thread, list_channels, list_users, etc.
# search_messages is added when a user token is configured.Use this when you need fine-grained control or want to combine tools from multiple providers in custom ways.
Sub-agent Model
Override the model used by the provider’s internal sub-agent. This 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())Model choice changes cost, latency and answer quality; measure the complete run, including sub-agent calls.
Query deadlines
A positive query_timeout (seconds, Python 3.11+) bounds each query_<id> tool call, including acquiring its sub-agent and streaming the answer. Expiry yields an error chunk. It does not bound direct query()/aquery(), raw tools in ContextMode.tools, or update_<id> calls. stream_sub_agent_events=False returns the sub-agent’s final answer instead of forwarding its events.
The base ContextProvider also accepts query_tool_name and update_tool_name overrides. Built-in constructors do not all expose those parameters.
Lifecycle Management
Bracket resource-owning providers with asetup() and aclose() in the same async lifecycle. The MCP guide has a complete runnable example with credentials, a connection check and finally cleanup.
- MCP and MCP-backed web providers hold sessions. MCP
asetup()logs connection failures rather than raising them; useawait provider.astatus()to check connectivity before proceeding. - Wiki providers need explicit
await wiki.asetup()before their query tools are exposed. Callawait wiki.sync()to refresh Git/Notion content; Notion needs this before its first read. - Filesystem, Workspace and Database do not require async session setup. Google/Slack authenticate lazily; provision their credentials before first use.
Use async calls for MCP; its synchronous query() is not implemented. Direct async calls are also preferable when already running inside an event loop.
RunContext Propagation
When a provider runs a sub-agent, it forwards nonempty user_id, session_id, metadata and dependencies from the caller:
Calling Agent
↓ tool call with run_context
Provider._query_tool()
↓ extracts user_id, session_id, metadata, dependencies
Sub-agent.arun(question, user_id=..., session_id=..., ...)The fields are context, not a universal authentication mechanism. Provider credentials, filesystem roots and SQL engines are configured on provider instances. Two different caller IDs can still use the same source credentials and data. Your application or custom provider must select and authorize caller-scoped resources when needed. Slack has a specific path for framework-injected action_token metadata.
Outer message history and session_state are not forwarded by this helper.