Skip to content

@Tool Decorator

The @Tool class decorator and related interfaces for defining tools declaratively. Attach metadata -- name, description, parameter schemas -- to a class, and the framework handles validation and registry integration automatically.

Import

import {
  Tool,
  ITool,
  ToolMetadata,
  ToolParameter,
  getToolMetadata,
  isTool,
  validateToolParams,
  createTool,
} from "@modernpath/agent-framework";

ITool Interface

The contract that every tool must satisfy.

interface ITool {
  execute(params: Record<string, any>): Promise<any>;
  validate?(params: Record<string, any>): void;
  cleanup?(): Promise<void>;
}
Method Required Description
execute(params) Yes Run the tool with the given parameters and return the result.
validate(params) No Validate parameters before execution. If not provided and @Tool is used, the decorator auto-generates a validator from ToolMetadata.parameters.
cleanup() No Release resources (e.g., close connections). Called during shutdown if the host manages tool lifecycle.

@Tool Decorator

Class decorator that attaches ToolMetadata to a tool class.

function Tool(metadata: ToolMetadata): ClassDecorator

Behavior

  1. Stores metadata in an internal WeakMap keyed by the class constructor
  2. Adds a static getMetadata() method to the class
  3. Validates that the class has an execute method (throws at decoration time if missing)
  4. If the class does not define a validate method, auto-generates one based on metadata.parameters

Usage

@Tool({
  name: "document-retrieve",
  description: "Retrieves a document from SharePoint by ID",
  version: "1.0.0",
  category: "sharepoint",
  tags: ["document", "sharepoint"],
  requiresAuth: true,
  parameters: {
    siteId: {
      type: "string",
      description: "SharePoint site ID",
    },
    documentId: {
      type: "string",
      description: "Document unique identifier",
    },
  },
  examples: [
    {
      description: "Retrieve a specific document",
      params: { siteId: "site-123", documentId: "doc-456" },
      expectedOutput: { content: "...", mimeType: "application/pdf" },
    },
  ],
})
class DocumentRetrieveTool implements ITool {
  async execute(params: Record<string, any>): Promise<any> {
    // Implementation...
    return { content: "...", mimeType: "application/pdf" };
  }
}

TypeScript decorator configuration

The @Tool decorator uses legacy TypeScript decorators. Ensure your tsconfig.json has "experimentalDecorators": true. The decorator does not require reflect-metadata.


ToolMetadata

Metadata describing a tool's identity, parameters, and capabilities.

interface ToolMetadata {
  name: string;
  description: string;
  parameters?: Record<string, ToolParameter>;
  version?: string;
  category?: string;
  requiresAuth?: boolean;
  rateLimit?: number;
  schema?: {
    inputJsonSchema?: any;
    outputJsonSchema?: any;
  };
  examples?: Array<{
    description: string;
    params: Record<string, any>;
    expectedOutput?: any;
  }>;
  tags?: string[];
}

Properties

Name Type Required Default Description
name string Yes -- Unique tool identifier.
description string Yes -- Human-readable description of what the tool does. Used by LLM planners and tool catalogs.
parameters Record<string, ToolParameter> No undefined Parameter schema. Keys are parameter names, values are ToolParameter definitions.
version string No undefined Tool version string.
category string No undefined Category for grouping (e.g. "sharepoint", "external", "analytics"). Used by ToolRegistry.search().
requiresAuth boolean No undefined Indicates the tool requires authentication to function.
rateLimit number No undefined Maximum calls per minute. Informational; enforcement is the caller's responsibility.
schema object No undefined Extended JSON Schema objects for richer protocol adapters (e.g. MCP). inputJsonSchema describes the input; outputJsonSchema describes the output. These are intentionally loose-typed to accept any JSON Schema draft.
examples Array<Example> No undefined Usage examples with sample parameters and expected outputs. Useful for documentation and testing.
tags string[] No undefined Freeform tags for filtering (e.g. ["document", "sharepoint"]). Used by ToolRegistry.search().

ToolParameter

Describes a single parameter in a tool's parameter schema.

interface ToolParameter {
  type: "string" | "number" | "boolean" | "array" | "object";
  description: string;
  required?: boolean;
  default?: any;
  pattern?: string;
  min?: number;
  max?: number;
  enum?: any[];
  items?: ToolParameter;
  properties?: Record<string, ToolParameter>;
}

Properties

