Tools

Tools enable models to interact with the external world — querying databases, calling APIs, performing calculations — breaking through the limitations of training data.

Tools are the bridge between models and the external world in deepseek-kit. Models can only generate text based on training data, but through tools, they can query real-time weather, search databases, execute code — anything you can implement in JavaScript can be encapsulated as a tool for the model to use. When the model determines that a tool call is needed, it generates a tool call request; your code executes the tool and returns the result, and the model continues reasoning based on the result.

Defining Tools

Use the tool() function to define a tool. Each tool consists of four core parts: name, description, parameter schema, and execute function:

import { tool } from 'deepseek-kit'
import { z } from 'zod'

const weatherTool = tool({
  name: 'getWeather',
  description: 'Query weather information for a specified city',
  schema: z.object({
    city: z.string().describe('City name'),
  }),
  execute: async (input) => {
    return `${input.city}: Sunny today, 22°C, humidity 60%.`
  },
})
  • name — Unique identifier for the tool. The model uses the name to choose which tool to call
  • description — Functional description of the tool, helping the model understand when it should call this tool
  • schema — Parameter schema defined with Zod, used both to describe the parameter structure to the model and to validate the parameters generated by the model
  • execute — Async execution function that receives schema-validated parameters and returns the tool execution result

Using Tools in Agents

Pass tools to createAgent, and the agent will autonomously decide when to call tools based on user input:

import { createAgent, createModel, tool } from 'deepseek-kit'
import { z } from 'zod'

const model = createModel({ model: 'deepseek-v4-flash' })

const weatherTool = tool({
  name: 'getWeather',
  description: 'Query weather information for a specified city',
  schema: z.object({
    city: z.string().describe('City name'),
  }),
  execute: async (input) => {
    return `${input.city}: Sunny today, 22°C, humidity 60%.`
  },
})

const agent = createAgent({
  model,
  tools: [weatherTool],
})

const result = await agent.generate({
  prompt: 'How\'s the weather in Beijing today?',
})

console.log(result.text)

You can also use tools directly with generateText:

import { createModel, generateText, tool } from 'deepseek-kit'
import { z } from 'zod'

const model = createModel({ model: 'deepseek-v4-flash' })

const result = await generateText({
  model,
  tools: [weatherTool],
  messages: [{ role: 'user', content: 'How\'s the weather in Beijing today?' }],
})

Strict Mode

When strict mode is enabled, deepseek-kit enforces parameter structure constraints at the JSON Schema level — setting additionalProperties: false and marking all properties as required. This improves the reliability of model-generated parameters:

const searchTool = tool({
  name: 'search',
  description: 'Search records in the database',
  schema: z.object({
    query: z.string().describe('Search keywords'),
    limit: z.number().describe('Maximum number of results'),
  }),
  strict: true,
  execute: async (input) => {
    return { results: [], total: 0 }
  },
})

Required Tools

Setting required: true forces the model to call the tool instead of freely choosing whether to call it. This is useful when you need to ensure the model always performs a specific operation (e.g., data validation, permission checks):

const validateTool = tool({
  name: 'validateInput',
  description: 'Validate the legitimacy of user input',
  schema: z.object({
    input: z.string().describe('Input to validate'),
  }),
  required: true,
  execute: async (input) => {
    return { valid: true, sanitized: input.input }
  },
})

When only one tool is marked as required, tool_choice is set to that tool; when multiple tools are marked as required, tool_choice is set to "required".

Multiple Tools

You can pass multiple tools simultaneously, and the model will autonomously choose which tools to call based on context:

const weatherTool = tool({
  name: 'getWeather',
  description: 'Query weather information for a city',
  schema: z.object({ city: z.string().describe('City name') }),
  execute: async input => getWeather(input.city),
})

const calculatorTool = tool({
  name: 'calculator',
  description: 'Perform mathematical calculations',
  schema: z.object({ expression: z.string().describe('Mathematical expression') }),
  execute: async input => evaluate(input.expression),
})

const agent = createAgent({
  model,
  tools: [weatherTool, calculatorTool],
})

