Skip to main content

Custom Tools

A guide to building and configuring custom tools in the thunk builder, including AI-based and code-based tool types.

Custom Tools

A custom tool is a capability defined by the thunk builder that the AI agent can invoke during a workflow run. The AI agent calls the tool by name, passes structured inputs, and acts on the return value.

There are two implementation types: AI and Code. An AI tool executes a natural language instruction. A code tool executes a JavaScript function. The tool builder UI, the input model, and the invocation behavior are identical for both types. The Implementation section is the only difference.


Tool Builder

Every custom tool is defined using three sections in the tool builder: Definition, Inputs, and Implementation.

Definition

Tool name is the snake_case identifier the AI agent uses to select the tool. The name should describe the decision, computation, or action the tool performs.

Description is the plain-language explanation of what the tool does and what it returns. The AI agent reads the description to determine when to call the tool. A precise description improves invocation accuracy. A vague description produces inconsistent behavior.

Inputs

Each input is defined as a named field with a description and a type. The AI agent reads the description of each input to determine what value to pass. Field names are case-sensitive and must match how they are referenced in the implementation.

Format constraints belong in the input description. If a date field expects DD/MM/YYYY, that constraint should appear in the description. If a field accepts only a fixed set of values, those values should be listed. If a field may be null and null has a specific meaning, that should be stated.

Implementation

The Implementation section contains the logic for the tool. The form it takes depends on the tool type and is described in the sections below.

  • Allow batch processing controls whether the AI agent may call the tool on multiple items in a single invocation. It is disabled by default and applies to all tool types.

  • Try it executes the tool with test values before deployment. It applies to all tool types.


Naming and Description Style Guide

The tool name, tool description, and input descriptions are read by the AI agent at runtime. Poorly written metadata produces incorrect or inconsistent invocations regardless of how well the implementation is written.

Tool Names

Tool names use snake_case. The name should express what the tool does, not how it does it.

Example

✅ Good name

is_email_send_required, is_continued_tracking_required, final_go_live_check

❌ Bad name

check, calculate, process

Names that describe the question the tool answers or the computation it performs work well. Generic names that do not distinguish the tool from others in the same thunk do not.

Tool Descriptions

The tool description should state what the tool does and what it returns, in one or two sentences. The AI agent uses this description to decide whether to call the tool at a given point in the workflow.

Example

✅ Good description

Produce a JSON object representing the semantic and syntactic structure of the input document.

❌ Bad description

Text analysis tool.

Descriptions that describe the tool's purpose in details it allows the AI to know what to expect when calling. Generic names that do not distinguish the tool from others in the same thunk do not.

Input Descriptions

Each input description should state what the field contains and any constraints on its value. Three categories of constraint matter most:

  • Format. If the value must follow a specific format, state it explicitly.

    • The submission date in DD/MM/YYYY format.

    • The submission date.

  • Allowed values. If the field accepts only a fixed set of values, list them.

    • The current status of the request. Expected values: "Pending", "Approved", "Rejected".

    • The current status of the request.

  • Null semantics. Each input has an Is Required option in the tool builder. If Is Required is enabled, the field will never be null and no null handling is needed. If Is Required is not enabled, the field may be null. When null has a specific meaning, state it in the description.

    • The date the record was closed. Null if the record is still open.

    • The date the record was closed.

Input names use PascalCase by convention when they correspond to fields in the thunk's data schema. This makes it easier to trace which schema field each input maps to.


How the AI Agent Calls a Custom Tool

The AI agent decides to call the tool based on the tool name and description. It constructs the input object by reading each input's description and populating it from available data. It then receives the return value and uses it in subsequent steps.


AI-based Tools Implementation

An AI tool implements its logic as a natural language instruction. The AI agent reads the instruction and produces a result. AI tools are appropriate when the task requires language understanding, judgment, or interpretation that cannot be expressed as fixed rules.

In addition to Inputs, an AI tool requires an Outputs section. Outputs are defined the same way as inputs: each output is a named field with a description and a type. The AI agent reads the output definitions to understand what values it is expected to produce. A well-written output description tells the agent both what the field contains and the format it should be in. Without defined outputs, the agent has no schema to produce values against.

The instruction can reference input field names directly. The AI agent substitutes each field name with the value passed at invocation time. The instruction is prose that describes what to evaluate, what to produce, and how to handle edge cases.

When the input is unstructured or the outcome requires interpretation, the logic cannot be expressed as code. produce_structured_document in the Benchmark Invoice Processor sample illustrates this. It extracts structured fields from an invoice.

