Define a Tool
Create tools usingdefineTool(). parameters takes a Standard Schema with JSON Schema support, such as a Zod 4.2+ schema, or plain JSON Schema. See Schemas for the supported libraries.
Strict tool schemas
Setstrict: true on a tool to make the provider enforce the tool’s parameter
schema during argument generation. Strictness is a per-tool opt-in: omitting
strict or setting strict: false keeps that tool non-strict on every
provider.
Strict-capable schema contract
A strict tool’s schema must fit the strict-capable subset that schema-enforcing providers accept. Core-ai validates strict tools against this contract locally, before any API call, and reports every violation with a fix hint. Non-strict tools are never validated against it. The contract applies to the tool’s JSON Schema, so it covers Standard Schemas and plain JSON Schema alike. The examples below use Zod; other libraries produce the equivalent JSON Schema keywords.- Closed objects whose keys the provider can require. A key must be
required, accept
null(.nullable()), or declare adefault(.default()), so the model can always send a value for it. Use.nullable()for values the model may leave out: a key that is only.optional()is rejected. - Object root.
parametersmust describe an object, such as az.object(); a primitive, array, or union at the root is rejected. - Allowed keywords — the intersection of what OpenAI and Anthropic accept
in strict mode, not the union:
$ref/$defs,type(object,array,string,number,integer,boolean,null),properties,required,additionalProperties: false,items,enum,const, unions (anyOffromz.union(), andoneOffromz.discriminatedUnion(), which providers receive asanyOf),format(date-time,time,date,duration,email,hostname,ipv4,ipv6,uuid),pattern,title,description,default,examples. - Rejected: numeric bounds (
minimum,maximum,multipleOf— Zod’s implicit safe-integer bounds onz.int()are exempt and stripped), string and array length constraints (minLength,maxLength,minItems,maxItems), URL formats (z.url()emitsformat: "uri", which OpenAI does not accept — usez.string()), intersections (z.intersection()/.and()emitallOf— flatten into onez.object()), tuples (prefixItems),not, open objects (z.record(),.catchall(),.passthrough(),z.looseObject()), and recursive schemas (z.lazy()).
Check model support
model.capabilities.tools.strictSchemas reports whether the selected model
supports strict tools and any provider limit:
{ supported: true }:
core-ai forwards the explicit strict: true and lets the provider reject the
request if the model genuinely lacks support. This keeps newly released
models usable without a registry update. Fine-tuned OpenAI ft:<base>:...
models inherit their base model’s capabilities instead.
Errors
Invalid strict configuration throwsToolSchemaStrictnessError
before the provider request. Its reason is one of:
'unsupported'— the model is known not to support strict tool schemas.'limit-exceeded'— the request contains more strict tools than the provider’smaxStrictToolslimit.'union-limit-exceeded'— the request’s strict tools have more union-typed parameters than the provider’smaxUnionParameterslimit. A union-typed parameter is a property whose schema is ananyOf, aoneOf, or atypearray with more than one entry, including every nullable key.'invalid-schema'— a strict tool’s schema is outside the strict-capable contract. The error carries aviolationsarray naming each offending schema path:
Parallel tool calls
OpenAI does not guarantee schema adherence when parallel tool calls are enabled. Core-ai does not change that setting for you: when you combine strict tools withproviderOptions.openai.parallelToolCalls: true, set it to false
yourself if you need guaranteed adherence.
Provider support
On the OpenAI family (
openai, azure-openai, kimi, openai-compat,
omnifact), strict tools are sent with strict: true and a normalized schema
($schema stripped, additionalProperties: false added recursively, oneOf
rewritten to anyOf, and nullable or defaulted keys listed in required —
all semantics-preserving). Non-strict tools are sent
with the tool’s JSON Schema unchanged. On the Responses API they carry an explicit
strict: false, because that API otherwise tries to normalize tools into
strict mode on its own; on Chat Completions, which is non-strict by default,
the field is omitted. Anthropic follows the same split: strict tools get the
provider’s normalization (which also drops the numeric and length constraints
its strict grammar rejects), non-strict tools are sent raw. Anthropic also
rejects a single strict schema it finds too complex (“Schema is too complex”;
30 plain required keys in one tool exceed it) with a provider error. core-ai
cannot check that limit because Anthropic does not publish it.
Strict tool schemas apply to function arguments during generate() and
stream(). They are separate from the structured response formats used by
generateObject() and
streamObject(). A provider can support one without
supporting the other.
Basic Tool Usage
Implement a complete tool calling flow:Tool Choice Strategies
Control when and how tools are used:Multiple Tools
Provide multiple tools for the model to choose from:Validate tool calls
Tool arguments come from the model, so validate them before you execute anything, whether the tool is strict or not. How you validate depends on the tool’sparameters:
- Standard Schema: pass the tool and the call to
validateToolCall(). It returns{ value }, the schema’s output typed as such, or{ issues }, listing each problem with amessageand an optionalpath. Invalid arguments are reported, never thrown. So is a validator that throws, such as a transform callingnew URL(): its error message becomes the only issue. - Plain JSON Schema: core-ai sends the schema as is and has no validator
for it, so
validateToolCall()returnsundefined. ValidateToolCall.argumentsyourself.
call.name first: validateToolCall() throws a ValidationError for a
call to another tool. value has the schema’s defaults and transforms applied,
while ToolCall.arguments keeps the arguments exactly as the model sent them.
See Handling tool calls for a complete loop.
JSON Schema tools
Use plain JSON Schema for tools whose schemas you receive at runtime, such as tools listed by an MCP server:validateToolCall() returns undefined for these tools. Validate their
arguments with a JSON Schema validator, or let the MCP server reject invalid
input. To validate them with validateToolCall(), wrap your validator in the
Standard Schema interface:
Handling Tool Calls
Execute validated tool calls the same way for strict and non-strict tools. Send validation issues back as an error tool result so the model can correct its call:Tool discovery
For large tool sets, such as the tools of several MCP servers, declare a few tools up front and let the model discover the rest withtoolDiscovery. See
Tool Discovery.
With discovery enabled, finishReason: 'tool-calls' also covers pending
discovery calls, which arrive in result.discoveryCalls and never in
toolCalls. Loop while finishReason is 'tool-calls' and answer both
toolCalls and discoveryCalls: a step can contain only discovery calls.
Tool Calling with Streaming
Handle tool calls during streaming:Advanced Tool Examples
- Database Query
- API Call
- File Operations
Tool Result Messages
Structure tool results properly:Helper: Convert Result to Message
UseresultToMessage() to simplify conversation building:
Best Practices
Validate tool arguments before execution
Validate tool arguments before execution
Check calls to Standard Schema tools with
validateToolCall(), and validate call.arguments yourself for plain JSON Schema tools:Add detailed tool descriptions
Add detailed tool descriptions
Help the model understand when to use tools:
Handle errors gracefully
Handle errors gracefully
Return errors as tool results instead of throwing:
Limit tool execution time
Limit tool execution time
Prevent tools from hanging:
Next Steps
Chat Completion
Learn basic message generation
Structured Outputs
Combine tools with structured responses
Streaming
Stream tool calls in real-time
Tool Discovery
Let the model discover tools on demand