# MentorHub class

> The MentorHub class of Novior.MentorAgent.Server.Hubs: SignalR hub that exposes MentorOrchestrator to remote clients (Blazor WASM, MAUI, React, Vue, mobile…

- URL: https://docs.mentoragent.net/api/novior-mentoragent-server-hubs/mentorhub
- Verified with: MentorAgent 1.2.0
- Last updated: 2026-10-27
- Packages: Novior.MentorAgent.Server

SignalR hub that exposes `MentorOrchestrator` to remote clients (Blazor WASM, MAUI, React, Vue, mobile, console, etc.). Each connection gets its own DI scope via [`MentorHubScopes`](https://docs.mentoragent.net/api/novior-mentoragent-server-hubs/mentorhubscopes) — built once on connect and reused for every message. This mirrors the Blazor Server "one scope per circuit" model, so the coordinator (and its MCP connections) is built once and the conversation `AgentSession` persists across messages. Protocol: Client → Server: UpdatePageContext, SendMessage, CancelRequest, ResetSession, RespondToApproval Server → Client: StreamingChunk, StreamingCompleted, BusyChanged, Error, ActionExecuting, ActionCompleted, ActionFailed, ConfirmationRequired, NavigationRequested, RagSourcesReady, TeamMemberSpeaking, UIActionExecuting, UIActionCompleted, UIActionRequested, McpServerStatusChanged, GeneratedImages, HostedToolsDeclared, Cards Over HTTP, beside the hub: POST /mentor/approve, POST /mentor/cancel, GET and POST /mentor/session (a hub call would queue behind the turn it is meant to reach)

## Definition

Namespace: `Novior.MentorAgent.Server.Hubs`. Package: `Novior.MentorAgent.Server`.

`public class MentorHub : Hub`

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

## Constructors

`public MentorHub(MentorHubScopes scopes, HubPageContextStore pageContextStore, MentorApprovalOwners approvalOwners, IOptions<MentorWidgetOptions> widgetOptions, ILogger<MentorHub> logger)`

## Methods

### `CancelRequest`

`public Task CancelRequest()`

Cancels the current AI request.

### `OnConnectedAsync`

`public override Task OnConnectedAsync()`

### `OnDisconnectedAsync`

`public override Task OnDisconnectedAsync(Exception? exception)`

### `ResetSession`

`public Task ResetSession()`

Resets the conversation session.

### `RespondToApproval`

`public Task RespondToApproval(string requestId, bool approved)`

Responds to a HITL confirmation dialog. ⚠️ Kept for backwards compatibility only — **use `POST /mentor/approve` instead**. SignalR dispatches at most one hub invocation at a time per connection, so while `SendMessage` is parked waiting for this answer the invocation sits in the queue and the approval never arrives. Works only if the turn is somehow no longer running. Applies to both `MentorHitlMode` values.

### `SendMessage`

`public Task SendMessage(string text, IReadOnlyList<MentorAttachment>? attachments = null)`

Sends a user message to the AI orchestrator.

**Clients must always send two arguments** — `null` or an empty list when the turn carries no images. The C\# default on `attachments` is a convenience for server-side callers and **does not make the argument optional on the wire**: SignalR binds invocation arguments positionally by count in the hub protocol's parser, before dispatch and before any `IHubFilter`, so a one-argument invocation is rejected with `InvalidDataException: Invocation provides 1 argument(s) but target expects 2`. Hub methods cannot be overloaded, so no signature accepts both arities.

This comment previously claimed the default kept 1-argument clients working. It did not, and the four reference clients in the package README were written against that claim (BUG-051). The wire arity is pinned by `Server/HubMethodArityTests`.

### `SendUserMessage`

`public Task SendUserMessage(MentorUserMessage message)`

Sends a user turn: `{ "text": "…", "attachments": null, "language": "it" }`. The language is the name or the ISO code of a `MentorLanguage`, in any case; anything else, or none, is `MentorOptions.Language`. A method of its own, because [`MentorHub.SendMessage`](https://docs.mentoragent.net/api/novior-mentoragent-server-hubs/mentorhub#sendmessage) keeps its two arguments (BUG-051): one object, which can carry more later without changing the arity again.

### `UpdatePageContext`

`public Task UpdatePageContext(PageContextSnapshot snapshot)`

Stores the page context snapshot for this connection.

---

- Previous: [MentorApprovalOwners class](https://docs.mentoragent.net/api/novior-mentoragent-server-hubs/mentorapprovalowners.md)
- Next: [MentorHubScopes class](https://docs.mentoragent.net/api/novior-mentoragent-server-hubs/mentorhubscopes.md)
