Skip to content
Tracon

TraconBuilderExtensions

Namespace Tracon · Assembly Tracon.Core.dll

The registration methods of the Tracon configuration chain that AddTracon returns.

public static class TraconBuilderExtensions
Inheritance
object ← TraconBuilderExtensions
Inherited Members
object.GetType(), object.MemberwiseClone(), object.ToString(), object.Equals(object?), object.Equals(object?, object?), object.ReferenceEquals(object?, object?), object.GetHashCode()

Every registration capability is an extension method on ITraconBuilder, so a new capability never changes the interface. Provider and storage packages follow the same shape with their own extensions (UsePostgreSql, UseOpenAI, and so on).

A chain only has to supply ITraconBuilder.Services: these methods write to that collection and nothing else.

AddAgent(ITraconBuilder, AgentDefinition)

Section titled “ AddAgent(ITraconBuilder, AgentDefinition)”

Defines a declarative agent in code. The definition passes through the Tracon compiler; model and tool validation is applied.

public static ITraconBuilder AddAgent(this ITraconBuilder builder, AgentDefinition definition)

builder ITraconBuilder

The Tracon configuration chain.

definition AgentDefinition

The agent definition.

ITraconBuilder

The chain, for further configuration.

builder.AddTracon()
.AddAgent(new AgentDefinition
{
Name = "support",
Instructions = "Answer support questions from the order data.",
Model = new ModelBinding { Provider = "openai", Model = "gpt-4o-mini" },
ToolNames = ["get_order_status"],
});

ArgumentNullException

builder is null.

AddAgent(ITraconBuilder, string, Func<IServiceProvider, AIAgent>, string?)

Section titled “ AddAgent(ITraconBuilder, string, Func<IServiceProvider, AIAgent>, string?)”

Defines a factory-based agent in code. How the agent is built is entirely up to the caller.

public static ITraconBuilder AddAgent(this ITraconBuilder builder, string name, Func<IServiceProvider, AIAgent> factory, string? description = null)

builder ITraconBuilder

The Tracon configuration chain.

name string

The agent name.

factory Func<IServiceProvider, AIAgent>

The factory that produces the agent.

description string?

A short description.

ITraconBuilder

The chain, for further configuration.

ArgumentNullException

builder is null.

AddAgentDecorator<TDecorator>(ITraconBuilder)

Section titled “ AddAgentDecorator<TDecorator>(ITraconBuilder)”

Registers a custom agent decorator as a singleton.

public static ITraconBuilder AddAgentDecorator<TDecorator>(this ITraconBuilder builder) where TDecorator : class, IAgentDecorator

builder ITraconBuilder

The Tracon configuration chain.

ITraconBuilder

The chain, for further configuration.

TDecorator

The decorator implementation type.

Calling this method more than once for the same decorator type has no effect. The decorator joins the pipeline alongside Tracon’s own (run recording, telemetry, tool approval); see IAgentDecorator.Order for where it lands. It must be thread-safe because the catalog can decorate agents concurrently.

builder.AddTracon()
.AddAgentDecorator<AuditingAgentDecorator>();

ArgumentNullException

builder is null.

AddAgentDecorator(ITraconBuilder, IAgentDecorator)

Section titled “ AddAgentDecorator(ITraconBuilder, IAgentDecorator)”

Registers a configured custom agent decorator as a singleton.

public static ITraconBuilder AddAgentDecorator(this ITraconBuilder builder, IAgentDecorator decorator)

builder ITraconBuilder

The Tracon configuration chain.

decorator IAgentDecorator

The decorator instance.

ITraconBuilder

The chain, for further configuration.

ArgumentNullException

builder is null.

AddAgentDecorator(ITraconBuilder, Func<IServiceProvider, IAgentDecorator>)

Section titled “ AddAgentDecorator(ITraconBuilder, Func<IServiceProvider, IAgentDecorator>)”

Registers a custom agent-decorator factory as a singleton.

public static ITraconBuilder AddAgentDecorator(this ITraconBuilder builder, Func<IServiceProvider, IAgentDecorator> factory)

builder ITraconBuilder

The Tracon configuration chain.

factory Func<IServiceProvider, IAgentDecorator>

The factory that creates the decorator.

ITraconBuilder

The chain, for further configuration.

ArgumentNullException

builder is null.

AddAgentExtension<TExtension>(ITraconBuilder)

Section titled “ AddAgentExtension<TExtension>(ITraconBuilder)”

Registers an agent extension as a singleton.

public static ITraconBuilder AddAgentExtension<TExtension>(this ITraconBuilder builder) where TExtension : class, IAgentExtension

builder ITraconBuilder

The Tracon configuration chain.

ITraconBuilder

The chain, for further configuration.

TExtension

The extension implementation type.

A definition selects the extension by IAgentExtension.Name in AgentDefinition.ExtensionNames. Calling this method more than once for the same type has no effect; two types that report the same name are rejected when the compiler is first used.

builder.AddTracon()
.AddAgentExtension<PolicyContextExtension>();

ArgumentNullException

builder is null.

AddAgentExtension(ITraconBuilder, IAgentExtension)

Section titled “ AddAgentExtension(ITraconBuilder, IAgentExtension)”

Registers a configured agent extension.

public static ITraconBuilder AddAgentExtension(this ITraconBuilder builder, IAgentExtension extension)

builder ITraconBuilder

The Tracon configuration chain.

extension IAgentExtension

The extension instance.