const result = await agent.generate({
  prompt: 'How\'s the weather in Beijing? Also calculate 25 * 4 for me.',
})

Tool Execution Results

Tool execution results are automatically serialized as strings and returned to the model. The tool() function internally handles result wrapping:

  • On success: Returns { success: true, data: <result> }
  • On failure: Returns { success: false, error: "<error message>" }

Your execute function can return any type — string, object, number — deepseek-kit handles serialization automatically:

const tool1 = tool({
  name: 'getString',
  description: 'Return a string result',
  schema: z.object({ query: z.string() }),
  execute: async () => 'This is a text result',
})

const tool2 = tool({
  name: 'getObject',
  description: 'Return a structured result',
  schema: z.object({ id: z.string() }),
  execute: async () => ({ name: 'John', age: 25, email: 'john@example.com' }),
})

Timeout and Retry

Timeout

Set a timeout for tools that may take a long time to avoid waiting indefinitely. The default timeout is 60 seconds:

const slowApiTool = tool({
  name: 'slowApi',
  description: 'Call a slow API',
  schema: z.object({ query: z.string() }),
  execute: async (input) => {
    return await callSlowApi(input.query)
  },
  timeout: 30000,
})

Retry

Configure automatic retry count for unreliable tools. When execution fails, deepseek-kit automatically retries until success or the maximum retry count is reached:

const unreliableApiTool = tool({
  name: 'unreliableApi',
  description: 'Call an unreliable API',
  schema: z.object({ query: z.string() }),
  execute: async (input) => {
    return await callUnreliableApi(input.query)
  },
  retries: 3,
})

Timeout and retry can be combined — each retry is subject to the timeout limit:

const robustTool = tool({
  name: 'robustApi',
  description: 'Call an API that needs retry and timeout protection',
  schema: z.object({ query: z.string() }),
  execute: async (input) => {
    return await callApi(input.query)
  },
  timeout: 10000,
  retries: 2,
})

Result Compaction

When tools return large outputs (e.g. file contents, API responses), the raw result can consume a significant portion of the model's context window. The compact option uses an LLM to compress verbose tool results while preserving all information the agent needs to continue reasoning.

Basic Usage

Enable compaction with default settings (threshold: 1500 characters, model: deepseek-v4-flash):

const readFileTool = tool({
  name: 'readFile',
  description: 'Read the contents of a file',
  schema: z.object({ path: z.string().describe('File path') }),
  compact: true,
  execute: async (input) => {
    return await fs.readFile(input.path, 'utf-8')
  },
})

Custom Configuration

You can customize the compaction behavior by passing an object instead of true:

const readFileTool = tool({
  name: 'readFile',
  description: 'Read the contents of a file',
  schema: z.object({ path: z.string().describe('File path') }),
  compact: {
    threshold: 3000,
    model: 'deepseek-v4',
  },
  execute: async (input) => {
    return await fs.readFile(input.path, 'utf-8')
  },
})
  • threshold (number, default 1500) — Minimum character length of a tool result before compaction is triggered. Results shorter than this are returned as-is.
  • model (Model, default 'deepseek-v4-flash') — The LLM used to compress content.

AbortSignal

deepseek-kit supports AbortSignal throughout the tool execution pipeline, allowing you to cancel in-progress tool calls. This is useful when users cancel requests or when you need to enforce time limits at the application level.

Usage with Agents

Pass a signal to generateText or agent.generate to enable cancellation:

const controller = new AbortController()

const result = await agent.generate({
  prompt: 'Read the contents of a large file',
  signal: controller.signal,
})

// Cancel after 5 seconds
setTimeout(() => controller.abort(), 5000)

When the signal is aborted:

  • In-progress tool executions are cancelled immediately
  • Pending retries are skipped
  • The agent loop stops and throws an AbortError

Integration with Timeout

AbortSignal works alongside the timeout option. The timeout acts as a per-attempt time limit, while AbortSignal provides external cancellation:

const controller = new AbortController()