Tool name: produce_structured_document

Inputs:

Name

Description

Type

fileUrl

URL of the document being processed.

TEXT

scenario

The scenario the document is part of, including descriptions of the entities involved and their roles.

TEXT

expectedLanguage

The language the document is expected to be written in.

TEXT

Outputs:

Name

Description

Type

DocumentStructure

A JSON object capturing all fields and values extracted from the source document, preserving the document's original structure and terminology.

TEXT

The thunk reads each named output field from the agent's response and makes it available to subsequent steps.

Read the document and produce a structured JSON object that captures all relational
information. Extract text exactly as it appears in the source document. Do not
modify values to any canonical form — if a field has a currency symbol, keep it;
if it has numerical punctuation, keep it. If a field is not present in the document,
return null for that field rather than inferring it from other values.

The instruction defines not just what to extract but how to behave when the document is incomplete or inconsistent.


Code-based Tools Implementation

A code tool is appropriate when the logic is rule-driven and the outcome is fully determined by the inputs. Use code when the decision can be expressed as conditions, comparisons, or calculations, and when the same inputs should always produce the same output. If the task requires reading unstructured text, exercising judgment, or interpreting ambiguous content, use an AI tool instead.

Every code tool uses the same function signature:

async function run(input) {
  return result;
}

The function is always named run and always declared async. The input parameter is an object whose keys correspond to the input names defined in the Inputs section. No external imports are available. Standard JavaScript and modern syntax are supported, including optional chaining, destructuring, and template literals.

Input fields can be null even when they are expected to have values. Guard against null inputs using optional chaining (?.) or fallback expressions (|| ""). Normalize string inputs with .trim() before comparison to avoid mismatches caused by whitespace. Guard clauses at the top of the function exit early when preconditions are not met, keeping the main logic path uncluttered.

Example: Schedule Wait Gate

A common pattern in scheduled workflows is a tool that decides whether a workflow instance should wait for the next scheduled run or conclude. The tool checks whether the item has reached a terminal status and returns a value the AI agent uses to route the workflow.

Basic Version

Tool name: is_continued_tracking_required

Inputs:

Name

Description

Type

CurrentStatus

The current status of the item. Expected values: "In Progress", "Pending Review", "Completed", "Cancelled", "Closed".

TEXT

async function run(input) {
  const terminalStatuses = ['Completed', 'Cancelled', 'Closed'];
  const status = (input.CurrentStatus || '').trim();
  if (terminalStatuses.includes(status)) {
    return 'NO';
  }
  return 'YES';
}

'YES' signals that tracking should continue; 'NO' signals that the workflow instance can conclude.

Enhanced Version

The tool can be extended to return a structured object when downstream steps require additional data.

Tool name: is_continued_tracking_required

Inputs:

Name

Description

Type

CurrentStatus

The current status of the item. Expected values: "In Progress", "Pending Review", "Completed", "Cancelled", "Closed".

TEXT

StartDate

The date the item was opened, in DD/MM/YYYY format.

TEXT

CurrentDate

Today's date, in DD/MM/YYYY format.

TEXT

Two new inputs provide the dates needed to compute how long the item was open. On conclusion, the AI agent uses FinalStatus and DaysOpen to update the record before the workflow concludes.

async function run(input) {
  const terminalStatuses = ['Completed', 'Cancelled', 'Closed'];
  const status = (input.CurrentStatus || '').trim();  if (!terminalStatuses.includes(status)) {
    const result = { continueTracking: true };
    return result;
  }  let daysOpen = null;
  if (input.StartDate && input.CurrentDate) {
    const [d1, m1, y1] = input.StartDate.split('/');
    const [d2, m2, y2] = input.CurrentDate.split('/');
    const start = new Date(y1, m1 - 1, d1);
    const end = new Date(y2, m2 - 1, d2);
    daysOpen = Math.round((end - start) / (1000 * 60 * 60 * 24));
  }  const result = {
    continueTracking: false,
    FinalStatus: status,
    DaysOpen: daysOpen
  };
  return result;
}

When the item is still active, the function returns early with continueTracking: true. When the item has reached a terminal status, it computes DaysOpen and returns the outcome as a structured object. The AI agent reads the object fields by name and uses them independently.


Examples in the Sample Library

  • Benchmark Invoice Processor — uses an AI tool, produce_structured_document, to read an invoice and return a structured JSON object capturing all relational data.

  • Lead Enrichment for Gyms — uses an AI tool, classify_image, to evaluate gym photos and categorize them based on content rules defined in the instruction.

Did this answer your question?