MentorAgent Docs
Menu

How-to guidePackage: Novior.MentorAgent

Add MentorAgent to an existing app

Add MentorAgent to a Blazor Web App you already have: install the package, register the services, mark your methods and place the widget.

View as Markdown
On this page

Use this page when you have a Blazor Web App and want an agent that calls methods your app already has. For a new app, follow the Quickstart instead. If your front end is React, Angular, Vue or a mobile app with an ASP.NET Core backend, follow Any front end: the actions you mark are the same.

  • Your app is a Blazor Web App on .NET 10. In Components/App.razor, <Routes> has @rendermode="InteractiveServer", or it has no render mode and each page or component chooses its own. Prerequisites lists the other versions you need.
  • If <Routes> uses InteractiveAuto or InteractiveWebAssembly, do not follow this page. Follow option B of Blazor WebAssembly and Auto instead.
  • You need the package of your model provider. MentorAgent does not include one. If you use Azure OpenAI with the Responses API, read Provider package versions first.

In the project that hosts the app (the server project, if the solution has a client project too):

.NET CLI

Terminal
dotnet add package Novior.MentorAgent

PackageReference

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

Central Package Management

Directory.Packages.props
<ItemGroup>
  <PackageVersion Include="Novior.MentorAgent" Version="1.0.0" />
</ItemGroup>
YourApp.csproj
<ItemGroup>
  <PackageReference Include="Novior.MentorAgent" />
</ItemGroup>

File-based app

app.cs
#:package Novior.MentorAgent@1.0.0

Then add the package of your model provider. The code below uses OpenAI:

Terminal
dotnet add package Microsoft.Extensions.AI.OpenAI --version 10.6.0

For another provider, see Model providers.

In Program.cs, add the using directives:

Program.cs
using Novior.MentorAgent.Extensions;
using Microsoft.Extensions.AI;
using OpenAI;

Then, before builder.Build(), register your service and MentorAgent:

Program.cs
builder.Services.AddScoped<CaseService>();
builder.Services.AddMentorAgent(options =>
{
    options.AppName = "Case management";
    options.AppDescription = "Tracks customer support cases, their status and their notes";
    options.ChatClient = new OpenAIClient(
            builder.Configuration["OpenAI:Key"] ?? throw new InvalidOperationException(
                "OpenAI:Key is missing: run dotnet user-secrets set OpenAI:Key <your-key>"))
        .GetChatClient("gpt-4o-mini")
        .AsIChatClient();
    // Every assembly that contains actions, agents or pages
    options.ScanAssemblies = [typeof(CaseService).Assembly];
});
  • Register every service whose methods you expose, as you register any service. MentorAgent gets the instance from dependency injection. A method whose service is not registered is left out, with a warning in the log.
  • ScanAssemblies lists the assemblies to search for actions, agents and pages. If your services live in a class library, list that library, and list the web project too if its pages use [MentorPage]. List each assembly once: a repeated entry is ignored, with a warning.
  • AppDescription is optional. It tells the model what your application is for, so give it a real sentence.
  • The key comes from configuration. In development, keep it in user secrets. Run dotnet user-secrets init if the project has no user secrets yet, then dotnet user-secrets set OpenAI:Key <your-key>. For other providers, see Model providers.

Put [MentorAction] on public methods of a service. It comes from Novior.MentorAgent.Attributes.

CaseService.cs
public class CaseService(ICaseRepository cases)
{
    [MentorAction(
        Description = "Gets the status of a support case",
        ReadOnly = true)]
    public Task<string> GetCaseStatusAsync(string caseNumber)
        => cases.GetStatusAsync(caseNumber);