ITraconBuilder

The chain, for further configuration.

ArgumentNullException

builder or extension is null.

AddAgentExtension(ITraconBuilder, string, Action<AgentExtensionContext>, string?)

Section titled “ AddAgentExtension(ITraconBuilder, string, Action<AgentExtensionContext>, string?)”

Registers an agent extension from a delegate. A definition selects it by name in AgentDefinition.ExtensionNames.

public static ITraconBuilder AddAgentExtension(this ITraconBuilder builder, string name, Action<AgentExtensionContext> configure, string? description = null)

builder ITraconBuilder

The Tracon configuration chain.

name string

The extension name: 1 to 128 characters of lowercase ASCII letters, digits, ., _ or -, unique among the registered extensions.

configure Action<AgentExtensionContext>

Changes the options of the agent that is being compiled, or adds a wrapper. It runs when an agent that names the extension is compiled, not on every run.

description string?

What the extension does, in one sentence. The console shows it next to the name and GET /api/agent-extensions returns it to every reader, so it must not carry a secret. null shows the name alone.

ITraconBuilder

The chain, for further configuration.

The escape door for a Microsoft Agent Framework option Tracon does not model. A hand-built agent in AddAgent(name, factory) bypasses the model ring, the tenant’s provider credential, egress and cost; an extension keeps all of them because the agent still goes through the compiler. The compiler refuses a change to a member the definition or a tenant and security decision owns (the tool chain, the tool approval settings, the chat history provider, file access, the working-directory skill scan and the model parameters among them). It does not look inside a provider or a wrapper the extension adds.

Adds your own context provider to the agents that select it:

builder.AddTracon()
.AddAgentExtension(
"house-policy",
context =>
{
if (context.ChatAgentOptions is { } options)
{
options.AIContextProviders = [.. options.AIContextProviders ?? [], new HousePolicyProvider()];
}
},
description: "Adds the house policy to the instructions of every model call.");

ArgumentNullException

builder or configure is null.

ArgumentException

name is empty.

Registers a custom agent source as a singleton.

public static ITraconBuilder AddAgentSource<TSource>(this ITraconBuilder builder) where TSource : class, IAgentSource

builder ITraconBuilder

The Tracon configuration chain.

ITraconBuilder

The chain, for further configuration.

TSource

The source implementation type.

Calling this method more than once for the same source type has no effect. The source can serve global or tenant-aware agents. It must be thread-safe because the catalog calls its methods concurrently.

builder.AddTracon()
.AddAgentSource<GitAgentSource>();

ArgumentNullException

builder is null.

AddAgentSource(ITraconBuilder, IAgentSource)

Section titled “ AddAgentSource(ITraconBuilder, IAgentSource)”

Registers a configured custom agent source as a singleton.

public static ITraconBuilder AddAgentSource(this ITraconBuilder builder, IAgentSource source)

builder ITraconBuilder

The Tracon configuration chain.

source IAgentSource

The source instance.

ITraconBuilder

The chain, for further configuration.

ArgumentNullException

builder is null.

AddAgentSource(ITraconBuilder, Func<IServiceProvider, IAgentSource>)

Section titled “ AddAgentSource(ITraconBuilder, Func<IServiceProvider, IAgentSource>)”

Registers a custom agent-source factory as a singleton.

public static ITraconBuilder AddAgentSource(this ITraconBuilder builder, Func<IServiceProvider, IAgentSource> factory)

builder ITraconBuilder

The Tracon configuration chain.

factory Func<IServiceProvider, IAgentSource>

The factory that creates the source.

ITraconBuilder

The chain, for further configuration.

ArgumentNullException

builder is null.

AddEvalCheck(ITraconBuilder, string, EvalCheck)

Section titled “ AddEvalCheck(ITraconBuilder, string, EvalCheck)”

Registers a custom eval check. Eval suites can reference it by this kind name in their checks field.

public static ITraconBuilder AddEvalCheck(this ITraconBuilder builder, string kind, EvalCheck check)

builder ITraconBuilder

The Tracon configuration chain.

kind string

The check type name. Must not collide with a built-in type (for example nonEmpty).

check EvalCheck

A code-written check that does not call the model.

ITraconBuilder

The chain, for further configuration.

An EvalCheck produced with Microsoft.Agents.AI.FunctionEvaluator.Create(...) is expected. Same reason as tools: custom logic is only defined in code, and a free-form expression cannot be written from the interface.

builder.AddTracon()
.AddEvalCheck("mentionsOrderId", FunctionEvaluator.Create(
"mentionsOrderId",
response => response.Contains("order", StringComparison.OrdinalIgnoreCase)));

ArgumentNullException

builder is null.

AddHostedTool(ITraconBuilder, string, HostedToolKind, Func<HostedToolContext, AITool>, string?)

Section titled “ AddHostedTool(ITraconBuilder, string, HostedToolKind, Func<HostedToolContext, AITool>, string?)”

Registers a tool the model provider runs on its own side: a web search, a code interpreter or a file search.

public static ITraconBuilder AddHostedTool(this ITraconBuilder builder, string name, HostedToolKind kind, Func<HostedToolContext, AITool> factory, string? description = null)

builder ITraconBuilder

The Tracon configuration chain.

name string

The name an agent definition selects the tool by in AgentDefinition.ToolNames. It is unique among function tools and hosted tools.

kind HostedToolKind

The kind of tool the factory builds.

factory Func<HostedToolContext, AITool>

