Context Providers
Last updated:
A Microsoft Agent Framework AIContextProvider adds invocation-specific instructions, messages, or tools without changing the agent definition. Use a provider when context must be assembled at run time. Use a tool when the model should decide whether to perform an action during the conversation.
Oscova's public providers include EnvironmentContextProvider, AgentPersonalContextProvider, AgentToolProvider, ToolSearchProvider, AgentAsToolProvider, VisionToolProvider, and MemoryContextProvider.
Important
Provider output is trusted model input. Microsoft Agent Framework does not sanitize instructions or messages returned by a provider. Treat files, memory, search results, remote data, and tool output as untrusted data rather than instruction authority.
Follow the provider lifecycle
For each agent invocation, Microsoft Agent Framework processes the configured provider list sequentially:
- Start with the run's current instructions, input and history messages, and tools.
- Call the first provider's
InvokingAsync. - Use that provider's merged
AIContextas input to the next provider. - Continue in list order, then send the final instructions, messages, and tools to the chat client.
- After the model completes or fails, call each provider's
InvokedAsyncin the same configured order.
The base AIContextProvider implementation gives ProvideAIContextAsync the accumulated instructions and tools. Its default message filter exposes only messages whose source is External, not chat-history or earlier-provider messages.
When a run continues an existing model response through ChatOptions.ContinuationToken, the framework skips pre-run context-provider assembly for that continuation.
When ProvideAIContextAsync returns, the base implementation:
- joins existing and new instructions with one newline.
- appends provider messages and stamps them with the provider type as their source.
- appends provider tools.
- preserves the previous value when the returned property is null.
The merge does not deduplicate tools or messages. Order providers deliberately and avoid contributing the same tool twice.
At the end of a successful invocation, the default InvokedCoreAsync filters request messages to external messages, keeps all response messages, and calls StoreAIContextAsync. On a failed invocation, it skips StoreAIContextAsync. Override InvokedCoreAsync only when the provider must inspect failures or control the default filtering itself.
Return a valid context
ProvideAIContextAsync must return a non-null AIContext.
- Return
new AIContext()when the provider has nothing to add. - Leave
Instructions,Messages, orToolsnull when that category has no contribution. - Return empty message or tool collections only when an empty sequence is meaningful to your own code. The default merge treats them as no additions.
- Do not return null and do not include null provider entries in an
AgentBuildRequestprovider list.
A provider exception or cancellation stops the pre-run pipeline. Later providers and the model are not invoked. A post-run provider exception also propagates and prevents later post-run providers from running. Honor the supplied cancellation token in file, database, HTTP, and other asynchronous work.
Create a provider
This provider adds a trusted, host-generated instruction. It returns an empty context when no note applies.
using Microsoft.Agents.AI;
public sealed class RuntimeNoteProvider(string? trustedNote) : AIContextProvider
{
protected override ValueTask<AIContext> ProvideAIContextAsync(
InvokingContext context,
CancellationToken cancellationToken = default)
{
var result = string.IsNullOrWhiteSpace(trustedNote)
? new AIContext()
: new AIContext
{
Instructions = $"Runtime note: {trustedNote}"
};
return ValueTask.FromResult(result);
}
}
Add the instance at the intended position in the ordered list returned by AgentBuildRequest.ContextProviderFactory. For asynchronously loaded context, await the source with cancellationToken and use ConfigureAwait(false) in library code.
Do not put raw retrieved text into Instructions. If untrusted data must be model-visible, bound its size, label and delimit it as data, validate its origin, and keep authorization in application or tool boundaries.
Keep provider state session-scoped
One provider instance can serve many invocations and sessions. Do not store current-user, current-message, or per-session state in ordinary provider fields.
Use AgentSession.StateBag when state must follow an agent session. Every provider has StateKeys. The default key is the concrete type name. Agent construction throws InvalidOperationException when:
- two context providers share a state key.
- a context provider shares a state key with the chat-history provider.
Override StateKeys with stable, unique values when attaching multiple instances of the same provider type.
AgentOrchestrator does not dispose context providers when an agent is evicted. Prefer provider instances backed by host-managed services and avoid per-build owned disposables unless the embedding host has an explicit lifetime owner.
Understand Oscova's default order
The configured Oscova host assembles providers in this order. Conditions are evaluated separately for the requested agent and any exposed agent-tool dependency.
| Order | Context source | Contribution | Condition |
|---|---|---|---|
| 1 | Environment | Instructions | Always. Resolves runtime facts and current workspace grants. |
| 2 | Tool exposure | Tools, plus instructions in tool-search mode | Always. Uses either direct selected-tool exposure or search_tools. |
| 3 | Agent instruction and personal variables | Instructions | Always. A blank instruction contributes nothing and does not call the template model factory. |
| 4 | Private notes | Instructions | Requested active agent only, and never a shared definition. Missing or unauthorized notes contribute nothing. |
| 5 | Called agents | Tools | Added when the definition has AgentToolIds. Missing or unavailable built agents are omitted. |
| 6 | Skills | Instructions and tools | Requested active agent only, with a user ID and at least one assigned skill. |
| 7 | Memory | Instructions and tools | Requested active agent only, with a user ID and memory enabled. |
| 8 | Library | Instructions and tools | Requested active agent only, with a user ID and the resolved read-only Library grant. |
| 9 | Todo list | Instructions, a synthetic user message, and tools | Requested active agent only when todo support is enabled. |
| 10 | Caller-supplied run providers | Provider-defined | Requested active agent only. The configured host uses this position for capabilities such as vision. |
The public AgentBuildRequest.ContextProviderFactory is more general than the configured host. An embedding host controls the entire list and may return a different order for each definition.
Know the public provider behavior
Environment context
EnvironmentContextProvider renders these Scriban variables:
working_directoryosarchitectureshellpath_separatorhome_directorytemp_directoryworkspace_directories
Use EnvironmentContextProvider.FromWorkspaceResolver when grants can change between invocations. The resolver is called once per render. With no directories, workspace_directories renders - none configured. The default host-derived working_directory is not available.
Agent instructions and personal context
AgentPersonalContextProvider renders its instruction template with the object returned by its model factory. A blank template returns an empty context without calling that factory.
The configured Oscova host supplies:
agent_namecurrent_datecurrent_timeuser_nameuser_usernameuser_emailuser_timezoneuser_context
The agent editor intentionally does not offer current_time. A minute-changing value in standing instructions destabilizes local-model prompt-cache prefixes. Use an allowed date/time tool for on-demand clock access.
user_context contains file-backed personal context only for a shared agent. The instruction must reference {{ user_context }} for that value to appear.
The ISharedAgentPersonalContextRepository contract keeps this persistence independent from host storage policy. The default SharedAgentPersonalContextRepository stores agent_id frontmatter and content as <slug>/context.md beneath an exact per-user root supplied by the host. The host also supplies the maximum UTF-8 text size and remains responsible for authorization, root topology, and cleanup triggers.
Tools and agent tools
AgentToolProvider resolves the currently allowed qualified tool names on each invocation. It omits missing, denied, and disallowed egress tools, wraps approval-required tools, and can suppress tools that require an unavailable approval channel.
ToolSearchProvider contributes its instructions, search_tools, pinned tools, and tools unlocked for that cached provider instance. Its allowed and pinned name lists are constructor snapshots. Actual tool resolution uses the current tool catalogue.
AgentAsToolProvider snapshots successfully resolved child agents when the provider is constructed, then contributes each one through AsAIFunction. Missing IDs are skipped.
VisionToolProvider contributes one analyze_image tool and creates its AnalyzeImageTool wrapper for each invocation.
Memory
MemoryContextProvider reads long-term, current-day, and previous-day memory on each invocation. Its default combined token budget is 2,000. It contributes the rendered memory instructions plus the memory tools supplied to its constructor.
If SystemPrompt omits {{ agent_memory }}, the provider appends the generated <agent_memory> block automatically.
Troubleshoot providers
- The agent fails during construction: check provider list entries and duplicate
StateKeys. - The model is never called: inspect the first provider exception or cancellation. The pre-run chain stops immediately.
- A later provider cannot see an earlier message: the default input-message filter exposes only external messages to
ProvideAIContextAsync. - Instructions appear in the wrong order: provider instructions are appended in configured list order.
- A tool appears twice: the framework concatenates provider tools without deduplication.
- A changed child agent is still callable: rebuild the parent because
AgentAsToolProvidersnapshots child agents at construction. - A changed workspace is missing: use the environment provider's run-time resolver and rebuild any provider whose constructor captured old grants.
- Post-run storage does not run after an error: the default
InvokedCoreAsyncskipsStoreAIContextAsyncon failure.