Choose what your agent can do beyond writing text, across built-in toolkits, browser client tools, and MCP servers.
Tools are how an agent moves from describing to doing. An agent with no tools can only answer from its instructions and its training. An agent with tools can search your documents, read your application state, call your APIs, and act on the results.
HeroUI Agent has three kinds, and the difference that matters is where each one runs.
| Kind | Runs in | Configured in | Best for |
|---|---|---|---|
| Built-in toolkits | HeroUI's hosted runtime | Dashboard | Capabilities we host for you |
| Client tools | The visitor's browser | Your code | Your application state and authenticated APIs |
| MCP servers | A server you point us at | Dashboard | Third-party systems that speak MCP |
Client tools are the ones to reach for first. Because they execute in the page with the signed-in person's own session, they inherit your existing permissions automatically — the hosted runtime only ever learns each tool's name, description, and schema, never its implementation or its results' path to your backend.
Toolkits are capabilities the hosted runtime provides. Switch them on per project; they apply to the next message with no deploy.
| Toolkit | What the agent can do | Requires |
|---|---|---|
| Search knowledge | Query the project's knowledge base and quote relevant passages | At least one Ready, enabled document |
| Web search | Search the public web for current information | — |
| Image search | Find images matching a description | Web search |
| News search | Search recent news coverage with publication dates | Web search |
Search knowledge needs both halves. The toolkit switch alone does nothing if the project has no document that is both Ready and enabled — the tool is not offered to the agent at all.
Image search and News search remain visible but disabled while Web search is off. Turning web search back on restores the preferences you chose previously. News search is off by default.
File generation is built into every Agent rather than configured as a toolkit. A user can ask a client tool to fetch and filter data, inspect metrics in the conversation, then say “export that to XLSX.” The runtime keeps the exact dataset across turns and sends it to a dedicated artifact subagent, independently of the selected conversation model. It can create a workbook with a Summary sheet, explicit dataset-to-sheet mappings, or one tab per category. PDF, PPTX, and DOCX exports can combine narrative, tables, and real line, bar, area, pie, or donut charts. Generated file cards remain downloadable from conversation history until that conversation is deleted.
For specialized document layouts, enable the corresponding built-in skill. Skills can be switched off individually without disabling basic exports.
Provider-hosted code execution is not Zero Data Retention or HIPAA eligible. File generation is processed first by OpenAI Code Interpreter and may use Anthropic Code Execution—directly or through Claude Platform on AWS—as a fallback. HeroUI deletes provider containers and files immediately after the attempt, but the providers' retention terms still apply. Do not use file generation for workloads whose policy forbids that processing.
For datasets too large for the 64 KiB client-tool result limit, call execution.uploadDataSource(...) inside the client tool and return the resulting AgentDataSource handle. JSON, CSV, and TSV sources up to 10 MB are uploaded directly to the artifact provider without adding their rows to the conversation model context.
Web results and knowledge excerpts are both handled as untrusted external content. The agent quotes them in prose and keeps them separate from the datasets your client tools return.
The selected conversation model chooses HeroUI components and streams their JSON spec through the
built-in composeUI tool. json-render compiles the stream, and HeroUI validates and renders the
result using its existing widgets, forms, exports and actions. Your client tools return data through
the normal SDK; no additional client integration is needed.
The model uses getUIComponents({names}) to retrieve component definitions and SpecStream examples.
It then calls composeUI({specStream, complete?}), using actual values from tool results or saved
conversation data. specStream is a string of JSONL patches that build a spec with root and
elements. New interfaces omit target or set it to null.
For example, if a tool returns July revenue of $153,000 and expenses of $193,000, a metric grid can use those values directly:
{"op":"add","path":"/root","value":"monthly-metrics"}
{"op":"add","path":"/elements","value":{}}
{"op":"add","path":"/elements/monthly-metrics","value":{"type":"metric-grid","props":{"title":"July 2026","metrics":[{"label":"Revenue","value":153000,"format":{"style":"currency","currency":"USD"}},{"label":"Expenses","value":193000,"format":{"style":"currency","currency":"USD"}}]},"children":[]}}Pass the JSONL as specStream. For larger interfaces, stream containers first, then complete
components in their final display order. Ready components appear while later patches arrive.
Use native children for layouts and named slots for item cards and tab panels.
Comparisons include every requested metric with its actual period, value and units. Preserve the
distinction between closed periods and forecasts, and use real baselines and history for changes
and sparklines. Filtering, sorting and calculations happen through the agent's data tools; reuse
completed results instead of fetching them again. Useful follow-up questions belong in the same
spec. Set complete: true only when the successful interface finishes the full request.
To edit an existing result, the model retrieves its validated spec with
getUISpec({target: {messageId, partId}}), then calls composeUI with the same target and only the
patches needed for the change. For example, moving a chart above a table changes their parent's
children array without copying the data. The runtime resolves the saved target from the authorized
conversation, creates a new assistant result and preserves the original.
Saved OpenUI responses remain readable and can be converted in memory for edits. Newly generated interfaces use json-render; displayed datasets remain available for subsequent exports.
Client tools are declared in code, not the dashboard, because their implementations are yours:
import {HeroUIAgent, createToolHelper} from "@heroui/agent";
import {z} from "@heroui/agent/zod";
const tool = createToolHelper<AppContext>();
const tools = [
tool({
name: "search_orders",
displayName: "Search orders",
description: "Find orders for the signed-in customer by status or date",
parameters: z.object({status: z.string().optional()}),
execute: ({status}, context) => context.apiClient.searchOrders({status}),
}),
];
<HeroUIAgent getAuthToken={getAuthToken} agentId={process.env.HEROUI_AGENT_ID!} tools={tools} />;A project can declare up to 20 client tools, and the manifest sent to the runtime is capped at 16 KB — keep descriptions purposeful rather than exhaustive.
See Client tools for the full API, typed context, and tool states.
The runtime owns these names, and a client tool that uses one is rejected:
callMcpTool, composeUI, executeSandbox, generateFile, getUIComponents, renderComponent, searchKnowledge, searchMcpTools, searchWeb
The mcp_ prefix is reserved wholesale, so no client tool can impersonate a tool borrowed from an MCP server.
Connecting an MCP server borrows its tools into your agent. Each one is exposed as mcp_{server}_{tool}, so the agent can tell which integration a tool came from, and results are labeled as untrusted data from that server.
Servers are added in the dashboard with an optional per-server allowlist, so you can connect a server that exposes twenty tools and permit only the three you want. See MCP servers for setup, authentication, and security guidance.
Tools that change something should ask first. Client tools declare needsApproval, and the embed's permission mode decides how that flag is honored:
| Mode | Behavior |
|---|---|
ask | Confirm every tool call, whether or not it declares approval |
auto | Confirm only tools that declare needsApproval |
full | Run every declared tool without confirmation |
auto is the default and the right choice for most products: reads run immediately, writes prompt. Set the mode with permissions.defaultMode, and use permissions.showPicker to let people change it themselves. The picker only appears once you declare client tools — with nothing to approve, there is nothing to pick.
Allowing full lets a visitor bypass every needsApproval flag you set. Only enable showPicker
when no declared tool can do something irreversible.