AI Tools

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.

Public Stats
Code
import { tool } from "ai"
import { z } from "zod"

// Fetch global earthquake counts (per day) from USGS for the last N days
export const publicStatsTool = tool({
  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"),
  }),
  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 interface StatsSeriesPoint {
  date: string // YYYY-MM-DD
  count: number
}

export interface PublicStatsResult {
  title: string
  series: StatsSeriesPoint[]
}

export default publicStatsTool
Public Stats
Source: USGS Earthquake Catalog
Get Weather
Code
import { tool } from "ai"
import { z } from "zod"

// Tool definition first
export const getWeatherTool = tool({
  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"),
  }),
  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
  },
})

// Re-export shape for result rendering components
export interface GetWeatherResult {
  location: string
  unit: "C" | "F"
  temperature: number
  condition: string
  high: number
  low: number
  humidity: number // 0..1
  windKph: number
  icon?: string
}

// 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 default getWeatherTool
Weather
Powered by your tool
San Francisco, California, US
14°C
Overcast
High
18°C
Low
14°C
Humidity
95%
Wind: 8 kph
News Search
Code
import { tool } from "ai"
import { z } from "zod"

// Tool first
export const newsSearchTool = tool({
  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),
  }),
  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 }
  },
})

export default newsSearchTool

// Public result shapes for UI
export interface NewsItem {
  id: string
  title: string
  url?: string
  publishedAt?: string
}

export interface NewsSearchResult {
  topic: string
  items: NewsItem[]
}

// 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[]
}
News
Topic: AI
Calculator
Code
import { tool } from "ai"
import { z } from "zod"

export const calculatorTool = tool({
  description: "Simple calculator for basic arithmetic.",
  inputSchema: z.object({
    a: z.number(),
    b: z.number(),
    operator: z.enum(["+", "-", "*", "/"]).default("+"),
  }),
  execute: async ({ a, b, operator }) => {
    let result: number
    switch (operator) {
      case "+":
        result = a + b
        break
      case "-":
        result = a - b
        break
      case "*":
        result = a * b
        break
      case "/":
        result = a / b
        break
      default:
        result = a + b
    }
    return { a, b, operator, result }
  },
})

export interface CalculatorResult {
  a: number
  b: number
  operator: "+" | "-" | "*" | "/"
  result: number
}

export default calculatorTool
{
  "a": 7,
  "b": 3,
  "operator": "+",
  "result": 10
}
Translate
Code
import { tool } from "ai"
import { z } from "zod"

export const translateTool = tool({
  description: "Translate a given text into a target language.",
  inputSchema: z.object({
    text: z.string().min(1),
    targetLanguage: z
      .string()
      .default("en")
      .describe("Target language code e.g. es, fr, de"),
    sourceLanguage: z
      .string()
      .default("en")
      .describe("Source language code e.g. en, fr, de"),
  }),
  execute: async ({ text, targetLanguage, sourceLanguage }) => {
    // Use MyMemory Translation API (free, no key). Requires explicit langpair.
    const url = `https://api.mymemory.translated.net/get?${new URLSearchParams({
      q: text,
      langpair: `${sourceLanguage}|${targetLanguage}`,
    }).toString()}`

    const res = await fetch(url)
    if (!res.ok) throw new Error(`Translate API failed: ${res.status}`)
    const data = (await res.json()) as MyMemoryResponse
    const translated = data?.responseData?.translatedText || text
    return { text, targetLanguage, translated }
  },
})

export interface TranslateResult {
  text: string
  targetLanguage: string
  translated: string
}

// MyMemory response type
interface MyMemoryResponse {
  responseData: { translatedText: string; match?: number }
  responseStatus: number
  matches?: unknown[]
}

export default translateTool
{
  "text": "Hello, world!",
  "targetLanguage": "es",
  "translated": "¡Hola, mundo!"
}
Time Now
Code
import { tool } from "ai"
import { z } from "zod"

export interface TimeNowResult {
  timeZone: string
  iso: string
  formatted: string
}