Builds the tool for the agent being compiled. It must return the type that matches kind (HostedWebSearchTool, HostedCodeInterpreterTool or HostedFileSearchTool) and a new instance on every call.

description string?

The description the agent editor shows, or null.

ITraconBuilder

The chain, for further configuration.

A hosted tool is code, as every tool is: the definition selects it by name, and its default is off. The provider runs the tool inside the model call, so Tracon puts no timeout, approval or permission in front of it, and what the tool reads (the web, a vector store, the data a code interpreter is given) goes to the provider the model call already goes to. Each call is billed by the provider: set Tracon:Pricing:HostedTools:{provider}:{kind}:PerCall so the cost reaches the run’s budget, the tenant’s quota and the run.completed webhook.

The provider’s API surface decides whether the kind runs. A definition that selects a kind the surface drops does not compile; the agent validation names the tool and the provider. An agent selects at most one hosted tool of each kind.

The factory runs on every run of an agent that selects the tool, for the tenant of the run; keep it cheap. A value that belongs to the tenant, such as the identifier of the tenant’s own vector store, is read there. An identifier is not a secret; never put a credential into a hosted tool.

builder.AddTracon()
.AddHostedTool(
"docs_search",
HostedToolKind.FileSearch,
context => new HostedFileSearchTool
{
Inputs = [new HostedVectorStoreContent(
context.Services.GetRequiredService<IConfiguration>()["VectorStores:Docs"]!)],
},
description: "Searches the product documentation.");

ArgumentNullException

builder, name or factory is null.

ArgumentOutOfRangeException

kind is not a defined kind.

TraconException

The tool name does not follow the tool naming rules.

AddLoopEvaluator(ITraconBuilder, string, LoopEvaluator)

Section titled “ AddLoopEvaluator(ITraconBuilder, string, LoopEvaluator)”

Registers a code-defined loop stop criterion with no description. A definition’s LoopSettings.Criteria can then reference it by this kind name.

public static ITraconBuilder AddLoopEvaluator(this ITraconBuilder builder, string kind, LoopEvaluator evaluator)

builder ITraconBuilder

The Tracon configuration chain.

kind string

The criterion name. It must not be one of the built-in kinds (completionMarker, todoCompletion, aiJudge, backgroundTaskCompletion, agentReview); shadowing one is rejected, so the same definition cannot mean different things in two applications.

evaluator LoopEvaluator

The criterion. One instance serves every agent and every run that names the kind, so it must be thread-safe and keep no state of one run in its fields.

ITraconBuilder

The chain, for further configuration.

The same registration as the overload that takes a description; the console then shows the kind name alone.

ArgumentNullException

builder is null.

AddLoopEvaluator(ITraconBuilder, string, LoopEvaluator, string?)

Section titled “ AddLoopEvaluator(ITraconBuilder, string, LoopEvaluator, string?)”

Registers a code-defined loop stop criterion. A definition’s LoopSettings.Criteria can then reference it by this kind name.

public static ITraconBuilder AddLoopEvaluator(this ITraconBuilder builder, string kind, LoopEvaluator evaluator, string? description)

builder ITraconBuilder

The Tracon configuration chain.

kind string

The criterion name. It must not be one of the built-in kinds (completionMarker, todoCompletion, aiJudge, backgroundTaskCompletion, agentReview); shadowing one is rejected, so the same definition cannot mean different things in two applications.

evaluator LoopEvaluator

The criterion. Microsoft.Agents.AI.DelegateLoopEvaluator wraps a plain function; a subclass of LoopEvaluator covers anything larger. One instance serves every agent and every run that names the kind, so it must be thread-safe and keep no state of one run in its fields.

description string?

What the criterion checks, in one sentence. The console shows it next to the kind and GET /api/loop-criteria returns it to every reader, so it must not carry a secret. null shows the kind alone.

ITraconBuilder

The chain, for further configuration.

Same reason as tools and eval checks: a stop criterion whose logic is code is only ever defined in code. A definition arriving from the management API names the criterion, it never writes it. A criterion that needs an application service takes the factory overload instead.

The loop types are marked for evaluation only by Microsoft Agent Framework, so naming one in your own code needs the suppression below:

#pragma warning disable MAAI001
builder.AddTracon()
.AddLoopEvaluator("hasCitations", new DelegateLoopEvaluator((context, ct) =>
new ValueTask<LoopEvaluation>(
context.LastResponse?.Text?.Contains("[1]", StringComparison.Ordinal) == true
? LoopEvaluation.Stop()
: LoopEvaluation.Continue("Add a numbered citation for every claim."))));
#pragma warning restore MAAI001

ArgumentNullException

builder is null.

AddLoopEvaluator(ITraconBuilder, string, Func<IServiceProvider, LoopEvaluator>)

Section titled “ AddLoopEvaluator(ITraconBuilder, string, Func<IServiceProvider, LoopEvaluator>)”

Registers a code-defined loop stop criterion that is created from the application’s services. A definition’s LoopSettings.Criteria can then reference it by this kind name.

public static ITraconBuilder AddLoopEvaluator(this ITraconBuilder builder, string kind, Func<IServiceProvider, LoopEvaluator> factory)

builder ITraconBuilder

The Tracon configuration chain.

kind string

The criterion name. It must not be one of the built-in kinds (completionMarker, todoCompletion, aiJudge, backgroundTaskCompletion, agentReview); shadowing one, or registering the same name twice, is rejected before any factory runs.

