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.
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.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:
Context providers
If you’ve built an agent with a real number of tools, you’ve hit three walls:- 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.
- Scope collisions.
searchin one toolkit collides withsearchin another.send_messagecould be Slack, email, or your CRM. The agent picks wrong half the time, and no naming convention fixes it. - System-prompt sprawl. Using Slack well requires Slack-specific guidance: look up user IDs before DMing, resolve channel names, prefer
conversations.historyfor channels andconversations.repliesfor threads. Multiply by every API. The system prompt becomes the union of every source’s quirks.
ContextProvider is a thin layer between the agent and the tools that fixes all three:
Agent↔ContextProvider↔Tools
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.
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
Web has multiple backends
WebContextProvider takes a backend, so you can swap providers without touching the agent:
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 getssend_messageand the lookup tools it needs to resolve names. The reader never seessend_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.
Lifecycle
Some providers hold async resources: MCP sessions, watched inboxes, cloned repos. They implementasetup() and aclose(). Bracket their use so the resource is owned by a single task:
FilesystemContextProvider, DatabaseContextProvider with sync engines, the SDK-based web backends) work without asetup / aclose.
Multi-provider in one agent
Three providers on one agent compose cleanly because each provider has its own namespace: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, subclassContextProvider. The base class handles tool wrapping, name derivation, and error shaping. You implement aquery and astatus:
query_faq tool. Same shape as every built-in provider.
Configurable 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:Worked examples
The full cookbook set is incookbook/12_context. Notable examples:
The Scout tutorial is a full agent built on context providers from the ground up.