Name Type Required Default Description
type string Yes -- Parameter data type. One of "string", "number", "boolean", "array", "object".
description string Yes -- Human-readable description.
required boolean No true Whether the parameter must be provided. Defaults to required (i.e., required !== false means required).
default any No undefined Default value if the parameter is not provided. A parameter with a default is never treated as missing.
pattern string No undefined Regex pattern for string validation.
min number No undefined Minimum value for numbers.
max number No undefined Maximum value for numbers.
enum any[] No undefined List of allowed values.
items ToolParameter No undefined Schema for array elements (when type is "array").
properties Record<string, ToolParameter> No undefined Schema for object properties (when type is "object").

Validation Rules

The built-in validateToolParams() function enforces these rules:

  1. Required check: If required !== false and no default is defined, the parameter must be present
  2. Type check: The JavaScript typeof (or Array.isArray for arrays) must match type
  3. Pattern check: For strings, the value must match the regex pattern
  4. Range check: For numbers, the value must be within [min, max]
  5. Enum check: The value must be one of the allowed enum values
  6. Nested validation: Array items and object properties are validated recursively

Helper Functions

getToolMetadata

Retrieve metadata from a tool instance or class.

function getToolMetadata(target: any): ToolMetadata | undefined

Checks the internal WeakMap for the target, then falls back to target.constructor. Returns undefined if no @Tool decorator was applied.

const tool = new WeatherTool();
const meta = getToolMetadata(tool);
console.log(meta?.name); // "weather"

isTool

Check whether an object has @Tool metadata attached.

function isTool(target: any): boolean
console.log(isTool(new WeatherTool())); // true
console.log(isTool({}));                // false

validateToolParams

Validate a parameter object against a parameter schema. Throws descriptive errors on validation failure.

function validateToolParams(
  params: Record<string, any>,
  paramMetadata: Record<string, ToolParameter>
): void
import { validateToolParams } from "@modernpath/agent-framework";

const schema = {
  city: { type: "string" as const, description: "City name" },
  days: { type: "number" as const, description: "Forecast days", min: 1, max: 14 },
};

validateToolParams({ city: "Helsinki", days: 7 }, schema); // OK
validateToolParams({ city: "Helsinki", days: 30 }, schema); // throws: "Parameter days must be at most 14"
validateToolParams({}, schema);                             // throws: "Missing required parameter: city"

createTool

Create an ITool object from a metadata definition and an execute function, without using the @Tool decorator.

function createTool(
  metadata: ToolMetadata,
  executeFn: (params: Record<string, any>) => Promise<any>
): ITool

The returned tool has an auto-generated validate method based on metadata.parameters.

import { createTool } from "@modernpath/agent-framework";

const echoTool = createTool(
  {
    name: "echo",
    description: "Echoes the input",
    parameters: {
      message: { type: "string", description: "Message to echo" },
    },
  },
  async (params) => ({ echo: params.message }),
);

// Register with the registry
registry.register("echo", echoTool);

Complete Example

import { Tool, ITool, ToolRegistry } from "@modernpath/agent-framework";

@Tool({
  name: "currency-convert",
  description: "Converts an amount between two currencies",
  version: "2.0.0",
  category: "finance",
  tags: ["currency", "conversion", "external"],
  requiresAuth: true,
  rateLimit: 100,
  parameters: {
    from: {
      type: "string",
      description: "Source currency code (ISO 4217)",
      pattern: "^[A-Z]{3}$",
    },
    to: {
      type: "string",
      description: "Target currency code (ISO 4217)",
      pattern: "^[A-Z]{3}$",
    },
    amount: {
      type: "number",
      description: "Amount to convert",
      min: 0,
    },
    precision: {
      type: "number",
      description: "Decimal places in result",
      required: false,
      default: 2,
      min: 0,
      max: 8,
    },
  },
  examples: [
    {
      description: "Convert 100 EUR to USD",
      params: { from: "EUR", to: "USD", amount: 100 },
      expectedOutput: { converted: 108.50, rate: 1.085 },
    },
  ],
})
class CurrencyConvertTool implements ITool {
  constructor(private readonly apiKey: string) {}

  async execute(params: Record<string, any>): Promise<any> {
    const { from, to, amount, precision = 2 } = params;
    const rate = await this.fetchRate(from, to);
    return {
      converted: Number((amount * rate).toFixed(precision)),
      rate,
      from,
      to,
    };
  }

  async cleanup(): Promise<void> {
    // Close any open connections
  }

  private async fetchRate(from: string, to: string): Promise<number> {
    // API call implementation...
    return 1.085;
  }
}

// Usage
const registry = new ToolRegistry();
registry.register("currency-convert", new CurrencyConvertTool("sk-..."));

await registry.execute("currency-convert", {
  from: "EUR",
  to: "USD",
  amount: 100,
});
// => { converted: 108.50, rate: 1.085, from: "EUR", to: "USD" }