MentorAgent Docs
Menu

Questa pagina non è ancora tradotta in italiano: qui la trovi in inglese.

TutorialTime to complete: 15 minPackage: Novior.MentorAgent

2. Require roles and confirmation

Tutorial step 2: restrict actions to roles and ask the user to confirm before the agent changes data, then try both cases.

In questo tutorial:

  • Restrict the actions that change data with RequiredRoles
  • Keep a confirmation on every change with RequiresConfirmation
  • Sign in as a user with the role and as one without, and compare
  • Tell what the model receives for a user without the role
On this page

At this point, anyone who reaches the widget can add a note. In this step you decide who may change a case. The app gets two roles for its support staff. The role Agents is for people, not for AI agents. Agents may add notes, and Supervisors may also close cases. Every change still asks for confirmation.

Before you start: finish 1. Turn service methods into actions.

Replace the content of CaseService.cs. The two reads stay as they are. AddNote gets roles, and a new action closes a case:

CaseService.cs
using System.ComponentModel;
using Novior.MentorAgent.Attributes;

public class CaseService(CaseStore store)
{
    [MentorAction(
        Description = "Gets a support case: its status, priority and notes",
        ReadOnly = true)]
    public SupportCase? GetCase(
        [Description("The case number, for example C-1")] string caseNumber)
        => store.Find(caseNumber);

    [MentorAction(
        Description = "Lists support cases, optionally by status",
        ReadOnly = true)]
    public IReadOnlyList<SupportCase> ListCases(
        [Description("Open, Waiting for customer or Closed; empty for all")]
        string? status = null)
        => store.List(status);

    [MentorAction(
        Description = "Adds a note to a support case",
        RequiresConfirmation = true,
        RequiredRoles = ["Agents", "Supervisors"])]
    public string AddNote(string caseNumber, string text)
    {
        store.AddNote(caseNumber, text);
        return $"Note added to case {caseNumber}.";
    }

    [MentorAction(
        Description = "Closes a support case with a resolution",
        RequiresConfirmation = true,
        RequiredRoles = ["Supervisors"])]
    public string CloseCase(string caseNumber, string resolution)
    {
        store.Close(caseNumber, resolution);
        return $"Case {caseNumber} closed.";
    }
}
  • RequiredRoles lists the roles that may run the action. Holding any one of them is enough: AddNote is for agents and supervisors, CloseCase only for supervisors.
  • RequiresConfirmation = true makes both actions ask the user to confirm. The role decides who may act. The confirmation asks the user, every time, whether to go ahead. A [MentorAction] that is not read-only asks for confirmation by default, even without this setting. Writing it makes the rule visible in your code.
  • CloseCase becomes the tool close_case.
  • The reads have no rules, so anyone who reaches the widget can call them. Protect reads the same way when the data is not for everyone.

The Cases app has no sign-in yet, so you add a simple one for this tutorial: a cookie and a test endpoint. MentorAgent checks roles with ClaimsPrincipal.IsInRole on the signed-in user, which it takes from the AuthenticationStateProvider that Blazor already has. By default, IsInRole reads the ClaimTypes.Role claims. In a real app, users sign in with ASP.NET Core Identity, Microsoft Entra ID or another scheme.

  1. At the top of Program.cs, add the using directives:

    Program.cs
    using System.Security.Claims;
    using Microsoft.AspNetCore.Authentication;
    using Microsoft.AspNetCore.Authentication.Cookies;
  2. Before builder.Build(), register cookie authentication:

    Program.cs
    builder.Services
        .AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
        .AddCookie();
  3. After var app = builder.Build();, add the test endpoints:

    Program.cs
    if (app.Environment.IsDevelopment())
    {
        // Tutorial only: sign in as a test user, with or without a role
        app.MapGet("/dev/sign-in", async (
            HttpContext http, string name, string? role) =>
        {
            List<Claim> claims = [new(ClaimTypes.Name, name)];
            if (!string.IsNullOrEmpty(role))
                claims.Add(new(ClaimTypes.Role, role));
    
            var scheme = CookieAuthenticationDefaults.AuthenticationScheme;
            await http.SignInAsync(
                new ClaimsPrincipal(new ClaimsIdentity(claims, scheme)));
            return Results.Redirect("/");
        });
    
        app.MapGet("/dev/sign-out", async (HttpContext http) =>
        {
            await http.SignOutAsync();
            return Results.Redirect("/");
        });
    }

How the test sign-in works:

  • /dev/sign-in?name=Alex&role=Agents signs you in as Alex, with the role Agents. Leave out role to sign in with no role, and open /dev/sign-out to sign out.
  • ASP.NET Core adds the authentication middleware by itself when authentication is registered, so the rest of the pipeline stays as it is.
  • Each endpoint redirects to the home page. The page loads again, and the widget works for the new user.

Run the app and try the same requests as three different users. To sign in as a user, add a path such as /dev/sign-in?name=Alex&role=Agents to the address of the app in the browser.

  1. Type "Close case C-2, it is a duplicate of C-1". No confirmation box appears. The agent tells you that it cannot do that here, and that signing in may help. The case stays open.
  1. Open /dev/sign-in?name=Alex&role=Agents.
  2. Type "Add a note to C-2: same issue as C-1". The confirmation box appears. Click Confirm, and the note is added.
  3. Type "Close case C-2, it is a duplicate of C-1". No box appears: the agent tells you that it cannot close cases for you. Its exact words vary.
  1. Open /dev/sign-in?name=Sam&role=Supervisors.
  2. Ask again to close C-2. The confirmation box shows the case number and the resolution.
  3. Click Cancel. The method does not run, and the agent tells you in one sentence that it did not proceed.
  4. Ask once more and click Confirm. Case C-2 is closed.
  • Only the tools the user may run. At the start of every turn, MentorAgent checks the roles of each action against the user. For Alex, close_case is left out of the tool list, and the model is told that some actions are not available, without naming them or their roles. So the model cannot offer to close the case.
  • Who is asking. For a signed-in user, the model also receives the name and the roles. For a user who is not signed in, it receives nothing about the user.
  • A refusal if the model calls it anyway. A model can still call a tool that it was not given, for example by repeating an earlier turn. The check runs again in your app when the tool is called: the method does not run, no box appears, and the model receives an access-denied error that names the required roles, or says that the user must sign in.
  • A cancellation. When the user clicks Cancel, the method does not run. The model is told that the user declined and that nothing was checked or changed. It is asked not to try again, not to give another reason, and not to describe the state of any record.

The order never changes: first the role, then the confirmation, then your method. MentorAgent reads the roles once more after the user confirms, so a user who lost the role while the box was open is refused.

The coordinator, the main agent that answers you, does everything itself. In 3. Hand off to a specialist agent you move the case actions to a specialist, and the rules move with them.

Two checks, in this order explains both checks, and Where nobody can answer what changes on the other channels.

  • Restrict the actions that change data with RequiredRoles
  • Keep a confirmation on every change with RequiresConfirmation
  • Sign in as a user with the role and as one without, and compare
  • Tell what the model receives for a user without the role
  • Add MentorAgent to an existing app — Add an AI agent to a .NET Blazor Web App you already have: install the package, register the services, mark the methods it may call and place the chat widget.
  • Attributes — The parameters of MentorAction, MentorAgent, MentorTeam, TeamMember, MentorPage and MentorSkill, with their defaults and rules.