# Model providers

> Connect OpenAI, Azure OpenAI, Ollama and other local models, or your own AIAgent, and choose models that support tool calling.

- URL: https://docs.mentoragent.net/configure/model-providers
- Verified with: MentorAgent 1.0.0
- Last updated: 2026-10-14
- Packages: Novior.MentorAgent

This page shows how to connect MentorAgent to a model: OpenAI, Azure OpenAI, Ollama and other local models, or an agent you build with Agent Framework, such as a Foundry agent. MentorAgent calls the model through `Microsoft.Extensions.AI`. In `AddMentorAgent` you give it one of two things: an `IChatClient`, which is Path A, or an Agent Framework `AIAgent` that you have already built, which is Path B.

## Choose a path

Use **Path A**, `options.ChatClient`, whenever the provider has an `IChatClient`: OpenAI, Azure OpenAI, Ollama and any server with an OpenAI-compatible API. MentorAgent builds the agent and every feature works.

Use **Path B**, `options.Agent`, for a provider you reach only through an `AIAgent`. Two examples are a Foundry project, with `Microsoft.Agents.AI.Foundry`, and Anthropic's Claude models, with `Microsoft.Agents.AI.Anthropic`: you add these Agent Framework packages yourself. On Path B the agent's instructions are yours, and some features need a `ChatClient` that MentorAgent can call itself:

| Feature | Path A: `ChatClient` | Path B: `Agent` |
|---|---|---|
| Actions, MCP client tools, RAG, Agent Skills | Yes | Yes |
| Specialists, teams, remote A2A agents, YAML agents | Yes | No |
| UI actions of the current page | One tool per action | One generic `invoke_ui_action` tool |
| Safety check | Yes | Only with `ClassifierChatClient` |
| Model routing, tool filtering, [hosted tools](https://docs.mentoragent.net/reference/options#hosted-tools), structured outputs | Yes | No |
| Memory capture after each turn | Yes | Only with `ClassifierChatClient`; the `remember` tool stays |
| Chat history | Set through `MentorOptions` | Set on your agent |

Set `ChatClient` or `Agent`, never both. Whatever the path, the model must support tool calling: every action is a tool.

## Install the packages

**.NET CLI**

```shell
dotnet add package Novior.MentorAgent
```

**PackageReference**

```xml title="YourApp.csproj"
<ItemGroup>
  <PackageReference Include="Novior.MentorAgent" Version="1.0.0" />
</ItemGroup>
```

**Central Package Management**

```xml title="Directory.Packages.props"
<ItemGroup>
  <PackageVersion Include="Novior.MentorAgent" Version="1.0.0" />
</ItemGroup>
```

```xml title="YourApp.csproj"
<ItemGroup>
  <PackageReference Include="Novior.MentorAgent" />
</ItemGroup>
```

**File-based app**

```csharp title="app.cs"
#:package Novior.MentorAgent@1.0.0
```

`Novior.MentorAgent` brings no model provider package. Add the ones that your provider needs:

| Provider | Packages to add |
|---|---|
| OpenAI, and Ollama through its OpenAI-compatible endpoint | `Microsoft.Extensions.AI.OpenAI`. It brings the `OpenAI` SDK and the `AsIChatClient()` method |
| Azure OpenAI | `Azure.AI.OpenAI` and `Microsoft.Extensions.AI.OpenAI` |
| Azure OpenAI with Microsoft Entra ID | `Azure.Identity` as well, for `DefaultAzureCredential` |
| A Foundry project (Path B) | `Microsoft.Agents.AI.Foundry`: see [Foundry agents (Path B)](https://docs.mentoragent.net/configure/model-providers#foundry-agents-path-b) |
| Anthropic (Path B) | `Microsoft.Agents.AI.Anthropic` |

`Azure.AI.OpenAI` 2.9.0-beta.1 is a prerelease, so `dotnet add package` needs a version for it. The examples on this page are compiled with these versions:

```shell
dotnet add package Microsoft.Extensions.AI.OpenAI --version 10.6.0
dotnet add package Azure.AI.OpenAI --version 2.9.0-beta.1
dotnet add package Azure.Identity --version 1.21.0
```

`Novior.MentorAgent` brings `System.ClientModel`, so `ApiKeyCredential` needs no extra package. How the versions fit together, and the one combination that needs a pin, is in [Provider package versions](https://docs.mentoragent.net/get-started/prerequisites#provider-package-versions).

## Minimal configuration

Every example on this page is the model line inside `AddMentorAgent(options => …)`. The rest of the configuration stays as on the page of your host, such as [Blazor Server app](https://docs.mentoragent.net/configure/blazor-server#minimal-configuration) or [Any front end](https://docs.mentoragent.net/configure/any-front-end#minimal-configuration). These `using` directives cover all the examples. Keep `Azure.AI.Projects` only for a Foundry project: it comes with the Foundry package, not with MentorAgent:

```csharp title="Program.cs"
using System.ClientModel;             // ApiKeyCredential (Ollama)
using Azure;                          // AzureKeyCredential
using Azure.AI.OpenAI;                // AzureOpenAIClient
using Azure.AI.Projects;              // AIProjectClient (Foundry): only with Microsoft.Agents.AI.Foundry
using Azure.Identity;                 // DefaultAzureCredential
using Microsoft.Extensions.AI;        // AsIChatClient, AsIEmbeddingGenerator
using OpenAI;                         // OpenAIClient
```

With OpenAI, keep the key in user secrets and set the client:

```shell
dotnet user-secrets set OpenAI:Key <your-key>
```

```csharp title="Program.cs"
options.ChatClient = new OpenAIClient(builder.Configuration["OpenAI:Key"]
        ?? throw new InvalidOperationException("Set OpenAI:Key with dotnet user-secrets."))
    .GetChatClient("gpt-4o-mini")
    .AsIChatClient();
```

## Complete configuration

This section connects the other providers: Azure OpenAI, Ollama and other local models, and Foundry agents. It also adds an embedding model and lists the options that matter on this page.

### Azure OpenAI

With an API key, read the endpoint and the key from configuration. The model name is the name of your deployment:

```csharp title="Program.cs"
options.ChatClient = new AzureOpenAIClient(
        new Uri(builder.Configuration["AzureOpenAI:Endpoint"]!),
        new AzureKeyCredential(builder.Configuration["AzureOpenAI:Key"]!))
    .GetChatClient("gpt-4o-mini")   // the deployment name
    .AsIChatClient();
```

```json
{
  "AzureOpenAI": {
    "Endpoint": "https://<your-resource>.openai.azure.com/"
  }
}
```

Keep the key in user secrets while you develop:

```shell
dotnet user-secrets set AzureOpenAI:Key <your-key>
```

Microsoft Entra ID needs no key. `DefaultAzureCredential` uses the managed identity when the app runs in Azure, and your developer sign-in on your machine. Give that identity access to the Azure OpenAI resource:

```csharp title="Program.cs"
options.ChatClient = new AzureOpenAIClient(
        new Uri(builder.Configuration["AzureOpenAI:Endpoint"]!),
        new DefaultAzureCredential())   // managed identity in Azure, your developer sign-in locally
    .GetChatClient("gpt-4o-mini")
    .AsIChatClient();
```

The Responses API needs one pin on the packages. `Azure.AI.OpenAI` 2.9.0-beta.1 calls a `ResponsesClient` constructor that `OpenAI` 2.11 removed. With `OpenAI` 2.11 or later, `GetResponsesClient()` throws `MissingMethodException` when your code calls it, although the build succeeds. Keep `OpenAI` at 2.10 by pinning `Microsoft.Extensions.AI.OpenAI` to `[10.6.0]`, as in [Provider package versions](https://docs.mentoragent.net/get-started/prerequisites#provider-package-versions). Chat Completions needs no pin. OpenAI and Ollama need none either.

With the Responses API the service keeps the conversation, so MentorAgent must not keep its own history, and it installs no compaction either:

```csharp title="Program.cs"
// The Responses client is experimental in the OpenAI SDK: without the pragma the build fails with OPENAI001
#pragma warning disable OPENAI001
options.ChatClient = new AzureOpenAIClient(
        new Uri(builder.Configuration["AzureOpenAI:Endpoint"]!),
        new DefaultAzureCredential())
    .GetResponsesClient()
    .AsIChatClient("gpt-4o-mini");   // the deployment name goes here
#pragma warning restore OPENAI001

// The service keeps the conversation: MentorAgent must not keep its own history
options.UseServiceManagedHistory = true;
```

### Ollama and other local models

Ollama exposes an OpenAI-compatible endpoint, so the OpenAI client talks to it:

```csharp title="Program.cs"
// Ollama's OpenAI-compatible endpoint: same SDK, no extra package
options.ChatClient = new OpenAIClient(
        new ApiKeyCredential("ollama"),                 // Ollama ignores it; the SDK requires one
        new OpenAIClientOptions
        {
            Endpoint = new Uri("http://localhost:11434/v1"),
            NetworkTimeout = TimeSpan.FromMinutes(20),   // a CPU model can take minutes on a long prompt
        })
    .GetChatClient("llama3.2")
    .AsIChatClient();

// The safety checks share the slow model: give them time, or point ClassifierChatClient at a faster one
options.SafetyCheckTimeout = TimeSpan.FromMinutes(2);
```

- **Tool calling.** Pick a model that supports tools. A model without them answers, but never runs your actions.
- **Context window.** MentorAgent's prompt can be longer than [Ollama](https://ollama.com)'s default context, and Ollama cuts the start of the prompt without an error. Create a variant with a larger context and use its name in `GetChatClient`:

  ```text title="Modelfile"
  FROM llama3.2
  PARAMETER num_ctx 16384
  ```

  ```shell
  ollama create llama3.2-16k -f Modelfile
  ```

- **Time.** A model on a CPU can take minutes on a long prompt: that is what `NetworkTimeout` in the snippet is for. It raises the HTTP time limit of the OpenAI client. The safety checks run on the same model, so give them time with `SafetyCheckTimeout`, or point `ClassifierChatClient` at a faster model.
- **Other servers.** A server with the OpenAI-compatible Chat Completions API is used the same way: change `Endpoint` and the model name.

### Foundry agents (Path B)

A Foundry project gives you an `AIAgent`. `Novior.MentorAgent` does not bring Agent Framework's Foundry package, so add `Microsoft.Agents.AI.Foundry` to your project. If the same project uses the Azure OpenAI Responses API, pin the Foundry package to this version: a newer one brings `OpenAI` 2.11 or later. The example on this page is compiled with this version:

```xml
<PackageReference Include="Microsoft.Agents.AI.Foundry" Version="[1.17.0-preview.260804.1]" />
```

Then build the agent and set `Agent` instead of `ChatClient`:

```csharp title="Program.cs"
// Path B: a pre-built AIAgent instead of a ChatClient
options.Agent = new AIProjectClient(
        new Uri(builder.Configuration["Foundry:ProjectEndpoint"]!),
        new DefaultAzureCredential())
    .AsAIAgent(model: "gpt-4o-mini", instructions: "You are the assistant of a case management application.");
```

On Path B the instructions you pass describe the agent's role and tone. MentorAgent adds the page, the user and your actions around them, but not its own coordinator prompt. The agent must be a `ChatClientAgent`, or another agent whose chat client invokes functions (`FunctionInvokingChatClient`), because that is where MentorAgent checks roles and confirmation. MentorAgent refuses any other agent when it builds the coordinator.

### Embedding model

An embedding model turns a text into a list of numbers that represents its meaning, so MentorAgent can find the texts closest to a message. It makes semantic tool filtering, semantic memory and semantic model routing possible. It uses a separate deployment:

```csharp title="Program.cs"
options.EmbeddingGenerator = new AzureOpenAIClient(
        new Uri(builder.Configuration["AzureOpenAI:Endpoint"]!),
        new DefaultAzureCredential())
    .GetEmbeddingClient("text-embedding-3-small")   // a separate embedding deployment
    .AsIEmbeddingGenerator();
```

Setting it switches nothing on. Each feature has its own option: `EnableToolFiltering`, `MemoryRelevanceFiltering` and `RoutingStrategy = MentorRoutingStrategy.Semantic`. If you turn one on without an embedding model, MentorAgent logs a warning and the feature stays off. [`RequireLookupForRecordQuestions`](https://docs.mentoragent.net/reference/options#requirelookupforrecordquestions) and [`RequireActionForChangeRequests`](https://docs.mentoragent.net/reference/options#requireactionforchangerequests) need it too, through tool filtering.

> **Important:**
> Tool filtering recognizes an action that changes data by its confirmation. When no action is relevant enough, it sends the three best-ranked actions that need no confirmation and no role. When every action it selects needs confirmation, it also sends up to three that do not. An action without confirmation can therefore reach the model on any message. A `[MentorAction]` that changes data asks for confirmation by default, so it stays out of that group. A write that declares `RequiresConfirmation = false`, and a method with only `[Description]`, do not: see [`EnableToolFiltering`](https://docs.mentoragent.net/reference/options#enabletoolfiltering).

### Options that matter here

| Option | What it does |
|---|---|
| [`ChatClient`](https://docs.mentoragent.net/reference/options#chatclient) | Path A: the model MentorAgent builds its agent on |
| [`Agent`](https://docs.mentoragent.net/reference/options#agent) | Path B: a pre-built `AIAgent`. Never together with `ChatClient` |
| [`EmbeddingGenerator`](https://docs.mentoragent.net/reference/options#embeddinggenerator) | The embedding model for filtering, memory and routing |
| [`ClassifierChatClient`](https://docs.mentoragent.net/reference/options#classifierchatclient) | The model for MentorAgent's own short decisions: safety checks, the off-topic check (`RefuseOutOfScope`), the facts to remember, and the question behind the two `Require…` options. It defaults to `ChatClient`; a small, fast model shortens the wait before each answer |
| [`StrongChatClient`](https://docs.mentoragent.net/reference/options#strongchatclient), [`RoutingStrategy`](https://docs.mentoragent.net/reference/options#routingstrategy) | A stronger model for the hard questions: see [Cost and model routing](https://docs.mentoragent.net/configure/production#cost-and-model-routing) |
| [`UseServiceManagedHistory`](https://docs.mentoragent.net/reference/options#useservicemanagedhistory) | Path A with a client whose service keeps the conversation, such as the Responses API |
| [`SafetyCheckTimeout`](https://docs.mentoragent.net/reference/options#safetychecktimeout), [`SafetyCheckFailure`](https://docs.mentoragent.net/reference/options#safetycheckfailure) | How long the safety checks may take, and what happens when they give no answer |
| [`ConfigureChatClientPipeline`](https://docs.mentoragent.net/reference/options#configurechatclientpipeline) | Adds your own `IChatClient` middleware to the Path A pipeline |

Every option is in [Configuration options](https://docs.mentoragent.net/reference/options).

## Security and roles

- **Keys stay on the server.** Never put a key in the source code or in a client project. Use user secrets while you develop, and environment variables or a secret store in production. On Azure, Entra ID with a managed identity removes the key altogether.
- **The provider does not decide what runs.** Roles and confirmation are checked by MentorAgent around each action, whatever the model and whatever the path: see [Roles, confirmation and governance](https://docs.mentoragent.net/concepts/governance).
- **Data leaves with the prompt.** The page context, the user's name and roles, memories, retrieved documents and action results are sent to the provider. A local model keeps them on your machine. [Data that leaves your application](https://docs.mentoragent.net/configure/production#data-that-leaves-your-application) lists everything that goes out.

## Common errors

- **The build does not find `OpenAIClient`, `AzureOpenAIClient` or `AsIChatClient`.** The provider packages are not in your project, because `Novior.MentorAgent` does not include a model provider package. Add them, as in [Install the packages](https://docs.mentoragent.net/configure/model-providers#install-the-packages).
- **`GetResponsesClient()` throws `MissingMethodException` when it is called.** `OpenAI` resolved to 2.11 or later. Pin `Microsoft.Extensions.AI.OpenAI` as in [Azure OpenAI](https://docs.mentoragent.net/configure/model-providers#azure-openai).
- **The build does not find `AIProjectClient` or the `Azure.AI.Projects` namespace.** The Foundry package is not in your project, because `Novior.MentorAgent` does not include it. Add `Microsoft.Agents.AI.Foundry` with the pinned version, as in [Foundry agents (Path B)](https://docs.mentoragent.net/configure/model-providers#foundry-agents-path-b).
- **The build stops with `OPENAI001` on `GetResponsesClient`.** The Responses client is experimental in the OpenAI SDK: keep the `#pragma` around it.
- **Every turn fails with "Only ConversationId or ChatHistoryProvider may be used, but not both".** The client keeps the conversation on the service: set `UseServiceManagedHistory = true`.
- **The app stops at startup with "[MentorAgent] Set ChatClient OR Agent, not both." or "[MentorAgent] Configure ChatClient or Agent."** Set exactly one of them. MentorAgent checks the options when the host starts. In a .NET MAUI app, which has no host start, the error comes the first time MentorAgent is used.
- **The widget or your own client shows "The assistant could not start", and the log says "The pre-built agent in options.Agent … cannot carry MentorAgent's tool gate".** The agent does not use a chat client that invokes functions (`FunctionInvokingChatClient`). Build it from a chat client, or use Path A.
- **The model answers but never calls your actions.** It does not support tool calling, or, on Ollama, the context window cut the prompt.
- **The safety check is skipped with a timeout warning.** The model is too slow for the default limit: raise `SafetyCheckTimeout` or set a faster `ClassifierChatClient`.

---

- Previous: [Channels: how clients reach the agent](https://docs.mentoragent.net/concepts/channels.md)
- Next: [Blazor Server app](https://docs.mentoragent.net/configure/blazor-server.md)
