Build entire apps as conversations.
An open-source TypeScript framework for AI-powered apps.
You define tools — things your app can do. An AI agent decides when to use them.
What is Glove?
Traditional apps encode user flows in UI — pages, routes, navigation hierarchies. Glove replaces that wiring with an agent. You define capabilities as tools. The agent orchestrates when to call them based on what users ask for.
User: "Find me running shoes under $100"
→ Agent calls search_products tool → pushes product grid to display
User: "Add the Nike ones to my cart and check out"
→ Agent calls add_to_cart → calls checkout → pushes payment form → waits for user
Works with OpenAI, Anthropic, Google Gemini, OpenRouter, and more. Bridge external tool servers (Notion, Linear, Gmail, ...) via MCP, or add real-time voice with glove-voice.
Packages
| Package | Description | npm |
|---|---|---|
glove-core |
Core agent framework — builder, tools, model adapters, stores | |
glove-react |
React hooks, <Render> component, defineTool, client bindings |
|
glove-next |
Next.js API route handlers (SSE streaming) | |
glove-voice |
Voice pipeline — STT/TTS/VAD adapters, ElevenLabs integration | |
glove-mcp |
Model Context Protocol integration — bridge MCP servers' tools, on-demand discovery, opt-in OAuth runner | |
glove-memory |
Memory layer — entity / episodic / resources / context primitives, schema-first, BYO storage | |
glove-mesh |
Inter-agent mesh networking — direct/broadcast/ack messaging on top of the inbox primitive, BYO transport | |
glove-scratchpad |
A database emulator for LLM tool use — expose an agent's capabilities as a relational database it queries with one execute_sql tool. Resources become tables, WHERE pushes arguments down, information_schema is discovery, transactions stage outbound effects, and every statement is parsed before any tool runs; default backend is glove-sql |
|
glove-sql |
Zero-dependency, pure-JS Postgres-subset SQL engine — runtime-built tables, joins/CTEs/set-ops/subqueries/window functions, serialises to bytes; the default backend for glove-scratchpad |
|
glove-lisp |
A Lisp REPL for LLM tool use — the same resource catalog as glove-scratchpad, exposed as functions in a tiny sandboxed Clojure-flavored Lisp behind one execute_lisp tool. Branch (decide-and-act) in one call, def keeps intermediates out of context, effects are exactly-once by construction (exploration) |
|
glove-continuum-signal |
Subprocess-based runtime for triggered (async) and concurrent (warm) agents — discovery, supervision, observability, IPC | |
glovebox-core |
Authoring kit + glovebox build CLI for shipping a Glove agent as a sandboxed container |
|
glovebox-kit |
In-container runtime — WebSocket server, storage adapters, auto-injected skills/hooks | |
glovebox-client |
Client SDK for talking to a deployed Glovebox server |
Quick Start
Server-side (Node.js / CLI)
npm install glove-core
import { Glove } from "glove-core/glove";
import { Displaymanager } from "glove-core/display-manager";
import { SqliteStore } from "glove-core";
import { createAdapter } from "glove-core/models/providers";
import { z } from "zod";
// 1. Pick a model
const model = createAdapter({
provider: "anthropic", // or "openai", "openrouter", "gemini", etc.
model: "claude-sonnet-4-20250514",
stream: true,
});
// 2. Create the agent
const app = new Glove({
store: new SqliteStore({ dbPath: "./glove.db", sessionId: "my-session" }),
// Or use a simple in-memory store — see glove-core docs
model,
displayManager: new Displaymanager(),
systemPrompt: "You are a helpful assistant.",
compaction_config: {
compaction_instructions: "Summarize the conversation so far.",
},
});
// 3. Register tools
app.fold({
name: "get_weather",
description: "Get current weather for a city",
inputSchema: z.object({ city: z.string() }),
async do(input) {
const res = await fetch(`https://wttr.in/${input.city}?format=j1`);
return await res.json();
},
});
// 4. Run
const agent = app.build();
const response = await agent.processRequest("What's the weather in Tokyo?");
Full-stack (React + Next.js)
npm install glove-react glove-next
Server — app/api/chat/route.ts:
import { createChatHandler } from "glove-next";
export const POST = createChatHandler({
provider: "anthropic",
model: "claude-sonnet-4-20250514",
});
Client — app/providers.tsx:
"use client";
import { GloveProvider, GloveClient } from "glove-react";
const client = new GloveClient({
endpoint: "/api/chat",
systemPrompt: "You are a helpful assistant.",
// Optional: fetch session ID from your backend instead of passing one directly
// getSessionId: () => fetch("/api/session").then(r => r.json()).then(d => d.sessionId),
});
export function Providers({ children }: { children: React.ReactNode }) {
return <GloveProvider client={client}>{children}</GloveProvider>;
}
Client — app/chat.tsx:
"use client";
import { useGlove } from "glove-react";
export function Chat() {
const { timeline, busy, sendMessage, sessionReady } = useGlove();
if (!sessionReady) return <div>Loading session...</div>;
return (
<div>
{timeline.map((entry, i) => (
<div key={i}>
<strong>{entry.kind === "user" ? "You" : "Agent"}:</strong>{" "}
{entry.kind === "tool" ? `[${entry.name}]` : entry.text}
</div>
))}
<input
onKeyDown={(e) => {
if (e.key === "Enter") {
sendMessage(e.currentTarget.value);
e.currentTarget.value = "";
}
}}
disabled={busy}
/>
</div>
);
}
Core Concepts
Tools
Tools are capabilities your app exposes to the agent. Each tool has a name, description, Zod schema, and an async handler:
app.fold({
name: "search_products",
description: "Search the product catalog",
inputSchema: z.object({ query: z.string() }),
async do(input, display) {
const results = await catalog.search(input.query);
// pushAndForget — show UI without blocking the tool
await display.pushAndForget({
renderer: "product_grid",
input: results,
});
return results;
},
});
Tools can also pause and wait for user input:
app.fold({
name: "checkout",
description: "Start checkout process",
inputSchema: z.object({ cartId: z.string() }),
async do(input, display) {
const cart = await carts.get(input.cartId);
// pushAndWait — tool execution pauses until user submits
const payment = await display.pushAndWait({
renderer: "payment_form",
input: cart,
});
return await orders.create(cart, payment);
},
});
Model Providers
Glove supports multiple providers through a unified adapter interface:
| Provider | Env Variable | Default Model |
|---|---|---|
openai |
OPENAI_API_KEY |
gpt-4.1 |
anthropic |
ANTHROPIC_API_KEY |
claude-sonnet-4-20250514 |
openrouter |
OPENROUTER_API_KEY |
anthropic/claude-sonnet-4 |
gemini |
GEMINI_API_KEY |
gemini-2.5-flash |
minimax |
MINIMAX_API_KEY |
MiniMax-M2.5 |
kimi |
MOONSHOT_API_KEY |
kimi-k2.5 |
glm |
ZHIPUAI_API_KEY |
glm-4-plus |
mimo |
MIMO_API_KEY (+ optional MIMO_BASE_URL) |
mimo-v2.5 |
ollama |
(none) | (user-specified) |
lmstudio |
(none) | (user-specified) |
bedrock |
AWS_ACCESS_KEY_ID |
anthropic.claude-3-5-sonnet-20241022-v2:0 |
import { createAdapter } from "glove-core/models/providers";
const model = createAdapter({
provider: "openai",
model: "gpt-4.1",
stream: true,
});
Local Models
Ollama and LM Studio run locally with no API key. Pass your model name directly:
const model = createAdapter({
provider: "ollama",
model: "llama3",
baseURL: "http://localhost:9999/v1", // optional, defaults to :11434
});
Or use the adapter classes directly:
import { AnthropicAdapter } from "glove-core/models/anthropic";
import { OpenAICompatAdapter } from "glove-core/models/openai-compat";
Reasoning Models
The OpenAI-compat adapter captures provider-emitted reasoning traces
(reasoning_content / reasoning) from DeepSeek-R1 / V4, Qwen3-Thinking,
GLM-4.5 / 4.6, Kimi K2, MiniMax M2.5, OpenRouter, and any other OpenAI-shape
endpoint that follows the convention. Set reasoning: true for sensible
defaults, or pass an object for fine-grained control:
// Capture reasoning into Message.reasoning_content; echo it back on tool turns
// (required by DeepSeek V4 and MiMo for multi-turn tool flows).
createAdapter({ provider: "openai", reasoning: true });
// Hint thinking depth — works with GPT-5 / o-series, GLM, MiniMax, Kimi, etc.
createAdapter({ provider: "openai", reasoning: { effort: "high" } });
// OpenRouter-style unified reasoning object.
createAdapter({
provider: "openrouter",
reasoning: { reasoningObject: { effort: "high", max_tokens: 2000 } },
});
// Provider-specific extras (e.g. Qwen3 dashscope's `enable_thinking`).
createAdapter({
provider: "openai",
baseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1",
reasoning: { extraBody: { enable_thinking: true, thinking_budget: 1024 } },
});
// Surface reasoning in the visible message text (wrapped in <think>…</think>).
createAdapter({ provider: "openai", reasoning: { includeInText: true } });
The MiMo provider has its own dedicated adapter — keep using
{ provider: "mimo", reasoningEffort, includeReasoningInText } for it.
Prompt Caching
Enable provider prompt caching with the cache option. Pass true for sensible
defaults or an object to tune the lifetime:
// Anthropic: cache_control breakpoints on the tools + system prefix and the
// latest turn, so each follow-up request reuses the prior context.
createAdapter({ provider: "anthropic", cache: true });
// Tune the cache lifetime (Anthropic / OpenRouter honour the TTL).
createAdapter({ provider: "anthropic", cache: { ttl: "1h" } });
// Same switch on the Next.js handler.
createChatHandler({ provider: "anthropic", cache: true });
What the switch does per provider:
| Provider | Behaviour when cache is enabled |
|---|---|
anthropic |
cache_control ephemeral breakpoints on the stable prefix (tools + system) and the latest turn; ttl ("5m" / "1h") honoured |
bedrock |
cachePoint checkpoints after tools, after system, and on the latest turn (cache-capable models only); ttl maps onto Bedrock's CacheTTL |
openrouter |
cache_control breakpoints forwarded to the upstream Anthropic / Gemini model |
openai, gemini, minimax, kimi, glm, mimo, ollama, lmstudio |
Cache automatically — no request-side effect |
Every adapter also reports provider cache usage on
ModelPromptResult.cache_creation_input_tokens /
cache_read_input_tokens (and on the model_response /
model_response_complete subscriber events) regardless of the cache setting —
inspect cache_read_input_tokens to confirm cache hits.
Cache usage for billing. The counts flow through the token-accounting path
so downstream clients can bill on them: the per-turn token_consumption
subscriber event carries cache_creation_input_tokens / cache_read_input_tokens
(TokenConsumptionCounter), MemoryStore.getTokenConsumption() returns the
cumulative session total, and in React useGlove().stats exposes the running
cache totals. For Next.js, createChatHandler reports provider cache usage on
the SSE done event so the client-side agent loop threads it into stats.
Stores
Stores handle conversation persistence. Implement the StoreAdapter interface for any backend:
- MemoryStore (from
glove-react) — in-memory, great for prototyping - SqliteStore (from
glove-core) — persistent, good for server-side agents - createRemoteStore (from
glove-react) — delegates to your own API endpoints - Custom StoreAdapter — implement the
StoreAdapterinterface for any backend (Redis, Postgres, etc.)
Subscribers
Observe agent events in real time:
import type { SubscriberAdapter } from "glove-core";
const subscriber: SubscriberAdapter = {
async record(event_type, data) {
switch (event_type) {
case "text_delta": // streaming text chunk
process.stdout.write(data.text);
break;
case "tool_use": // tool invocation started
console.log(`Calling: ${data.name}`);
break;
case "tool_use_result": // tool finished
console.log(`Result: ${data.result.status}`);
break;
}
},
};
app.addSubscriber(subscriber);
Voice
Add real-time voice interaction with glove-voice:
import { createElevenLabsAdapters } from "glove-voice";
import { useGloveVoice } from "glove-react/voice";
// Set up adapters with server-side token auth
const { stt, createTTS } = createElevenLabsAdapters({
getSTTToken: () => fetch("/api/voice/stt-token").then(r => r.json()).then(d => d.token),
getTTSToken: () => fetch("/api/voice/tts-token").then(r => r.json()).then(d => d.token),
voiceId: "JBFqnCBsd6RMkjVDRZzb",
});
// In your React component
const { runnable } = useGlove({ tools, sessionId });
const voice = useGloveVoice({ runnable, voice: { stt, createTTS } });
// voice.mode: "idle" | "listening" | "thinking" | "speaking"
Two turn modes: VAD (hands-free with barge-in) and Manual (push-to-talk). Token-based auth keeps API keys server-side.
MCP Integration
Bridge tools from any Model Context Protocol server into a Glove agent with glove-mcp. The model can discover and activate MCP servers from a static catalogue mid-conversation via the discovermcp subagent.
import { mountMcp } from "glove-mcp";
import type { McpAdapter, McpCatalogueEntry } from "glove-mcp";
const ENTRIES: McpCatalogueEntry[] = [
{
id: "notion",
name: "Notion",
description: "Search, read, and edit pages in a Notion workspace.",
url: "https://mcp.notion.com/mcp",
tags: ["docs", "notes", "wiki"],
},
];
await mountMcp(runnable, {
adapter: myAdapter, // implements McpAdapter — getActive / activate / deactivate / getAccessToken
entries: ENTRIES,
clientInfo: { name: "my-app", version: "1.0.0" },
});
The framework's only auth seam is McpAdapter.getAccessToken(id) — return a bearer token however you obtained it. For the MCP authorization spec OAuth flow, glove-mcp/oauth ships an opt-in runMcpOAuth runner and reference OAuthStore implementations.
See the glove-mcp README and examples/mcp-cli for the full picture.
Glovebox
Glovebox packages a Glove agent as an isolated, network-addressable service. Wrap a built runnable with glovebox.wrap(runnable, config), run glovebox build, ship the generated dist/ (Dockerfile + nixpacks alternative + esbuild server bundle + manifest + bearer key) to any container host. The deployed server exposes one authenticated WebSocket endpoint per session; glovebox-client speaks to it. Files cross the wire as FileRef (inline | url | server | s3 | gcs), never raw bytes.
Five base images cover the common toolsets (glovebox/base, glovebox/media, glovebox/docs, glovebox/python, glovebox/browser). A storage policy DSL routes inputs and outputs by size — small payloads go inline, larger ones park on the server (or S3) and the client pulls them through the same SDK call. The kit auto-injects an environment skill, workspace skill, /output hook, and /clear-workspace hook, and prepends an environment preamble to the agent's system prompt at boot.
import { glovebox, rule, composite } from "glovebox-core"
import { agent } from "./my-agent"
export default glovebox.wrap(agent, {
base: "glovebox/media",
packages: { apt: ["ffmpeg"] },
storage: {
outputs: composite([
rule.inline({ below: "1MB" }),
rule.localServer({ ttl: "1h" }),
]),
},
})
glovebox build ./glovebox.ts
docker run -p 8080:8080 -e GLOVEBOX_KEY="$(cat dist/glovebox.key)" my-app
See the Glovebox Guide and the per-package READMEs for the full picture: glovebox-core, glovebox-kit, glovebox-client.
Architecture
Glove is built on five adapter interfaces. Swap any layer without changing application logic.
┌─────────────────────────────────────────┐
│ Agent — the agentic loop │
│ PromptMachine — model wrapper │
│ Executor — tool runner (Zod+Effect)│
│ Observer — session tracking │
│ DisplayManager — UI state machine │
│ GloveVoice — voice pipeline │
└─────────────────────────────────────────┘
▼ ▼ ▼
ModelAdapter StoreAdapter VoiceAdapters
(any LLM) (any DB) (STT/TTS/VAD)
Documentation
- Getting Started
- Core API Reference
- Server-Side Agents — CLI tools, backend services, WebSocket servers
- Voice
- Full Documentation
Examples
The repo includes six example agents:
Weather Agent
A simple terminal agent with weather lookup and activity suggestions using Ink.
pnpm weather:agent
Coding Agent
A full-featured coding assistant with file operations, bash, git tools, and a React web UI.
# Terminal mode
pnpm coding:agent
# Server + web UI
pnpm coding:server
pnpm coding:client
Next.js Trip Planner
A trip planning agent using defineTool, <Render>, renderResult, and display strategies.
cd examples/nextjs-agent && pnpm dev
Coffee Shop
An e-commerce coffee ordering experience with product catalog, cart, checkout, and voice interaction — built with defineTool, <Render>, display strategies, and glove-voice.
cd examples/coffee && pnpm dev
Lola
A voice-first movie companion with TMDB-powered tools, SileroVAD, and a cinematic amber/charcoal UI.
cd examples/lola && pnpm dev
MCP CLI
A multi-MCP server-side agent — connects to hosted MCP servers (Notion, Linear, Gmail) with the discovermcp subagent. Includes reference OAuth-flow CLIs that exercise glove-mcp/oauth.
pnpm mcp:notion-mcp-auth # one-time OAuth dance
pnpm mcp:cli # multi-MCP agent with discovery
Claude Code Skill
This repo includes an Agent Skill that gives Claude Code (and other compatible agents) deep knowledge of the Glove framework — architecture, API reference, patterns from the examples, and common gotchas.
Install with npx skills
npx skills add porkytheblack/glove -a claude-code
Or install globally (available in all projects):
npx skills add porkytheblack/glove -a claude-code -g
Once installed, Claude Code automatically uses the skill when you work with Glove code. You can also invoke it directly with /glove.
Manual install
Copy the .claude/skills/glove/ directory into your project's .claude/skills/ folder.
Development
# Install dependencies
pnpm install
# Build all packages
pnpm build
# Typecheck
pnpm typecheck
Grants
Glove Voice is supported by ElevenLabs.
License
MIT
