AI Tools Registry

Install AI SDK tools into your project with the shadcn CLI. Some tools include UI components to render the result in your chat view.
To learn how to use tools, check out the AI SDK docs.

To add your own tools to the AI Tools Registry, file a PR on our public GitHub repository.

New
Image Generation
API key required
import { openai } from "@ai-sdk/openai"
import {
  tool,
  experimental_generateImage as generateImage,
  type UIToolInvocation,
  type JSONValue,
} from "ai"

import { ImageResultSchema, ImageInputSchema } from "./schema"
import type { ImageResult, ImageItem } from "./schema"

export const imageOpenAITool = tool({
  name: "image-openai",
  description: "Generate images using OpenAI via Vercel AI SDK (gpt-image-1).",
  inputSchema: ImageInputSchema,
  outputSchema: ImageResultSchema,
  execute: async ({
    prompt,
    referenceImageUrl,
    n,
    aspectRatio,
    seed,
    negativePrompt,
  }): Promise<ImageResult> => {
    type GenerateImageReturn = Awaited<ReturnType<typeof generateImage>>
    type BaseGenerated = NonNullable<GenerateImageReturn["images"]>[number]
    type EnhancedGenerated = BaseGenerated & {
      url?: string
      base64?: string
      contentType?: string
      width?: number
      height?: number
    }
    type ProviderOptions = NonNullable<
      Parameters<typeof generateImage>[0]["providerOptions"]
    >
    const coerceAspectRatio = (
      ar?: string
    ): `${number}:${number}` | undefined =>
      ar && /^\d+:\d+$/.test(ar) ? (ar as `${number}:${number}`) : undefined

    const buildProviderOptions = (
      np?: unknown
    ): ProviderOptions | undefined => {
      if (typeof np === "string")
        return {
          negativePrompt: { value: np } as Record<string, JSONValue>,
        } as ProviderOptions
      if (np && typeof np === "object")
        return {
          negativePrompt: np as Record<string, JSONValue>,
        } as ProviderOptions
      return undefined
    }

    const normalizeImages = (images?: BaseGenerated[] | null): ImageItem[] =>
      images?.map((img) => {
        const x = img as EnhancedGenerated
        return {
          url: x.url,
          base64: x.base64,
          mimeType: (x as { mimeType?: string }).mimeType || x.contentType,
          width: x.width,
          height: x.height,
        }
      }) ?? []

    const buildImageResult = (params: {
      provider: string
      prompt: string
      images: ImageItem[]
      aspectRatio?: string
      seed?: number
    }): ImageResult => params as ImageResult

    const ar = coerceAspectRatio(aspectRatio)
    const providerOptions = buildProviderOptions(negativePrompt)
    const { images } = await generateImage({
      model: openai.image("openai/gpt-image-1"),
      prompt,
      aspectRatio: ar,
      seed,
      n,
      ...(providerOptions ? { providerOptions } : {}),
    })

    const out = normalizeImages(images)
    return buildImageResult({
      provider: "openai",
      prompt,
      images: out,
      aspectRatio,
      seed,
    })
  },
})

export type ImageToolType = UIToolInvocation<typeof imageOpenAITool>

Generated Images

“A serene landscape with mountains at sunrise”

demo
A serene landscape with mountains at sunrise
A serene landscape with mountains at sunrise
A serene landscape with mountains at sunrise
Web Search
API key required
import { perplexity } from "@ai-sdk/perplexity"
import { tool, generateObject, UIToolInvocation } from "ai"
import { z } from "zod"

import { WebSearchSchema, WebSearchResult } from "./schema"

export const webSearchPerplexityTool = tool({
  name: "websearch-perplexity",
  description:
    "Search the web using Perplexity Sonar via Vercel AI SDK. Requires Perplexity API key. See Vercel docs.",
  inputSchema: z.object({
    query: z.string().min(1),
    limit: z.number().min(1).max(20).default(5),
  }),
  outputSchema: WebSearchSchema,
  execute: async ({ query, limit }) => {
    // Use Sonar (or Sonar Pro) for search-grounded results

    const { object } = await generateObject({
      model: perplexity("sonar"),
      schema: WebSearchSchema,
      system:
        "You are a search assistant. Return strictly the JSON schema provided. For each result include title, url, a short snippet, and a source hostname.",
      prompt: `Search the web for: ${query}. Return up to ${limit} high-quality, diverse results with proper URLs.`,
    })

    // Ensure limit is respected in case the model over-returns
    const normalized: WebSearchResult = {
      query,
      results: (object.results || []).slice(0, limit).map((r) => {
        let source = r.source
        if (!source) {
          try {
            source = new URL(r.url).hostname
          } catch {
            source = undefined
          }
        }
        return { title: r.title, url: r.url, snippet: r.snippet, source }
      }),
    }
    return normalized
  },
})