factory Func<IServiceProvider, LoopEvaluator>

Creates the criterion from the application’s root service provider. It runs once, on the first compilation of an agent whose loop names kind, and its result serves every agent, every run and every tenant from then on.

ITraconBuilder

The chain, for further configuration.

The same registration as the overload that takes a description; the console then shows the kind name alone. That overload’s remarks say when the factory runs and how a criterion reads the tenant.

ArgumentNullException

builder or factory is null.

AddLoopEvaluator(ITraconBuilder, string, Func<IServiceProvider, LoopEvaluator>, string?)

Section titled “ AddLoopEvaluator(ITraconBuilder, string, Func<IServiceProvider, LoopEvaluator>, string?)”

Registers a code-defined loop stop criterion that is created from the application’s services. A definition’s LoopSettings.Criteria can then reference it by this kind name.

public static ITraconBuilder AddLoopEvaluator(this ITraconBuilder builder, string kind, Func<IServiceProvider, LoopEvaluator> factory, string? description)

builder ITraconBuilder

The Tracon configuration chain.

kind string

The criterion name. It must not be one of the built-in kinds (completionMarker, todoCompletion, aiJudge, backgroundTaskCompletion, agentReview); shadowing one, or registering the same name twice, is rejected before any factory runs.

factory Func<IServiceProvider, LoopEvaluator>

Creates the criterion from the application’s root service provider. It runs once, on the first compilation of an agent whose loop names kind, and its result serves every agent, every run and every tenant from then on.

description string?

What the criterion checks, in one sentence. The console shows it next to the kind and GET /api/loop-criteria returns it to every reader, so it must not carry a secret. null shows the kind alone.

ITraconBuilder

The chain, for further configuration.

A criterion cannot take a service from LoopContext, which carries none; the factory is where it takes them. The factory runs at the first compilation rather than at setup, so a criterion may take the agent catalog (IAgentCatalog): at setup the catalog is still being built. When the factory throws, that compilation fails with a TraconCompilationException naming the kind, and the next compilation calls the factory again; a successful result is kept.

The provider is the root one. Take singletons from it; a scoped service would outlive its scope. The tenant is not known when the factory runs: read ITenantContext.TenantId inside EvaluateAsync, never in the factory or the constructor, or every tenant would see the first tenant’s value.

A reviewer agent as the criterion. The criterion below computes a check in code, hands it to a reviewer agent from the catalog with the draft, and reads the reviewer’s own JSON verdict. The reviewer runs as a child of the run being evaluated: it appears under that run in the run tree, spends from the same tree budget, and its tokens are its own run’s usage. Per-run state, such as the round number, lives in LoopContext.AdditionalProperties, which belongs to one loop run. The draft is the last message of the turn: a harness turn can write a plan or an earlier version first, and LastResponse.Text joins every message of the turn.

#pragma warning disable MAAI001
builder.AddTracon()
.AddLoopEvaluator(
"reviewed",
services =>
{
var catalog = services.GetRequiredService<IAgentCatalog>();
return new DelegateLoopEvaluator(async (context, cancellationToken) =>
{
var messages = context.LastResponse?.Messages;
var draft = messages is { Count: > 0 } ? messages[messages.Count - 1].Text : string.Empty;
var words = draft.Split(' ', StringSplitOptions.RemoveEmptyEntries).Length;
var round = context.AdditionalProperties.TryGetValue("reviewed.round", out var r) ? (int)r! + 1 : 1;
context.AdditionalProperties["reviewed.round"] = round;
var scope = TraconRunContext.Current;
if (scope?.Budget is { } budget && (scope.Depth + 1 > budget.MaxDepth || !budget.TryReserveRun()))
{
throw new InvalidOperationException("The run tree has no room for the reviewer.");
}
var reviewer = await catalog.ResolveAsync("reviewer", culture: null, cancellationToken)
?? throw new InvalidOperationException("No agent named 'reviewer'.");
var options = scope is null ? null : new TraconRunOptions
{
ParentRunId = scope.RunId,
RootRunId = scope.RootRunId,
Depth = scope.Depth + 1,
Budget = scope.Budget,
SessionId = scope.SessionId,
};
var review = await reviewer.RunAsync(
$"Round {round}. Word count: {words}. Review this draft:\n{draft}",
session: null, options, cancellationToken);
// The reviewer's ResponseFormat is a JSON schema: {"pass": bool, "feedback": string}.
using var verdict = JsonDocument.Parse(review.Text);
return verdict.RootElement.GetProperty("pass").GetBoolean()
? LoopEvaluation.Stop()
: LoopEvaluation.Continue(verdict.RootElement.GetProperty("feedback").GetString() ?? "Revise the draft.");
});
},
description: "The reviewer agent approves the draft.");
#pragma warning restore MAAI001

ArgumentNullException

builder or factory is null.

AddModelProvider<TProvider>(ITraconBuilder)

Section titled “ AddModelProvider<TProvider>(ITraconBuilder)”

Registers a custom model provider as a singleton.

public static ITraconBuilder AddModelProvider<TProvider>(this ITraconBuilder builder) where TProvider : class, IModelProvider

builder ITraconBuilder

The Tracon configuration chain.

ITraconBuilder

The chain, for further configuration.

TProvider

The provider implementation type.

Calling this method more than once for the same provider type has no effect. Prefer this overload when the provider has no state to configure by hand; use TraconBuilderExtensions.AddModelProvider or the factory overload when it does.

