Install
$ agentstack add skill-fellipeutaka-leon-tanstack-ai ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README — it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming — see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps — measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
TanStack AI (React)
AI chat framework with isomorphic tools, streaming, and full type safety.
Packages
@tanstack/ai— core:chat(),toolDefinition(),toServerSentEventsResponse(),maxIterations()@tanstack/ai-react— React:useChat()hook, re-exports connection adapters@tanstack/ai-client— headless:ChatClient,clientTools(),createChatClientOptions(),InferChatMessages@tanstack/ai-{openai,anthropic,gemini,ollama,grok,openrouter,fal}— adapter packages
Quick Start
Install
npm install @tanstack/ai @tanstack/ai-react @tanstack/ai-openai
Server (Next.js API Route)
import { chat, toServerSentEventsResponse } from "@tanstack/ai";
import { openaiText } from "@tanstack/ai-openai";
export async function POST(request: Request) {
const { messages } = await request.json();
const stream = chat({
adapter: openaiText("gpt-5.2"),
messages,
});
return toServerSentEventsResponse(stream);
}
Client (React)
import { useState } from "react";
import { useChat, fetchServerSentEvents } from "@tanstack/ai-react";
export function Chat() {
const [input, setInput] = useState("");
const { messages, sendMessage, isLoading } = useChat({
connection: fetchServerSentEvents("/api/chat"),
});
return (
{messages.map((message) => (
{message.role}:
{message.parts.map((part, idx) => {
if (part.type === "text") return {part.content};
if (part.type === "thinking") return {part.content};
return null;
})}
))}
{ e.preventDefault(); sendMessage(input); setInput(""); }}>
setInput(e.target.value)} disabled={isLoading} />
Send
);
}
useChat Hook
const {
messages, // UIMessage[] — current messages
sendMessage, // (content: string | MultimodalContent) => Promise
append, // (message: ModelMessage | UIMessage) => Promise
isLoading, // boolean
error, // Error | undefined
stop, // () => void — cancel current stream
reload, // () => Promise — regenerate last response
clear, // () => void — clear all messages
setMessages, // (messages: UIMessage[]) => void
addToolResult, // (result: { toolCallId, tool, output, state? }) => Promise
addToolApprovalResponse, // (response: { id, approved }) => Promise
} = useChat({
connection: fetchServerSentEvents("/api/chat"),
tools?, // client tool implementations
initialMessages?, // UIMessage[]
id?, // string — unique chat instance id
body?, // additional body params sent with every request
onResponse?, // (response) => void
onChunk?, // (chunk) => void
onFinish?, // (message) => void
onError?, // (error) => void
});
Message Structure
Messages use UIMessage with a parts array:
interface UIMessage {
id: string;
role: "user" | "assistant";
parts: (TextPart | ThinkingPart | ToolCallPart | ToolResultPart)[];
}
Render parts by type:
part.type === "text"—part.content(string)part.type === "thinking"—part.content(model reasoning, UI-only, not sent back)part.type === "tool-call"—part.name,part.input,part.output,part.statepart.type === "tool-result"—part.output,part.state
Connection Adapters
import { fetchServerSentEvents, fetchHttpStream, stream } from "@tanstack/ai-react";
// SSE (recommended — auto-reconnection)
fetchServerSentEvents("/api/chat", { headers: { Authorization: "Bearer token" } })
// HTTP stream (NDJSON)
fetchHttpStream("/api/chat")
// Custom
stream(async (messages, data, signal) => { /* return async iterable */ })
Adapters
Model passed to adapter factory — one function per activity for tree-shaking:
import { openaiText } from "@tanstack/ai-openai"; // openaiText('gpt-5.2')
import { anthropicText } from "@tanstack/ai-anthropic"; // anthropicText('claude-sonnet-4-5')
import { geminiText } from "@tanstack/ai-gemini"; // geminiText('gemini-2.5-pro')
import { ollamaText } from "@tanstack/ai-ollama"; // ollamaText('llama3')
import { grokText } from "@tanstack/ai-grok"; // grokText('grok-4')
import { openRouterText } from "@tanstack/ai-openrouter"; // openRouterText('openai/gpt-5')
Tools Overview
Two-step process: define schema with toolDefinition(), then implement with .server() or .client().
import { toolDefinition } from "@tanstack/ai";
import { z } from "zod";
const getWeatherDef = toolDefinition({
name: "get_weather",
description: "Get current weather for a location",
inputSchema: z.object({ location: z.string() }),
outputSchema: z.object({ temperature: z.number(), conditions: z.string() }),
needsApproval: false, // optional
});
// Server implementation — runs on server with DB/API access
const getWeather = getWeatherDef.server(async ({ location }) => {
const data = await fetchWeather(location);
return { temperature: data.temp, conditions: data.conditions };
});
// Client implementation — runs in browser for UI/localStorage
const getWeatherClient = getWeatherDef.client((input) => {
return { temperature: 72, conditions: "cached" };
});
For detailed tool patterns (server, client, hybrid, approval, agentic cycle), see [references/tools.md](references/tools.md).
Type Safety
Use clientTools() + createChatClientOptions() + InferChatMessages for full type inference:
import { clientTools, createChatClientOptions, type InferChatMessages } from "@tanstack/ai-client";
const tools = clientTools(updateUI, saveToStorage); // no 'as const' needed
const chatOptions = createChatClientOptions({
connection: fetchServerSentEvents("/api/chat"),
tools,
});
type ChatMessages = InferChatMessages;
// In component:
const { messages } = useChat(chatOptions);
// messages typed — part.name is discriminated union, part.input/output typed from Zod schemas
Devtools
npm install -D @tanstack/react-ai-devtools @tanstack/react-devtools
import { TanStackDevtools } from "@tanstack/react-devtools";
import { aiDevtoolsPlugin } from "@tanstack/react-ai-devtools";
Additional Guides
- Server setup patterns (Next.js, TanStack Start): see [references/server-setup.md](references/server-setup.md)
- Tool system (server, client, hybrid, approval, agentic cycle): see [references/tools.md](references/tools.md)
- Advanced features (multimodal, structured outputs, runtime adapter switching): see [references/advanced.md](references/advanced.md)
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: fellipeutaka
- Source: fellipeutaka/leon
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.