export type WebSearchToolInvocation = UIToolInvocation<
  typeof webSearchPerplexityTool
>

Web Search

Query chatgpt
  • ChatGPT is a conversational AI model developed by OpenAI that can assist with a wide range of tasks.
    openai.com
  • What is ChatGPT?en.wikipedia.org
    ChatGPT is a generative artificial intelligence chatbot developed by OpenAI and launched in November 2022.
    en.wikipedia.org
  • We’ve trained a model called ChatGPT which interacts in a conversational way.
    openai.com
Public Stats
import { UIToolInvocation, tool } from "ai"
import { z } from "zod"

// Fetch global earthquake counts (per day) from USGS for the last N days
export const StatsSeriesPointSchema = z.object({
  date: z.string(),
  count: z.number(),
})

export const PublicStatsSchema = z.object({
  title: z.string(),
  series: z.array(StatsSeriesPointSchema),
})

export type StatsSeriesPoint = z.infer<typeof StatsSeriesPointSchema>
export type PublicStatsResult = z.infer<typeof PublicStatsSchema>

export const publicStatsTool = tool({
  name: "stats",
  description:
    "Fetch daily counts of global earthquakes from USGS for the last N days.",
  inputSchema: z.object({
    daysBack: z
      .number()
      .int()
      .min(1)
      .max(365)
      .default(30)
      .describe("How many days back from today (UTC) to include"),
    minMagnitude: z
      .number()
      .min(0)
      .max(10)
      .default(5)
      .describe("Minimum magnitude to include"),
  }),
  outputSchema: PublicStatsSchema,
  execute: async ({ daysBack, minMagnitude }): Promise<PublicStatsResult> => {
    const end = new Date()
    const start = new Date(end.getTime() - daysBack * 24 * 60 * 60 * 1000)

    const fmt = (d: Date) => d.toISOString().slice(0, 10)

    const params = new URLSearchParams({
      format: "geojson",
      starttime: fmt(start),
      endtime: fmt(end),
      minmagnitude: String(minMagnitude),
    })
    const url = `https://earthquake.usgs.gov/fdsnws/event/1/query?${params.toString()}`
    const res = await fetch(url)
    if (!res.ok) throw new Error(`USGS API failed: ${res.status}`)
    const data = (await res.json()) as {
      features?: Array<{ properties?: { time?: number } }>
    }

    const counts = new Map<string, number>()
    for (const f of data.features ?? []) {
      const t = f?.properties?.time
      if (!Number.isFinite(t)) continue
      const day = new Date(Number(t)).toISOString().slice(0, 10)
      counts.set(day, (counts.get(day) || 0) + 1)
    }

    const series: StatsSeriesPoint[] = []
    for (let i = daysBack; i >= 0; i--) {
      const d = new Date(end.getTime() - i * 24 * 60 * 60 * 1000)
      const day = d.toISOString().slice(0, 10)
      series.push({ date: day, count: counts.get(day) || 0 })
    }

    return { title: `Global M${minMagnitude}+ earthquakes`, series }
  },
})

export default publicStatsTool

export type StatsToolType = UIToolInvocation<typeof publicStatsTool>

Global M5+ earthquakes

Source: USGS Earthquake Catalog

Get Weather
import { UIToolInvocation, tool } from "ai"
import { z } from "zod"

// Tool definition first
export const GetWeatherSchema = z.object({
  location: z.string(),
  unit: z.enum(["C", "F"]),
  temperature: z.number(),
  condition: z.string(),
  high: z.number(),
  low: z.number(),
  humidity: z.number(),
  windKph: z.number(),
  icon: z.string().optional(),
})

export type GetWeatherResult = z.infer<typeof GetWeatherSchema>