builder.AddTracon()
.AddModelProvider<OnPremiseModelProvider>();

ArgumentNullException

builder is null.

AddModelProvider(ITraconBuilder, IModelProvider)

Section titled “ AddModelProvider(ITraconBuilder, IModelProvider)”

Registers a model provider.

public static ITraconBuilder AddModelProvider(this ITraconBuilder builder, IModelProvider provider)

builder ITraconBuilder

The Tracon configuration chain.

provider IModelProvider

The provider.

ITraconBuilder

The chain, for further configuration.

The shipped provider packages (UseOpenAI, UseAnthropic, and the rest) call this method. Register your own provider here when the model sits behind an endpoint none of them describes.

builder.AddTracon()
.AddModelProvider(new OnPremiseModelProvider(endpoint));

ArgumentNullException

builder is null.

AddModelProvider(ITraconBuilder, Func<IServiceProvider, IModelProvider>)

Section titled “ AddModelProvider(ITraconBuilder, Func<IServiceProvider, IModelProvider>)”

Registers a model provider through a factory.

public static ITraconBuilder AddModelProvider(this ITraconBuilder builder, Func<IServiceProvider, IModelProvider> factory)

builder ITraconBuilder

The Tracon configuration chain.

factory Func<IServiceProvider, IModelProvider>

The factory that produces the provider.

ITraconBuilder

The chain, for further configuration.

ArgumentNullException

builder is null.

Registers a custom run judge as a singleton.

public static ITraconBuilder AddRunJudge<TJudge>(this ITraconBuilder builder) where TJudge : class, IRunJudge

builder ITraconBuilder

The Tracon configuration chain.

ITraconBuilder

The chain, for further configuration.

TJudge

The judge implementation type.

Repeating this overload for the same implementation type has no effect. The container creates and owns the singleton. The judge must be thread-safe because evaluations can overlap, including when a timed-out call finishes after a retry starts.

builder.AddTracon()
.AddRunJudge<ResponseQualityJudge>();

ArgumentNullException

builder is null.

Registers a configured run-judge instance as a singleton.

public static ITraconBuilder AddRunJudge(this ITraconBuilder builder, IRunJudge judge)

builder ITraconBuilder

The Tracon configuration chain.

judge IRunJudge

The judge instance.

ITraconBuilder

The chain, for further configuration.

The caller owns the instance and any resources it holds. Different configured instances of the same CLR type are preserved. Their IRunJudge.Name values must still be unique.

ArgumentNullException

builder is null.

AddRunJudge(ITraconBuilder, Func<IServiceProvider, IRunJudge>)

Section titled “ AddRunJudge(ITraconBuilder, Func<IServiceProvider, IRunJudge>)”

Registers a run-judge factory as a singleton.

public static ITraconBuilder AddRunJudge(this ITraconBuilder builder, Func<IServiceProvider, IRunJudge> factory)

builder ITraconBuilder

The Tracon configuration chain.

factory Func<IServiceProvider, IRunJudge>

The factory that creates the judge.

ITraconBuilder

The chain, for further configuration.

The container owns the produced singleton. The factory must not capture a scoped dependency because the result outlives that scope. Different factories and configured results are preserved.

ArgumentNullException

builder is null.

AddScopedTool(ITraconBuilder, AIFunction, Action<ToolRegistrationOptions>)

Section titled “ AddScopedTool(ITraconBuilder, AIFunction, Action<ToolRegistrationOptions>)”

Registers a tool that runs inside its own dependency-injection scope on every call.

public static ITraconBuilder AddScopedTool(this ITraconBuilder builder, AIFunction tool, Action<ToolRegistrationOptions> configure)

builder ITraconBuilder

The Tracon configuration chain.

tool AIFunction

The tool to register.

configure Action<ToolRegistrationOptions>

Configures the tool metadata.

ITraconBuilder

The chain, for further configuration.

Identical in shape to TraconBuilderExtensions.AddTool — the one difference is the word “scoped”, and its meaning is exactly this: every call opens a fresh DependencyInjection.IServiceScope, exposes it through AIFunctionArguments.Services, and closes it as soon as the call finishes. Microsoft Agent Framework otherwise passes an empty provider there — a dependency resolved from AIFunctionArguments.Services without this method throws or returns nothing, it is never silently wrong.

Use this for a tool that needs a repository, a DbContext, or any other per-call dependency. Two concurrent calls never share a scope.

builder.AddTracon()
.AddScopedTool(AIFunctionFactory.Create(
async (string orderId, AIFunctionArguments arguments) =>
{
var orders = arguments.Services!.GetRequiredService<IOrderRepository>();
return await orders.GetAsync(orderId);
},
"get_order"),
options => options.RequiredPermission = "orders.read");

ArgumentNullException

builder is null.

AddScopedTool(ITraconBuilder, AIFunction)

Section titled “ AddScopedTool(ITraconBuilder, AIFunction)”

Registers a tool that runs inside its own dependency-injection scope on every call, with default metadata.

public static ITraconBuilder AddScopedTool(this ITraconBuilder builder, AIFunction tool)

builder ITraconBuilder

The Tracon configuration chain.

tool AIFunction

The tool to register.

ITraconBuilder

The chain, for further configuration.

ArgumentNullException

builder is null.

AddSkill(ITraconBuilder, AgentSkillDefinition)

Section titled “ AddSkill(ITraconBuilder, AgentSkillDefinition)”