export const timeNowTool = tool({
  description: "Get the current time for a given IANA timezone.",
  inputSchema: z.object({
    timeZone: z.string().default("UTC"),
    locale: z.string().default("en-US"),
  }),
  execute: async ({ timeZone, locale }) => {
    const now = new Date()
    return {
      timeZone,
      iso: now.toISOString(),
      formatted: now.toLocaleString(locale, { timeZone }),
    }
  },
})

export default timeNowTool
{
  "timeZone": "UTC",
  "iso": "2025-09-04T13:57:29.413Z",
  "formatted": "Thu, 04 Sep 2025 13:57:29 GMT"
}
Web Search
Code
import { tool } from "ai"
import { z } from "zod"

// Tool first
export const webSearchTool = tool({
  description: "Search the web and return relevant results.",
  inputSchema: z.object({
    query: z.string().min(1),
    limit: z.number().min(1).max(20).default(5),
    lang: z.string().optional(),
    country: z.string().optional(),
  }),
  execute: async ({ query, limit, lang, country }) => {
    // Prefer Brave Search API if a token is provided, else fall back to DuckDuckGo IA API
    // You can get up to 2k queries per month for free - https://brave.com/search/api/
    const braveToken = process.env.BRAVE_SEARCH_API_KEY

    if (braveToken) {
      try {
        const params = new URLSearchParams({
          q: query,
          count: String(limit),
        })
        // Pass through optional hints where possible (Brave will ignore unknown params)
        if (country) params.set("country", country)
        if (lang) params.set("search_lang", lang)

        const url = `https://api.search.brave.com/res/v1/web/search?${params.toString()}`
        const res = await fetch(url, {
          headers: {
            Accept: "application/json",
            "X-Subscription-Token": braveToken,
          },
        })

        if (!res.ok) throw new Error(`Brave API failed: ${res.status}`)

        const data = BraveSearchSchema.parse(await res.json())

        const items = Array.isArray(data.web?.results)
          ? data.web!.results!
          : Array.isArray(data.results)
            ? data.results!
            : []

        const results: WebSearchItem[] = items
          .slice(0, limit)
          .map((r) => {
            const title = r.title || r.url || "Untitled"
            const url = r.url || ""
            let source: string | undefined
            try {
              source =
                r.profile?.long_name ||
                (url ? new URL(url).hostname : undefined)
            } catch {
              source = r.profile?.long_name || undefined
            }
            return {
              title,
              url,
              snippet: r.description || undefined,
              source: source || "Brave",
            }
          })
          .filter((r) => !!r.url)

        if (results.length > 0) {
          return { query, results }
        }
        // If Brave returned no results, fall through to DDG
      } catch (err) {
        if (err instanceof Error) {
          console.error(err.message)
        }
        // Swallow and fall back to DDG
      }
    } else {
      console.info("Brave Search API key not found, falling back to DuckDuckGo")
    }

    // DuckDuckGo Instant Answer API (free, no key). Not full web search
    const url = `https://api.duckduckgo.com/?${new URLSearchParams({
      q: query,
      format: "json",
      no_redirect: "1",
      no_html: "1",
      t: "ai-tools-registry",
      kl: lang ? `${lang}-en` : "",
    }).toString()}`

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

    const flatten = (
      items: DDGRelated[] = [],
      acc: DDGTopic[] = []
    ): DDGTopic[] => {
      for (const it of items) {
        // If group
        if ((it as DDGRelatedGroup).Topics)
          flatten((it as DDGRelatedGroup).Topics, acc)
        else if ((it as DDGTopic).FirstURL && (it as DDGTopic).Text)
          acc.push(it as DDGTopic)
      }
      return acc
    }

    const related = flatten(data.RelatedTopics)
    const results: WebSearchItem[] = related.slice(0, limit).map((r) => {
      let hostname: string | undefined
      try {
        hostname = new URL(r.FirstURL).hostname
      } catch {
        hostname = undefined
      }
      return {
        title: r.Text,
        url: r.FirstURL,
        snippet: undefined,
        source: hostname || "DuckDuckGo",
      }
    })

    return { query, results }
  },
})

export default webSearchTool

