Getting Started

Build a working AI-powered chat app in 15 minutes. By the end, you will have a Next.js app where users can ask about the weather and the AI calls your custom tool to answer.

Prerequisites

Before you start, make sure you have:

What you should know: This guide assumes familiarity with React components, hooks (useState), and basic TypeScript. If you know how to build a form in React, you have everything you need. We will explain Zod and Glove-specific concepts as they come up.

Key Concepts

Three ideas power every Glove app. Refer back to these if anything later in the guide feels unfamiliar:

Tools are capabilities your app can perform — things like “get weather” or “search products.” Each tool has a name, a description (so the AI knows what it does), an input schema (defined with Zod, a validation library), and a do function that runs when the AI calls it.

The display stack lets tools show UI to the user. A tool can push a React component onto a stack that your app renders. There are two modes: pushAndWait pauses the tool until the user responds (like a confirmation dialog), while pushAndForget shows UI and lets the tool keep running (like showing a data card). We won't use the display stack in this guide, but you can learn about it in Concepts.

The agent loop is the engine that drives everything. When a user sends a message, the AI reads the available tools, calls whichever tools it needs, reads the results, and either responds or calls more tools. This loop repeats until the AI has enough information to give a final answer.

1. Install packages

Install Glove and Zod (the validation library Glove uses for tool inputs):

terminalbash
pnpm add glove-core glove-react glove-next zod

Or with npm:

terminalbash
npm install glove-core glove-react glove-next zod

Here is what each package does:

2. Create the server route

Create an API route that handles chat requests. The createChatHandler function from glove-next does this in one line — it connects to your AI provider and streams responses back:

app/api/chat/route.tstypescript
import { createChatHandler } from "glove-next";

// This creates a POST endpoint that streams AI responses
export const POST = createChatHandler({
  provider: "openai",    // or "anthropic"
  model: "gpt-4o-mini",  // or "claude-sonnet-4-20250514"
});

Set your API key in .env.local at the root of your project:

.env.localbash
OPENAI_API_KEY=sk-...

Using Anthropic? Change to provider: "anthropic" and model: "claude-sonnet-4-20250514", then set ANTHROPIC_API_KEY instead. See all supported providers.

3. Define your tools

Create a GloveClient with a system prompt and tools. This is where you tell the AI what your app can do:

lib/glove.tstypescript
import { GloveClient } from "glove-react";
import { z } from "zod";

export const gloveClient = new GloveClient({
  // Where to send chat requests (the route you created above)
  endpoint: "/api/chat",

  // Instructions for the AI — what role should it play?
  systemPrompt: "You are a helpful weather assistant.",

  // Tools — capabilities the AI can use
  tools: [
    {
      name: "get_weather",
      description: "Get the current weather for a city.",

      // Zod schema: defines what input the AI must provide
      // z.object() creates an object schema, z.string() validates a string
      inputSchema: z.object({
        city: z.string().describe("The city to get weather for"),
      }),

      // This runs when the AI decides to use this tool
      async do(input) {
        // In a real app, you'd call a weather API here
        return {
          city: input.city,
          temperature: "72°F",
          condition: "Sunny",
        };
      },
    },
  ],
});

The inputSchema tells the AI what arguments the tool expects, and Zod validates them at runtime. The do function runs when the AI calls this tool — its return value is sent back to the AI as the tool result.

4. Add the provider

Wrap your app with GloveProvider so any component can access the agent. Create a client component for the provider:

app/providers.tsxtsx
"use client";

import { GloveProvider } from "glove-react";
import { gloveClient } from "@/lib/glove";

export function Providers({ children }: { children: React.ReactNode }) {
  return <GloveProvider client={gloveClient}>{children}</GloveProvider>;
}

Then wrap your root layout with it:

app/layout.tsxtsx
import { Providers } from "./providers";

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body>
        <Providers>{children}</Providers>
      </body>
    </html>
  );
}

5. Build the chat UI

Use the useGlove hook to get the conversation state and a function to send messages:

app/page.tsxtsx
"use client";

import { useState } from "react";
import { useGlove } from "glove-react";

export default function Chat() {
  // useGlove gives you everything you need:
  // - timeline: array of messages and tool calls
  // - streamingText: text being streamed right now
  // - busy: true while the AI is thinking
  // - sendMessage: send a user message
  const { timeline, streamingText, busy, sendMessage } = useGlove();
  const [input, setInput] = useState("");

  function handleSubmit(e: React.FormEvent) {
    e.preventDefault();
    if (!input.trim() || busy) return;
    sendMessage(input.trim());
    setInput("");
  }

  return (
    <div style={{ maxWidth: 600, margin: "2rem auto", fontFamily: "sans-serif" }}>
      <h1>Weather Chat</h1>

      {/* Render the conversation */}
      <div>
        {timeline.map((entry, i) => {
          // Each entry has a "kind" that tells you what type it is
          if (entry.kind === "user") {
            return (
              <div key={i} style={{ margin: "1rem 0" }}>
                <strong>You:</strong> {entry.text}
              </div>
            );
          }

          if (entry.kind === "agent_text") {
            return (
              <div key={i} style={{ margin: "1rem 0" }}>
                <strong>Assistant:</strong> {entry.text}
              </div>
            );
          }

          if (entry.kind === "tool") {
            return (
              <div
                key={i}
                style={{
                  margin: "0.5rem 0",
                  padding: "0.5rem",
                  background: "#f0f0f0",
                  borderRadius: 4,
                  fontSize: "0.875rem",
                }}
              >
                Tool: <strong>{entry.name}</strong>{entry.status}
              </div>
            );
          }

          return null;
        })}
      </div>

      {/* Show text as it streams in */}
      {streamingText && (
        <div style={{ margin: "1rem 0", opacity: 0.7 }}>
          <strong>Assistant:</strong> {streamingText}
        </div>
      )}

      {/* Message input */}
      <form onSubmit={handleSubmit} style={{ display: "flex", gap: "0.5rem", marginTop: "1rem" }}>
        <input
          value={input}
          onChange={(e) => setInput(e.target.value)}
          placeholder="Ask about the weather..."
          disabled={busy}
          style={{ flex: 1, padding: "0.5rem" }}
        />
        <button type="submit" disabled={busy}>
          Send
        </button>
      </form>
    </div>
  );
}

6. Run it

terminalbash
pnpm dev

Open http://localhost:3000 and try asking “What's the weather in Tokyo?”. The AI will call your get_weather tool and respond with the result.

Next steps

You have a working agent. Here is where to go next: