# 4 · Put agents in a team

> Tutorial step 4: let two agents review a case in a group chat before anything changes, while roles and confirmation still apply.

- URL: https://docs.mentoragent.net/get-started/tutorial/team
- Verified with: MentorAgent 1.0.0
- Last updated: 2026-10-13
- Duration: 15 min
- Packages: Novior.MentorAgent

Some requests deserve a second opinion before anyone acts. Closing a case is one of them: someone should check the notes first. In this step a team of two agents reviews a case. An analyst reads it and a reviewer decides. When they approve, the specialist from step 3 closes the case.

**Before you start:** finish [3 · Hand off to a specialist agent](https://docs.mentoragent.net/get-started/tutorial/specialist-agent).

## Give the analyst a read-only tool

Create `CaseReports.cs`:

```csharp title="CaseReports.cs"
using Novior.MentorAgent.Attributes;

public class CaseReports(CaseStore store)
{
    [MentorAction(
        Description = "Gets a support case with all its notes, for a closure review",
        ReadOnly = true)]
    public SupportCase? GetCaseForReview(string caseNumber)
        => store.Find(caseNumber);
}
```

Here, team members only read. Changes go through the specialist, where roles and confirmation apply. `ReadOnly = true` declares that the tool only reads, and MentorAgent then tells the Analyst that its tools are read-only. `CaseReports` is the toolbox of the Analyst only. The team lists it in the `Tools` of that member, as you see below, so the coordinator does not get `get_case_for_review`.

## Define the team

Create `ClosureReviewTeam.cs`:

```csharp title="ClosureReviewTeam.cs"
using Novior.MentorAgent.Attributes;

[MentorTeam(
    Name = "ClosureReview",
    Description = "Reviews whether a support case is ready to be closed.",
    TriggerOn = ["closure review"],
    MaxIterations = 4,
    HandoffTo = ["CaseSpecialist"])]
public class ClosureReviewTeam
{
    [TeamMember(
        Role = "Analyst",
        Instructions = "Read the case and its notes. Say whether the customer's problem is solved, quoting the notes.",
        Tools = [typeof(CaseReports)])]
    public object? Analyst { get; set; }

    [TeamMember(
        Role = "Reviewer",
        Instructions = "Check the analyst's answer against the notes. Approve the closure only if a note shows the problem is solved.")]
    public object? Reviewer { get; set; }

    [TeamTerminationCondition]
    public bool ShouldTerminate(string lastMessage, string lastSpeaker)
        => lastSpeaker == "Reviewer"
           && (lastMessage.Contains("[TEAM:APPROVED]") || lastMessage.Contains("[TEAM:REJECTED]"));
}
```

- `[MentorTeam]` turns the class into a group chat. The coordinator receives it as one tool named after the team: `closure_review`.
- `Description` tells the coordinator what the team is for. `TriggerOn` adds a rule to the coordinator's instructions: always call the team when a request mentions a closure review. Keep these words specific: a broad word such as "case" would send ordinary requests through the team.
- Each `[TeamMember]` property is one member: an agent with a `Role` and its `Instructions`. The members speak in turn, in the order of the properties. The property is only a place for the attribute: MentorAgent ignores its type and its value, so leave it unset.
- MentorAgent tells every member to mark a final verdict with `[TEAM:APPROVED]` or `[TEAM:REJECTED]`. `ShouldTerminate`, the method marked with `[TeamTerminationCondition]`, ends the discussion as soon as the Reviewer gives one. The markers are removed before the text reaches the user.
- `MaxIterations = 4` ends the discussion after four member turns in any case. Without a termination method, the team always talks until this limit. Each time the coordinator calls the team, the count starts again.
- `HandoffTo` names the specialist that carries out an approved decision. When any message of the discussion carries `[TEAM:APPROVED]`, the coordinator is told to execute the approved actions. It hands them to CaseSpecialist. Any member's message counts, not only the Reviewer's. That is one more reason to leave the change itself to the specialist, behind its role check and its confirmation.

## Register the tools

In `Program.cs`, register the class of the analyst's tools:

```csharp title="Program.cs"
builder.Services.AddScoped<CaseReports>();   // the Analyst's tools
```

The team class itself needs no registration. A tool class that is not registered is left out with a warning in the log, and the member works without it.

## Try it

Run the app and sign in as Sam: `/dev/sign-in?name=Sam&role=Supervisors`.

1. Type "Run a closure review of case C-3". While the team works, the widget shows which member is speaking. The only note on C-3 says that the customer was asked for the export log. So the reviewer should reject the closure, and the coordinator tells you why.
2. Type "Add a note to C-3: the customer confirmed the export works now", and confirm.
3. Ask for the closure review again. This time the reviewer can approve. The coordinator passes the closure to CaseSpecialist, and the confirmation box appears before the case is closed. Click **Confirm**.

The verdict comes from the model, so its wording, and sometimes the decision, can change from one run to the next.

## What a team costs

Every member turn is at least one model call, plus one for each tool it calls. In this step:

- a review that the reviewer settles in its first turn takes five model calls: the coordinator calls the team, the analyst calls `get_case_for_review` and answers, the reviewer decides, and the coordinator writes the reply;
- an approved review that ends with the closure takes nine, because the handoff adds four: the coordinator calls `route_to_specialist`, the router picks CaseSpecialist, and the specialist calls `close_case` and answers;
- a discussion where nobody decides stops after four member turns, the value of `MaxIterations`.

All of them run on your `ChatClient` and count in the token usage.

## When a team is worth it

- **Use a team** when a request needs more than one point of view before anyone acts: a review, an approval, an analysis that a single agent tends to rush.
- **Do not use a team** for lookups and single changes. An action, or a specialist, answers faster and costs less.
- **Keep the tools of the members read-only**, and leave changes to actions and specialists, where roles and confirmation apply.

## Next steps

You have finished the tutorial. The Cases agent reads and changes cases, checks roles, asks for confirmation, hands work to a specialist and runs a review team.

- [The three agent levels](https://docs.mentoragent.net/concepts/agent-levels#cost-and-latency) compares actions, specialists and teams, with their costs.
- [Roles, confirmation and governance](https://docs.mentoragent.net/concepts/governance#where-nobody-can-answer) explains the checks on every channel.
- [Channels](https://docs.mentoragent.net/concepts/channels#which-channel-fits-your-client) shows how the same actions reach JavaScript clients, MCP clients and other agents.
- Set MentorAgent up for your host and model provider with the [Configure](https://docs.mentoragent.net/configure) pages.
- Before you go to production, replace the test sign-in with your real one and go through the [Production checklist](https://docs.mentoragent.net/configure/production#authentication-and-endpoints).

---

- Previous: [3 · Hand off to a specialist agent](https://docs.mentoragent.net/get-started/tutorial/specialist-agent.md)
- Next: [Add MentorAgent to an existing app](https://docs.mentoragent.net/get-started/existing-app.md)
