A provider tool runs on the provider's servers. Use it for web search or code execution that you do not host.
Import the factory of the provider tool from the /tools subpath of your adapter, and add it to chat({ tools }). The provider runs the tool. TypeScript stops you if the model does not support that tool.
Each adapter has its provider tools on a /tools subpath:
import { webSearchTool } from '@tanstack/ai-anthropic/tools'
import { codeInterpreterTool } from '@tanstack/ai-openai/tools'
import { googleSearchTool } from '@tanstack/ai-gemini/tools'This route gives Claude web search, with at most 3 searches for each request:
// api/chat.ts
import {
chat,
chatParamsFromRequest,
toServerSentEventsResponse,
} from '@tanstack/ai'
import { anthropicText } from '@tanstack/ai-anthropic'
import { webSearchTool } from '@tanstack/ai-anthropic/tools'
export async function POST(request: Request) {
const { messages, threadId, runId } = await chatParamsFromRequest(request)
const stream = chat({
adapter: anthropicText('claude-opus-5-5'),
messages,
threadId,
runId,
tools: [
webSearchTool({
name: 'web_search',
type: 'web_search_20250305',
max_uses: 3,
}),
],
})
return toServerSentEventsResponse(stream)
}Ask your chat "Summarize today's AI news." The model searches the web on Anthropic's servers and answers with what it found. You wrote no search code.
| Provider | Tools |
|---|---|
| Anthropic | webSearchTool, webFetchTool, codeExecutionTool, computerUseTool, bashTool, textEditorTool, memoryTool. See Anthropic adapter. |
| OpenAI | webSearchTool, webSearchPreviewTool, fileSearchTool, imageGenerationTool, codeInterpreterTool, mcpTool, computerUseTool, localShellTool, shellTool, applyPatchTool. See OpenAI adapter. |
| Gemini | codeExecutionTool, fileSearchTool, googleSearchTool, googleSearchRetrievalTool, googleMapsTool, urlContextTool, computerUseTool. See Gemini adapter. |
| OpenRouter | webSearchTool, webFetchTool. See OpenRouter adapter. |
| Grok | grokWebSearchTool, grokXSearchTool, grokFileSearchTool, grokMCPTool. See Grok adapter. |
| Groq | Function tools only. No provider tools. |
Anthropic and OpenAI can also load hosted skills into their code sandbox. See Provider Skills.
The supports.tools array of each model in model-meta.ts is the source of truth:
For the exact list of a model, open the adapter page, or read the supports.tools array of the model in model-meta.ts.
If the names are different, you can put a factory tool and your own function in the same tools array.
Provider tool results, such as Anthropic web_search sources and web_fetch page contents, stay on the assistant message. When you send the earlier conversation into the next chat() call, the model still sees them. You do not need extra code:
import { chat, StreamProcessor } from '@tanstack/ai'
import { anthropicText } from '@tanstack/ai-anthropic'
import { webSearchTool } from '@tanstack/ai-anthropic/tools'
const adapter = anthropicText('claude-opus-5-5')
const tools = [webSearchTool({ name: 'web_search', type: 'web_search_20250305' })]
const processor = new StreamProcessor()
for await (const chunk of chat({
adapter,
tools,
messages: [{ role: 'user', content: 'Find two sources on the drone market.' }],
threadId: 'thread-1',
})) {
processor.processChunk(chunk)
}
processor.finalizeStream()
// The follow-up turn can still cite the previous search results.
const followUp = chat({
adapter,
tools,
messages: [
...processor.getMessages(),
{ role: 'user', content: 'List the exact sources you used.' },
],
threadId: 'thread-1',
})OpenAI and Gemini web search calls put a metadata.sources array on the assistant tool-call part. Each source has a url, and it can also have a title or a pageAge. Use the list to show source links:
import { getProviderExecutedMetadata } from '@tanstack/ai'
import type { UIMessage } from '@tanstack/ai'
function logSources(message: UIMessage) {
for (const part of message.parts) {
if (part.type !== 'tool-call') continue
const sources = getProviderExecutedMetadata(part)?.sources ?? []
for (const source of sources) {
console.log(source.title ?? source.url, source.url)
}
}
}The provider runs the search, so do not run this tool call in your app. The raw provider response stays in metadata.openai or metadata.gemini.
These OpenAI tools work the other way: the model returns the call, and your app runs it.
See OpenAI: Advanced and computerUseTool.
To run computer use actions on a browser or a desktop without your own action code, use createComputerUse from @tanstack/ai-computer-use. The same tools work with OpenAI and Anthropic, and each thread gets its own machine. See Computer use.
import { chat } from '@tanstack/ai'
import { openaiText } from '@tanstack/ai-openai'
import { createComputerUse } from '@tanstack/ai-computer-use'
import { playwrightComputer } from '@tanstack/ai-computer-playwright'
declare const threadId: string
const computerUse = createComputerUse({
driver: playwrightComputer({ startUrl: 'https://tanstack.com' }),
})
const stream = chat({
adapter: openaiText('gpt-6.1-sol'),
messages: [{ role: 'user', content: 'Find the TanStack AI docs' }],
threadId,
tools: computerUse.tools,
middleware: [computerUse.middleware],
interrupts: [computerUse.interrupt],
})Did you use createWebSearchTool from @tanstack/ai-openrouter? See Migration Guide section 5.