tools

Framework for registering LLM tools with custom UI rendering in assistant-ui. Supports both backend tools (via AI SDK) and frontend-only tools, each with optional custom UI rendering through makeAssistantToolUI Includes human-in-the-loop patterns with requires-action status for confirmation workflows and interactive tool responses Tool UI receives execution status, arguments, results, and a submitResult callback for interactive tools Common setup patterns provided for weather APIs, clipboard operations, and file deletion confirmations with validation checks

INSTALLATION
npx skills add https://github.com/assistant-ui/skills --skill tools
Run in your project or agent environment. Adjust flags if your CLI version differs.

SKILL.md

assistant-ui Tools

Always consult assistant-ui.com/llms.txt for latest API.

Tools let LLMs trigger actions with custom UI rendering.

References

Tool Types

Where does the tool execute?

├─ Backend (LLM calls API) → AI SDK tool()

│  └─ Want custom UI? → makeAssistantToolUI

└─ Frontend (browser-only) → makeAssistantTool

   └─ Want custom UI? → makeAssistantToolUI

Backend Tool with UI

// Backend (app/api/chat/route.ts)

import { tool, stepCountIs } from "ai";

import { z } from "zod";

const tools = {

  get_weather: tool({

    description: "Get weather for a city",

    inputSchema: z.object({ city: z.string() }),

    execute: async ({ city }) => ({ temp: 22, city }),

  }),

};

const result = streamText({

  model: openai("gpt-4o"),

  messages,

  tools,

  stopWhen: stepCountIs(5),

});
// Frontend

import { makeAssistantToolUI } from "@assistant-ui/react";

const WeatherToolUI = makeAssistantToolUI({

  toolName: "get_weather",

  render: ({ args, result, status }) => {

    if (status === "running") return <div>Loading weather...</div>;

    return <div>{result?.city}: {result?.temp}°C</div>;

  },

});

// Register in app

<AssistantRuntimeProvider runtime={runtime}>

  <WeatherToolUI />

  <Thread />

</AssistantRuntimeProvider>

Frontend-Only Tool

import { makeAssistantTool } from "@assistant-ui/react";

import { z } from "zod";

const CopyTool = makeAssistantTool({

  toolName: "copy_to_clipboard",

  parameters: z.object({ text: z.string() }),

  execute: async ({ text }) => {

    await navigator.clipboard.writeText(text);

    return { success: true };

  },

});

<AssistantRuntimeProvider runtime={runtime}>

  <CopyTool />

  <Thread />

</AssistantRuntimeProvider>

API Reference

// makeAssistantToolUI props

interface ToolUIProps {

  toolCallId: string;

  toolName: string;

  args: Record<string, unknown>;

  argsText: string;

  result?: unknown;

  status: "running" | "complete" | "incomplete" | "requires-action";

  submitResult: (result: unknown) => void;  // For interactive tools

}

Human-in-the-Loop

const DeleteToolUI = makeAssistantToolUI({

  toolName: "delete_file",

  render: ({ args, status, submitResult }) => {

    if (status === "requires-action") {

      return (

        <div>

          <p>Delete {args.path}?</p>

          <button onClick={() => submitResult({ confirmed: true })}>Confirm</button>

          <button onClick={() => submitResult({ confirmed: false })}>Cancel</button>

        </div>

      );

    }

    return <div>File deleted</div>;

  },

});

Common Gotchas

Tool UI not rendering

  • toolName must match exactly (case-sensitive)
  • Register UI inside AssistantRuntimeProvider

Tool not being called

  • Check tool description is clear
  • Use stopWhen: stepCountIs(n) to allow multi-step

Result not showing

  • Tool must return a value
  • Check status === "complete" before accessing result
BrowserAct

Let your agent run on any real-world website

Bypass CAPTCHA & anti-bot for free. Start local, scale to cloud.

Explore BrowserAct Skills →

Stop writing automation&scrapers

Install the CLI. Run your first Skill in 30 seconds. Scale when you're ready.

Start free
free · no credit card