- MCP Server ToolsUpdated
- Managed MCP with chat()Updated
- Manual MCPUpdated
- MCP Type GenerationUpdated
- MCP Client InputUpdated
- MCP Server Tools: AdvancedNew
- MCP EventsNew
- MCP AppsUpdated
- Serve Tools over HTTPUpdated
- Serve Resources and PromptsUpdated
- Ask for InputUpdated
- Draft Text from a ToolUpdated
- MCP Server TasksUpdated
- MCP Server AuthUpdated
- MCP Server SessionsUpdated
- MCP Server on stdioUpdated
Persistence (Advanced)
- GenerationsUpdated
- Realtime Voice ChatUpdated
- Text-to-SpeechUpdated
- TranscriptionUpdated
- Transcription: AdvancedNew
- Voice CreationUpdated
- Audio RecordingUpdated
- Audio GenerationUpdated
- Image GenerationUpdated
- Image Generation: AdvancedNew
- Video GenerationUpdated
- Video Generation: AdvancedNew
- Live GenerationUpdated
- World GenerationUpdated
- Generation HooksUpdated
- Build your first harnessUpdated
- Host optionsNew
- Send and show mediaUpdated
- Connect clientsUpdated
- Write a client in any languageNew
- Access and tenantsNew
- Run in the terminalUpdated
- Deploy a harnessUpdated
- Send inputs safelyUpdated
- Run a tool without a model
- Durable sessionsUpdated
- Run background work on workersNew
- List, rename, and fork sessionsUpdated
- Lifecycle and retentionNew
- Run side effects onceUpdated
- Wait for a personNew
- Build on the session logUpdated
- Control how a turn endsUpdated
- Store settings per threadUpdated
- Compact a long sessionUpdated
- Fork and reset a threadUpdated
- One log for a session treeUpdated
- Write a pluginUpdated
- Track usage and costUpdated
- Ask before risky tool callsUpdated
- Give your agent coding toolsUpdated
- Sandbox and extend the coding toolsUpdated
- Run the coding tools for many threadsNew
- Undo a turnUpdated
- Build a coding agentUpdated
- Delegate to coding agentsUpdated
- Switch between agent profilesUpdated
- Auth and connectorsUpdated
- Share a thread between peopleUpdated
- Connect model providersUpdated
- Build your own UIUpdated
- Code mode in a harnessUpdated
- Work until a goal is metUpdated
- Run agents from a harnessUpdated
- Hand off a conversationNew
- Add skillsUpdated
- Use MCP serversUpdated
- Give your agent a computerNew
- Use from any MCP clientUpdated
- Use a harness from an editor (ACP)Updated
- OverviewUpdated
- Quick StartUpdated
- ProvidersUpdated
- HarnessesUpdated
- Harness AuthUpdated
- WorkspaceUpdated
- ToolsUpdated
- PolicyUpdated
- Lifecycle & SnapshotsUpdated
- Portable SnapshotsUpdated
- Keep Files After ReloadUpdated
- Save a Named VersionUpdated
- Branch From a VersionUpdated
- Send a Frozen FileUpdated
- Let the Agent Save and ForkUpdated
- Pick Which Files to KeepUpdated
- What a Snapshot StoresUpdated
- Instance DurabilityUpdated
- Durable RunsUpdated
- EventsUpdated
Sandboxes (Advanced)
- Runtime ContextUpdated
- Bring Your Own KeyUpdated
- Keep Keys on the ServerUpdated
- Debug LoggingUpdated
- Multimodal ContentUpdated
- Files APIUpdated
- Per-Model Type SafetyUpdated
- Runtime Adapter SwitchingUpdated
- Tree-ShakingUpdated
- Extend AdapterUpdated
- Extend Adapter: AdvancedNew
- Typed Pre-Configured OptionsUpdated
- Test with a Fake ModelUpdated
- Prompt CachingUpdated
- Mid-Conversation ChangesUpdated
- Approval Flow ProcessingUpdated
Per-Model Type Safety
Provider options are typed from the model you pass to the adapter. An option that model does not support is a type error.
TanStack AI gives modelOptions the type of the exact model that you select. Your editor shows only the options that the model has, and TypeScript rejects the others.
Quick setup#
There is nothing to turn on. Pass the model to the adapter factory, then write modelOptions:
import { chat } from '@tanstack/ai'
import { openaiText } from '@tanstack/ai-openai'
// gpt-6.1-sol supports structured outputs, so `text` is allowed.
const stream = chat({
adapter: openaiText('gpt-6.1-sol'),
messages: [{ role: 'user', content: 'Give me a short answer.' }],
threadId: 'thread-1',
modelOptions: {
text: {
format: {
type: 'json_schema',
name: 'my_schema',
schema: {
type: 'object',
properties: { answer: { type: 'string' } },
required: ['answer'],
additionalProperties: false,
},
},
},
},
})Now pick a model without structured outputs. The same option is a type error:
import { chat } from '@tanstack/ai'
import { openaiText } from '@tanstack/ai-openai'
// gpt-4-turbo does not support structured outputs, so `text` is rejected.
const stream = chat({
adapter: openaiText('gpt-4-turbo'),
messages: [{ role: 'user', content: 'Give me a short answer.' }],
threadId: 'thread-1',
modelOptions: {
text: {}, // TypeScript error: 'text' does not exist in type
},
})TypeScript reports:
error TS2353: Object literal may only specify known properties, and 'text' does not exist in type ...'.Tip
For structured output, use chat({ outputSchema }) in most apps. It works with every provider and validates the result. The raw text option is for control that is specific to the provider. See Structured Outputs.
What you get#
- Wrong model options fail at compile time, not in production.
- Autocomplete shows only the options of the selected model.
- The types document what each model can do.
- There is no runtime cost. All checks happen at compile time.
How it works#
Each adapter factory keeps the model name as a type parameter: openaiText<TModel>(model). So the adapter knows the exact model at the type level.
chat() looks up the options for that model in a map for each model (ResolveProviderOptions<TModel>). Each entry lists only the options that the model supports. A model without structured outputs has no text property in its options type. TypeScript then rejects text as an unknown property.
The same types also work in these places:
- Typed Pre-Configured Options: keep the options for one model in a reusable object.
- Extend Adapter: attach typed modelOptions to your own models.