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

Install in an Astro site

Six steps, from an empty folder to a tool the agent cursor runs, in an Astro site with a React island.

Six steps, from an empty folder to a tool the agent cursor runs. In Astro, React runs in islands, so the provider and the components that have tools share one island. This site’s own landing page is built that way. No React on the page? Install without a framework shows createWebAgents, which a plain Astro <script> can call.

Create a project

Skip this if you already have an Astro site with React. Create the site, then add React for the islands.

npm create astro@latest
pnpm create astro@latest
yarn create astro@latest
bun create astro@latest
nub create astro@latest
aube create astro@latest
npx astro add react
pnpm dlx astro add react
yarn dlx astro add react
bunx astro add react
nubx astro add react
aube dlx astro add react

Install the packages

The agent cursor and the React bindings. Nothing else: the provider loads a WebMCP polyfill where the browser has none, and the devtools panel in development.

npm install @webmcp-agents/core @webmcp-agents/react
pnpm add @webmcp-agents/core @webmcp-agents/react
yarn add @webmcp-agents/core @webmcp-agents/react
bun add @webmcp-agents/core @webmcp-agents/react
nub add @webmcp-agents/core @webmcp-agents/react
aube add @webmcp-agents/core @webmcp-agents/react

Add the styles

Import the cursor’s stylesheet in your layout’s front matter, next to your own.

---
import "@webmcp-agents/core/cursor.css"; 
---

Add the provider

<WebAgentsProvider> installs the cursor and registers your tools; in a browser without WebMCP it loads a polyfill first, by itself, and in development it installs the devtools panel. It goes on a page in the next step.

import { WebAgentsProvider } from "@webmcp-agents/react";
import type { ReactNode } from "react";

export function Providers({ children }: { children: ReactNode }) {
  return <WebAgentsProvider>{children}</WebAgentsProvider>;
}

Add your first tool

The action is one function with two callers: a person clicks the button, an agent calls the tool. useWebTool registers the tool while the component is on the page. A plain <button> is enough: the cursor finds it by its role and name.

import { useWebTool } from "@webmcp-agents/react";
import { useState } from "react";

export function ClearCart() {
  const [items, setItems] = useState(["Ocean Tee", "Canvas Tote"]);

  const clear = () => {
    setItems([]);
    return "The cart is empty.";
  };

  // An agent calls this.
  useWebTool({
    id: "clear_cart",
    title: "Clear cart",
    description:
      "Empty the cart. Use when the user asks to start over. " +
      "The user confirms on the page.",
    destructive: true,
    requireConfirmation: true,
    execute: clear,
  });

  // A person clicks this.
  return (
    <div>
      <p>
        {items.length > 0 ? items.join(", ") : "The cart is empty."}
      </p>

      <button type="button" onClick={clear}>
        Clear cart
      </button>
    </div>
  );
}

Put the component inside the provider in one island, and render the island on a page. client:load renders it on the server and hydrates it in the browser.

import { ClearCart } from "./clear-cart";
import { Providers } from "./providers";

// One island: the provider and the components whose tools it shows.
export function Cart() {
  return (
    <Providers>
      <ClearCart />
    </Providers>
  );
}
---
import { Cart } from "../components/cart";
import Layout from "../layouts/Layout.astro";
---

<Layout>
  <Cart client:load />
</Layout>

Run it

Start the site and open it.

npm run dev
pnpm run dev
yarn run dev
bun run dev
nub run dev
aube run dev

Open the devtools panel (bottom right, or Alt+Shift+A), expand clear_cart and press Run. The cursor flies to Clear cart and waits there. Click the button yourself: the cart empties, and the tool answers “The cart is empty.”

Next

Define a tool

What you just wrote, line by line: execute, the title the cursor looks for, and target for when the name is not enough.

Was this page helpful?