    [MentorAction(
        Description = "Adds a note to a support case",
        RequiresConfirmation = true)]
    public Task AddNoteAsync(string caseNumber, string text)
        => cases.AddNoteAsync(caseNumber, text);
}
  • A method that only reads declares ReadOnly = true. The agent calls it without asking.
  • A method that changes data asks the user to confirm before it runs. A [MentorAction] that is not read-only asks by default. RequiresConfirmation = true states the rule in your code.
  • A method with [Description] alone has no rules. It never asks, and anyone who reaches the agent can call it. Do not use it for a method that changes data.
  • The tool name is the method name in snake case, without a trailing Async: GetCaseStatusAsync becomes get_case_status. Options that refer to a tool use this name.
  • Only public instance methods declared on the class itself count. Static, private and inherited methods are ignored.

The attributes can go on the service your pages already use, or on a small class that calls your existing code, as CaseService does here with ICaseRepository. A separate class lets you choose exactly what the agent sees.

Add the widget's namespace to Components/_Imports.razor:

Components/_Imports.razor
@using Novior.MentorAgent.Abstractions.Components

Where the widget goes depends on the render mode of your app. Open Components/App.razor and look at <Routes>:

Global Server interactivity

<Routes> has @rendermode="InteractiveServer", so every page and the layout are interactive. Put the widget at the end of Components/Layout/MainLayout.razor:

Components/Layout/MainLayout.razor
<ChatWidget />

Interactivity per page or component

<Routes> has no @rendermode: each page or component chooses its own, and the layout renders statically. Pin the widget to the server. To do this, give it its own render mode, InteractiveServer, at the end of Components/Layout/MainLayout.razor:

Components/Layout/MainLayout.razor
<ChatWidget @rendermode="InteractiveServer" />

The widget is interactive on every page, including pages that render statically or run in WebAssembly. A page that hands data to the agent through IMentorPageContext must be interactive itself.

If your app was created with WebAssembly interactivity, it has no Server interactivity yet. With the pinned widget, every page then answers HTTP 500. Add Server interactivity in Program.cs, as described in Blazor WebAssembly and Auto.

If <Routes> uses InteractiveAuto or InteractiveWebAssembly, the layout comes from the client project and can run in the browser. MentorAgent cannot run in the browser, so a widget that runs on the server fails in that layout. Follow option B of Blazor WebAssembly and Auto instead.

Run the app, open a page and click the widget button. Ask a question that one of your read methods answers, then ask for a change and check that the confirmation box appears before the method runs.

If the agent answers without calling your method, see Troubleshooting.

MentorAgent reads the signed-in user through the AuthenticationStateProvider your app already has: nothing else needs configuring. With a signed-in user:

  • The prompt includes the user's name and roles, so the model knows who it is talking to.

  • Roles. RequiredRoles restricts an action to users who hold at least one of the roles, checked with ClaimsPrincipal.IsInRole (the ClaimTypes.Role claims, by default):

    CaseSupervisorService.cs
    [MentorAction(
        Description = "Moves a support case to another team",
        RequiresConfirmation = true,
        RequiredRoles = ["Supervisors"])]
    public Task ReassignCaseAsync(string caseNumber, string team)
        => cases.ReassignAsync(caseNumber, team);

    A signed-out user, or one without the role, is refused before any confirmation box appears. Register this service too.

  • Memory and the per-user rate limit, when you turn them on, are kept for each user. MentorAgent identifies the user by the ClaimTypes.NameIdentifier claim. If it is missing, it uses the sub claim, then the oid claim, then the user name. Without sign-in, memory and the rate limit treat each session as a separate anonymous user.

With ASP.NET Core Identity, roles reach the claims only when role support is registered: the Blazor template's AddIdentityCore<ApplicationUser>() needs .AddRoles<IdentityRole>(). See Role-based authorization (external site).

Roles, confirmation and governance explains the checks, and 2 · Require roles and confirmation tries them step by step.

Configure MentorAgent for the way you use it:

  • Blazor Server app — Configure MentorAgent in a Blazor Web App with Server interactivity: services, the options that matter, the widget and authentication.
  • Roles, confirmation and governance — How MentorAgent checks roles and asks for confirmation before an action runs, on every channel, and what it leaves to your code.