Defines a skill in code. A skill defined in code takes precedence over a runtime skill with the same name.

public static ITraconBuilder AddSkill(this ITraconBuilder builder, AgentSkillDefinition skill)

builder ITraconBuilder

The Tracon configuration chain.

skill AgentSkillDefinition

The skill to register.

ITraconBuilder

The chain, for further configuration.

A skill is instruction text an agent loads by name. When it carries AgentSkillDefinition.Scripts, those scripts run on the server like stored ones: they need AllowStoredScripts, an allow-listed interpreter, and a grant pinned to their content. The hash to grant is read from GET /api/skills/{name}, which resolves a code skill first. A deployment that changes a script changes its hash, so the script needs a new grant before it runs again.

builder.AddTracon()
.AddSkill(new AgentSkillDefinition
{
TenantId = "default",
Name = "refund-policy",
Description = "How a refund decision is made.",
Instructions = "A refund under 100 USD is approved without review.",
});

ArgumentNullException

builder is null.

AddSpeechSynthesizer(ITraconBuilder, string, Func<IServiceProvider, ISpeechSynthesizer>)

Section titled “ AddSpeechSynthesizer(ITraconBuilder, string, Func<IServiceProvider, ISpeechSynthesizer>)”

Registers a named speech synthesizer as a singleton.

public static ITraconBuilder AddSpeechSynthesizer(this ITraconBuilder builder, string name, Func<IServiceProvider, ISpeechSynthesizer> factory)

builder ITraconBuilder

The Tracon configuration chain.

name string

The registration name: 1 to 64 characters of a-z, 0-9, ., _ or -, compared in lower case.

factory Func<IServiceProvider, ISpeechSynthesizer>

Creates the synthesizer once, the first time the name is resolved.

ITraconBuilder

The chain, for further configuration.

A caller selects the synthesizer by name through ISpeechProviderResolver.GetSynthesizerAsync: a workflow function node, or POST /api/voice/speak with provider. The unnamed default (UseVoice, or your own ISpeechSynthesizer registration) is not affected, and the call can come before or after UseVoice.

The tenant’s egress policy applies to the synthesizer’s ISpeechSynthesizer.ProviderName, not to name: two names for one service are one provider to the policy. A factory that returns the same instance for two names shares that instance, and its concurrency limit, between them.

A synthesizer that can call the service with a tenant’s own key sets ISpeechSynthesizer.SupportsTenantCredentials and reads SpeechRequest.Credential. One that cannot is refused for a tenant that has its own binding for its provider name.

builder.AddTracon()
.AddSpeechSynthesizer("studio", provider => new StudioSpeechSynthesizer(
provider.GetRequiredService<IConfiguration>()["Studio:ApiKey"]));

ArgumentNullException

builder or factory is null.

ArgumentException

name is not a valid speech provider name.

InvalidOperationException

A synthesizer with the same name is already registered.

AddSpeechSynthesizer(ITraconBuilder, string, TextToSpeechClientBinding)

Section titled “ AddSpeechSynthesizer(ITraconBuilder, string, TextToSpeechClientBinding)”

Registers a named speech synthesizer that serves a Microsoft.Extensions.AI text-to-speech client.

public static ITraconBuilder AddSpeechSynthesizer(this ITraconBuilder builder, string name, TextToSpeechClientBinding binding)

builder ITraconBuilder

The Tracon configuration chain.

name string

The registration name: 1 to 64 characters of a-z, 0-9, ., _ or -, compared in lower case.

binding TextToSpeechClientBinding

Which client in the container serves the name, and what the client cannot say about itself.

ITraconBuilder

The chain, for further configuration.

The result is an ordinary named synthesizer: the rules of TraconBuilderExtensions.AddSpeechSynthesizer apply. Every call passes the tenant’s egress policy against the provider name, and a tenant that has its own key for that provider is refused: a bridged client was built with the setup credential and cannot use another.

The host does not start when the client is not registered or has no provider name. Billing is an estimate: the client reports no billed characters, so Tracon records the length of the text and labels it SpeechUsageSource.Estimated. A request for character timing (SpeechRequest.IncludeTimestamps) is refused, and the synthesizer cannot voice a dialogue.

Do not put a secret in SpeechRequest.ProviderSettings: the values reach the client unchanged, and a client wrapped in LoggingTextToSpeechClient logs its options at Trace level.

#pragma warning disable MEAI001 // Microsoft.Extensions.AI marks its speech clients experimental
builder.Services.AddKeyedTextToSpeechClient("studio", provider => new StudioTextToSpeechClient(
provider.GetRequiredService<IConfiguration>()["Studio:ApiKey"]));
builder.AddTracon()
.AddSpeechSynthesizer("studio", new TextToSpeechClientBinding
{
ServiceKey = "studio",
ProviderName = "studio",
MaxCharactersPerRequest = 4000,
});

ArgumentNullException

builder or binding is null.

ArgumentException

name is not a valid speech provider name, or TextToSpeechClientBinding.MaxCharactersPerRequest is not greater than zero.

InvalidOperationException

A synthesizer with the same name is already registered.

AddSpeechTranscriber(ITraconBuilder, string, Func<IServiceProvider, ISpeechTranscriber>)

Section titled “ AddSpeechTranscriber(ITraconBuilder, string, Func<IServiceProvider, ISpeechTranscriber>)”

Registers a named speech transcriber as a singleton.

public static ITraconBuilder AddSpeechTranscriber(this ITraconBuilder builder, string name, Func<IServiceProvider, ISpeechTranscriber> factory)

