Skip to Content
DocumentationBuilding with Loopstack๐Ÿ› ๏ธ Custom Tools

๐Ÿ› ๏ธ Creating Custom Tools

Custom tools are reusable components that encapsulate specific functionality in your Loopstack workflows. They can accept arguments, use dependency injection, and integrate seamlessly with the workflow execution.

Basic Tool Structure

A custom tool extends the ToolBase class and implements the execute() method:

import { z } from 'zod'; import { ToolBase } from '@loopstack/core'; import { BlockConfig, ToolResult, WithArguments } from '@loopstack/common'; import { Injectable } from '@nestjs/common'; const propertiesSchema = z.object({ a: z.number(), b: z.number(), }); type MathSumArgs = z.infer<typeof propertiesSchema>; @Injectable() @BlockConfig({ config: { description: 'Add two numbers together', }, }) @WithArguments(propertiesSchema) export class MathSumTool extends ToolBase<MathSumArgs> { async execute(args: MathSumArgs): Promise<ToolResult<number>> { const sum = args.a + args.b; return { data: sum, }; } }

Key Components

1. Required Decorators

@Injectable()

  • Standard NestJS decorator marking the class as injectable
  • Required for dependency injection within the framework

@BlockConfig

  • Defines the toolโ€™s metadata and configuration
  • config.description: Human-readable description of what the tool does

@WithArguments(schema)

  • Defines the expected arguments using a Zod schema

2. Properties Schema

Define the expected arguments using Zod and export a type for use in the execute method:

const propertiesSchema = z.object({ a: z.number(), b: z.number(), }).strict(); export type MathSumArgs = z.infer<typeof propertiesSchema>;

3. Base Class

Extend ToolBase with your arguments type as a generic parameter:

export class MathSumTool extends ToolBase<MathSumArgs> { // ... }

4. Execute Method

The execute() method contains your toolโ€™s logic. Arguments are passed directly as a parameter:

async execute(args: MathSumArgs): Promise<ToolResult<number>> { const result = args.a + args.b; return { data: result, }; }

Using Tools in Workflows

1. Register the Tool in Your Workflow

Use the @Tool() decorator to register tools in your workflow class:

@Injectable() @BlockConfig({ configFile: __dirname + '/custom-tool-example.workflow.yaml', }) export class CustomToolExampleWorkflow extends WorkflowBase { @Tool() private mathTool: MathSumTool; @Tool() private createChatMessage: CreateChatMessage; }

2. Register as Provider

Add the tool and its dependencies to your moduleโ€™s providers:

import { Module } from '@nestjs/common'; @Module({ providers: [ CustomToolExampleWorkflow, MathSumTool, // ... ], }) export class ExampleModule {}

3. Call in Workflow YAML

Use the tool in your workflow transitions. Reference tools by their property name (camelCase):

title: "Custom Tool Example" description: | This workflow demonstrates the usage of custom tools. It performs a simple addition operation using a custom MathSumTool. ui: form: properties: a: title: 'First number (a)' b: title: 'Second number (b)' transitions: - id: calculate from: start to: end call: # Use a custom tool - id: calculation tool: mathTool args: a: ${ args.a } b: ${ args.b } assign: total: ${ result.data } # Display the result - tool: createChatMessage args: role: 'assistant' content: | Tool calculation result: {{ args.a }} + {{ args.b }} = {{ total }}
Last updated on