const tool = tool({
  name: 'slowApi',
  description: 'Call a slow API',
  schema: z.object({ query: z.string() }),
  timeout: 30000,
  execute: async (input) => {
    return await callSlowApi(input.query)
  },
})

// Both timeout and signal can cancel execution
const result = await agent.generate({
  tools: [tool],
  prompt: 'Search for something',
  signal: controller.signal,
})

Parameter Validation

The tool() function automatically uses the Zod Schema to validate model-generated parameters. If parameters don't conform to the schema, the validation error is caught and returned to the model as a failure result, rather than throwing an exception that interrupts the flow:

const searchTool = tool({
  name: 'search',
  description: 'Search records',
  schema: z.object({
    query: z.string().min(1).describe('Search keywords'),
    limit: z.number().int().min(1).max(100).describe('Number of results'),
  }),
  execute: async (input) => {
    return searchDatabase(input.query, input.limit)
  },
})

If the model generates limit: -1, Zod validation will fail, and the model will receive an error message like "Invalid arguments: limit: Number must be greater than or equal to 1", giving it the opportunity to correct the parameters and retry.

Error Handling

Errors during tool execution are automatically caught and returned to the model. You can handle errors yourself in the execute function, or let deepseek-kit's default mechanism take over:

const safeTool = tool({
  name: 'safeApi',
  description: 'Safe API call',
  schema: z.object({ url: z.string() }),
  execute: async (input) => {
    try {
      const response = await fetch(input.url)
      if (!response.ok) {
        return { error: `Request failed: ${response.status}` }
      }
      return await response.json()
    }
    catch (error) {
      return { error: `Network error: ${error instanceof Error ? error.message : String(error)}` }
    }
  },
})

Tool Calling in Streaming

When using the stream() method, tool call events are pushed in real time as part of the stream events:

const stream = agent.stream({
  prompt: 'How\'s the weather in Beijing today?',
})

for await (const event of stream) {
  switch (event.type) {
    case 'text-delta':
      process.stdout.write(event.textDelta)
      break
    case 'tool-call':
      console.log(`\nCalling tool: ${event.toolCalls.map(t => t.function.name).join(', ')}`)
      break
    case 'step':
      console.log(`\nStep ${event.step}`)
      break
    case 'finish':
      console.log('\nDone!')
      break
  }
}

API Reference

tool() Parameters

namerequiredstring
Unique identifier for the tool. The model uses the name to choose which tool to call.
descriptionrequiredstring
Functional description of the tool. Helps the model understand the tool's purpose and when to call it.
schemarequiredz.ZodObject
Zod Schema for parameters. Used both to describe the parameter structure to the model and to validate model-generated parameters.
executerequired(args: z.infer<T>) => ToolResult | Promise<ToolResult>
Tool execution function. Receives schema-validated parameters and returns an execution result of any type.
strictboolean
false
Enable strict mode. Forces the JSON Schema to set additionalProperties: false and mark all properties as required.
requiredboolean
false
Mark as required tool. When enabled, the model is forced to call this tool.
timeoutnumber
60000
Execution timeout in milliseconds. Tool calls that exceed this time will be terminated.
retriesnumber
0
Maximum number of retries on failure. Each retry is subject to the timeout limit.
compactboolean | ToolCompactConfig
undefined
Enable result compaction. When true, tool results exceeding the threshold are compressed via an LLM to save context window space. Pass an object { threshold?: number, model?: Model } for custom settings.

Tool Type

ToolReturnType<typeof tool>
Return type of the tool() function. Contains the original configuration properties and processed parameters (JSON Schema) and execute (wrapped execution function).

Tool Result Types

ToolSuccess{ success: true, data: T }
Result format when tool execution succeeds. The data field contains the return value of the execute function.
ToolFailure{ success: false, error: string }
Result format when tool execution fails. The error field contains the error description.

ToolChoice Type

autostring
The model autonomously decides whether to call tools (default behavior).
nonestring
The model will not call any tools.
requiredstring
The model must call at least one tool.
{ type: 'function', function: { name: string } }object
The model must call the specified tool. This mode is automatically used when only one tool has required: true.