builder ITraconBuilder

The Tracon configuration chain.

name string

The registration name: 1 to 64 characters of a-z, 0-9, ., _ or -, compared in lower case. Transcriber names are separate from synthesizer names.

factory Func<IServiceProvider, ISpeechTranscriber>

Creates the transcriber once, the first time the name is resolved.

ITraconBuilder

The chain, for further configuration.

A caller selects the transcriber by name through ISpeechProviderResolver.GetTranscriberAsync. The rules of TraconBuilderExtensions.AddSpeechSynthesizer apply: the tenant’s policy reads ISpeechTranscriber.ProviderName, and a tenant credential reaches the transcriber only in SpeechTranscriptionOptions.Credential.

builder.AddTracon()
.AddSpeechTranscriber("on-prem", provider => new OnPremiseTranscriber(
new Uri("http://gpu-01:9000/")));

ArgumentNullException

builder or factory is null.

ArgumentException

name is not a valid speech provider name.

InvalidOperationException

A transcriber with the same name is already registered.

AddSpeechTranscriber(ITraconBuilder, string, SpeechToTextClientBinding)

Section titled “ AddSpeechTranscriber(ITraconBuilder, string, SpeechToTextClientBinding)”

Registers a named speech transcriber that serves a Microsoft.Extensions.AI speech-to-text client.

public static ITraconBuilder AddSpeechTranscriber(this ITraconBuilder builder, string name, SpeechToTextClientBinding binding)

builder ITraconBuilder

The Tracon configuration chain.

name string

The registration name: 1 to 64 characters of a-z, 0-9, ., _ or -, compared in lower case. Transcriber names are separate from synthesizer names.

binding SpeechToTextClientBinding

Which client in the container serves the name, and what the client cannot say about itself.

ITraconBuilder

The chain, for further configuration.

The rules of TraconBuilderExtensions.AddSpeechSynthesizer apply: the tenant’s egress policy and provider bindings read the provider name, and the host does not start when the client is missing or has no name.

The client’s contract takes the audio stream and no media type, so mediaType is not passed on. The transcript has no duration unless the client reports a start and an end time, and a transcription without a duration has no cost.

#pragma warning disable MEAI001 // Microsoft.Extensions.AI marks its speech clients experimental
builder.Services.AddKeyedSpeechToTextClient("studio", provider => new StudioSpeechToTextClient(
provider.GetRequiredService<IConfiguration>()["Studio:ApiKey"]));
builder.AddTracon()
.AddSpeechTranscriber("studio", new SpeechToTextClientBinding
{
ServiceKey = "studio",
ProviderName = "studio",
});

ArgumentNullException

builder or binding is null.

ArgumentException

name is not a valid speech provider name.

InvalidOperationException

A transcriber with the same name is already registered.

AddTool(ITraconBuilder, AIFunction, Action<ToolRegistrationOptions>)

Section titled “ AddTool(ITraconBuilder, AIFunction, Action<ToolRegistrationOptions>)”

Registers a tool.

public static ITraconBuilder AddTool(this ITraconBuilder builder, AIFunction tool, Action<ToolRegistrationOptions> configure)

builder ITraconBuilder

The Tracon configuration chain.

tool AIFunction

The tool to register.

configure Action<ToolRegistrationOptions>

Configures the tool metadata.

ITraconBuilder

The chain, for further configuration.

The AOT-safe overload: the caller supplies the built AI.AIFunction, so no reflection is involved.

builder.AddTracon()
.AddTool(refundTool, options => options.RequiresApproval = true);

ArgumentNullException

builder is null.

Registers a tool with default metadata.

public static ITraconBuilder AddTool(this ITraconBuilder builder, AIFunction tool)

builder ITraconBuilder

The Tracon configuration chain.

tool AIFunction

The tool to register.

ITraconBuilder

The chain, for further configuration.

ArgumentNullException

builder is null.

AddTool(ITraconBuilder, Delegate, string?, string?, Action<ToolRegistrationOptions>?)

Section titled “ AddTool(ITraconBuilder, Delegate, string?, string?, Action<ToolRegistrationOptions>?)”

Builds and registers a tool from a method.

[RequiresUnreferencedCode("Building a tool from a method uses reflection; type information may be lost in trimmed applications.")]
[RequiresDynamicCode("Building a tool from a method may require code generation at runtime.")]
public static ITraconBuilder AddTool(this ITraconBuilder builder, Delegate method, string? name = null, string? description = null, Action<ToolRegistrationOptions>? configure = null)

builder ITraconBuilder

The Tracon configuration chain.

method Delegate

The method to expose as a tool.

name string?

The tool name.

description string?

The tool description.

configure Action<ToolRegistrationOptions>?

Configures the tool metadata.

ITraconBuilder

The chain, for further configuration.

ArgumentNullException

builder is null.

Registers, as tools, the methods on a type that are marked with TraconToolAttribute.

[RequiresUnreferencedCode("Tool scanning uses reflection; method information may be lost in trimmed applications.")]
[RequiresDynamicCode("Tool scanning may require code generation at runtime.")]
public static ITraconBuilder AddToolsFrom<T>(this ITraconBuilder builder)

builder ITraconBuilder

The Tracon configuration chain.

ITraconBuilder

The chain, for further configuration.

T

The type to scan.

