useAgentSession
A React hook for a conversation with one adapter about the current page, in the chat's own message shape.
A React hook for a conversation with one adapter about the current page, in the chat’s own message shape. It is what <AgentChat> renders and what useAgentChat wraps; reach for it to build a chat UI on the session logic directly.
Import
import { useAgentSession } from "@webmcp-agents/chat";
import type {
AgentSession,
UseAgentSessionOptions,
} from "@webmcp-agents/chat";
Example
A one-field chat: it sends the message, lists the replies’ text, and offers Try again after a failed run.
import {
createCloudAdapter,
useAgentSession,
} from "@webmcp-agents/chat";
import { shopper } from "@/agents";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
// Once, at module scope.
const cloud = createCloudAdapter();
// Under <WebAgentsProvider>, so the agent's tools are on the page.
export function MiniChat() {
const chat = useAgentSession(cloud, {
agent: shopper,
storageKey: false,
});
return (
<form
className="flex flex-col gap-3"
onSubmit={(event) => {
event.preventDefault();
const data = new FormData(event.currentTarget);
chat.send(String(data.get("q")));
event.currentTarget.reset();
}}
>
{chat.messages.map((message) => (
<p key={message.id}>
{message.parts
.map((part) => (part.type === "text" ? part.text : ""))
.join("")}
</p>
))}
{chat.status === "error" && (
<Button type="button" onClick={chat.retry}>
Try again
</Button>
)}
<Input
name="q"
aria-label="Message"
disabled={chat.adapterStatus.state !== "ready"}
/>
</form>
);
}
Options
useAgentSession(adapter, options?). The first argument is the adapter; the second is a UseAgentSessionOptions.
| Name | Type | Default | Description |
|---|---|---|---|
adapter |
AgentAdapter |
required | The agent that runs the model loop (the first argument). Create it once, at module scope. |
storageKey |
string | false |
"web-agents:chat" |
sessionStorage key for the conversation, so it survives route changes and reloads. false keeps it in memory only. |
agent |
WebAgent |
not set | The page’s agent (defineWebAgent). tools is filtered through it (plus one delegate_to_<id> tool per sub-agent), the request carries it, and a delegate_to_* call runs the sub-agent’s own loop on the same adapter, nested under the call (AgentPart.agent). Without it the model sees every page tool and no instructions. |
Returns
An AgentSession:
| Name | Type | Description |
|---|---|---|
messages |
AgentMessage[] |
The conversation. |
status |
"idle" | "streaming" | "error" |
Whether a run is streaming, or the last one failed. |
error |
SessionError | undefined |
The last run’s failure: { message, code? }. |
tools |
ToolDescriptor[] |
What the model sees right now: the page’s tools through the agent’s filter, plus its delegate tools. |
adapterStatus |
AdapterStatus |
The adapter’s status, kept current through its subscribe(). An adapter without status() is ready. |
agent |
WebAgent | undefined |
The agent in use. |
send(text) |
(text: string) => void |
Trim text and start a run. Ignored when empty or while streaming. |
stop() |
() => void |
Abort the run; open tool calls become cancelled. |
retry() |
() => void |
Resume the interrupted turn from its last finished tool call. Ignored while streaming or with no user message. |
reset() |
() => void |
Stop and start a new conversation. |
What a run does
- Waits for the page’s tools to settle (no
toolchangefor 100 ms, at most 1.5 s), then reads them. - Calls
adapter.run()with the messages, atoolsgetter that always returns the latest list, anAbortSignaland acallToolthat runscallModelContextToolwith that signal. - After each tool call, waits for the tools to settle again (150 ms quiet, at most 1.5 s) and refreshes them, since the call may have navigated or opened a form.
- Feeds every stream part to
sessionReducer. Coreparkandstartagent events mark the open call of that tool asawaiting-confirmationorrunning.
Notes
- Retry resumes. It sends
resumable(messages): the conversation through the last tool call that finished, so nothing is done twice (no second add to cart). With nothing finished, the turn starts over from the user’s message. - Only the last 60 messages are stored. Loading from storage marks any call that was running as
cancelled. - A new
sendorretryaborts a previous run; parts from a superseded run are dropped. - An adapter that throws becomes an
errorpart with the thrown message. - Unmounting aborts the run.
- Render it under
<WebAgentsProvider>(or any page withdocument.modelContext); without WebMCPtoolsis empty.
See also
- Add the chat, the page that teaches the chat
useAgentChat, the same session as AI SDKUIMessagessessionReducerandresumablecallModelContextToolandlistToolDescriptors<AgentChat>