// Tool definition first
export const getWeatherTool = tool({
  name: "weather",
  description: "Get the current weather for a location.",
  inputSchema: z.object({
    location: z.string().describe("City name, address or coordinates"),
    unit: z.enum(["C", "F"]).default("C"),
  }),
  outputSchema: GetWeatherSchema,
  execute: async ({ location, unit }) => {
    const { latitude, longitude, name } = await geocodeLocation(location)

    const params = new URLSearchParams({
      latitude: String(latitude),
      longitude: String(longitude),
      current: [
        "temperature_2m",
        "relative_humidity_2m",
        "wind_speed_10m",
        "weather_code",
      ].join(","),
      daily: ["temperature_2m_max", "temperature_2m_min"].join(","),
      timezone: "auto",
      temperature_unit: unit === "F" ? "fahrenheit" : "celsius",
      wind_speed_unit: "kmh",
    })

    const url = `https://api.open-meteo.com/v1/forecast?${params.toString()}`
    const res = await fetch(url)
    if (!res.ok) throw new Error(`Weather API failed: ${res.status}`)
    const data = (await res.json()) as ForecastResponse

    const current = data?.current
    const daily = data?.daily
    if (!current || !daily) throw new Error("Malformed weather API response")

    const weatherCode = Number(current.weather_code)
    const mapped = mapWeatherCode(weatherCode)

    const result: GetWeatherResult = {
      location: name,
      unit,
      temperature: Math.round(Number(current.temperature_2m)),
      condition: mapped.condition,
      high: Math.round(Number(daily.temperature_2m_max?.[0])),
      low: Math.round(Number(daily.temperature_2m_min?.[0])),
      humidity: Math.max(
        0,
        Math.min(1, Number(current.relative_humidity_2m) / 100)
      ),
      windKph: Math.round(Number(current.wind_speed_10m)),
      icon: mapped.icon,
    }

    return result
  },
})

// API response types (from Open-Meteo)
interface GeocodeItem {
  id: number
  name: string
  latitude: number
  longitude: number
  elevation?: number
  country_code?: string
  admin1?: string
  timezone?: string
}

interface GeocodeResponse {
  results?: GeocodeItem[]
}

interface ForecastCurrent {
  time: string
  interval: number
  temperature_2m: number
  relative_humidity_2m: number
  wind_speed_10m: number
  weather_code: number
}

interface ForecastDaily {
  time: string[]
  temperature_2m_max: number[]
  temperature_2m_min: number[]
}

interface ForecastResponse {
  current: ForecastCurrent
  daily: ForecastDaily
}

// Helper functions (hoisted)
async function geocodeLocation(location: string): Promise<{
  latitude: number
  longitude: number
  name: string
}> {
  // Allow "lat,lon" inputs without geocoding
  const coordMatch = location
    .trim()
    .match(/^\s*(-?\d+(?:\.\d+)?)\s*,\s*(-?\d+(?:\.\d+)?)\s*$/)
  if (coordMatch) {
    const latitude = parseFloat(coordMatch[1])
    const longitude = parseFloat(coordMatch[2])
    return {
      latitude,
      longitude,
      name: `${latitude.toFixed(3)}, ${longitude.toFixed(3)}`,
    }
  }

  const url = `https://geocoding-api.open-meteo.com/v1/search?name=${encodeURIComponent(
    location
  )}&count=1&language=en&format=json`
  const res = await fetch(url)
  if (!res.ok) throw new Error(`Geocoding failed: ${res.status}`)
  const data = (await res.json()) as GeocodeResponse
  const first = data?.results?.[0]
  if (!first) throw new Error(`Location not found: ${location}`)
  const nameParts = [first.name, first.admin1, first.country_code].filter(
    Boolean
  )
  return {
    latitude: first.latitude,
    longitude: first.longitude,
    name: nameParts.join(", "),
  }
}

function mapWeatherCode(code: number): { condition: string; icon?: string } {
  switch (code) {
    case 0:
      return { condition: "Clear sky", icon: "weather-sun" }
    case 1:
      return { condition: "Mainly clear", icon: "weather-sun" }
    case 2:
      return { condition: "Partly cloudy", icon: "weather-partly" }
    case 3:
      return { condition: "Overcast", icon: "weather-cloud" }
    case 45:
    case 48:
      return { condition: "Fog", icon: "weather-fog" }
    case 51:
    case 53:
    case 55:
    case 56:
    case 57:
      return { condition: "Drizzle", icon: "weather-drizzle" }
    case 61:
    case 63:
    case 65:
    case 66:
    case 67:
      return { condition: "Rain", icon: "weather-rain" }
    case 71:
    case 73:
    case 75:
    case 77:
      return { condition: "Snow", icon: "weather-snow" }
    case 80:
    case 81:
    case 82:
      return { condition: "Showers", icon: "weather-showers" }
    case 85:
    case 86:
      return { condition: "Snow showers", icon: "weather-snow" }
    case 95:
    case 96:
    case 99:
      return { condition: "Thunderstorm", icon: "weather-thunder" }
    default:
      return { condition: "Unknown" }
  }
}