Marking is an explicit choice: every new method added to the class is not automatically exposed to agents. Static methods bind directly; for instance methods, the owning object is resolved from the service provider at call time.

A static class cannot be a type argument (a C# rule). If your tools live in a static class, use the TraconBuilderExtensions.AddToolsFrom overload instead.

This method uses reflection and is not safe under trimming or native AOT scenarios. Applications targeting AOT should use the TraconBuilderExtensions.AddTool overload instead.

builder.AddTracon()
.AddToolsFrom<OrderTools>();

ArgumentNullException

builder is null.

TraconException

T has no marked method, or a marked method cannot be converted to a tool.

Registers, as tools, the methods on a type that are marked with TraconToolAttribute.

[RequiresUnreferencedCode("Tool scanning uses reflection; method information may be lost in trimmed applications.")]
[RequiresDynamicCode("Tool scanning may require code generation at runtime.")]
public static ITraconBuilder AddToolsFrom(this ITraconBuilder builder, Type type)

builder ITraconBuilder

The Tracon configuration chain.

type Type

The type to scan. May be a static class.

ITraconBuilder

The chain, for further configuration.

A static class cannot be a type argument under C# rules; this overload makes the AddToolsFrom(typeof(OrderTools)) form possible.

ArgumentNullException

builder or type is null.

TraconException

type has no marked method, or a marked method cannot be converted to a tool.

Configure(ITraconBuilder, Action<TraconOptions>)

Section titled “ Configure(ITraconBuilder, Action<TraconOptions>)”

Modifies the runtime settings.

public static ITraconBuilder Configure(this ITraconBuilder builder, Action<TraconOptions> configure)

builder ITraconBuilder

The Tracon configuration chain.

configure Action<TraconOptions>

The settings modifier.

ITraconBuilder

The chain, for further configuration.

Runs after the configuration section is bound, so a value set here wins over appsettings.json.

builder.AddTracon()
.Configure(options => options.Tools.DefaultTimeout = TimeSpan.FromSeconds(60));

ArgumentNullException

builder is null.

Declares that T must resolve to the application’s own registration. The host does not start when Tracon’s built-in default is what resolves.

public static ITraconBuilder RequireCustomBinding<T>(this ITraconBuilder builder) where T : class

builder ITraconBuilder

The Tracon configuration chain.

ITraconBuilder

The chain, for further configuration.

T

One of the seven embedding points: ITenantContext, IRunAttributionContext, IToolAuthorizationHandler, IRunAuthorizationHandler, IRunEventSink, IAttachmentStorage, or IToolApprovalPresenter; or a contract Tracon registers a built-in default for: a store interface, or ISkillScriptExecutor once UseSkillScripts is called. Any other type stops the host from starting, with a message naming the seven.

Off by default: an application that never calls this behaves exactly as before. Every extension point is registered with TryAdd, so a host that binds nothing runs on the built-in default and starts silently. That suits a first run; it does not suit a deployment whose module order can leave an authorization handler on the permissive default without anyone noticing until the first unauthorized request.

The check runs while the host starts, not when endpoints are mapped, so an embedded host with no HTTP surface gets the same guarantee. Register the implementation BEFORE AddTracon: a TryAdd registration made afterwards is dropped, and the built-in default stays.

This is a composition gate. It proves which implementation is bound; it proves nothing about whether that implementation decides correctly.

builder.Services.AddSingleton<IRunAuthorizationHandler, OrderDeskAuthorization>();
builder.AddTracon()
.RequireCustomBinding<IRunAuthorizationHandler>()
.RequireCustomBinding<IToolAuthorizationHandler>();

ArgumentNullException

builder is null.

RequireProductionProfile(ITraconBuilder, Action<TraconProductionProfileOptions>?)

Section titled “ RequireProductionProfile(ITraconBuilder, Action<TraconProductionProfileOptions>?)”

Refuses to start the host while a security-sensitive decision is still on its permissive default and the deployment has not accepted the risk by name.

public static ITraconBuilder RequireProductionProfile(this ITraconBuilder builder, Action<TraconProductionProfileOptions>? configure = null)

builder ITraconBuilder

The Tracon configuration chain.

configure Action<TraconProductionProfileOptions>?

The risks this deployment has deliberately decided to carry. Omit it to accept none, which is the strictest form.

ITraconBuilder

The chain, for further configuration.

This does not make anything secure and changes no setting. It sets no value, chooses no policy and turns nothing on. The only thing it does is turn a skipped decision into a startup failure - which is the one thing a permissive default cannot do for itself.

Off by default: an application that never calls this behaves exactly as before. AddTracon brings every security-sensitive switch up permissive on purpose, so a first run surprises nobody. A production deployment wants the opposite, and today it can reach production having never separated tenants, never decided who owns a session and never registered a content guard, in complete silence.

Six decisions are asked about: tenant separation, session ownership, at-rest content protection, content inspection, request rate limiting and retention. Each one is either answered by turning the feature on, or accepted by name. Acceptance is per item and there is no way to accept them all at once.

The set of decisions is a versioned contract. A later release that adds one stops a host that calls this method until the new decision is answered or accepted. Read that cost before adopting the method: it is the method working, not failing.

This is a composition gate, not a security proof. It proves a feature is switched on; it proves nothing about whether the policy behind it is right.

builder.AddTracon()
.RequireProductionProfile(profile => profile
.Accept(TraconProductionRisk.SingleTenant)
.Accept(TraconProductionRisk.UnboundedRetention));

ArgumentNullException

builder is null.