A provider skill is a hosted skill, such as a PowerPoint skill, that Anthropic or OpenAI runs in its own sandbox.
Attach the skill IDs to the execution tool of the provider. The provider installs the skills and runs them. You only give the IDs.
A skill does nothing without its execution tool. The tool starts the sandbox, and the skills run inside it.
TanStack AI has two kinds of skills. Pick the one that fits:
If you need the sandbox of the provider, for example to run code or make files, use hosted skills. For everything else, use portable skills.
Use only one kind in a chat() call. If you attach hosted skills to a code_execution or shell tool and also add withSkills, the middleware throws. The reason: the model gets two catalogs and two protocols.
npm i @tanstack/ai-anthropicpnpm add @tanstack/ai-anthropicyarn add @tanstack/ai-anthropicbun add @tanstack/ai-anthropicImport codeExecutionTool from @tanstack/ai-anthropic/tools, not from the root of the adapter. Pass a skills array as the second argument:
import {
chat,
chatParamsFromRequest,
toServerSentEventsResponse,
} from '@tanstack/ai'
import { anthropicText } from '@tanstack/ai-anthropic'
import { codeExecutionTool } 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: [
codeExecutionTool(
{ type: 'code_execution_20250825', name: 'code_execution' },
{
skills: [{ type: 'anthropic', skill_id: 'pptx', version: 'latest' }],
},
),
],
})
return toServerSentEventsResponse(stream)
}Ask your chat "Make a three-slide deck about our Q3 results." The model loads the pptx skill and makes the file in the sandbox of Anthropic.
The adapter does the setup for you:
You do not set beta headers yourself.
Each item in the skills array is an AnthropicContainerSkill:
| Field | Type | Required | Notes |
|---|---|---|---|
| type | 'anthropic' | 'custom' | yes | 'anthropic' for skills that Anthropic hosts. 'custom' for your own bundles. |
| skill_id | string | yes | 1 to 64 characters. |
| version | string | no | A version string, or 'latest'. The default is 'latest'. |
You can attach at most 8 skills to a request. If you pass more, or a skill_id that is not valid, the factory throws when you call it.
The OpenAI shellTool takes an environment object, and that object can hold a skills array. This works only with the Responses API. The Chat Completions API does not support the shell tool.
npm i @tanstack/ai-openaipnpm add @tanstack/ai-openaiyarn add @tanstack/ai-openaibun add @tanstack/ai-openaiimport {
chat,
chatParamsFromRequest,
toServerSentEventsResponse,
} from '@tanstack/ai'
import { openaiText } from '@tanstack/ai-openai'
import { shellTool } from '@tanstack/ai-openai/tools'
export async function POST(request: Request) {
const { messages, threadId, runId } = await chatParamsFromRequest(request)
const stream = chat({
adapter: openaiText('gpt-6.1-sol'),
messages,
threadId,
runId,
tools: [
shellTool({
environment: {
type: 'container_auto',
skills: [
{ type: 'skill_reference', skill_id: 'skill_abc', version: '2' },
],
},
}),
],
})
return toServerSentEventsResponse(stream)
}Replace skill_abc with the ID of your skill.
Each item in the skills array is a SkillReference:
| Field | Type | Required | Notes |
|---|---|---|---|
| type | 'skill_reference' | yes | Always 'skill_reference' for OpenAI. |
| skill_id | string | yes | The skill ID that OpenAI gives you. |
| version | string | no | A positive integer as a string (for example '2'), or 'latest'. |
version is a string, not a number.
The factories wire only hosted skills that you reference by ID:
codeExecutionTool and shellTool do not handle inline bundles, local paths, or skill creation through the upload API.
modelOptions.container.skills is not in 1.0. Pass skills to codeExecutionTool(config, { skills }).