// Public result shapes for UI
export interface WebSearchItem {
  title: string
  url: string
  snippet?: string
  source?: string
}

export interface WebSearchResult {
  query: string
  results: WebSearchItem[]
}

// DuckDuckGo Instant Answer types
// DuckDuckGo Instant Answer types (runtime-validated)
export const DDGTopicSchema = z
  .object({
    FirstURL: z.string().url(),
    Text: z.string(),
  })
  .passthrough()

export type DDGTopic = z.infer<typeof DDGTopicSchema>

export const DDGRelatedGroupSchema = z
  .object({
    Name: z.string().optional(),
    Topics: z.array(DDGTopicSchema),
  })
  .passthrough()

export type DDGRelatedGroup = z.infer<typeof DDGRelatedGroupSchema>

export const DDGRelatedSchema = z.union([DDGTopicSchema, DDGRelatedGroupSchema])
export type DDGRelated = z.infer<typeof DDGRelatedSchema>

export const DDGResponseSchema = z
  .object({
    Results: z.array(DDGTopicSchema).optional(),
    RelatedTopics: z.array(DDGRelatedSchema).optional(),
  })
  .passthrough()

export type DDGResponse = z.infer<typeof DDGResponseSchema>

// Brave Search Web API types (minimal, aligned to used fields)
export const BraveProfileSchema = z
  .object({
    name: z.string().optional(),
    url: z.string().url().optional(),
    long_name: z.string().optional(),
    img: z.string().url().optional(),
  })
  .passthrough()

export const BraveWebResultSchema = z
  .object({
    title: z.string().optional(),
    url: z.string().url().optional(),
    description: z.string().optional(),
    profile: BraveProfileSchema.optional(),
  })
  .passthrough()

export const BraveWebSchema = z
  .object({
    results: z.array(BraveWebResultSchema).optional(),
  })
  .passthrough()

export const BraveSearchSchema = z
  .object({
    type: z.string().optional(),
    web: BraveWebSchema.optional(),
    results: z.array(BraveWebResultSchema).optional(),
  })
  .passthrough()

export type BraveProfile = z.infer<typeof BraveProfileSchema>
export type BraveWebResult = z.infer<typeof BraveWebResultSchema>
export type BraveWeb = z.infer<typeof BraveWebSchema>
export type BraveSearchResponse = z.infer<typeof BraveSearchSchema>
Markdown
Code
import { tool } from "ai"
import { z } from "zod"

export interface MarkdownResult {
  markdown: string
}

export const markdownTool = tool({
  description: "Render Markdown content (recommended in UI).",
  inputSchema: z.object({
    markdown: z.string().min(1),
  }),
  execute: async ({ markdown }) => {
    return { markdown }
  },
})

export default markdownTool
Markdown
Rendered with react-markdown

Hello World

This is markdown.

  • Item one
  • Item two

Tip: You can copy the tool code from the left.

QR Code Generator
Code
import { tool } from "ai"
import { z } from "zod"
import QRCode from "qrcode"

export const qrCodeTool = tool({
  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"),
  }),
  execute: async ({ data, size }) => {
    const output = await QRCode.toDataURL(data, {
      width: size,
      margin: 4,
    })

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

    return result
  },
})

export interface QRCodeResult {
  data: string
  size: number
  output: string
}

export default qrCodeTool
QR Code
https://ai-tools-registry.vercel.app
QR code encoding 'https://ai-tools-registry.vercel.app'
Size: 300px
All Tools
Public Stats
AI SDK tool that fetches global earthquake counts from USGS and renders a chart.
Get Weather
AI SDK tool that returns mock weather for a location. Includes a WeatherCard renderer.
News Search
Return recent headlines for a topic (mock). Includes a NewsList renderer.
Calculator
Simple calculator tool for basic arithmetic.
Translate
Translate a string to a target language (mock).
Time Now
Get the current time for a timezone.
Web Search
AI SDK tool that searches the web. Includes a results renderer.
Markdown Renderer
AI SDK tool that transports markdown; render with streamdown.
QR Code Generator
AI SDK tool that generates QR codes for text or URLs. Includes a QR code display component.