# Configuration options

> Every MentorOptions property with its type, default and effect, grouped by area, plus the options of the WebAssembly client and the YAML loader.

- URL: https://docs.mentoragent.net/reference/options
- Verified with: MentorAgent 1.0.0
- Last updated: 2026-10-14
- Packages: Novior.MentorAgent, Novior.MentorAgent.Abstractions, Novior.MentorAgent.Server, Novior.MentorAgent.Blazor, Novior.MentorAgent.Declarative

`MentorOptions` holds every server-side setting of MentorAgent. You set it once, in the delegate you pass to `AddMentorAgent`. Each option below has its type, its default and its effect; a required option has no default.

Some options mention the coordinator, the main agent that answers the user, and its specialists: see [The three agent levels](https://docs.mentoragent.net/concepts/agent-levels).

```csharp title="Program.cs"
builder.Services.AddScoped<CaseService>();
builder.Services.AddMentorAgent(options =>
{
    // Required: without them the options fail validation
    options.AppName = "Cases";
    options.ScanAssemblies = [typeof(CaseService).Assembly];
    options.ChatClient = chatClient; // or options.Agent, never both

    // Everything else has a default: set only what you change
    options.AppDescription = "Support case management for the service desk";
    options.RateLimitPerUser = 20;
});
```

## Where the options live

| Class | Package | Where you set it |
|---|---|---|
| `MentorOptions` | `Novior.MentorAgent` | `AddMentorAgent(options => …)` |
| `MentorMcpServer`, `MentorHostedMcpServer`, `MentorRemoteAgent`, `AgentCardInfo` | `Novior.MentorAgent` | The `McpServers`, `HostedMcpServers`, `RemoteAgents` and `AgentCard` options |
| `ModelPrice` | `Novior.MentorAgent.Abstractions` | The values of `ModelPricing` |
| `MentorAgentBlazorOptions` | `Novior.MentorAgent.Blazor` | `AddMentorAgentBlazor(options => …)`, in a WebAssembly client |
| `AgentDisplayInfo` | `Novior.MentorAgent.Abstractions` | The values of `MentorAgentBlazorOptions.RemoteAgentDisplays` |
| `MentorDeclarativeOptions` | `Novior.MentorAgent.Declarative` | `AddMentorAgentDeclarative(o => …)` |

`Novior.MentorAgent.Server` has no options class. The SignalR hub, the Server-Sent Events (SSE) endpoint, the MCP server and the A2A agent all read `MentorOptions`. The paths of the hub and of the SSE endpoint are parameters of `MapMentorAgentServer`.

`Novior.MentorAgent.Server` also registers the MCP server and the A2A agent. `AddMentorAgentServer()` does it when their options are on. A host without the hub, such as a Blazor Server app, calls `AddMentorAgentMcp()` or `AddMentorAgentA2A()` after `AddMentorAgent()`.

The widget reads a copy of its settings as `MentorWidgetOptions`, which MentorAgent fills from `MentorOptions` or from `MentorAgentBlazorOptions`. Set them there, not on `MentorWidgetOptions`.

## Validation

MentorAgent checks the options when the host starts, not when you call `AddMentorAgent`. In an ASP.NET Core app or in a worker, an invalid configuration stops the start with the reason, before the first request. An app without such a host, such as a .NET MAUI app, has no start to check: the error appears the first time MentorAgent reads the options. The check throws `InvalidOperationException` when:

- `AppName` is empty or blank;
- `ScanAssemblies` is empty;
- neither `ChatClient` nor `Agent` is set, or both are;
- both `ChatHistoryProvider` and `ChatHistoryProviderFactory` are set.

## When MentorAgent reads the options

- **The callback runs twice.** `AddMentorAgent` runs your callback once on a copy of the options, to decide what to register, and once more when it builds the real options. Keep the callback free of side effects that must happen once.
- **Some options come from the copy.** `EnableObservability`, `ObservabilitySourceName`, `McpServerEnabled` and `A2AServerEnabled` are read at registration, and so is `AppName` for the MCP server. Set them in the callback: a `Configure<MentorOptions>` registered after `AddMentorAgent` does not reach them. MentorAgent reads every other option from the real options.
- **A callback that throws on the copy** leaves those options at their defaults. A warning with the exception is logged when the host starts.

## Core

### `AppName`

`string` · Required

The name of your application. It goes into the system prompt, into the topic that the safety check and the off-topic check judge messages against, and into the A2A [agent card](https://docs.mentoragent.net/reference/glossary#agent-card).

### `AppDescription`

`string` · Default: `""`

One or two sentences on what your application does. The model reads it as context. The input safety check also uses it to judge messages: without a description, the check must guess what your application is about.

### `Language`

`MentorLanguage` · Default: `English`

The language of the replies and of the widget's texts: `English`, `Italian`, `French`, `German`, `Spanish`, `Portuguese`, `Dutch`, `Polish`, `Japanese` or `Chinese`. The confirmation box is built from the action's `Description` and its parameter names, which are not translated: write them in your users' language.

### `MentorshipLevel`

`MentorshipLevel` · Default: `Standard`

How much the agent explains and suggests:

- `Minimal`: it carries out requests without explanations or suggestions. For expert users.
- `Standard`: balanced answers, with suggestions when they are relevant.
- `Proactive`: it explains what it did and why, suggests next steps and warns of risks. It may offer a next step only when a tool of the current request, a page, a skill, a specialist or a team can carry it out. The model does not always follow this rule, so it can still suggest an action your application does not have: see [Known issues](https://docs.mentoragent.net/whats-new#known-issues).

### `ChatClient`

`IChatClient?` · Default: `null`

The model, as a `Microsoft.Extensions.AI` `IChatClient`: Azure OpenAI, OpenAI, Ollama or any provider that has one. This is the recommended path: specialists, teams, remote agents, [hosted tools](https://docs.mentoragent.net/reference/glossary#hosted-tool), tool filtering and routing need it. Set this or `Agent`, not both.

MentorAgent brings no SDK of a model provider. Add the package that your provider needs, for example `Azure.AI.OpenAI` or `Microsoft.Extensions.AI.OpenAI`. See [Choose a path](https://docs.mentoragent.net/configure/model-providers#choose-a-path) in Model providers.

### `Agent`

`AIAgent?` · Default: `null`

A ready-made Agent Framework `AIAgent`. Use it for providers that do not expose an `IChatClient`, such as Foundry's `AIProjectClient`. For Foundry, add its package yourself. Features that need `ChatClient` are not available. Set this or `ChatClient`, not both. See [Foundry agents (Path B)](https://docs.mentoragent.net/configure/model-providers#foundry-agents-path-b).

### `EmbeddingGenerator`

`IEmbeddingGenerator<string, Embedding<float>>?` · Default: `null`

An embedding model, which turns a text into a list of numbers that represents its meaning, so that MentorAgent can compare texts. [`EnableToolFiltering`](https://docs.mentoragent.net/reference/options#enabletoolfiltering), [`MemoryRelevanceFiltering`](https://docs.mentoragent.net/reference/options#memoryrelevancefiltering) and the `Semantic` [`RoutingStrategy`](https://docs.mentoragent.net/reference/options#routingstrategy) need it. It does not turn them on: each has its own option. If you turn one on without an embedding model, MentorAgent logs a warning and the feature stays off.

### `ScanAssemblies`

`Assembly[]` · Required

The assemblies MentorAgent scans for actions, agents, teams, pages and skills. Usually `[typeof(Program).Assembly]`. See [Attributes](https://docs.mentoragent.net/reference/attributes).

## Widget

### `Theme`

`MentorTheme` · Default: `Default`

The widget's theme: `Default`, `Dark`, `Minimal` or `Custom`. `Custom` injects no theme overrides and renders like `Default`: your stylesheet decides what changes.

### `Position`

`ChatPosition` · Default: `BottomRight`

Where the widget sits: `BottomRight`, `BottomLeft`, `TopRight`, `TopLeft`, or a full-height side panel with `SideRight` and `SideLeft`.

### `PrimaryColor`

`string?` · Default: `null`

An accent color in hex, for example `#2563EB`. `null` keeps the theme's color.

### `BotName`

`string` · Default: `"Mentor AI"`

The name shown in the widget header.

### `AvatarUrl`

`string?` · Default: `null`

The URL of the avatar shown in the widget. `null` uses the built-in one.

### `WelcomeMessage`

`string?` · Default: `null`

The message shown while the chat is empty. The widget renders it as HTML, so never build it from user input. `null` uses the built-in message.

### `InputPlaceholder`

`string?` · Default: `null`

The placeholder of the input box. With `null`, the widget uses the text in the language of [`Language`](https://docs.mentoragent.net/reference/options#language): "Write a message..." in English.

### `EnableSuggestions`

`bool` · Default: `true`

Shows starter suggestion chips in the widget's welcome panel. No hub event or SSE frame carries suggestions, so it has no effect on a React, Angular or Vue client, or on any other client of the hub.

### `EnableActionFeedback`

`bool` · Default: `true`

Shows a feedback line while one of your tools runs; over the hub it sends `ActionExecuting` and `ActionCompleted`. Hosted tools have their own switch, [`ShowHostedToolActivity`](https://docs.mentoragent.net/reference/options#showhostedtoolactivity).

### `EnableOnboardingTour`

`bool` · Default: `false`

Shows a short guided tour the first time a user opens the widget. MentorAgent generates it from your `[MentorPage]` pages and the agent's tools. `Novior.MentorAgent.Server` answers `GET /mentor/tour` even when this option is `false`: a client of the server decides for itself whether to show the tour.

### `EnableGenerativeCards`

`bool` · Default: `true`

Renders a `MentorCard` returned by a tool as a card. With `false` the data reaches the model as text, and the model describes it.

### `EnableRichResponses`

`bool` · Default: `true`

Asks the coordinator to format answers as Markdown, with tables and lists. The widget always renders Markdown safely. With `false`, the coordinator is asked for short answers in plain text.

## Voice

### `EnableVoiceInput`

`bool` · Default: `false`

Shows a microphone button that uses the browser's Speech Recognition API. The browser does the recognition: in Chrome, for example, it sends the audio to its vendor's service, and only the transcript reaches MentorAgent. Another client does its own capture and sends the transcript as an ordinary message.

### `EnableVoiceOutput`

`bool` · Default: `false`

Reads replies aloud with the browser's Speech Synthesis API. Synthesis always happens on the client.

### `VoiceStreaming`

`bool` · Default: `true`

Speaks each sentence as it streams, instead of reading the finished answer back.

### `VoiceBargeIn`

`bool` · Default: `true`

Stops the spoken answer when the user acts: presses the microphone, sends a message, presses Stop or starts a new conversation.

### `VoiceHandsFree`

`bool` · Default: `false`

Lets the user talk without touching the widget: the message is sent when the user stops speaking, and the microphone opens again when the spoken answer ends. Ignored unless both `EnableVoiceInput` and `EnableVoiceOutput` are on.

### `VoiceRate`

`double` · Default: `1.0`

The speech rate, passed to `SpeechSynthesisUtterance.rate`. The useful range is about 0.5 to 2.0.

## Image input

### `EnableImageInput`

`bool` · Default: `false`

Lets users send images with a message. In the widget, they can upload, paste or drag and drop an image, or give its URL. In `Novior.MentorAgent.Server`, images arrive as attachments of `SendMessage` and `POST /mentor/chat`. The chat model must support images.

### `MaxImageBytes`

`int` · Default: `4194304`

The maximum size of one image, 4 MB. Checked on the server.

### `MaxImagesPerMessage`

`int` · Default: `4`

The maximum number of images in one message. Checked on the server.

### `AllowedImageTypes`

`IReadOnlyList<string>` · Default: `["image/png", "image/jpeg", "image/gif", "image/webp"]`

The accepted MIME types, as an allow-list. Anything else is dropped with a warning.

## Confirmation and approval

Before an action runs, MentorAgent can ask the user to confirm it, in the confirmation box. Agent Framework calls this step an approval: on this page, confirmation and approval mean the same thing. See [Roles, confirmation and governance](https://docs.mentoragent.net/concepts/governance).

### `RequireConfirmation`

`bool` · Default: `true`

Turns confirmation on or off for the whole application. With `true`, the default, a tool asks for confirmation in three cases:

- you declare `RequiresConfirmation = true` on an action, see [`RequiresConfirmation`](https://docs.mentoragent.net/reference/attributes#mentoraction);
- the [`RequiresApproval`](https://docs.mentoragent.net/reference/options#requiresapproval) predicate matches the tool, or you set [`MentorMcpServer.RequiresConfirmation`](https://docs.mentoragent.net/reference/options#mentormcpserverrequiresconfirmation) on a server of [`McpServers`](https://docs.mentoragent.net/reference/options#mcpservers);
- the tool is a `[MentorAction]` that changes data and declares nothing: see [`RequireConfirmationForWrites`](https://docs.mentoragent.net/reference/options#requireconfirmationforwrites).

Do not confuse this option, `RequireConfirmation`, with the attribute parameter `RequiresConfirmation`.

With `false`:

- no action or tool asks for confirmation;
- `RequiredRoles` still refuses a user without the role;
- the only approvals that still appear are those for the MCP servers that the model provider calls directly ([`HostedMcpServers`](https://docs.mentoragent.net/reference/options#hostedmcpservers));
- tool filtering still reads what you declared: see [`EnableToolFiltering`](https://docs.mentoragent.net/reference/options#enabletoolfiltering);
- [`RequireActionForChangeRequests`](https://docs.mentoragent.net/reference/options#requireactionforchangerequests) requires no action, because no action shows a confirmation box.

See [Roles, confirmation and governance](https://docs.mentoragent.net/concepts/governance).

### `RequireConfirmationForWrites`

`bool` · Default: `true`

Makes a `[MentorAction]` ask for confirmation when it declares neither `ReadOnly = true` nor `RequiresConfirmation`. MentorAgent treats such an action as one that changes data. To keep an action out, declare it:

- `ReadOnly = true` on an action that only reads, or [`IsReadOnlyAction`](https://docs.mentoragent.net/reference/options#isreadonlyaction) by tool name;
- `RequiresConfirmation = false` on a write that must run without asking.

```csharp title="CaseReview.cs"
public class CaseReview(ICaseReviewStore store)
{
    // Declares nothing: MentorAgent treats it as a write, and it asks for confirmation
    [MentorAction(Description = "Reopens a closed support case")]
    public Task ReopenCaseAsync(string caseNumber, string reason)
        => store.ReopenAsync(caseNumber, reason);

    // Only reads: it never asks
    [MentorAction(Description = "Lists the notes of a support case", ReadOnly = true)]
    public Task<string> ListCaseNotesAsync(string caseNumber)
        => store.ListNotesAsync(caseNumber);

    // A write that is easy to undo and must run without asking
    [MentorAction(Description = "Flags a support case for review", RequiresConfirmation = false)]
    public Task FlagCaseAsync(string caseNumber)
        => store.FlagAsync(caseNumber);
}
```

An action that asks is withheld from the [MCP server](https://docs.mentoragent.net/configure/mcp-server) and from the A2A [agent card](https://docs.mentoragent.net/reference/glossary#agent-card), like every action that asks: nobody there can answer. Tool filtering does not send it when a message matches nothing.

The option covers `[MentorAction]` methods only. A method with only `[Description]` asks for nothing and stays on `/mcp`: mark it `[MentorAction]` to have the default.

The first time MentorAgent reads your actions, a warning lists the writes that run without confirmation, in two lists: the `[MentorAction]` methods that opted out, and the methods with only `[Description]` that are not declared read-only. MentorAgent reads your actions:

- at startup, when the host maps the A2A agent or turns on [`WarmUpAtStartup`](https://docs.mentoragent.net/reference/options#warmupatstartup);
- at the first MCP request or the first request for the tour;
- otherwise, at the first message.

With `false`, an action asks only when it declares `RequiresConfirmation = true`. [`RequireConfirmation`](https://docs.mentoragent.net/reference/options#requireconfirmation) set to `false` turns off every confirmation of your actions, this default included.

### `HitlMode`

`MentorHitlMode` · Default: `Blocking`

How the server handles a confirmation. HITL stands for human in the loop, and Agent Framework calls a confirmation an approval.

- `Blocking`: MentorAgent's own flow. The run waits for the user's answer.
- `Native`: Agent Framework's approval protocol, for interoperability. After the answer, the turn resumes in a new run.

The confirmation box, the client protocol and the model calls are the same in both modes. When the user declines, in both modes the model is told that the user declined, that nothing was checked or changed, and that it must not give another reason.

With `Native`, Stop pressed while a confirmation box waits does not break the conversation: the next message answers the open request as not run. A snapshot saved after that Stop and restored later still carries the open request: see [Known issues](https://docs.mentoragent.net/whats-new#a-snapshot-saved-after-stop-on-a-native-confirmation). See [Blocking and Native modes](https://docs.mentoragent.net/concepts/governance#blocking-and-native-modes).

### `RequiresApproval`

`Func<string, bool>?` · Default: `null`

A predicate on the tool name: return `true` to ask the user to confirm before that tool runs. It works in addition to `RequiresConfirmation`. Use it for tools that have no attribute you can set, such as MCP tools and skills. The name is the tool name, not the method name: `CloseCaseAsync` becomes `close_case`.

### `IsReadOnlyAction`

`Func<string, bool>?` · Default: `null`

A predicate on the tool name: return `true` to declare that the action only reads. It does the same as [`ReadOnly`](https://docs.mentoragent.net/reference/attributes#mentoraction) on `[MentorAction]`, for methods that have only `[Description]`, or to declare many actions in one place. MentorAgent takes your word for it and does not check it.

- The [MCP server](https://docs.mentoragent.net/configure/mcp-server) publishes the action with `readOnlyHint`.
- [`RequireLookupForRecordQuestions`](https://docs.mentoragent.net/reference/options#requirelookupforrecordquestions) can require the agent to call it.
- A tool that needs confirmation is never read-only, whatever the predicate returns.
- It does not apply to the tools of the MCP servers you connect to: their own `readOnlyHint` decides.

The name is the tool name, not the method name: `GetCaseStatusAsync` becomes `get_case_status`.

### `AutoApprovalRules`

`IList<Func<ToolAutoApprovalRuleContext, ValueTask<bool>>>` · Default: empty

Works only with `HitlMode = Native`. MentorAgent evaluates the rules in order. The first rule that returns `true` approves the call, and no confirmation box appears. Setting a rule, or `MaxAutoApprovalIterations`, also turns on "don't ask again": after the user approves a call, matching calls stop asking for the rest of the session. A rule skips the question, not the role check: `RequiredRoles` still applies. Rules do not cover the tools of specialists and teams. A call that [`RequireActionForChangeRequests`](https://docs.mentoragent.net/reference/options#requireactionforchangerequests) requires is never approved by a rule: the confirmation box always appears.

### `MaxAutoApprovalIterations`

`int?` · Default: `null`

`Native` mode only. A cap on how many times one turn may run again because its approvals were granted by `AutoApprovalRules`. Each run is a billable model call. `null` uses Agent Framework's default.

## Security and limits

### `EnableSafetyCheck`

`bool` · Default: `false`

Before each turn, asks a model whether the message is a prompt injection, a jailbreak or an attempt to extract the system prompt or credentials. It adds one call per message, on [`ClassifierChatClient`](https://docs.mentoragent.net/reference/options#classifierchatclient).

### `SafetyCheckTimeout`

`TimeSpan` · Default: `00:00:15`

How long the input and output checks may take before they count as failed. `TimeSpan.Zero` or a negative value means no limit, and Stop still cancels a check. Raise it for a slow local model.

### `SafetyCheckFailure`

`MentorSafetyCheckFailure` · Default: `Allow`

What happens when a safety check gives no verdict, after a timeout or an error. `Allow` lets the message or the reply through, and logs a warning. `Block` refuses the message, or withholds the reply. A check that finds the message or the reply unsafe blocks it with both values.

### `RefuseOutOfScope`

`bool` · Default: `false`

Politely refuses a message that plainly has nothing to do with your application, without calling the coordinator. When the classifier is unsure, the message is answered: a name it does not recognize may be one of your records, and greetings, follow-up questions and questions about the agent are answered too.

MentorAgent judges the message against [`HostedToolDomainScope`](https://docs.mentoragent.net/reference/options#hostedtooldomainscope) or, when that is not set, against your app's name, description, and the names and descriptions of your pages. With `EnableSafetyCheck` on, one call answers both questions, with a longer prompt. Without it, the option adds one small model call per turn. When [`HostedToolDomainCheck`](https://docs.mentoragent.net/reference/options#hostedtooldomaincheck) is on, a turn that is about to declare a hosted tool still asks that check its own, stricter question. Each refusal counts in `GuardrailBlockCount` and in the `mentoragent.guardrail_blocks` metric.

### `WarmUpAtStartup`

`bool` · Default: `false`

Builds one coordinator when the application starts, so the first user does not wait for the shared work: tool embeddings, shared MCP sessions, remote agent cards. It runs once the host has started, after a web host starts listening. A failure of the warm-up itself never stops the application, but an invalid configuration does. It needs a generic host, such as ASP.NET Core or a worker: a .NET MAUI app runs no hosted service, so there the option does nothing.

### `MaxMessageLength`

`int` · Default: `4000`

The maximum length of a message, in characters. A longer message is refused before the model is called. `0` means no limit.

### `RateLimitPerUser`

`int` · Default: `0`

The maximum number of messages per user in each `RateLimitWindowSecs` window. `0` turns the limit off. MentorAgent counts a signed-in user by the same key as memory: the `NameIdentifier` claim, else `sub`, `oid` or the name. It counts an anonymous user by [`AnonymousIdentity`](https://docs.mentoragent.net/reference/options#anonymousidentity). With the default, `PerSession`, a page reload starts a new count. So only signed-in users get a real limit per user.

The tool calls of the [MCP server](https://docs.mentoragent.net/configure/mcp-server) are limited too, with the same limit and window, but in an allowance of their own, separate from the chat messages of the same user. MentorAgent counts the caller by its `NameIdentifier`, `sub` or `oid` claim, or else by its name. A caller with none of them is counted by remote address when the host registers `IHttpContextAccessor`, as `AddMentorAgentServer()` and `AddMentorAgentA2A()` do. Otherwise all such callers share one allowance, and one busy client can use it up for the others. A call over the limit gets an `isError` result.

### `RateLimitWindowSecs`

`int` · Default: `60`

The length of the rate-limit window, in seconds.

### `IncludeWorkflowExceptionDetails`

`bool` · Default: `false`

Puts the exception message of a failing specialist or team into the reply the user reads. Development only.

## Middleware and guardrails

These options add checks on messages and replies, your own or built in, and let you change tool results, error messages and the pipeline around `ChatClient`. A guardrail is a check you write: it returns `true` when the text is safe.

### `InputGuardrail`

`Func<string, CancellationToken, Task<bool>>?` · Default: `null`

Your own check on each incoming message; return `true` when it is safe. When set, it runs on every message and replaces the built-in check of `EnableSafetyCheck`.

### `OutputGuardrail`

`Func<string, CancellationToken, Task<bool>>?` · Default: `null`

Your own check on the completed reply; return `true` when it is safe. While it is set, each reply is held back and shown after the check, without live streaming.

### `EnableOutputSafetyCheck`

`bool` · Default: `false`

The built-in check on the reply: it blocks a reply that leaks instructions or secrets, discloses someone's personal data or contains harmful content. The reply is shown after the check. It runs on `ClassifierChatClient`, or on `ChatClient` when that is not set, and a custom `OutputGuardrail` takes precedence.

### `OnToolResult`

`Func<string, object?, object?>?` · Default: `null`

Transforms or redacts a tool result before it returns to the model. It receives the tool name and the result. It also runs on the result of each call to the [MCP server](https://docs.mentoragent.net/configure/mcp-server), before the caller sees it. If the hook throws, an MCP caller gets an `isError` result and not the data. In the chat, the model is told that the action ran and that its result was withheld, and not to run it again.

### `OnException`

`Func<Exception, string?>?` · Default: `null`

Maps an exception to the message the user sees. Return `null` to keep the built-in message.

When a tool throws, the message goes to the chat and to the model:

- With the hook set, the chat never shows the text of the exception. It shows your message. When the hook returns `null` or an empty string, or throws, it shows "The action could not be completed." in the language of the widget.
- The model gets your message or, without one, the message of the exception, because it needs a reason to answer. Return a message for any exception whose text must not reach the model.
- Without the hook, the chat and the model both get the message of the exception.

Only an exception of the tool itself counts as a failure. The log has the type of the exception, and the whole exception at Debug level.

### `ConfigureChatClientPipeline`

`Func<ChatClientBuilder, ChatClientBuilder>?` · Default: `null`

Adds your own middleware, such as logging or caching, to the pipeline that MentorAgent builds around `ChatClient`. Your middleware wraps MentorAgent's own middleware: it is the outermost layer.

## Session and history

These options control the history: the messages of the current conversation, which the model receives again at each turn. By default MentorAgent keeps the history in memory. A history provider can store it elsewhere, for example in a database.

### `MaxSessionMessages`

`int` · Default: `50`

The maximum number of messages that the default in-memory history keeps. Every message counts, tool calls and tool results included. MentorAgent removes whole turns, oldest first, so the model still sees the data that a tool read in the previous turn. `0` or a negative value turns trimming off, with a warning. MentorAgent does not read it when [`ChatReducer`](https://docs.mentoragent.net/reference/options#chatreducer) is set, for a history provider you supply, or with [`UseServiceManagedHistory`](https://docs.mentoragent.net/reference/options#useservicemanagedhistory).

### `ChatReducer`

`IChatReducer?` · Default: `null`

Replaces the reducer that keeps the default in-memory history short. A reducer decides which messages the history keeps. With `null`, MentorAgent uses a `MentorChatReducer` of `MaxSessionMessages` messages. It cuts whole turns, oldest first, and keeps every tool call together with its result.

When you set it, MentorAgent uses it as it is and does not read `MaxSessionMessages`. One instance serves every session, so it must not keep the state of one conversation. To keep only the text of the conversation, set a `MessageCountingChatReducer`: it is experimental in `Microsoft.Extensions.AI` (diagnostic `MEAI001`).

It applies to the default history of `ChatClient` only. A `ChatHistoryProvider` or `ChatHistoryProviderFactory` of yours is used as it is: give it a reducer yourself, for example `new MentorChatReducer(50)` from the namespace `Novior.MentorAgent.Core`. With `UseServiceManagedHistory`, or when you supply a provider, a set `ChatReducer` has no effect and MentorAgent logs a warning.

### `ChatHistoryProvider`

`ChatHistoryProvider?` · Default: `null`

A persistent history provider. One instance serves every user in the process: use it only in a single-user host, or with a provider that partitions its own storage. With `Agent`, set the provider on the agent instead.

### `ChatHistoryProviderFactory`

`Func<IServiceProvider, ChatHistoryProvider>?` · Default: `null`

Builds a history provider once per coordinator: per [circuit](https://docs.mentoragent.net/reference/glossary#circuit), per hub connection, per app instance. Prefer it whenever the application has more than one user. Setting it together with `ChatHistoryProvider` fails validation. The provider is kept across new conversations, so it must keep the transcript in the session, not in a field: see [Conversation history and memory](https://docs.mentoragent.net/configure/production#conversation-history-and-memory).

### `UseServiceManagedHistory`

`bool` · Default: `false`

Set it when the chat client keeps the conversation on the service, as the OpenAI and Azure OpenAI Responses client does. MentorAgent then installs no history provider and no compaction, and `MaxSessionMessages`, `ChatReducer`, `EnableCompaction` and your history provider no longer apply. A session snapshot records how the history is kept, and a host that keeps it the other way refuses the snapshot.

## Memory

Memory keeps facts about each user across conversations, such as a name, a role or a preference, and adds them to the prompt.

### `UseMemoryContext`

`bool` · Default: `false`

Turns on memory across conversations. MentorAgent identifies a signed-in user by the `NameIdentifier` claim, else by `sub`, `oid` or the name of the identity. A key made from `sub`, `oid` or the name starts with the claim name (`sub:…`, `name:…`), so a name never matches the identifier of another user. It identifies an anonymous user by [`AnonymousIdentity`](https://docs.mentoragent.net/reference/options#anonymousidentity). A signed-in user with none of these is keyed as an anonymous one. Memories live in RAM unless you register your own `IMentorMemoryStore` before `AddMentorAgent`.

### `AnonymousIdentity`

`MentorAnonymousIdentity` · Default: `PerSession`

Who an anonymous user is, for memory and for the rate limit. `PerSession` gives each circuit or connection its own key. `Shared` gives every anonymous user the same key, `anonymous`. Use `Shared` only in a single-user app, such as a .NET MAUI app: on a website, users would share memories and the rate limit. Ignored for a signed-in user.

### `MemoryContextCount`

`int` · Default: `10`

How many memories go into the prompt: the most recent, or the most relevant with `MemoryRelevanceFiltering`.

### `MemoryRelevanceFiltering`

`bool` · Default: `false`

Injects only the memories related to the message, by embedding similarity, plus the identity and preference facts. Needs `EmbeddingGenerator`; without it the most recent ones are used.

### `MemoryAutoCapture`

`bool` · Default: `true`

With `UseMemoryContext` on, a small extraction call after each message saves lasting facts about the user. With `ChatClient`, this call is the only way memories are saved, and the `remember` tool is removed. With `Agent`, the `remember` tool stays, and the extraction runs only when `ClassifierChatClient` is set. `false` saves the extra call.

## RAG

Retrieval-augmented generation (RAG) lets the model answer from your own documents. For each message, before the coordinator answers, MentorAgent searches them and adds the relevant ones to the prompt.

You write the search yourself, as a class that implements `IMentorRagSource`, and register it in dependency injection. MentorAgent does not wrap an `ITextSearch` for you: call it from your own source.

| Member | What it does |
|---|---|
| `SearchAsync(string query, int maxResults, CancellationToken)` | Searches with the text of the message. Every source implements it. |
| `SearchAsync(MentorRagRequest request, CancellationToken)` | The method that MentorAgent calls. Its default implementation calls the first one, so implement it only when the search needs more than the text. |
| `DefaultMinScore` | The relevance threshold for the scores of your source. It applies when you do not set [`RagMinScore`](https://docs.mentoragent.net/reference/options#ragminscore). |

Do not make each `SearchAsync` call the other: the search would never return.

A `MentorRagRequest` holds:

| Property | Holds |
|---|---|
| `Query`, `MaxResults` | The message of the user, and [`RagResultCount`](https://docs.mentoragent.net/reference/options#ragresultcount). |
| `RecentMessages` | The messages before this one, oldest first: the user's messages and the replies of the last three turns of this conversation, as text. It is empty on the first turn, after a reset and after a restore. Over Server-Sent Events it is always empty, because each request is a conversation of its own. |
| `User` | The caller of the hub, of `/mentor/chat` or of an A2A request. In a Blazor circuit, the user that the `AuthenticationStateProvider` of the host reports. It is `null` when the host has no authentication. An anonymous visitor has an unauthenticated principal. Filter restricted documents on it. |
| `PageName`, `PageData` | The name of the current page and the data that the page put in context with `IMentorPageContext`. |

A result is a `MentorRagResult`: the content, the source URL, the title and the score. It can also carry `Metadata`, facts about the document such as a date or a version. The model sees them under the title of the document, one `key: value` per entry, so it can tell the 2025 price list from the 2026 one. They also travel to the client with the citation chips, and the model can repeat them in its reply. Put nothing in them that the user must not see.

```csharp title="CaseKnowledgeSource.cs"
public class CaseKnowledgeSource(ICaseKnowledge knowledge) : IMentorRagSource
{
    // The scores of this source are cosine similarities, between 0 and 1: say which threshold suits them
    public float? DefaultMinScore => 0.6f;

    // The method that MentorAgent calls: it sees who is asking
    public Task<IReadOnlyList<MentorRagResult>> SearchAsync(MentorRagRequest request, CancellationToken ct = default)
        => FindAsync(request.Query, request.MaxResults, request.User, ct);

    // Required by the interface. Share the search through a third method: never call one SearchAsync from the other
    public Task<IReadOnlyList<MentorRagResult>> SearchAsync(string query, int maxResults, CancellationToken ct = default)
        => FindAsync(query, maxResults, user: null, ct);

    async Task<IReadOnlyList<MentorRagResult>> FindAsync(string text, int max, ClaimsPrincipal? user, CancellationToken ct)
    {
        // The knowledge base returns only the articles that this user may read
        var articles = await knowledge.FindAsync(text, max, user, ct);
        return [.. articles.Select(a => new MentorRagResult(a.Text, a.Url, a.Title, a.Score)
        {
            // Facts the model weighs with the text: it can tell the 2025 price list from the 2026 one
            Metadata = new Dictionary<string, string> { ["valid from"] = a.ValidFrom },
        })];
    }
}
```

### `UseRag`

`bool` · Default: `false`

Searches your knowledge base before each coordinator call and adds the relevant documents to the prompt. Needs a registered `IMentorRagSource`.

### `RagResultCount`

`int` · Default: `5`

How many documents are retrieved for each message.

### `RagMinScore`

`float` · Default: `2`

The minimum relevance score of a document. The scale depends on your source. With keyword search, `2` means about two matches. With cosine similarity (a score from 0 to 1), use 0.5 to 0.75. `0` turns filtering off.

MentorAgent uses the first of these thresholds that applies:

1. this option, when you set it in code or in configuration;
2. the `DefaultMinScore` of the source, when it declares one;
3. the default of this option, `2`.

A source with cosine scores should declare a `DefaultMinScore`. Without one, the default `2` discards every document, and a warning says which threshold discarded them.

### `RagSystemPromptTemplate`

`string` · Default: `"Use the following documents to answer:\n{documents}"`

The text that introduces the documents in the prompt; `{documents}` is replaced with them.

### `ShowRagSources`

`bool` · Default: `false`

Shows citation chips under the agent's replies. The pages of a hosted web search and the documents of a file search use the same chips. The `Metadata` of a document travels with its chip. Over the hub, the client receives it in the `metadata` field of `RagSourcesReady`.

## Agent Skills

A skill is a set of instructions, with optional resources, that the model loads only when it needs them. Until then, the prompt carries only the skill's name and description.

### `EnableSkills`

`bool` · Default: `false`

Turns on skill discovery and the `load_skill` and `read_skill_resource` tools. Skills are declared with [`[MentorSkill]`](https://docs.mentoragent.net/reference/attributes#mentorskill) or as files.

### `SkillsFolder`

`string` · Default: `"Skills"`

The folder of file-based skills, with one sub-folder and one `SKILL.md` per skill. Relative to the content root, or absolute.

### `SkillSources`

`IList<AgentSkillsSource>` · Default: empty

More skill sources, through Agent Framework's own abstraction: a database, a remote catalog or `AgentFileSkillsSource`. Your own skills win a name collision.

### `SkillFilter`

`Func<AgentSkill, bool>?` · Default: `null`

Keeps or drops a skill that comes from `SkillSources`. It never applies to the skills MentorAgent discovered itself.

### `SkillsRefreshInterval`

`TimeSpan?` · Default: `null`

How long a list of skills from `SkillSources` stays fresh. While it is fresh, every session in the process shares it. The first session that starts after the interval reads the sources again. With `null`, there is no shared list: every new session reads the sources. Set it for a remote or slow source.

## MCP

The Model Context Protocol (MCP) lets an AI application use tools from other programs. MentorAgent can be an MCP client: it uses the tools of external MCP servers. It can also be an MCP server: it publishes your actions to MCP clients.

### `McpServers`

`MentorMcpServer[]?` · Default: `null`

External MCP servers your application connects to as a client. Their tools become tools of the coordinator, like your actions. See [`MentorMcpServer`](https://docs.mentoragent.net/reference/options#mentormcpserver).

### `McpServerEnabled`

`bool` · Default: `false`

Exposes your actions as an MCP server. The server is in the `Novior.MentorAgent.Server` package: call `AddMentorAgentMcp()` after `AddMentorAgent` (`AddMentorAgentServer()` does it for you), and `app.MapMentorAgentMcp()`. MentorAgent reads this option when you call `AddMentorAgent`, so set it in that callback.

Actions that need confirmation are never published, and that includes a `[MentorAction]` that asks by default: see [`RequireConfirmationForWrites`](https://docs.mentoragent.net/reference/options#requireconfirmationforwrites) and [where nobody can answer](https://docs.mentoragent.net/concepts/governance#where-nobody-can-answer). An action declared read-only is published with `readOnlyHint`: some MCP clients skip their own confirmation for such a tool, so declare it only on an action that really only reads. Every tool call passes through [`OnToolResult`](https://docs.mentoragent.net/reference/options#ontoolresult) and [`RateLimitPerUser`](https://docs.mentoragent.net/reference/options#ratelimitperuser).

The endpoint has no CORS policy, which suits desktop and IDE clients. A client that runs in a browser needs an opt-in. See [MCP server](https://docs.mentoragent.net/configure/mcp-server#minimal-configuration).

### `McpServerPath`

`string` · Default: `"/mcp"`

The path `MapMentorAgentMcp()` maps when you pass none. A path passed to the method wins.

### `McpCallerPrincipal`

`Func<ClaimsPrincipal?>?` · Default: `null`

Tells MentorAgent who is calling the MCP server. An action with `RequiredRoles` is then published to an authenticated caller who has one of its roles. With `null`, every action with `RequiredRoles` is [withheld](https://docs.mentoragent.net/reference/glossary#withheld). They are also withheld when the resolver returns `null` or an unauthenticated user, or when it throws.

MentorAgent tests the roles with `ClaimsPrincipal.IsInRole`. A `roles` claim works once the identity names it as its `RoleClaimType`. With JwtBearer and `MapInboundClaims = false`, set `TokenValidationParameters.RoleClaimType = "roles"`. MentorAgent calls the resolver once for each MCP request and never keeps the answer for another caller, so keep it cheap.

### `ShowMcpStatus`

`bool` · Default: `false`

Shows a badge in the widget header with the connection status of `McpServers`.

## A2A

The Agent-to-Agent (A2A) protocol lets AI agents call each other. MentorAgent can hand requests to remote A2A agents, and it can answer as an A2A agent itself. An A2A agent describes itself in an agent card, a JSON document at `/.well-known/agent-card.json`.

### `RemoteAgents`

`MentorRemoteAgent[]?` · Default: `null`

Remote A2A agents the coordinator can hand requests to, like local specialists. They need `ChatClient`. Any user of your application can reach them: see [`MentorRemoteAgent`](https://docs.mentoragent.net/reference/options#mentorremoteagent).

### `A2AServerEnabled`

`bool` · Default: `false`

Exposes your application as an A2A agent. The agent is in the `Novior.MentorAgent.Server` package: call `AddMentorAgentA2A()` after `AddMentorAgent` (`AddMentorAgentServer()` does it for you), and `app.MapMentorAgentA2A()`. MentorAgent reads this option when you call `AddMentorAgent`. See [A2A server](https://docs.mentoragent.net/configure/a2a-server#minimal-configuration).

### `A2AServerPath`

`string` · Default: `"/a2a"`

The path of the A2A task handler when you pass none to `MapMentorAgentA2A()`. The agent card stays at `/.well-known/agent-card.json`.

### `A2AServerUrl`

`string?` · Default: `null`

The full public URL of your A2A endpoint, path included. MentorAgent publishes it as it is in the agent card. With `null`, the card carries only the path: a MentorAgent caller resolves it against the address of the card, and another A2A client may not. Set it whenever other agents call this one.

### `AgentCard`

`AgentCardInfo?` · Default: `null`

Extra metadata for the agent card; the name and description come from `AppName` and `AppDescription`. See [`AgentCardInfo`](https://docs.mentoragent.net/reference/options#agentcardinfo).

### `ShowA2AStatus`

`bool` · Default: `false`

Shows a badge in the widget header that lists the configured remote agents.

## Hosted tools

Hosted tools run on the model provider's infrastructure, not in your application: web search, code interpreter, file search, image generation and remote MCP servers. MentorAgent's roles and confirmation do not apply to them: see [`HostedTools`](https://docs.mentoragent.net/reference/options#hostedtools).

### `HostedTools`

`MentorHostedTools` · Default: `None`

The provider-hosted tools that the model may use, as flags: `WebSearch`, `CodeInterpreter`, `FileSearch`, `ImageGeneration`, `HostedMcp`. They run on the provider's infrastructure and need `ChatClient`. Which ones work depends on the client: the Responses client supports them all, and the Chat Completions client supports at most web search. Roles, confirmation and `OnToolResult` do not apply to them.

### `FileSearchVectorStoreIds`

`IReadOnlyList<string>?` · Default: `null`

The provider's vector stores that `FileSearch` searches. Required when `FileSearch` is on: without an id the tool is skipped and a warning is logged.

### `FileSearchMaxResults`

`int?` · Default: `null`

An upper bound on file-search matches. `null` lets the provider decide.

### `HostedImageModel`

`string?` · Default: `null`

The model `ImageGeneration` uses, for example `gpt-image-1-mini`. On Azure OpenAI it is not enough: the image deployment must also travel in the `x-ms-oai-image-generation-deployment` request header, which you add where you build the Azure client.

### `HostedImageSize`

`string?` · Default: `null`

The size of generated images as `WIDTHxHEIGHT`, for example `1024x1024`: the setting that controls the cost of image generation. A value that cannot be read is ignored with a warning.

### `HostedMcpServers`

`IReadOnlyList<MentorHostedMcpServer>?` · Default: `null`

The remote MCP servers the provider connects to for `HostedMcp`. Required when `HostedMcp` is on. See [`MentorHostedMcpServer`](https://docs.mentoragent.net/reference/options#mentorhostedmcpserver).

### `ShowHostedToolActivity`

`bool` · Default: `true`

Shows hosted-tool work while it happens: "Searching the web…", "Running code…", "Generating the image…". Citations also need `ShowRagSources`.

### `ShowHostedToolsStatus`

`bool` · Default: `false`

Shows a badge in the widget header that lists the active hosted tools. A development aid.

### `FilterHostedTools`

`bool` · Default: `true`

Offers each hosted tool to the model only when the semantic filter finds it relevant to the message. Needs `EnableToolFiltering` and `EmbeddingGenerator`; without them nothing is filtered and a warning is logged.

### `HostedToolFilterMinScore`

`float?` · Default: `null`

The similarity threshold for hosted tools; `null` means 0.15. Keep it low: this filter is only a first, rough selection, and [`HostedToolDomainCheck`](https://docs.mentoragent.net/reference/options#hostedtooldomaincheck) makes the final decision.

### `HostedToolDomainCheck`

`bool` · Default: `false`

Before a hosted tool runs, asks a small model whether the message concerns your application. The check runs once per turn, and only when a hosted tool passed the filter. If the check itself fails, because of an error or an unexpected answer, the turn goes on. When the check says that the message is not about your application, the agent is told that the hosted tools are not for this request. It still answers with your own actions when the request is about your application.

### `HostedToolDomainScope`

`string?` · Default: `null`

The topic the off-topic check judges against: what counts as being about your application. `null` derives it from `AppName`, `AppDescription` and the page names. When hosted tools are on, the derived text also lists the enabled capabilities, and says that they do not widen the topic. `RefuseOutOfScope` uses the text you set, as written.

### `HostedToolDomainClassifier`

`Func<string, CancellationToken, Task<bool>>?` · Default: `null`

Replaces the model call of the off-topic check with your own decision: return `true` when the message is about your application.

### `MaxHostedToolCallsPerSession`

`int` · Default: `0`

A hard cap on hosted-tool calls per session. Beyond it, hosted tools are no longer offered to the model. `0` means no cap. Over SignalR the session is the connection, while each request to `/mentor/chat`, `GET` or `POST`, is a session of its own.

## Tokens and cost

These options reduce the tokens sent to the model on each call, and so the cost.

### `EnableToolFiltering`

`bool` · Default: `false`

Sends the model only the tools relevant to the message, by embedding similarity. The core tools are always sent: navigation, memory, delegation to specialists and teams, skills and UI actions. Needs `EmbeddingGenerator` and `ChatClient`: without them, every tool is sent and a warning is logged.

The embedding ranks the actions well, but its score is not calibrated: a question that needs data can score below a greeting. So the filter does not rely on the threshold alone:

- When no action scores above `ToolFilterMinScore`, the three best-ranked actions that need no confirmation and no role are sent anyway, within `ToolFilterMaxTools`. An MCP tool qualifies only when its server marks it `readOnlyHint`. This adds about 80 input tokens to each model call on such turns, greetings included.
- When every selected action needs confirmation, up to three of the best-ranked actions that do not need it are sent with them, within `ToolFilterMaxTools`.
- When a single selected action needs no confirmation, it is sent alone, unless [`ToolFilterMinTools`](https://docs.mentoragent.net/reference/options#toolfiltermintools) asks for more.

An action needs confirmation when it has `RequiresConfirmation`, when `RequiresApproval` matches it, when it comes from a server with `MentorMcpServer.RequiresConfirmation`, or when it is a `[MentorAction]` that [`RequireConfirmationForWrites`](https://docs.mentoragent.net/reference/options#requireconfirmationforwrites) makes ask. The filter reads these declarations even when `RequireConfirmation` is `false`. An action without confirmation can therefore reach the model on any message. Let every action that changes data ask: a `[MentorAction]` does by default, but a method with `[Description]` alone does not.

A message that matches no action carries the actions that the previous message selected. So the answer to the agent's own question, such as "Yes, go ahead", still arrives with the action that the question was about. The actions are carried one message back only, never twice, and an action that needs confirmation is never carried alone. A new or restored conversation carries nothing.

Two opt-in options use the selection of the filter: [`RequireLookupForRecordQuestions`](https://docs.mentoragent.net/reference/options#requirelookupforrecordquestions) and [`RequireActionForChangeRequests`](https://docs.mentoragent.net/reference/options#requireactionforchangerequests).

### `ToolFilterMaxTools`

`int` · Default: `12`

The maximum number of matched tools sent, in addition to the core tools.

### `ToolFilterMinTools`

`int` · Default: `0`

Once one action scores above `ToolFilterMinScore`, at least this many of the best-ranked actions are sent, up to `ToolFilterMaxTools`. With `0`, the threshold alone decides. It is a trade-off. It can add the read action that the model needed. It can also add a wrong action that the model picks instead. You do not need it to keep an action that needs confirmation from reaching the model alone: [`EnableToolFiltering`](https://docs.mentoragent.net/reference/options#enabletoolfiltering) already sends other actions with it. It does not apply to a message where no action scores above `ToolFilterMinScore`: that message gets the three best-ranked actions that need no confirmation and no role.

### `ToolFilterMinScore`

`float` · Default: `0.35`

The minimum cosine similarity, from 0 to 1, for a tool to count as relevant. Higher is stricter. The line decides which actions match, not whether a message needs an action: see [`EnableToolFiltering`](https://docs.mentoragent.net/reference/options#enabletoolfiltering).

### `RequireLookupForRecordQuestions`

`bool` · Default: `false`

Makes the agent read a value before it answers a question about your records. A classifier model reads each message. When it says that the message asks for a value of your records (a price, a stock level, the status of an order), the first model call must call a read-only action. It must be one that the tool filter selected for that message. The agent can also hand the request to a specialist or a team. It then writes the reply from what the action returned.

- **Which actions.** Those you declared read-only, with [`ReadOnly`](https://docs.mentoragent.net/reference/attributes#mentoraction) or [`IsReadOnlyAction`](https://docs.mentoragent.net/reference/options#isreadonlyaction), and the tools that a connected MCP server marks `readOnlyHint`. An action that needs confirmation or a role is never required.
- **What it needs.** [`EnableToolFiltering`](https://docs.mentoragent.net/reference/options#enabletoolfiltering), an [`EmbeddingGenerator`](https://docs.mentoragent.net/reference/options#embeddinggenerator), a `ChatClient` and at least one declared read. Otherwise the option has no effect, and a startup warning says so.
- **What it costs.** One small classifier call on each turn whose selection holds a declared read. Because tool filtering also sends reads when nothing matches, that is nearly every turn. The call runs on [`ClassifierChatClient`](https://docs.mentoragent.net/reference/options#classifierchatclient) when you set one, within [`SafetyCheckTimeout`](https://docs.mentoragent.net/reference/options#safetychecktimeout). The reply starts after the result of the action, so a `Proactive` sentence such as "I am looking it up" no longer comes first.
- **When it does not force.** A message addressed by name to one of your specialists, teams or remote agents is not forced. If the classifier fails, times out or gives an answer it cannot read, the turn runs as it would without the option.

### `RequireActionForChangeRequests`

`bool` · Default: `false`

Makes the agent start a change with the action itself. The confirmation box then comes before any sentence. Without it, the model can answer "Stock updated" without calling anything: no box appears, and nothing changes.

A [classifier](https://docs.mentoragent.net/reference/glossary#classifier) model reads each message. It checks two things: whether the message asks to carry out one of your actions that need confirmation, and whether the message gives every input that the action needs. If both are true, the first model call must call one of those actions, chosen from the ones that the tool filter selected for that message. The agent can also hand the request to a specialist or a team. When the message asks for one of your `[MentorPage]` pages, the first call must be `navigate_to`.

- **A request that leaves something out is not forced.** The classifier sees the inputs that each action requires. A request is not complete when an input is missing, when the record is only described ("cancel the last order"), when a name is given where the input is an ID, or when the new value depends on the current one ("add 5 units"). The agent may then ask, as it would without the option. An input counts as needed only when the action's schema marks it required.
- **Which actions.** Only actions that show a confirmation box: confirmation declared with `RequiresConfirmation`, [`RequiresApproval`](https://docs.mentoragent.net/reference/options#requiresapproval) or [`MentorMcpServer.RequiresConfirmation`](https://docs.mentoragent.net/reference/options#mentormcpserverrequiresconfirmation), or asked by default for a `[MentorAction]` that changes data ([`RequireConfirmationForWrites`](https://docs.mentoragent.net/reference/options#requireconfirmationforwrites)), and [`RequireConfirmation`](https://docs.mentoragent.net/reference/options#requireconfirmation) on. An action is never required when its input schema cannot be shown faithfully: a schema with `$ref` or a combination of schemas, or an MCP tool that marks no parameter as required.
- **With `HitlMode.Native`.** No rule of [`AutoApprovalRules`](https://docs.mentoragent.net/reference/options#autoapprovalrules) is consulted for a required call, so the box always appears.
- **Pages.** A page that your application does not have, or something within the page that is already open, is not forced. A request from another agent never has to navigate.
- **What it needs and costs.** [`EnableToolFiltering`](https://docs.mentoragent.net/reference/options#enabletoolfiltering) and an [`EmbeddingGenerator`](https://docs.mentoragent.net/reference/options#embeddinggenerator). One small classifier call on each turn while the application has a page, or the message selected such an action. With [`RequireLookupForRecordQuestions`](https://docs.mentoragent.net/reference/options#requirelookupforrecordquestions) on as well, one call answers both. A startup warning says when the option has no effect, or covers page requests only.

The turn that answers a confirmation left open by Stop is never forced.

### `EnableCompaction`

`bool` · Default: `false`

Shortens a long conversation history before each model call, without calling a model: old tool results are collapsed, and the last [`CompactionMaxTurns`](https://docs.mentoragent.net/reference/options#compactionmaxturns) turns are kept. It changes what is sent to the model, never the conversation that is stored: the history provider and the session snapshots keep every message. It works with `ChatClient` and a history kept by your application: the built-in provider, `ChatHistoryProviderFactory` or `ChatHistoryProvider`. With [`UseServiceManagedHistory`](https://docs.mentoragent.net/reference/options#useservicemanagedhistory) it is not installed.

### `CompactionTokenThreshold`

`int` · Default: `4000`

The size of the history, in tokens, at which compaction starts. If the history is still larger after compaction, it is truncated to this size.

### `CompactionMaxTurns`

`int` · Default: `8`

The recent user turns kept intact.

## Model routing and classifier

Model routing sends the turns that need it to a stronger, more expensive model, [`StrongChatClient`](https://docs.mentoragent.net/reference/options#strongchatclient), and keeps the others on `ChatClient`. [`ClassifierChatClient`](https://docs.mentoragent.net/reference/options#classifierchatclient) is the model for MentorAgent's own short decisions: a small, fast model is enough.

### `StrongChatClient`

`IChatClient?` · Default: `null`

A stronger, more expensive model for the turns that need it; `ChatClient` stays the default. Routing also needs what [`RoutingStrategy`](https://docs.mentoragent.net/reference/options#routingstrategy) requires: otherwise a warning is logged and every turn stays on `ChatClient`.

### `RoutingStrategy`

`MentorRoutingStrategy` · Default: `Custom`

How MentorAgent chooses between `ChatClient` and `StrongChatClient`:

- `Custom`: calls your `UseStrongModelAsync` or `UseStrongModel`.
- `Semantic`: compares the message with `RoutingComplexExemplars`. Needs `EmbeddingGenerator`.
- `Classifier`: asks a small model, [`RoutingClassifierClient`](https://docs.mentoragent.net/reference/options#routingclassifierclient), with one extra call per turn.
- `Cascade`: answers with the cheap model first, and runs the turn again on the strong model when the answer is not good enough. This works only on calls that do not stream. Chat turns stream, so there `Cascade` decides like `Classifier`.

### `UseStrongModelAsync`

`Func<IReadOnlyList<ChatMessage>, CancellationToken, Task<bool>>?` · Default: `null`

`Custom` only: your router. It sees the whole conversation; return `true` to use `StrongChatClient`. It wins over `UseStrongModel`.

### `UseStrongModel`

`Func<string, bool>?` · Default: `null`

`Custom` only: a synchronous predicate on the latest user message. Prefer `UseStrongModelAsync`.

### `RoutingComplexExemplars`

`IReadOnlyList<string>?` · Default: `null`

`Semantic` only: example turns that deserve the strong model. `null` uses a built-in multilingual set.

### `RoutingThreshold`

`float` · Default: `0.35`

`Semantic` only: the similarity at which a turn is escalated. Higher is stricter.

### `RoutingClassifierClient`

`IChatClient?` · Default: `null`

`Classifier` and `Cascade` only: the client that judges the turn. `null` uses `ChatClient`.

### `ClassifierChatClient`

`IChatClient?` · Default: `null`

The client for MentorAgent's own small decisions:

- the input and output safety checks;
- the off-topic checks of hosted tools and of `RefuseOutOfScope`;
- the fact extraction of `MemoryAutoCapture`;
- the grounding check of A2A replies;
- the question behind [`RequireLookupForRecordQuestions`](https://docs.mentoragent.net/reference/options#requirelookupforrecordquestions) and [`RequireActionForChangeRequests`](https://docs.mentoragent.net/reference/options#requireactionforchangerequests), asked at temperature 0.

Most of these calls run before the user sees the reply, so point it at a small, fast deployment. `null` uses `ChatClient`. With `Agent` and no `ChatClient`, the safety checks run only when this is set.

## Observability and dashboard

### `EnableObservability`

`bool` · Default: `false`

Emits OpenTelemetry traces and metrics that follow the OpenTelemetry conventions for generative AI (GenAI), plus MentorAgent's own spans and counters.

### `ObservabilityIncludeSensitiveData`

`bool` · Default: `false`

Includes prompt and response content in the telemetry. Development only.

### `ObservabilitySourceName`

`string` · Default: `"MentorAgent"`

The name of the `ActivitySource` and of the `Meter`: add it with `.AddSource(name).AddMeter(name)`. An exporter that listens on another name receives nothing.

### `ModelPricing`

`IReadOnlyDictionary<string, ModelPrice>?` · Default: `null`

Token prices per model, for the dashboard's cost estimate. MentorAgent ships no prices. Enter them under the model id that the dashboard shows; on Azure OpenAI, that id is the deployment name. See [`ModelPrice`](https://docs.mentoragent.net/reference/options#modelprice).

### `DashboardRole`

`string` · Default: `"Admin"`

The role that `GET /mentor/admin/metrics` of `Novior.MentorAgent.Server` requires; `""` leaves it open, for development only. The `MentorDashboard` component does not check it: protect the page that hosts it.

### `MetricsPersistenceInterval`

`TimeSpan` · Default: `00:00:30`

How often the metrics are saved to a registered `IMentorMetricsStore`; they are also saved on shutdown. Without a store the metrics live in RAM and restart from zero.

### `MetricsRetention`

`TimeSpan` · Default: `7.00:00:00`

How far back the dashboard keeps its hourly time series: seven days.

## `MentorMcpServer`

An entry of [`McpServers`](https://docs.mentoragent.net/reference/options#mcpservers): an MCP server your application connects to, over HTTP or by starting a local process.

### `MentorMcpServer.Name`

`string` · Required

The server's name, shown in the MCP status badge.

### `MentorMcpServer.ServerUrl`

`string?` · Default: `null`

The endpoint of an HTTP MCP server. Set this or `Command`.

### `MentorMcpServer.Command`

`string?` · Default: `null`

The executable of a local server that talks over stdio, for example `npx`. On Windows, script launchers such as `npx` run through `cmd.exe /c` for you.

### `MentorMcpServer.Arguments`

`IList<string>?` · Default: `null`

The command-line arguments of the local process.

### `MentorMcpServer.EnvironmentVariables`

`IDictionary<string, string>?` · Default: `null`

Environment variables for the local process.

### `MentorMcpServer.AllowedTools`

`string[]?` · Default: `null`

The only tools to take from the server. `null` takes them all.

### `MentorMcpServer.RequiresConfirmation`

`bool` · Default: `false`

Asks the user to confirm every call to a tool of this server.

### `MentorMcpServer.Shared`

`bool?` · Default: `null`

Whether one connection serves the whole process. `null` decides by transport: a `ServerUrl` server is shared, a `Command` server gets one process per session. Set `true` only for a local server you know is stateless.

## `MentorHostedMcpServer`

An entry of [`HostedMcpServers`](https://docs.mentoragent.net/reference/options#hostedmcpservers): a remote MCP server the model provider connects to. Your process never talks to it.

### `MentorHostedMcpServer.Name`

`string` · Required

The identifier the provider uses for the server. The model sees it.

### `MentorHostedMcpServer.Url`

`string` · Required

The address of the server. The provider connects to it, so it must be reachable from the provider's network: `localhost` does not work.

### `MentorHostedMcpServer.Description`

`string?` · Default: `null`

A description passed to the provider, for more context.

### `MentorHostedMcpServer.AllowedTools`

`IList<string>?` · Default: `null`

The only tools the provider may call. `null` allows them all; prefer a list for a server you do not control.

### `MentorHostedMcpServer.RequireApproval`

`bool` · Default: `true`

Every call needs the user's approval, shown in the usual confirmation box. Set `false` only for a read-only server you trust.

### `MentorHostedMcpServer.AlwaysRequireApprovalTools`

`IList<string>?` · Default: `null`

Tools that always need approval. This list wins over `RequireApproval`.

### `MentorHostedMcpServer.NeverRequireApprovalTools`

`IList<string>?` · Default: `null`

Tools that never need approval. This list wins over `RequireApproval`.

### `MentorHostedMcpServer.Headers`

`IDictionary<string, string>?` · Default: `null`

HTTP headers that the provider sends to the server, for example an authorization token. They leave your process: use a token with few permissions that expires soon.

## `MentorRemoteAgent`

An entry of [`RemoteAgents`](https://docs.mentoragent.net/reference/options#remoteagents): an A2A agent the coordinator can hand requests to.

A remote agent has no role check of its own: any user of your application can reach it through `route_to_specialist`. To restrict it, do one of these:

- protect the `/a2a` endpoint on the remote host and give roles to the actions there;
- do not configure the agent on hosts whose users may not use it.

The remote host receives the credentials in [`Headers`](https://docs.mentoragent.net/reference/options#mentorremoteagentheaders), and they are the same for every user of your application. So the roles on the remote host limit what your application may do there. They do not limit which of your users may do it.

### `MentorRemoteAgent.Name`

`string` · Required

The agent's name in the handoff graph and in the coordinator's prompt. It must be unique.

### `MentorRemoteAgent.Description`

`string` · Required

What the agent does. The coordinator reads it to decide when to hand a request to it.

### `MentorRemoteAgent.AgentCardUrl`

`string` · Required

The base URL of the agent, without `/.well-known/agent-card.json`: the SDK adds it.

### `MentorRemoteAgent.Headers`

`Dictionary<string, string>?` · Default: `null`

HTTP headers sent with the card request and with every task, for example `Authorization`.

## `AgentCardInfo`

The value of [`AgentCard`](https://docs.mentoragent.net/reference/options#agentcard): extra fields of the card served at `/.well-known/agent-card.json`.

### `AgentCardInfo.Version`

`string` · Default: `"1.0.0"`

The agent version published on the card.

### `AgentCardInfo.IconUrl`

`string?` · Default: `null`

The URL of the agent's icon.

### `AgentCardInfo.Provider`

`string?` · Default: `null`

The provider or organization name.

### `AgentCardInfo.DocumentationUrl`

`string?` · Default: `null`

The URL of the agent's documentation.

### `AgentCardInfo.Tags`

`string[]?` · Default: `null`

Reserved: it is not published on the card.

## `ModelPrice`

A value of [`ModelPricing`](https://docs.mentoragent.net/reference/options#modelpricing), created with `new ModelPrice(InputPer1M, OutputPer1M)`. Prices are in your billing currency, per million tokens.

### `ModelPrice.InputPer1M`

`decimal` · Required

The price of one million input tokens.

### `ModelPrice.OutputPer1M`

`decimal` · Required

The price of one million output tokens.

## `MentorAgentBlazorOptions`

In a Blazor WebAssembly or Auto client, `AddMentorAgentBlazor(options => …)` configures the widget and the connection to the server's hub. The model, the actions and every rule stay in `MentorOptions`, on the server. See [option B of Blazor WebAssembly and Auto](https://docs.mentoragent.net/configure/blazor-webassembly#option-b-run-the-widget-in-webassembly).

### `MentorAgentBlazorOptions.HubUrl`

`string` · Default: `"/mentor-hub"`

The URL of the `Novior.MentorAgent.Server` SignalR hub. A relative value, such as the default, is resolved against the base address of the page, as a browser does: `/mentor-hub` at the root of the origin, and `mentor-hub` under the base path of the app. Use a relative value when the server also serves the client. For a server on another origin, set its absolute URL.

If the URL is absolute, the widget also sends Stop (`/mentor/cancel`) and the answers to confirmations (`/mentor/approve`) to the same origin. If it is relative, they resolve against the `BaseAddress` of the `HttpClient` of the client. The widget sends them through an `HttpClient` that you register: the client project of a Blazor Web App has none.

### `MentorAgentBlazorOptions.AccessTokenProvider`

`Func<Task<string?>>?` · Default: `null`

Returns the bearer token for the hub. It is read on connect and on every reconnect, and sent with every Stop and confirmation request. Without it every user is anonymous to the server: `RequiredRoles` actions are refused, and memory and rate limit follow the server's `AnonymousIdentity`.

### `MentorAgentBlazorOptions.ConfigureConnection`

`Action<HttpConnectionOptions>?` · Default: `null`

Changes the hub's connection options: headers, transports, cookies. It runs after `AccessTokenProvider`.

### `MentorAgentBlazorOptions.BotName`

`string` · Default: `"Mentor AI"`

The name shown in the widget header.

### `MentorAgentBlazorOptions.WelcomeMessage`

`string?` · Default: `null`

The message shown while the chat is empty. It is rendered as HTML, so never build it from user input.

### `MentorAgentBlazorOptions.AvatarUrl`

`string?` · Default: `null`

The URL of the avatar shown in the widget.

### `MentorAgentBlazorOptions.InputPlaceholder`

`string?` · Default: `null`

The placeholder of the input box. `null` uses the text in the language of [`Language`](https://docs.mentoragent.net/reference/options#mentoragentblazoroptionslanguage).

### `MentorAgentBlazorOptions.Theme`

`MentorTheme` · Default: `Default`

The widget's theme, as [`Theme`](https://docs.mentoragent.net/reference/options#theme) on the server.

### `MentorAgentBlazorOptions.Position`

`ChatPosition` · Default: `BottomRight`

Where the widget sits, as [`Position`](https://docs.mentoragent.net/reference/options#position) on the server.

### `MentorAgentBlazorOptions.PrimaryColor`

`string?` · Default: `null`

An accent color in hex.

### `MentorAgentBlazorOptions.Language`

`MentorLanguage` · Default: `English`

The language of the widget's texts. The language of the replies is [`Language`](https://docs.mentoragent.net/reference/options#language) on the server.

### `MentorAgentBlazorOptions.EnableVoiceInput`

`bool` · Default: `false`

Shows the microphone button.

### `MentorAgentBlazorOptions.EnableVoiceOutput`

`bool` · Default: `false`

Reads replies aloud.

### `MentorAgentBlazorOptions.VoiceStreaming`

`bool` · Default: `true`

Speaks each sentence as it streams.

### `MentorAgentBlazorOptions.VoiceBargeIn`

`bool` · Default: `true`

Stops the spoken answer when the user acts, as [`VoiceBargeIn`](https://docs.mentoragent.net/reference/options#voicebargein) on the server.

### `MentorAgentBlazorOptions.VoiceHandsFree`

`bool` · Default: `false`

Lets the user talk without touching the widget, as [`VoiceHandsFree`](https://docs.mentoragent.net/reference/options#voicehandsfree) on the server. Ignored unless both voice options are on.

### `MentorAgentBlazorOptions.VoiceRate`

`double` · Default: `1.0`

The speech rate; the useful range is about 0.5 to 2.0.

### `MentorAgentBlazorOptions.EnableSuggestions`

`bool` · Default: `false`

Shows suggestion chips in the welcome panel. Off by default here, unlike the server's option.

### `MentorAgentBlazorOptions.EnableOnboardingTour`

`bool` · Default: `false`

Shows the guided tour on first open, with the steps fetched from `TourUrl`. The server answers `GET /mentor/tour` even when its own `EnableOnboardingTour` is `false`.

### `MentorAgentBlazorOptions.TourUrl`

`string` · Default: `"/mentor/tour"`

Where the tour steps come from. A relative value is resolved against the origin of `HubUrl` when that is absolute, otherwise against the app's base address.

### `MentorAgentBlazorOptions.EnableImageInput`

`bool` · Default: `false`

Shows the image attachment controls. The server's `EnableImageInput` must be on too: that is where images are checked.

### `MentorAgentBlazorOptions.MaxImageBytes`

`int` · Default: `4194304`

The size cap per image in the client, 4 MB. Keep it equal to the server's option.

### `MentorAgentBlazorOptions.MaxImagesPerMessage`

`int` · Default: `4`

The cap on images per message in the client. Keep it equal to the server's option.

### `MentorAgentBlazorOptions.AllowedImageTypes`

`List<string>` · Default: `["image/png", "image/jpeg", "image/gif", "image/webp"]`

The MIME allow-list in the client. Keep it equal to the server's option.

### `MentorAgentBlazorOptions.ShowRagSources`

`bool` · Default: `false`

Shows citation chips under the agent's replies.

### `MentorAgentBlazorOptions.ShowHostedToolsStatus`

`bool` · Default: `false`

Shows the badge of the active hosted tools.

### `MentorAgentBlazorOptions.HostedTools`

`MentorHostedTools` · Default: `None`

Leave it unset. The widget uses it only until the connection is up; then the server sends the hosted tools it really has, and the widget uses those.

### `MentorAgentBlazorOptions.ShowMcpStatus`

`bool` · Default: `false`

Shows the MCP status badge.

### `MentorAgentBlazorOptions.HasMcpServers`

`bool` · Default: `false`

Tells the widget that the server has MCP servers, which enables the MCP badge.

### `MentorAgentBlazorOptions.McpServerNames`

`List<string>` · Default: empty

The names of the server's MCP servers, shown as connecting until their status arrives.

### `MentorAgentBlazorOptions.ShowA2AStatus`

`bool` · Default: `false`

Shows the badge of the remote A2A agents.

### `MentorAgentBlazorOptions.HasRemoteAgents`

`bool` · Default: `false`

Tells the widget that the server has remote agents, which enables the A2A badge.

### `MentorAgentBlazorOptions.RemoteAgentDisplays`

`List<AgentDisplayInfo>` · Default: empty

The remote agents listed in the A2A badge, one [`AgentDisplayInfo`](https://docs.mentoragent.net/reference/options#agentdisplayinfo) each.

## `AgentDisplayInfo`

A value of [`RemoteAgentDisplays`](https://docs.mentoragent.net/reference/options#mentoragentblazoroptionsremoteagentdisplays): a remote agent as the WebAssembly widget shows it in the A2A detail bar. It only drives the display; the remote agents themselves are configured on the server, in [`RemoteAgents`](https://docs.mentoragent.net/reference/options#remoteagents).

### `AgentDisplayInfo.Name`

`string` · Default: `""`

The agent's name in the detail bar.

### `AgentDisplayInfo.AgentCardUrl`

`string?` · Default: `null`

The address shown next to the name, usually the agent's base URL, as in [`MentorRemoteAgent.AgentCardUrl`](https://docs.mentoragent.net/reference/options#mentorremoteagentagentcardurl).

## `MentorDeclarativeOptions`

`AddMentorAgentDeclarative(o => …)` tells `Novior.MentorAgent.Declarative` where to find agents written in YAML. A definition chooses the model, writes the instructions and names the tools, so treat it as code. See [Specialists in YAML](https://docs.mentoragent.net/concepts/agent-levels#specialists-in-yaml).

### `MentorDeclarativeOptions.Directory`

`string?` · Default: `null`

The folder to scan, absolute or relative to the application's base directory. Use a path that ships with the application, never an upload folder or a path built from a request.

### `MentorDeclarativeOptions.SearchPattern`

`string` · Default: `"*.agent.yaml"`

The file pattern inside `Directory`.

### `MentorDeclarativeOptions.Recursive`

`bool` · Default: `false`

Scans the sub-folders too.

### `MentorDeclarativeOptions.Definitions`

`IList<string>` · Default: empty

YAML definitions passed as text, for example from a database. They load before the files of `Directory`. Add to the list: it cannot be replaced.

### `MentorDeclarativeOptions.ConfigurationSection`

`string?` · Default: `null`

The configuration section whose values a definition may reference. `null` exposes no configuration. Name one section, never the whole configuration: a single key that is not a valid Power Fx name stops every definition from loading.

---

- Previous: [Attributes](https://docs.mentoragent.net/reference/attributes.md)
- Next: [Glossary](https://docs.mentoragent.net/reference/glossary.md)
