Skip to content

Commit e32583e

Browse files
feat: Provider tools on /tools subpath with per-model type gating (#466)
* feat(ai): add ProviderTool phantom-branded subtype * feat(ai): add toolCapabilities to TextAdapter type channel * feat(ai): gate TextActivityOptions.tools on model toolCapabilities * fix(ai): prevent ProviderTool subsumption in TextActivityOptions.tools union * test(ai): type tests for TextActivityOptions.tools gating * test(ai): fix import order and cover broad-TKind ProviderTool rejection * feat(ai-anthropic): add supports.tools and ToolCapabilitiesByName map * refactor(ai-anthropic): use Array for supports.tools to match supports.input * feat(ai-anthropic): brand provider tool factory return types * refactor(ai-anthropic): replace tools barrel with named re-exports * fix(ai-anthropic): build AnthropicTool union from branded types * Revert "fix(ai-anthropic): build AnthropicTool union from branded types" This reverts commit 801255b. * feat(ai-anthropic): thread toolCapabilities through text adapter * feat(ai-anthropic): export AnthropicChatModelToolCapabilitiesByName * feat(ai-anthropic): add /tools subpath export * test(ai-anthropic): per-model type tests for provider tools * test(ai-anthropic): cover all unsupported tools in haiku-3-5 negative case * feat(ai-openai): add ToolCapabilitiesByName and expanded supports.tools * feat(ai-openai): brand provider tool factories and replace barrel export * feat(ai-openai): add /tools subpath export * test(ai-openai): per-model type tests for provider tools * feat(ai-gemini): split capabilities into capabilities + tools Separates tool-type entries (code_execution, file_search, search_grounding, grounding_with_gmaps, url_context, image_generation) from general capability flags in ModelMeta.supports. Adds a new tools field to the supports shape, renames grounding_with_gmaps → google_maps and search_grounding → google_search, drops image_generation (no tool factory yet), and introduces GeminiChatModelToolCapabilitiesByName. Threads a fifth TToolCapabilities generic through GeminiTextAdapter and exports the new type map from the root. * feat(ai-gemini): brand provider tool factories and replace barrel export * feat(ai-gemini): add /tools subpath export and type tests * feat(ai-openrouter)!: move and rename createWebSearchTool to webSearchTool on /tools subpath Renames createWebSearchTool → webSearchTool, brands its return type as OpenRouterWebSearchTool (ProviderTool<'openrouter', 'web_search'>), moves web_search exports to the new ./tools subpath, adds OpenRouterChatModelToolCapabilitiesByName mapped type (all chat models support web_search via the gateway), threads TToolCapabilities through the text adapter, and adds per-model type tests. * feat(ai-grok, ai-groq): add supports.tools, /tools subpath, and thread toolCapabilities - Add tools?: ReadonlyArray<never> to ModelMeta.supports interface in both packages - Add tools: [] as const to every chat model constant - Export GrokChatModelToolCapabilitiesByName / GroqChatModelToolCapabilitiesByName type maps - Add 5th TToolCapabilities generic to GrokTextAdapter / GroqTextAdapter via ResolveToolCapabilities - Add ./tools subpath to package.json exports and vite.config.ts entry for both packages - Re-export new ToolCapabilitiesByName types from root index.ts in both packages * chore(scripts): update sync-models templates to include supports.tools * test(ai-anthropic): runtime smoke test for provider tool factories * docs: add provider tools concept page * docs(adapters): add Provider Tools sections for every adapter * docs: cross-link provider tools concept page and migration section 6 * chore: changesets for provider-tools surface * fix(ai-groq): reorder imports to satisfy import/first ESLint rule * fix(changesets): bump adapters to minor for new /tools subpath + openrouter breaking * fix(ai-openrouter): match @deprecated tag wording across adapters * fix(changesets): confirm gemini relocation claim is accurate for all active models Investigated file_search and search_grounding entries in model-meta.ts: - All active (exported) models: correctly have these entries in tools: array - Commented-out preview models (GEMINI_2_5_FLASH_LIVE, GEMINI_2_FLASH_LIVE): still have them in capabilities: but are not part of the released API - Changeset claim is accurate: relocation is complete for all models that matter * fix(ai-gemini): export convertToolsToProviderFormat from /tools * test(ai-openai): cover all 10 unsupported tools in gpt-3.5-turbo * test(ai-gemini): add negative cases for gemini-3.1-pro-preview * fix(ai-anthropic): debrand customTool — return plain Tool (universal) * fix(ai-openai): debrand customTool/functionTool — remove unused brand types * docs: remove customTool from branded-factories matrix * test(ai-anthropic): cover debranded customTool acceptance on any model * ci: apply automated fixes * review: address CodeRabbit PR feedback - Tighten TProviderOptions constraint from 'extends object' to 'extends Record<string, any>' across all five text adapters to match BaseTextAdapter (openai, anthropic, gemini, grok, groq). - OpenAI & Anthropic: wire chatStream / structuredOutput to the TProviderOptions generic so per-model provider options are enforced at the call site, matching the openrouter/gemini pattern. - OpenRouter webSearchTool: brand via a stable __kind marker and gate the converter on that; reject malformed metadata explicitly instead of trusting tool.name === 'web_search' (users can collide on name). - Docs: fix fileSearchTool example to use the real fileSearchStoreNames shape; switch webSearchTool example back to type: 'web_search' (preview variant is a separate factory); update local_shell/shell/apply_patch supported-models copy to match the actual supports.tools gating; drop 'todays' typo. - Tests: apply ESLint import/order + sort-imports to the five tools-per-model type-safety specs. * review: address CodeRabbit feedback (round 2) - ai-openrouter: tighten web_search metadata validation against null/arrays (typeof 'object' alone accepted both) - ai-gemini: include googleSearchRetrievalTool in the "rejects all provider tools" case so no gemini provider-tool kind can regress silently * review: rename generic `A` to `TAdapter` in typedTools helpers Satisfies @typescript-eslint/naming-convention rule (type params must match /^(T|T[A-Z][A-Za-z]+)$/). Applies the fix to all four per-adapter tools-per-model-type-safety test files (anthropic, gemini, openai, openrouter). * fix(ai-openrouter): type metadata.web_search as unknown for runtime null/array guard Prior typing as `WebSearchToolConfig['web_search']` was non-nullable at the type level, so `metadata.web_search === null` and `Array.isArray(...)` tripped `@typescript-eslint/no-unnecessary-condition` and failed CI lint. Widen to `unknown` so the defensive null/array runtime checks are type-meaningful, then narrow on return. * chore: remove accidentally-committed terminalOutput artifact --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
1 parent d02950a commit e32583e

97 files changed

Lines changed: 2976 additions & 328 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@tanstack/ai-anthropic': minor
3+
---
4+
5+
Expose provider-tool factories (`webSearchTool`, `codeExecutionTool`, `computerUseTool`, `bashTool`, `textEditorTool`, `webFetchTool`, `memoryTool`, `customTool`) on a new `/tools` subpath. Each factory now returns a branded type (e.g. `AnthropicWebSearchTool`) that is gated against the selected model's `supports.tools` list. Existing factory signatures and runtime behavior are unchanged; old config-type aliases (`WebSearchTool`, `BashTool`, etc.) remain as `@deprecated` aliases pointing at the renamed `*ToolConfig` types.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@tanstack/ai': minor
3+
---
4+
5+
Add `ProviderTool<TProvider, TKind>` phantom-branded tool subtype and a `toolCapabilities` channel on `TextAdapter['~types']`. `TextActivityOptions['tools']` is now typed so that adapter-exported provider tools are gated against the selected model's `supports.tools` list. User tools from `toolDefinition()` remain unaffected.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@tanstack/ai-gemini': minor
3+
---
4+
5+
Expose provider-tool factories (`codeExecutionTool`, `fileSearchTool`, `googleSearchTool`, `googleSearchRetrievalTool`, `googleMapsTool`, `urlContextTool`, `computerUseTool`) on a new `/tools` subpath, each returning a branded type gated against the selected model's `supports.tools` list.
6+
7+
Note: `supports.capabilities` entries that described tools (`code_execution`, `file_search`, `grounding_with_gmaps` → renamed `google_maps`, `search_grounding` → renamed `google_search`, `url_context`) have been relocated to the new `supports.tools` field. The `capabilities` array loses those entries. This is a model-meta shape change but not a runtime break.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@tanstack/ai-grok': patch
3+
---
4+
5+
Expose the `/tools` subpath and add an empty `supports.tools: []` channel per model so Grok adapters participate in the core tool-capability type gating. No provider-specific tool factories are exposed yet — define your own tools with `toolDefinition()` from `@tanstack/ai`.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@tanstack/ai-groq': patch
3+
---
4+
5+
Expose the `/tools` subpath and add an empty `supports.tools: []` channel per model so Groq adapters participate in the core tool-capability type gating. No provider-specific tool factories are exposed yet — define your own tools with `toolDefinition()` from `@tanstack/ai`.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@tanstack/ai-openai': minor
3+
---
4+
5+
Expose provider-tool factories (`webSearchTool`, `webSearchPreviewTool`, `fileSearchTool`, `imageGenerationTool`, `codeInterpreterTool`, `mcpTool`, `computerUseTool`, `localShellTool`, `shellTool`, `applyPatchTool`, `customTool`) on a new `/tools` subpath. Each factory returns a branded type (e.g. `OpenAIWebSearchTool`) gated against the selected model's `supports.tools` list. `supports.tools` was expanded to include `web_search_preview`, `local_shell`, `shell`, `apply_patch`. Existing factory signatures and runtime behavior are unchanged.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@tanstack/ai-openrouter': minor
3+
---
4+
5+
**Breaking export change.** `createWebSearchTool` has been removed from the package root. Import `webSearchTool` from `@tanstack/ai-openrouter/tools` instead. See Migration Guide §6 for the before/after snippet.
6+
7+
Alongside: the new `/tools` subpath exposes `webSearchTool` (branded `OpenRouterWebSearchTool`) and the existing `convertToolsToProviderFormat`. A new `supports.tools` channel on each chat model gates provider tools at the type level.

docs/adapters/anthropic.md

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,3 +237,198 @@ Creates an Anthropic summarization adapter with an explicit API key.
237237
- [Getting Started](../getting-started/quick-start) - Learn the basics
238238
- [Tools Guide](../tools/tools) - Learn about tools
239239
- [Other Adapters](./openai) - Explore other providers
240+
241+
## Provider Tools
242+
243+
Anthropic exposes several native tools beyond user-defined function calls.
244+
Import them from `@tanstack/ai-anthropic/tools` and pass them into
245+
`chat({ tools: [...] })`.
246+
247+
> For the full concept, a comparison matrix, and type-gating details, see
248+
> [Provider Tools](../tools/provider-tools.md).
249+
250+
### `webSearchTool`
251+
252+
Enables Claude to run Anthropic's native web search with inline citations.
253+
Scope the search with `allowed_domains` or `blocked_domains` (mutually
254+
exclusive); set `max_uses` to cap per-turn cost.
255+
256+
```typescript
257+
import { chat } from "@tanstack/ai";
258+
import { anthropicText } from "@tanstack/ai-anthropic";
259+
import { webSearchTool } from "@tanstack/ai-anthropic/tools";
260+
261+
const stream = chat({
262+
adapter: anthropicText("claude-opus-4-6"),
263+
messages: [{ role: "user", content: "What's new in AI this week?" }],
264+
tools: [
265+
webSearchTool({
266+
name: "web_search",
267+
type: "web_search_20250305",
268+
max_uses: 2,
269+
}),
270+
],
271+
});
272+
```
273+
274+
**Supported models:** every current Claude model. `claude-3-haiku` supports
275+
only `web_search` (not `web_fetch`). See [Provider Tools](../tools/provider-tools.md#which-models-support-which-tools).
276+
277+
### `webFetchTool`
278+
279+
Lets Claude fetch the contents of a URL directly, useful when you want the
280+
model to read a specific page rather than run a search. Takes no required
281+
arguments — pass an optional config object to override defaults.
282+
283+
```typescript
284+
import { chat } from "@tanstack/ai";
285+
import { anthropicText } from "@tanstack/ai-anthropic";
286+
import { webFetchTool } from "@tanstack/ai-anthropic/tools";
287+
288+
const stream = chat({
289+
adapter: anthropicText("claude-sonnet-4-5"),
290+
messages: [{ role: "user", content: "Summarise https://example.com" }],
291+
tools: [webFetchTool()],
292+
});
293+
```
294+
295+
**Supported models:** Claude Sonnet 4.x and above. See [Provider Tools](../tools/provider-tools.md#which-models-support-which-tools).
296+
297+
### `codeExecutionTool`
298+
299+
Gives Claude a sandboxed code-execution environment so it can run Python
300+
snippets, analyse data, and return results inline. Choose the version string
301+
that matches your desired API revision.
302+
303+
```typescript
304+
import { chat } from "@tanstack/ai";
305+
import { anthropicText } from "@tanstack/ai-anthropic";
306+
import { codeExecutionTool } from "@tanstack/ai-anthropic/tools";
307+
308+
const stream = chat({
309+
adapter: anthropicText("claude-sonnet-4-5"),
310+
messages: [{ role: "user", content: "Plot a histogram of [1,2,2,3,3,3]" }],
311+
tools: [
312+
codeExecutionTool({ name: "code_execution", type: "code_execution_20250825" }),
313+
],
314+
});
315+
```
316+
317+
**Supported models:** Claude Sonnet 4.x and above. See [Provider Tools](../tools/provider-tools.md#which-models-support-which-tools).
318+
319+
### `computerUseTool`
320+
321+
Allows Claude to observe a virtual desktop (screenshots) and interact with it
322+
via keyboard and mouse events. Provide the screen resolution so Claude can
323+
calculate accurate coordinates.
324+
325+
```typescript
326+
import { chat } from "@tanstack/ai";
327+
import { anthropicText } from "@tanstack/ai-anthropic";
328+
import { computerUseTool } from "@tanstack/ai-anthropic/tools";
329+
330+
const stream = chat({
331+
adapter: anthropicText("claude-sonnet-4-5"),
332+
messages: [{ role: "user", content: "Open the browser and go to example.com" }],
333+
tools: [
334+
computerUseTool({
335+
type: "computer_20250124",
336+
name: "computer",
337+
display_width_px: 1024,
338+
display_height_px: 768,
339+
}),
340+
],
341+
});
342+
```
343+
344+
**Supported models:** Claude Sonnet 3.5 and above. See [Provider Tools](../tools/provider-tools.md#which-models-support-which-tools).
345+
346+
### `bashTool`
347+
348+
Provides Claude with a persistent bash shell session, letting it run arbitrary
349+
commands, install packages, or manipulate files on the host. Choose the type
350+
string that matches your API revision.
351+
352+
```typescript
353+
import { chat } from "@tanstack/ai";
354+
import { anthropicText } from "@tanstack/ai-anthropic";
355+
import { bashTool } from "@tanstack/ai-anthropic/tools";
356+
357+
const stream = chat({
358+
adapter: anthropicText("claude-sonnet-4-5"),
359+
messages: [{ role: "user", content: "List all TypeScript files in src/" }],
360+
tools: [bashTool({ name: "bash", type: "bash_20250124" })],
361+
});
362+
```
363+
364+
**Supported models:** Claude Sonnet 3.5 and above. See [Provider Tools](../tools/provider-tools.md#which-models-support-which-tools).
365+
366+
### `textEditorTool`
367+
368+
Gives Claude a structured text-editor interface for viewing and modifying files
369+
using `str_replace`, `create`, `view`, and `undo_edit` commands. Choose the
370+
type string for the API revision you target.
371+
372+
```typescript
373+
import { chat } from "@tanstack/ai";
374+
import { anthropicText } from "@tanstack/ai-anthropic";
375+
import { textEditorTool } from "@tanstack/ai-anthropic/tools";
376+
377+
const stream = chat({
378+
adapter: anthropicText("claude-sonnet-4-5"),
379+
messages: [{ role: "user", content: "Fix the bug in src/index.ts" }],
380+
tools: [
381+
textEditorTool({ type: "text_editor_20250124", name: "str_replace_editor" }),
382+
],
383+
});
384+
```
385+
386+
**Supported models:** Claude Sonnet 3.5 and above. See [Provider Tools](../tools/provider-tools.md#which-models-support-which-tools).
387+
388+
### `memoryTool`
389+
390+
Enables Claude to store and retrieve information across conversation turns
391+
using Anthropic's managed memory service. Call with no arguments to use
392+
default configuration.
393+
394+
```typescript
395+
import { chat } from "@tanstack/ai";
396+
import { anthropicText } from "@tanstack/ai-anthropic";
397+
import { memoryTool } from "@tanstack/ai-anthropic/tools";
398+
399+
const stream = chat({
400+
adapter: anthropicText("claude-sonnet-4-5"),
401+
messages: [{ role: "user", content: "Remember that I prefer metric units" }],
402+
tools: [memoryTool()],
403+
});
404+
```
405+
406+
**Supported models:** Claude Sonnet 4.x and above. See [Provider Tools](../tools/provider-tools.md#which-models-support-which-tools).
407+
408+
### `customTool`
409+
410+
Creates a tool with an inline JSON Schema input definition instead of going
411+
through `toolDefinition()`. Useful when you need fine-grained control over the
412+
schema shape or want to add `cache_control`. Unlike branded provider tools,
413+
`customTool` returns a plain `Tool` and is accepted by any chat model.
414+
415+
```typescript
416+
import { chat } from "@tanstack/ai";
417+
import { anthropicText } from "@tanstack/ai-anthropic";
418+
import { customTool } from "@tanstack/ai-anthropic/tools";
419+
import { z } from "zod";
420+
421+
const stream = chat({
422+
adapter: anthropicText("claude-sonnet-4-5"),
423+
messages: [{ role: "user", content: "Look up user 42" }],
424+
tools: [
425+
customTool(
426+
"lookup_user",
427+
"Look up a user by ID and return their profile",
428+
z.object({ userId: z.number() }),
429+
),
430+
],
431+
});
432+
```
433+
434+
**Supported models:** all current Claude models. See [Provider Tools](../tools/provider-tools.md#which-models-support-which-tools).

0 commit comments

Comments
 (0)