Skip to content
WebMCP Agents
Esc
↑↓navigate↵open⌘Jpreview
On this page

useWebAgent

Runs an agent from a component, with live status and no chat on the page.

Runs an agent from a component, with live status and no chat on the page. It is the React form of agent.invoke: the page’s tools run under the cursor, a confirm-gated tool waits for the person’s click (status is "waiting"), and sub-agents run nested. Reach for it when a button or a form should hand a task to the agent and show how it is going.

Import

import { useWebAgent } from "@webmcp-agents/chat";
import type {
  UseWebAgentOptions,
  WebAgentHandle,
  WebAgentStatus,
} from "@webmcp-agents/chat";

Example

A button that runs the agent and lists each step it takes, including a call parked on the person’s click.

import { useWebAgent } from "@webmcp-agents/chat";

import { shopper } from "@/agents";
import { Button } from "@/components/ui/button";

// Under <WebAgentsProvider>, so the agent's tools are on the page.
export function Reorder() {
  const agent = useWebAgent(shopper);

  return (
    <div className="flex flex-col items-start gap-3">
      <Button
        onClick={() => agent.invoke("Reorder my last purchase.")}
        disabled={agent.status === "running"}
      >
        {agent.status === "running" ? "Working…" : "Reorder"}
      </Button>

      <ul>
        {agent.steps.map((step) => (
          <li key={step.id}>
            {step.tool}: {step.state}
            {step.prompt && ` (${step.prompt})`}
          </li>
        ))}
      </ul>

      {agent.status === "waiting" && <p>Click to confirm.</p>}
      {agent.error && <p role="alert">{agent.error.message}</p>}
      {agent.text && <p>{agent.text}</p>}
    </div>
  );
}

Options

useWebAgent(agent, options?). The first argument is the agent; the second holds the hook’s own options.

Name Type Default Description
agent WebAgent required The agent, from defineWebAgent. The latest render’s value is used, so invoke stays one function.
adapter AgentAdapter one shared createCloudAdapter() What runs the model: the same default as <AgentChat> and agent.invoke, your /api/chat. Create a custom adapter once, at module scope. Passed in the second argument: useWebAgent(agent, { adapter }).

Returns

A WebAgentHandle:

Name Type Description
invoke(text) (text: string) => Promise<WebAgentResult | undefined> Run the agent on one message: a fresh conversation each time, and a run in flight is stopped first. Resolves with the result (text, steps, messages), or undefined when the run was stopped or failed. It never rejects, so onClick={() => agent.invoke("…")} needs no catch; a failure is in error. An empty message does nothing.
stop() () => void Stop the run in flight: the model, the tool running and any sub-agent. status goes back to idle and the open steps show as cancelled.
status WebAgentStatus "idle" (none yet, finished or stopped), "running", "waiting" (a tool is parked on the page until the person clicks; see the steps’ prompt) or "error" (the last run failed).
steps WebAgentStep[] The tool calls of the current or last run, live, a sub-agent’s included (with agent set). See WebAgentStep.
text string The agent’s answer so far, streamed.
error SessionError Why the last run failed: { message, code? }, with a code the adapter gave (offline, rate limited…). Cleared by the next invoke.

Notes

  • It aborts the run in flight when the component unmounts.
  • The page’s tools are read again after each tool call, once they have settled, so a navigation’s new tools are there for the next step.
  • It needs the page’s tools to be registered, so render it under <WebAgentsProvider>. The agent’s tools list picks from them.
  • To render a full conversation instead, use useAgentChat or <AgentChat>.

See also

Was this page helpful?