Getting Started with Allem SDK Agents: Agentic AI with Tool Calling for React
Learn how to build AI agents with @allem-sdk/agents — multi-step tool calling, status tracking, and typed tool definitions built on the Vercel AI SDK.

@allem-sdk/agents brings agentic AI to React. Your AI can call tools, execute multi-step workflows, and report its status back to the UI — all with typed hooks built on the Vercel AI SDK.
Installation
npm install @allem-sdk/agents @allem-sdk/ai ai @ai-sdk/react
Install your preferred AI provider:
npm install @ai-sdk/google
# or @ai-sdk/anthropic, @ai-sdk/openai
For defining tool schemas:
npm install zod
How It Works
- You define tools with descriptions and typed parameters
- The AI model decides when to call a tool based on the conversation
- The tool executes and returns results to the model
- The model uses the results to form its response
- Your UI tracks every step through the
useAllemAgenthook
Step 1: Define Tools
Use createAllemTool with a Zod schema for type-safe parameters:
// lib/tools.ts
import { createAllemTool } from "@allem-sdk/agents";
import { z } from "zod";
export const weatherTool = createAllemTool({
description: "Get the current weather for a city",
parameters: z.object({
city: z.string().describe("The city name"),
}),
execute: async ({ city }) => {
const res = await fetch(`https://api.weather.com/v1?city=${city}`);
return res.json();
},
});
export const searchTool = createAllemTool({
description: "Search the web for information",
parameters: z.object({
query: z.string().describe("The search query"),
limit: z.number().optional().describe("Max results to return"),
}),
execute: async ({ query, limit = 5 }) => {
const results = await searchAPI(query, limit);
return results;
},
});
Step 2: Create the API Route
// app/api/agent/route.ts
import { createAllemAgentHandler } from "@allem-sdk/agents";
import { google } from "@ai-sdk/google";
import { weatherTool, searchTool } from "@/lib/tools";
export const POST = createAllemAgentHandler({
providers: {
google: (model) => google(model ?? "gemini-2.0-flash"),
},
defaultProvider: "google",
systemPrompt: "You are a helpful assistant with access to weather and search tools.",
tools: {
weather: weatherTool,
search: searchTool,
},
});
Step 3: Build the UI
"use client";
import { AllemAIProvider } from "@allem-sdk/ai";
import { useAllemAgent } from "@allem-sdk/agents";
function AgentChat() {
const {
messages,
sendMessage,
agentStatus,
steps,
currentToolCalls,
isAgentRunning,
} = useAllemAgent();
return (
<div>
{/* Status indicator */}
{agentStatus === "thinking" && <p>Thinking...</p>}
{agentStatus === "calling-tool" && (
<p>Using tool: {currentToolCalls[0]?.toolName}...</p>
)}
{/* Messages */}
{messages.map((m) => (
<div key={m.id}>
<strong>{m.role === "user" ? "You" : "Agent"}:</strong>
{m.parts
?.filter((p) => p.type === "text")
.map((p) => p.text)
.join("")}
</div>
))}
{/* Step count */}
{steps.length > 0 && (
<p className="text-sm text-gray-500">{steps.length} steps taken</p>
)}
{/* Input */}
<form
onSubmit={(e) => {
e.preventDefault();
const input = e.currentTarget.elements.namedItem("msg") as HTMLInputElement;
sendMessage({ text: input.value });
input.value = "";
}}
>
<input name="msg" placeholder="Ask the agent..." disabled={isAgentRunning} />
<button type="submit" disabled={isAgentRunning}>Send</button>
</form>
</div>
);
}
export default function AgentPage() {
return (
<AllemAIProvider api="/api/agent" provider="google">
<AgentChat />
</AllemAIProvider>
);
}
Agent Status States
The agentStatus property tracks what the agent is doing:
| Status | When | What to Show |
|---|---|---|
idle |
No conversation yet, or agent finished | Ready state, input field |
thinking |
AI is generating a response | Loading spinner, "Thinking..." |
calling-tool |
AI decided to call a tool | Tool name, "Looking up weather..." |
done |
Agent finished its response | Final answer, step summary |
AgentProvider for Tool Metadata
Register tool metadata so your UI can display what tools are available:
import { AgentProvider, useAgentTools } from "@allem-sdk/agents";
const tools = [
{ name: "weather", description: "Get current weather for a city" },
{ name: "search", description: "Search the web for information" },
];
function ToolList() {
const tools = useAgentTools();
return (
<div className="text-sm text-gray-500">
<p>Available tools:</p>
<ul>
{tools.map((t) => (
<li key={t.name}>{t.name} — {t.description}</li>
))}
</ul>
</div>
);
}
export default function App() {
return (
<AllemAIProvider api="/api/agent" provider="google">
<AgentProvider tools={tools}>
<ToolList />
<AgentChat />
</AgentProvider>
</AllemAIProvider>
);
}
useAllemAgent Returns
All properties from useAllemChat, plus:
| Property | Type | Description |
|---|---|---|
agentStatus |
AgentStatus |
"idle" | "thinking" | "calling-tool" | "done" |
steps |
AgentStep[] |
Ordered list of steps the agent has taken |
currentToolCalls |
AgentToolCall[] |
Tool calls from the most recent step |
isAgentRunning |
boolean |
Whether the agent is actively processing |
Complete Agent with Status UI
"use client";
import { AllemAIProvider } from "@allem-sdk/ai";
import { useAllemAgent } from "@allem-sdk/agents";
import { Button, Card, CardBody, Spinner, Badge } from "@allem-ui/react";
import { ChatContainer, ChatList, ChatBubble, ChatInput } from "@allem-ui/chat";
function StatusBadge({ status }: { status: string }) {
const colors = {
idle: "neutral",
thinking: "warning",
"calling-tool": "primary",
done: "success",
};
return (
<Badge color={colors[status] ?? "neutral"} variant="outline">
{status === "calling-tool" ? "Using tool" : status}
</Badge>
);
}
function Agent() {
const {
messages,
sendMessage,
agentStatus,
steps,
currentToolCalls,
isAgentRunning,
} = useAllemAgent();
return (
<Card>
<CardBody>
<div className="flex items-center justify-between mb-4">
<StatusBadge status={agentStatus} />
{steps.length > 0 && (
<span className="text-sm text-gray-500">{steps.length} steps</span>
)}
</div>
<ChatContainer>
<ChatList>
{messages.map((m) => (
<ChatBubble
key={m.id}
variant={m.role === "user" ? "sent" : "received"}
avatar={m.role === "user" ? "You" : "AI"}
>
{m.parts
?.filter((p) => p.type === "text")
.map((p) => p.text)
.join("")}
</ChatBubble>
))}
{agentStatus === "calling-tool" && (
<ChatBubble variant="received" avatar="AI">
<div className="flex items-center gap-2">
<Spinner size="sm" />
Using {currentToolCalls[0]?.toolName}...
</div>
</ChatBubble>
)}
</ChatList>
<ChatInput
onSend={(text) => sendMessage({ text })}
isLoading={isAgentRunning}
placeholder="Ask the agent..."
/>
</ChatContainer>
</CardBody>
</Card>
);
}
export default function AgentPage() {
return (
<AllemAIProvider api="/api/agent" provider="google">
<Agent />
</AllemAIProvider>
);
}
What's Next
- Explore Allem SDK AI for non-agentic chat
- Try Allem SDK Forms for structured input
- Read the full agents documentation
Enjoyed this article?
I write about building products, AI, aviation, and the journey of entrepreneurship. Follow along for more.
Keep reading

Getting Started with Allem UI Date Picker: Accessible Calendars for React
Learn how to set up @allem-ui/date-picker with Calendar, DatePicker, DateRangePicker, and TimeField components built on React Aria.

Getting Started with Allem UI Data Grid: Sortable, Filterable Tables for React
Learn how to set up @allem-ui/data-grid with TanStack Table to build sortable, filterable, and paginated data tables in React.

Getting Started with Allem UI React: Build Accessible Interfaces in Minutes
Learn how to install @allem-ui/react, set up Tailwind CSS v4, and build your first accessible interface with 30+ production-ready components.