Creating custom tools

Create a custom Tool to run JavaScript, TypeScript, or Python without calling an external API. Use it for validation, data transformation, or business logic. The Tool receives your defined parameters and returns a result to the Agent.

Create a custom Tool

Create and configure a custom Tool with these steps:

  1. In the sidebar, click Tools.
  2. Click Create Tool.
  3. Enter the Tool name and description.
  4. Select Custom Code.
  5. Click Create Tool.

After the tool editor opens, configure the code and parameter schema with these steps:

  1. In Configuration, choose an Execution Environment.
  2. If you choose Daytona Sandbox, choose a Language.
  3. Enter the implementation in the language-specific code editor.
  4. Open Parameters and click Add Parameter.
  5. Configure each parameter, then click Save.

Pick an execution environment

Choose an execution environment that supports the language and network behavior that your code needs.

Execution environmentSupported languagesNetwork accessUse it for
Cloudflare Worker (Default)JavaScriptNo network accessJavaScript with built-in helpers
Daytona SandboxJavaScript, TypeScript, PythonFollows the Daytona sandbox policyPython, TypeScript, or container-based execution
QuickJS (Legacy)JavaScriptNo network accessExisting JavaScript tools that use the legacy runtime

Use Cloudflare Worker (Default) for JavaScript that uses async/await, standard built-ins, or the helpers namespace. Runtype captures console.log output in execution logs.

Use Daytona Sandbox for TypeScript or Python. Runtype injects each valid parameter name as a variable in your code. To configure Daytona, open Settings and select Integrations. Under Code Execution, enter the Daytona API key and click Save Key. Runtype can use a platform key when you do not add your own key. Without a Daytona key, the Tool returns an execution error.

Use QuickJS (Legacy) to maintain existing JavaScript tools. QuickJS runs JavaScript synchronously and exposes the helper functions as global functions instead of the helpers namespace.

Custom code cannot use managed secrets through {{secret:NAME}}. In this syntax, NAME is the managed secret name. Use an external Tool when you need managed credentials or an HTTP request.

Write Tool code

The runtime provides input in different ways. Cloudflare Worker and QuickJS code reads the parameters object. Daytona Sandbox injects each valid parameter as a variable. Use this example with Cloudflare Worker or QuickJS:

1const { orderAmount, customerTier } = parameters
2
3let discount = 0
4if (customerTier === 'premium') {
5 discount = orderAmount > 1000 ? 0.2 : 0.15
6} else if (customerTier === 'standard') {
7 discount = orderAmount > 1000 ? 0.1 : 0.05
8}
9
10const discountedAmount = orderAmount * (1 - discount)
11
12return {
13 originalAmount: orderAmount,
14 discount: discount * 100,
15 finalAmount: discountedAmount,
16}

Use this Python example with Daytona. Define a parameter named numbers with type array before you run the example:

1import json
2import statistics
3
4result = {
5 'sum': sum(numbers),
6 'mean': statistics.mean(numbers),
7 'median': statistics.median(numbers),
8 'min': min(numbers),
9 'max': max(numbers)
10}
11
12print(json.dumps(result))

Print one JSON value from Python so Runtype can parse the result. Use the language runtime and packages that Daytona Sandbox provides. Outbound network access follows its sandbox policy.

Runtime behavior

The runtime handles return values and errors as follows:

  • Return a value from JavaScript code in Cloudflare Worker or QuickJS. The Tool passes the returned value to the Agent.
  • Print a JSON value from Python or TypeScript in Daytona Sandbox. Runtype parses the output as JSON when possible.
  • Handle expected failures with a structured error object. An unhandled exception fails the Tool and returns an error to the Agent.
  • Use console.log in Cloudflare Worker or QuickJS code when you need output in execution logs.

Available JavaScript features

Cloudflare Worker provides these JavaScript features:

  • Standard built-ins such as Array, Object, Math, Date, and JSON.
  • async/await, arrow functions, destructuring, and template literals.
  • Regular expressions and string manipulation.
  • The helpers namespace.
  • Captured console.log output in execution logs.

QuickJS provides the following JavaScript features:

  • Standard built-ins such as Array, Object, Math, Date, and JSON.
  • Regular expressions and string manipulation.
  • Helper functions such as parseHTML() and extractEmails() as global functions.
  • Synchronous JavaScript execution.

Daytona Sandbox provides the language runtime and container capabilities in its configuration. Use the selected language’s standard library and available packages.

Cloudflare Worker helper functions

Cloudflare Worker exposes the following functions in the helpers namespace:

HelperDescription
helpers.parseHTML(html)Parses HTML and returns a title, headings, links, text, and selector methods.
helpers.extractEmails(text)Extracts email addresses from text.
helpers.extractURLs(text)Extracts URLs from text.
helpers.extractPhoneNumbers(text)Extracts phone numbers from text.
helpers.formatDate(date, format)Formats a date with YYYY-MM-DD HH:mm:ss patterns.
helpers.parseDate(dateStr)Parses a date string and returns a Date object or null.
helpers.addDays(date, days)Adds or subtracts days from a date.
helpers.truncate(text, length)Truncates text to the specified length.
helpers.slugify(text)Converts text into a URL-friendly slug.
helpers.markdown2html(md)Converts basic Markdown into HTML.
helpers.html2markdown(html)Converts basic HTML into Markdown.
helpers.isValidEmail(email)Checks whether a string matches the supported email format.
helpers.isValidURL(url)Checks whether a string is a valid URL.
helpers.isValidJSON(str)Checks whether a string contains valid JSON.
helpers.base64Encode(str)Encodes a string as base64.
helpers.base64Decode(str)Decodes a base64 string.
helpers.urlEncode(str)URL-encodes a string.
helpers.urlDecode(str)URL-decodes a string.

In QuickJS, call these helper functions without the helpers. prefix.

Define parameters

Open Parameters, then click Add Parameter to define the input schema. Configure each field as follows:

FieldPurpose
NameNames the parameter that your code reads.
TypeSets string, number, boolean, object, or array.
DescriptionExplains the value that the Agent must provide.
RequiredMarks the parameter as mandatory.
Default ValueSets the value that Runtype uses when the Agent does not provide one.

Use this parameter schema with the discount example:

NameTypeRequiredDescription
orderAmountnumberYesTotal order value in USD.
customerTierstringYesstandard or premium.

Set execution limits

Set Timeout (ms) between 1,000 and 300,000. The default is 30,000 milliseconds. Cloudflare Worker and QuickJS use this value. Daytona Sandbox uses its sandbox execution limit.

The Memory Limit control offers 8 MB, 16 MB, and 32 MB options. The effective memory limit depends on the selected execution environment.

Validate Tool code

Runtype validates custom code before it saves a Tool. JavaScript and TypeScript use syntax and semantic checks. Python uses syntax checks.

Validation errors block a save. The following checks produce errors:

  • eval() with the error code USE_OF_EVAL.
  • new Function() with the error code USE_OF_FUNCTION_CTOR.
  • Syntax errors.

Validation warnings appear in the dashboard but do not block a save. The following checks produce warnings:

  • An unbounded while (true) loop without a break or await, with the warning code INFINITE_LOOP.
  • A dynamic import() expression, with the warning code DYNAMIC_IMPORT.
  • A missing return statement in JavaScript or TypeScript for Cloudflare Worker and QuickJS, with the warning code RETURN_UNDEFINED.

Runtype runs the validation again whenever you update the Tool.

Handle errors

Return a structured error object when the Agent can recover from an expected input error. Use this JavaScript example:

1const { email } = parameters
2
3const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
4if (!emailRegex.test(email)) {
5 return { success: false, error: 'Invalid email format' }
6}
7
8return { success: true, isValid: true, email }

Test a Tool

Run a test from the tool editor with these steps:

  1. Open Test Tool.
  2. Enter sample values in the Params tab.
  3. Click Run Test.
  4. Review the status, result, error, and execution time in the Result tab.
  5. Open the History tab to review previous tests.

Apply best practices

Use these practices when you create a custom Tool:

  • Give each Tool one responsibility.
  • Validate parameter values before processing them.
  • Name each Tool after its operation, such as calculate_shipping_cost.
  • Return structured objects so the Agent can use individual fields.
  • Return { success: false, error: "..." } for expected errors instead of throwing.
  • Keep one operation in each Tool so you can isolate failures.
  • Describe the Tool purpose, usage condition, output, limitations, and side effects.

Examples

Email validator

Use this JavaScript example to validate an email address:

1const { email } = parameters
2const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
3return {
4 isValid: emailRegex.test(email),
5 email,
6}

Define one required email parameter with type string.

Date formatter

Use this JavaScript example to format an ISO date or return the default ISO representation:

1const { isoDate, format } = parameters
2const date = new Date(isoDate)
3
4if (format === 'short') {
5 return { formatted: date.toLocaleDateString() }
6} else if (format === 'long') {
7 return {
8 formatted: date.toLocaleDateString('en-US', {
9 weekday: 'long',
10 year: 'numeric',
11 month: 'long',
12 day: 'numeric',
13 }),
14 }
15}
16
17return { formatted: date.toISOString() }

Define isoDate as a required string parameter and format as a string parameter with the default value iso.

Array aggregator

Use this Python example with Daytona to aggregate an array of numbers:

1import json
2import statistics
3
4result = {
5 'sum': sum(numbers),
6 'average': statistics.mean(numbers),
7 'min': min(numbers),
8 'max': max(numbers),
9 'count': len(numbers)
10}
11
12print(json.dumps(result))

Define one required numbers parameter with type array and numeric items.

Next steps

Use these links to continue: