CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-langchain

TypeScript framework for building LLM-powered applications with agents, tools, middleware, and model interoperability

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

tools.mddocs/guides/

Tool Guide

This guide covers creating and using tools to give agents the ability to take actions.

Creating Tools

Basic Tool

import { tool } from "langchain";
import { z } from "zod";

const calculator = tool(
  async ({ expression }) => {
    try {
      return String(eval(expression));
    } catch (error) {
      return `Error: ${error.message}`;
    }
  },
  {
    name: "calculator",
    description: "Evaluate mathematical expressions. Use for arithmetic calculations.",
    schema: z.object({
      expression: z.string().describe("The mathematical expression to evaluate"),
    }),
  }
);

Tool with Complex Schema

import { tool } from "langchain";
import { z } from "zod";

const searchDatabase = tool(
  async ({ query, filters, limit }) => {
    const results = await database.search({
      query,
      filters,
      limit,
    });
    return JSON.stringify(results, null, 2);
  },
  {
    name: "search_database",
    description: "Search the database with optional filters and result limit",
    schema: z.object({
      query: z.string().describe("Search query string"),
      filters: z.object({
        category: z.string().optional(),
        dateFrom: z.string().optional(),
        dateTo: z.string().optional(),
      }).optional().describe("Optional filters"),
      limit: z.number().default(10).describe("Maximum number of results"),
    }),
  }
);

Tool with External API

import { tool } from "langchain";
import { z } from "zod";

const getWeather = tool(
  async ({ location, units }) => {
    const response = await fetch(
      `https://api.weather.com/${location}?units=${units}`
    );
    if (!response.ok) {
      return `Error: Failed to fetch weather data`;
    }
    const data = await response.json();
    return JSON.stringify(data);
  },
  {
    name: "get_weather",
    description: "Get current weather for a location",
    schema: z.object({
      location: z.string().describe("City name or zip code"),
      units: z.enum(["celsius", "fahrenheit"]).default("celsius"),
    }),
  }
);

Tool with Side Effects

import { tool } from "langchain";
import { z } from "zod";

const sendEmail = tool(
  async ({ to, subject, body }) => {
    try {
      await emailService.send({ to, subject, body });
      return `Email sent successfully to ${to}`;
    } catch (error) {
      return `Failed to send email: ${error.message}`;
    }
  },
  {
    name: "send_email",
    description: "Send an email to a recipient",
    schema: z.object({
      to: z.string().email().describe("Recipient email address"),
      subject: z.string().describe("Email subject"),
      body: z.string().describe("Email body content"),
    }),
  }
);

Tool with Error Handling

import { tool } from "langchain";
import { z } from "zod";

const apiCall = tool(
  async ({ endpoint, method, body }) => {
    try {
      const response = await fetch(endpoint, {
        method,
        headers: { "Content-Type": "application/json" },
        body: body ? JSON.stringify(body) : undefined,
      });

      if (!response.ok) {
        return `Error: HTTP ${response.status} - ${response.statusText}`;
      }

      const data = await response.json();
      return JSON.stringify(data);
    } catch (error) {
      return `Error: ${error.message}`;
    }
  },
  {
    name: "api_call",
    description: "Make an API call to a specified endpoint",
    schema: z.object({
      endpoint: z.string().url().describe("API endpoint URL"),
      method: z.enum(["GET", "POST", "PUT", "DELETE"]).default("GET"),
      body: z.any().optional().describe("Request body for POST/PUT"),
    }),
  }
);

Tool with Context Access

import { tool } from "langchain";
import { z } from "zod";

const personalizedGreeting = tool(
  async ({ name }, config) => {
    // Access runtime configuration
    const userId = config?.configurable?.user_id;
    const preferences = await loadUserPreferences(userId);

    return `Hello ${name}! ${preferences.greeting_style}`;
  },
  {
    name: "personalized_greeting",
    description: "Generate a personalized greeting",
    schema: z.object({
      name: z.string().describe("Name to greet"),
    }),
  }
);

Using Tools with Agents

Single Tool

import { createAgent, tool } from "langchain";
import { z } from "zod";

const calculator = tool(
  async ({ expression }) => String(eval(expression)),
  {
    name: "calculator",
    description: "Evaluate mathematical expressions",
    schema: z.object({
      expression: z.string(),
    }),
  }
);

const agent = createAgent({
  model: "openai:gpt-4o",
  tools: [calculator],
});

const result = await agent.invoke({
  messages: [{ role: "user", content: "What is 25 * 4?" }],
});

Multiple Tools

import { createAgent, tool } from "langchain";
import { z } from "zod";

const add = tool(
  async ({ a, b }) => String(a + b),
  {
    name: "add",
    description: "Add two numbers",
    schema: z.object({ a: z.number(), b: z.number() }),
  }
);

const multiply = tool(
  async ({ a, b }) => String(a * b),
  {
    name: "multiply",
    description: "Multiply two numbers",
    schema: z.object({ a: z.number(), b: z.number() }),
  }
);

const divide = tool(
  async ({ a, b }) => {
    if (b === 0) return "Error: Division by zero";
    return String(a / b);
  },
  {
    name: "divide",
    description: "Divide two numbers",
    schema: z.object({ a: z.number(), b: z.number() }),
  }
);

const agent = createAgent({
  model: "openai:gpt-4o",
  tools: [add, multiply, divide],
});

const result = await agent.invoke({
  messages: [{ role: "user", content: "What is (10 + 5) * 3?" }],
});

Tool Collections

// Organize tools by category
const mathTools = [addTool, multiplyTool, divideTool];
const searchTools = [webSearchTool, databaseSearchTool];
const communicationTools = [sendEmailTool, sendSlackTool];

// Create agent with all tools
const agent = createAgent({
  model: "openai:gpt-4o",
  tools: [...mathTools, ...searchTools, ...communicationTools],
});

Tool Patterns

Async Long-Running Tools

import { tool } from "langchain";
import { z } from "zod";

const longTask = tool(
  async ({ taskId }) => {
    // Start background job
    const job = await jobQueue.submit(taskId);

    // Wait for completion
    const result = await job.wait({ timeout: 30000 });

    return `Task ${taskId} completed: ${result}`;
  },
  {
    name: "long_task",
    description: "Execute a long-running background task",
    schema: z.object({
      taskId: z.string().describe("Task identifier"),
    }),
  }
);

Tools with Validation

import { tool } from "langchain";
import { z } from "zod";

const createUser = tool(
  async ({ email, age, username }) => {
    // Additional runtime validation
    if (await userExists(email)) {
      return "Error: User with this email already exists";
    }

    const user = await database.createUser({ email, age, username });
    return `User created: ${user.id}`;
  },
  {
    name: "create_user",
    description: "Create a new user account",
    schema: z.object({
      email: z.string().email(),
      age: z.number().min(13).max(120),
      username: z.string().min(3).max(20).regex(/^[a-zA-Z0-9_]+$/),
    }),
  }
);

Tools with Rate Limiting

import { tool } from "langchain";
import { z } from "zod";
import { RateLimiter } from "limiter";

const limiter = new RateLimiter({ tokensPerInterval: 10, interval: "minute" });

const apiCall = tool(
  async ({ endpoint }) => {
    // Check rate limit
    const allowed = await limiter.removeTokens(1);
    if (!allowed) {
      return "Error: Rate limit exceeded. Please try again later.";
    }

    const response = await fetch(endpoint);
    const data = await response.json();
    return JSON.stringify(data);
  },
  {
    name: "rate_limited_api",
    description: "Make rate-limited API call",
    schema: z.object({
      endpoint: z.string().url(),
    }),
  }
);

Tools with Caching

import { tool } from "langchain";
import { z } from "zod";

const cache = new Map();

const expensiveOperation = tool(
  async ({ input }) => {
    // Check cache
    if (cache.has(input)) {
      return cache.get(input);
    }

    // Perform expensive operation
    const result = await performExpensiveComputation(input);

    // Cache result
    cache.set(input, result);

    return result;
  },
  {
    name: "expensive_operation",
    description: "Perform cached expensive operation",
    schema: z.object({
      input: z.string(),
    }),
  }
);

Tools with Retry Logic

import { tool } from "langchain";
import { z } from "zod";

const resilientApiCall = tool(
  async ({ url }) => {
    const maxRetries = 3;
    let lastError;

    for (let i = 0; i < maxRetries; i++) {
      try {
        const response = await fetch(url);
        if (!response.ok) throw new Error(`HTTP ${response.status}`);
        const data = await response.json();
        return JSON.stringify(data);
      } catch (error) {
        lastError = error;
        if (i < maxRetries - 1) {
          await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
        }
      }
    }

    return `Error after ${maxRetries} attempts: ${lastError.message}`;
  },
  {
    name: "resilient_api_call",
    description: "Make API call with automatic retries",
    schema: z.object({
      url: z.string().url(),
    }),
  }
);

Alternative Tool APIs

DynamicTool (String Input)

import { DynamicTool } from "langchain";

const weatherTool = new DynamicTool({
  name: "get_weather",
  description: "Get weather for a location (pass location as string)",
  func: async (input: string) => {
    const response = await fetch(`https://api.weather.com/${input}`);
    const data = await response.json();
    return JSON.stringify(data);
  },
});

DynamicStructuredTool

import { DynamicStructuredTool } from "langchain";
import { z } from "zod";

const weatherTool = new DynamicStructuredTool({
  name: "get_weather",
  description: "Get weather for a location",
  schema: z.object({
    location: z.string().describe("City name"),
    units: z.enum(["celsius", "fahrenheit"]).default("celsius"),
  }),
  func: async ({ location, units }) => {
    const response = await fetch(
      `https://api.weather.com/${location}?units=${units}`
    );
    const data = await response.json();
    return JSON.stringify(data);
  },
});

Extending Tool Base Class

import { Tool } from "langchain";

class CustomTool extends Tool {
  name = "custom_tool";
  description = "A custom tool implementation";

  async _call(input: string): Promise<string> {
    // Implement tool logic
    return `Processed: ${input}`;
  }
}

const tool = new CustomTool();

Extending StructuredTool Base Class

import { StructuredTool } from "langchain";
import { z } from "zod";

class CustomStructuredTool extends StructuredTool {
  name = "custom_structured_tool";
  description = "A custom structured tool";
  schema = z.object({
    query: z.string(),
    limit: z.number().optional(),
  });

  async _call({ query, limit = 10 }): Promise<string> {
    // Implement tool logic
    const results = await search(query, limit);
    return JSON.stringify(results);
  }
}

const tool = new CustomStructuredTool();

Best Practices

Tool Naming

  • Use descriptive, action-oriented names: search_web, create_task, send_email
  • Use snake_case for consistency
  • Make names intuitive for LLMs
  • Avoid abbreviations unless universally understood

Tool Descriptions

  • Be specific about what the tool does
  • Mention key parameters and their purpose
  • Explain when the tool should be used
  • Keep descriptions concise (1-2 sentences)
  • Include examples if the usage is not obvious

Schema Design

  • Use .describe() on all fields to help the LLM
  • Provide sensible defaults where appropriate
  • Use z.enum() for constrained choices
  • Make optional parameters truly optional
  • Use validation (.email(), .url(), .min(), .max()) where appropriate

Error Handling

  • Return error messages as strings (don't throw)
  • Include helpful context in error messages
  • Validate inputs before expensive operations
  • Handle network failures gracefully
  • Provide actionable error messages

Return Values

  • Return structured data as JSON strings
  • Keep return values concise
  • Include only relevant information
  • Consider the LLM's context window
  • Format data for easy parsing by the LLM

Performance

  • Cache results when appropriate
  • Implement timeouts for external calls
  • Use rate limiting for API calls
  • Consider async/await for I/O operations
  • Batch operations when possible

Security

  • Validate and sanitize all inputs
  • Never expose sensitive data in tool output
  • Use environment variables for API keys
  • Implement proper authentication
  • Be cautious with tools that modify data
  • Consider using human-in-the-loop for dangerous operations

Testing

  • Test tools independently before using with agents
  • Verify error handling paths
  • Test with invalid inputs
  • Mock external dependencies
  • Test rate limiting and retries

See Tool API Reference for complete API documentation.

docs

glossary.md

index.md

quick-reference.md

task-index.md

tile.json