Skip to content
Development
Skill

/tanstack-ai

TanStack AI (alpha) provider-agnostic type-safe chat with streaming for OpenAI, Anthropic, Gemini, Ollama. Use for chat APIs, React/Solid frontends with useChat/ChatClient, isomorphic tools, tool approval flows, agent loops, multimodal inputs, or troubleshooting streaming and

From plugin
secondsky-claude-skills
219183 skills42 agents62 commands2 MCP
Install
$ npx -y skills add secondsky/claude-skills --skill tanstack-ai --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/tanstack-ai

Context preview

The summary Claude sees to decide when to auto-load this skill.

TanStack AI (alpha) provider-agnostic type-safe chat with streaming for OpenAI, Anthropic, Gemini, Ollama. Use for chat APIs, React/Solid frontends with useChat/ChatClient, isomorphic tools, tool approval flows, agent loops, multimodal inputs, or troubleshooting streaming and

SKILL.md

tanstack-ai.SKILL.md
name: tanstack-ai
description: "TanStack AI (alpha) provider-agnostic type-safe chat with streaming for OpenAI, Anthropic, Gemini, Ollama. Use for chat APIs, React/Solid frontends with useChat/ChatClient, isomorphic tools, tool approval flows, agent loops, multimodal inputs, or troubleshooting streaming and tool definitions."

metadata:
  keywords:
    - TanStack AI
    - "@tanstack/ai"
    - "@tanstack/ai-react"
    - "@tanstack/ai-client"
    - "@tanstack/ai-solid"
    - "@tanstack/ai-openai"
    - "@tanstack/ai-anthropic"
    - "@tanstack/ai-gemini"
    - "@tanstack/ai-ollama"
    - toolDefinition
    - client tools
    - server tools
    - tool approval
    - agent loop
    - streaming
    - SSE
    - connection adapters
    - multimodal
    - type-safe models
    - TanStack Start
    - Next.js API
    - toStreamResponse
    - fetchServerSentEvents
    - chat
    - useChat
    - ChatClient
    - needsApproval

license: MIT

TanStack AI (Provider-Agnostic LLM SDK)

**Status**: Production Ready ✅ **Last Updated**: 2025-12-09 **Dependencies**: Node.js 20+, TypeScript 5+; React 19+ for `@tanstack/ai-react`; Solid 1.8+ for `@tanstack/ai-solid` **Latest Versions**: @tanstack/ai@latest (alpha), @tanstack/ai-react@latest, @tanstack/ai-client@latest, adapters: @tanstack/ai-openai@latest @tanstack/ai-anthropic@latest @tanstack/ai-gemini@latest @tanstack/ai-ollama@latest

---

Quick Start (7 Minutes)

1) Install core + adapter

pnpm add @tanstack/ai @tanstack/ai-react @tanstack/ai-openai
# swap adapters as needed: @tanstack/ai-anthropic @tanstack/ai-gemini @tanstack/ai-ollama
pnpm add zod              # recommended for tool schemas

**Why this matters:**

  • Core is framework-agnostic; React binding just wraps the headless client. citeturn1search3
  • Adapters abstract provider quirks so you can change models without rewriting code. citeturn1search3

2) Ship a streaming chat endpoint (Next.js or TanStack Start)

// app/api/chat/route.ts (Next.js) or src/routes/api/chat.ts (TanStack Start)
import { chat, toStreamResponse } from '@tanstack/ai'
import { openai } from '@tanstack/ai-openai'
import { tools } from '@/tools/definitions' // definitions only

export async function POST(request: Request) {
  const { messages, conversationId } = await request.json()
  const stream = chat({
    adapter: openai(),
    messages,
    model: 'gpt-4o',
    tools,
  })
  return toStreamResponse(stream)
}

**CRITICAL:**

  • Pass tool **definitions** to the server so the LLM can request them; implementations live in their runtimes. citeturn0search7
  • Always stream; chunked responses keep UIs responsive and reduce token waste. citeturn0search1

3) Wire the client with `useChat` + SSE

// components/Chat.tsx
import { useChat, fetchServerSentEvents } from '@tanstack/ai-react'
import { clientTools } from '@tanstack/ai-client'
import { updateUIDef } from '@/tools/definitions'

const updateUI = updateUIDef.client(({ message }) => {
  alert(message)
  return { success: true }
})

export function Chat() {
  const tools = clientTools(updateUI)
  const { messages, sendMessage, isLoading, approval } = useChat({
    connection: fetchServerSentEvents('/api/chat'),
    tools,
  })

  return (
    <form onSubmit={e => { e.preventDefault(); sendMessage(e.currentTarget.prompt.value) }}>
      <textarea name="prompt" disabled={isLoading} />
      {approval?.pending && (
        <button type="button" onClick={() => approval.approve()}>
          Approve tool
        </button>
      )}
    </form>
  )
}

**CRITICAL:**

  • Use `fetchServerSentEvents` (or matching adapter) to mirror the streaming response. citeturn0search0
  • Keep client tool names identical to definitions to avoid “tool not found” errors. citeturn0search7

---

The 4-Step Setup Process

Step 1: Choose provider + model safely

  • Add the correct adapter and set the matching API key (`OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, `GEMINI_API_KEY`, or Ollama host).
  • Prefer per-model option typing from adapters to avoid invalid options (e.g., vision-only fields). citeturn1search3

Step 2: Define tools once, implement per runtime

// tools/definitions.ts
import { z, toolDefinition } from '@tanstack/ai'

export const getWeatherDef = toolDefinition({
  name: 'getWeather',
  description: 'Get current weather for a city',
  inputSchema: z.object({ city: z.string() }),
  needsApproval: true,
})

export const getWeather = getWeatherDef.server(async ({ city }) => {
  const data = await fetch(`https://api.weather.gov/points?q=${city}`).then(r => r.json())
  return { summary: data.properties?.relativeLocation?.properties?.city ?? city }
})

export const showToast = getWeatherDef.client(({ city }) => {
  console.log(`Showing toast for ${city}`)
  return { acknowledged: true }
})

**Key Points:**

  • `needsApproval: true` forces explicit user approval for sensitive actions. citeturn0search1
  • Keep tools single-purpose and idempotent; return structured objects instead of throwing errors. citeturn0search1

Step 3: Create connection adapter + chat options

  • Server: `toStreamResponse(stream)` for HTTP streaming; `toServerSentEventsStream` helper for Server-Sent Events. citeturn0search3turn0search4
  • Client: `fetchServerSentEvents('/api/chat')` or a custom adapter for websockets if needed. citeturn0search0
  • Configure `agentLoopStrategy` (e.g., `maxIterations(8)`) to cap tool recursion. citeturn1search4

Step 4: Add observability + guardrails

  • Log tool executions and stream chunks for debugging; alpha exposes hooks while devtools are in progress. citeturn0search1
  • Validate inputs with Zod; fail fast and return typed error objects.
  • Enforce timeouts on external API calls inside tools to prevent stuck agent loops.

---

Critical Rules

Always Do

✅ Stream responses; avoid waiting for full completions. citeturn0search1 ✅ Pass **definitions** to the server and **implem

Read more
Ships withsecondsky-claude-skills

145 production-ready skills for Claude Code CLI 🔌 Platform / Harness Support These plugins ship as Claude Code marketplace plugins (.claude-plugin/ manifests) and Codex CLI plugins (.codex-plugin/ manifests).

Get the whole plugin

Other skills on secondsky-claude-skills.