export type WeatherToolType = UIToolInvocation<typeof getWeatherTool>

Weather

San Francisco

weather-sun

Current conditions

21°C

Sunny

High 24°C • Low 18°C
Humidity
45%
Wind
8 kph
News Search
import { UIToolInvocation, tool } from "ai"
import { z } from "zod"

export const NewsItemSchema = z.object({
  id: z.string(),
  title: z.string(),
  url: z.string().url().optional(),
  publishedAt: z.string().optional(),
})

export const NewsSearchSchema = z.object({
  topic: z.string(),
  items: z.array(NewsItemSchema),
})

// Tool first
export const newsSearchTool = tool({
  name: "news",
  description: "Return recent headlines related to a topic.",
  inputSchema: z.object({
    topic: z.string().min(1),
    limit: z.number().min(1).max(20).default(5),
  }),
  outputSchema: NewsSearchSchema,
  execute: async ({ topic, limit }) => {
    // Use Hacker News Algolia Search API (no API key required)
    const url = `https://hn.algolia.com/api/v1/search?${new URLSearchParams({
      query: topic,
      tags: "story",
      hitsPerPage: String(limit),
    }).toString()}`

    const res = await fetch(url)
    if (!res.ok) throw new Error(`News API failed: ${res.status}`)
    const data = (await res.json()) as AlgoliaSearchResponse

    const items: NewsItem[] = (data.hits || []).map((h) => ({
      id: String(h.objectID),
      title: h.title || h.story_title || "(untitled)",
      url: h.url || h.story_url || undefined,
      publishedAt: h.created_at || undefined,
    }))

    return { topic, items }
  },
})

// Public result shapes for UI
export type NewsItem = z.infer<typeof NewsItemSchema>
export type NewsSearchResult = z.infer<typeof NewsSearchSchema>

// Response types from Algolia API
interface AlgoliaHit {
  objectID: string
  title?: string
  story_title?: string
  url?: string
  story_url?: string
  created_at?: string
}

interface AlgoliaSearchResponse {
  hits: AlgoliaHit[]
}

export type NewsToolType = UIToolInvocation<typeof newsSearchTool>

News

Latest on AI
QR Code Generator
import { UIToolInvocation, tool } from "ai"
import { z } from "zod"
import QRCode from "qrcode"

export const QRCodeSchema = z.object({
  data: z.string(),
  size: z.number(),
  output: z.string(),
})

export type QRCodeResult = z.infer<typeof QRCodeSchema>

export const qrCodeTool = tool({
  name: "qrcode",
  description: "Generate QR codes for text, URLs, or other data.",
  inputSchema: z.object({
    data: z
      .string()
      .min(1)
      .describe("The text or URL to encode in the QR code"),
    size: z
      .number()
      .min(100)
      .max(500)
      .default(300)
      .describe("Size of the QR code in pixels"),
  }),
  outputSchema: QRCodeSchema,
  execute: async ({ data, size }) => {
    const output = await QRCode.toDataURL(data, {
      width: size,
      margin: 4,
    })

    const result: QRCodeResult = {
      data,
      size,
      output,
    }

    return result
  },
})

export type QRCodeToolType = UIToolInvocation<typeof qrCodeTool>

QR Code

https://ai-tools-registry.vercel.app

QR code encoding 'https://ai-tools-registry.vercel.app'
All Tools
Image Generation
AI SDK tool that generates images from text prompts. Includes a grid renderer.
Image Generation (OpenAI)
Generate images using OpenAI via Vercel AI SDK. Shares renderer.
Image Generation (FAL.ai)
Generate images using FAL.ai via Vercel AI SDK. Shares renderer.
Image Generation (Runware)
Generate images using Runware via Vercel AI SDK. Shares renderer.
Image Generation (Gemini)
Generate images using Google Gemini via Vercel AI SDK. Shares renderer.
Web Search
AI SDK tool that searches the web. Includes a results renderer.
Web Search (Perplexity)
Web search via Perplexity Sonar using Vercel AI SDK (requires Perplexity API key). Shares renderer.
Web Search (EXA)
Web search via EXA API (requires EXA_API_KEY). Shares renderer.
Web Search (Firecrawl)
Web search via Firecrawl API (requires FIRECRAWL_API_KEY). Shares renderer.
Web Search (Brave)
Web search via Brave Search API (requires BRAVE_SEARCH_API_KEY). Shares renderer.