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

Install in a TanStack Start app

Six steps, from an empty folder to a tool the agent cursor runs, in a TanStack Start app.

Six steps, from an empty folder to a tool the agent cursor runs. No example app in the repository runs TanStack Start: the code on this page is type-checked against the packages, not run.

Create a project

Skip this if you already have a TanStack Start app. The TanStack CLI creates one; its defaults are fine.

npx @tanstack/cli@latest create
pnpm dlx @tanstack/cli@latest create
yarn dlx @tanstack/cli@latest create
bunx @tanstack/cli@latest create
nubx @tanstack/cli@latest create
aube dlx @tanstack/cli@latest create

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 as a URL in the root route and add it to the document’s links, next to your own.

import cursorCss from "@webmcp-agents/core/cursor.css?url"; 
import appCss from "../styles.css?url";

// In head():
links: [
  { rel: "stylesheet", href: appCss },
  { rel: "stylesheet", href: cursorCss }, 
],

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. Your page renders inside it right away, on the server too.

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

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

Wrap the page in it in the root route. The highlighted lines are the ones you add.

import {
  createRootRoute,
  HeadContent,
  Scripts,
} from "@tanstack/react-router";
import cursorCss from "@webmcp-agents/core/cursor.css?url";
import type { ReactNode } from "react";

import { Providers } from "../components/providers";
import appCss from "../styles.css?url";

export const Route = createRootRoute({
  head: () => ({
    meta: [
      { charSet: "utf-8" },
      {
        name: "viewport",
        content: "width=device-width, initial-scale=1",
      },
    ],
    links: [
      { rel: "stylesheet", href: appCss },
      { rel: "stylesheet", href: cursorCss },
    ],
  }),
  shellComponent: RootDocument,
});

function RootDocument({ children }: { children: ReactNode }) {
  return (
    <html lang="en">
      <head>
        <HeadContent />
      </head>
      <body>
        <Providers>{children}</Providers>
        <Scripts />
      </body>
    </html>
  );
}

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>
  );
}

Render it in a route.

import { createFileRoute } from "@tanstack/react-router";

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

export const Route = createFileRoute("/")({ component: Home });

function Home() {
  return (
    <main>
      <ClearCart />
    </main>
  );
}

Run it

Start the app 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?