Back to Copilotkit

useFrontendTool

showcase/shell-docs/src/content/reference/vue/hooks/useFrontendTool.mdx

1.61.19.6 KB
Original Source

Overview

useFrontendTool registers a client-side tool with CopilotKit at the current component scope. When the agent decides to call the tool, the provided handler function executes in the browser. Optionally, you can supply a render (a Vue component or render function) to display custom UI in the chat showing the tool's execution progress and result.

The composable manages the full registration lifecycle: it registers the tool (and its optional renderer) immediately, re-runs the registration when reactive dependencies change, and removes the tool automatically when the owning scope is disposed. Parameter schemas are defined with a Standard Schema such as a Zod object, which is used for both validation and type inference.

<Callout type="info"> Import from the `/v2` subpath: `@copilotkit/vue/v2`. Like all CopilotKit composables, `useFrontendTool` must be called from within a component that is a descendant of [`CopilotKitProvider`](/reference/vue/components/CopilotKitProvider). </Callout>

Signature

ts
import type { WatchSource } from "vue";
import { useFrontendTool } from "@copilotkit/vue/v2";

function useFrontendTool<T extends Record<string, unknown>>(
  tool: VueFrontendTool<T>,
  deps?: WatchSource<unknown>[],
): void;

Parameters

<PropertyReference name="tool" type="VueFrontendTool<T>" required> The tool definition object. <PropertyReference name="name" type="string" required> A unique name for the tool. The agent references this name when deciding to call the tool. If a tool with this name is already registered (for the same `agentId`), the existing registration is overridden and a warning is logged. </PropertyReference> <PropertyReference name="description" type="string"> A natural-language description that tells the agent what the tool does and when to use it. </PropertyReference> <PropertyReference name="parameters" type="StandardSchemaV1<any, T>"> A [Standard Schema](https://standardschema.dev) (for example a Zod object) defining the tool's input parameters. The schema is used for both validation and type inference of the `handler` arguments. May be omitted for tools that take no parameters. </PropertyReference>

<PropertyReference name="handler" type="(args: T, context: { toolCall, agent, signal? }) => Promise<unknown>"

An async function that executes when the agent calls the tool. Receives the
validated, typed arguments and a context object: `toolCall` (the raw tool
call metadata), `agent` (the agent instance that invoked the tool), and
`signal` (an `AbortSignal` that is aborted when the user stops the agent).
Long-running handlers can check `signal.aborted` or pass `signal` to
`fetch` to cancel cooperatively. The resolved value is surfaced back to the
agent as the tool result.
</PropertyReference>

<PropertyReference name="render" type="Component<RenderProps<T>> | ((props: RenderProps<T>) => VNodeChild)"

An optional Vue component or render function used to visualize tool
execution in the chat. It receives a props object: `name` (the tool name),
`toolCallId` (the unique id of this tool call), `args` (the arguments,
partial while streaming during `InProgress`, complete during `Executing`
and `Complete`), `status` (one of `ToolCallStatus.InProgress`,
`ToolCallStatus.Executing`, or `ToolCallStatus.Complete`), and `result`
(the string result, available only when status is `Complete`).
</PropertyReference> <PropertyReference name="available" type="boolean" default="true"> Whether the tool is exposed to the agent. Set to `false` to hide the tool from the agent without unregistering it. Changing this reactive value re-runs the registration. </PropertyReference> <PropertyReference name="agentId" type="string"> Optional agent scope. When set, the tool is only available to the agent with this id. Duplicate-name detection is scoped per `agentId`. </PropertyReference> <PropertyReference name="followUp" type="boolean"> Whether the agent should automatically continue the run after the tool result is returned. </PropertyReference> </PropertyReference> <PropertyReference name="deps" type="WatchSource<unknown>[]"> An optional list of reactive watch sources (for example a `ref` or a getter). The registration re-runs whenever any of these change. Use this when your `handler` or `render` captures reactive values that should refresh the registration. The composable already watches `tool.name` and `tool.available` internally. </PropertyReference>

Behavior

  • Immediate registration. The tool is registered as soon as the composable runs (the underlying watch uses { immediate: true }).
  • Duplicate handling. If a tool with the same name already exists for the same agentId, the previous registration is removed, the new one is added, and an override warning is logged. Only one tool per name (per agent) is active at a time.
  • Renderer registration. When render is provided, it is registered as a tool call renderer. The renderer is registered even when parameters is omitted, so parameter-free tools (for example confirm dialogs) can still render UI in the chat.
  • Reactivity. The registration re-runs when tool.name, tool.available, or any deps watch source changes. Pass changing reactive values through deps.
  • Automatic cleanup. The tool is removed when the watch cleans up, including when the owning component scope is disposed. A render registered alongside the tool is intentionally left in place so earlier chat history can still render it.
  • No return value. The composable returns void.

Usage

Basic tool with a Zod schema and async handler

vue
<script setup lang="ts">
import { ref } from "vue";
import { z } from "zod";
import { useFrontendTool } from "@copilotkit/vue/v2";

const todos = ref<string[]>([]);

useFrontendTool({
  name: "addTodo",
  description: "Add a new item to the user's todo list",
  parameters: z.object({
    text: z.string().describe("The todo item text"),
    priority: z.enum(["low", "medium", "high"]).describe("Priority level"),
  }),
  handler: async ({ text, priority }) => {
    todos.value.push(text);
    return `Added "${text}" with ${priority} priority`;
  },
});
</script>

<template>
  <ul>
    <li v-for="(todo, i) in todos" :key="i">{{ todo }}</li>
  </ul>
</template>

Tool with a custom Vue renderer

Provide a render component to visualize the tool call in chat. The component receives args, status, and result as props.

vue
<!-- WeatherToolView.vue -->
<script setup lang="ts">
import { ToolCallStatus } from "@copilotkit/core";

defineProps<{
  name: string;
  toolCallId: string;
  args: { city?: string; units?: "celsius" | "fahrenheit" };
  status: ToolCallStatus;
  result?: string;
}>();
</script>

<template>
  <div v-if="status === ToolCallStatus.InProgress" class="animate-pulse">
    Fetching weather for {{ args.city }}...
  </div>
  <div v-else-if="status === ToolCallStatus.Complete && result" class="rounded border p-4">
    <h3>{{ JSON.parse(result).city }}</h3>
    <p>{{ JSON.parse(result).temperature }}&deg; {{ JSON.parse(result).units }}</p>
    <p>{{ JSON.parse(result).conditions }}</p>
  </div>
</template>
vue
<!-- App.vue -->
<script setup lang="ts">
import { z } from "zod";
import { useFrontendTool } from "@copilotkit/vue/v2";
import WeatherToolView from "./WeatherToolView.vue";

useFrontendTool({
  name: "getWeather",
  description: "Fetch and display weather information for a city",
  parameters: z.object({
    city: z.string().describe("City name"),
    units: z.enum(["celsius", "fahrenheit"]).default("celsius"),
  }),
  handler: async ({ city, units }, { signal }) => {
    const response = await fetch(`/api/weather?city=${city}&units=${units}`, {
      signal,
    });
    return JSON.stringify(await response.json());
  },
  render: WeatherToolView,
});
</script>

Conditionally available tool

Expose available as a getter that reads a reactive value. The registration watch re-reads available when that value changes, so the tool is shown or hidden automatically. (Passing the value through deps as well is harmless but not required.)

vue
<script setup lang="ts">
import { computed, toRef } from "vue";
import { z } from "zod";
import { useFrontendTool } from "@copilotkit/vue/v2";

const props = defineProps<{ isAdmin: boolean }>();
const isAdmin = toRef(props, "isAdmin");

useFrontendTool(
  {
    name: "deleteUser",
    description: "Delete a user account by ID (admin only)",
    parameters: z.object({
      userId: z.string().describe("The ID of the user to delete"),
    }),
    handler: async ({ userId }) => {
      await fetch(`/api/users/${userId}`, { method: "DELETE" });
      return `User ${userId} deleted`;
    },
    // Expose `available` as a getter so the registration watch re-reads it
    // whenever `isAdmin` changes.
    get available() {
      return isAdmin.value;
    },
  },
  [isAdmin],
);
</script>
<Cards> <Card title="useComponent" description="Convenience composable that renders a Vue component from tool args. Built on useFrontendTool." href="/reference/vue/hooks/useComponent" /> <Card title="useRenderTool" description="Register a renderer for a tool call with full access to status (in progress, executing, complete) and results." href="/reference/vue/hooks/useRenderTool" /> <Card title="CopilotKitProvider" description="The provider that must wrap any component calling useFrontendTool." href="/reference/vue/components/CopilotKitProvider" /> </Cards>