npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@isdk/tool-func

v0.1.1

Published

A manager for creating, registering, and executing reusable tool functions with rich metadata.

Downloads

20

Readme

@isdk/tool-func

A powerful TypeScript framework for creating, managing, and executing modular tool functions. It's perfect for building AI agent tools, backend services, and extensible plugin systems with a clean, decoupled architecture.

✨ Core Features

  • 📦 Modular & Reusable Tools: Define functions as ToolFunc instances with rich metadata.
  • 🌐 Global Registry: A static registry (ToolFunc.items) allows any part of an application to access and run registered functions by name.
  • 🔗 Dependency Management: Use the depends property to declare dependencies on other ToolFuncs, which are then auto-registered.
  • 🏷️ Aliasing & Tagging: Assign multiple names (alias) or tags to a function for flexibility and grouping.
  • 🚀 Lifecycle Hooks: Use the setup method for one-time initialization logic.
  • 🔄 Asynchronous Capabilities: Built-in support for cancellable tasks, timeouts, and concurrency control using makeToolFuncCancelable.
  • 🌊 Streamable Responses: Easily create and handle streaming responses with the stream property and createCallbacksTransformer.

📦 Installation

npm install @isdk/tool-func

🚀 Basic Usage

1. Define a Tool

Create a ToolFunc instance to define your tool's metadata and implementation.

import { ToolFunc } from '@isdk/tool-func';

const getUser = new ToolFunc({
  name: 'getUser',
  description: 'Retrieves a user by ID.',
  params: { id: { type: 'string', required: true } },
  func: (params) => ({ id: params.id, name: 'John Doe' }),
});

2. Register the Tool

Register the tool to make it available in the global registry.

getUser.register();

3. Run the Tool

Execute the tool from anywhere in your application using the static run method.

async function main() {
  const user = await ToolFunc.run('getUser', { id: '123' });
  console.log(user); // Outputs: { id: '123', name: 'John Doe' }
}

main();

🌟 Advanced Usage

Dependency Management

Declare dependencies on other tools, and they will be registered automatically.

const welcomeUser = new ToolFunc({
  name: 'welcomeUser',
  description: 'Generates a welcome message.',
  params: { userId: 'string' },
  depends: {
    // `getUser` will be auto-registered when `welcomeUser` is registered.
    userFetcher: getUser,
  },
  func: function(params) {
    // `this` is the ToolFunc instance, so we can use `runSync`
    const user = this.runSync('userFetcher', { id: params.userId });
    return `Hello, ${user.name}!`;
  },
});

welcomeUser.register();

const message = await ToolFunc.run('welcomeUser', { userId: '456' });
console.log(message); // "Hello, John Doe!"

Lifecycle Hooks: The setup Method

The setup hook provides a way to run one-time initialization logic when a ToolFunc instance is created. This is useful for configuring the instance, setting up initial state, or modifying properties before the tool is registered or used. The this context inside setup refers to the ToolFunc instance itself.

const statefulTool = new ToolFunc({
  name: 'statefulTool',
  customState: 'initial', // Define a custom property
  setup() {
    // `this` is the statefulTool instance
    console.log(`Setting up ${this.name}...`);
    this.customState = 'configured';
    this.initializedAt = new Date();
  },
  func() {
    return `State: ${this.customState}, Initialized: ${this.initializedAt.toISOString()}`;
  }
});

console.log(statefulTool.customState); // "configured"

statefulTool.register();
console.log(await ToolFunc.run('statefulTool'));
// "State: configured, Initialized: ..."

Asynchronous & Cancellable Tasks

Add powerful async capabilities like cancellation and concurrency control.

import { ToolFunc, makeToolFuncCancelable, AsyncFeatures } from '@isdk/tool-func';

// Create a cancellable version of the ToolFunc class
const CancellableToolFunc = makeToolFuncCancelable(ToolFunc, {
  maxTaskConcurrency: 5, // Allow up to 5 concurrent tasks
});

const longRunningTask = new CancellableToolFunc({
  name: 'longRunningTask',
  asyncFeatures: AsyncFeatures.Cancelable, // Mark as cancelable
  func: async function(params, aborter) {
    console.log('Task started...');
    await new Promise(resolve => setTimeout(resolve, 5000)); // 5s task
    aborter.throwIfAborted(); // Check for cancellation
    console.log('Task finished!');
    return { success: true };
  }
});

longRunningTask.register();

// Run the task and get its aborter
const promise = ToolFunc.run('longRunningTask');
const task = promise.task;

// Abort the task after 2 seconds
setTimeout(() => {
  task.abort('User cancelled');
}, 2000);

Streaming Responses

Define a tool that can stream its output.

import { ToolFunc, createCallbacksTransformer } from '@isdk/tool-func';

const streamingTool = new ToolFunc({
  name: 'streamingTool',
  stream: true, // Enable streaming capability
  func: function(params) {
    const stream = new ReadableStream({
      async start(controller) {
        for (let i = 0; i < 5; i++) {
          controller.enqueue({ chunk: i });
          await new Promise(r => setTimeout(r, 100));
        }
        controller.close();
      }
    });

    // Use a transformer to handle stream events
    const transformer = createCallbacksTransformer({
      onTransform: (chunk) => console.log('Received:', chunk),
      onFinal: () => console.log('Stream finished!'),
    });

    return stream.pipeThrough(transformer);
  }
});

streamingTool.register();
ToolFunc.run('streamingTool');

Parameter Handling: Object vs. Positional

ToolFunc supports both object-based and positional parameters for flexibility. While both are functional, object parameters are generally recommended for their clarity and self-documenting nature.

Object Parameters (Recommended)

When params is defined as an object, the func receives a single object argument containing all parameters by name. This is the default and most straightforward approach.

const greetUser = new ToolFunc({
  name: 'greetUser',
  description: 'Greets a user by name and age.',
  params: {
    name: { type: 'string', required: true },
    age: { type: 'number' },
  },
  func: (args) => {
    const { name, age } = args;
    return `Hello, ${name}! ${age ? `You are ${age} years old.` : ''}`;
  },
});

greetUser.register();
console.log(await ToolFunc.run('greetUser', { name: 'Alice', age: 30 }));
// Outputs: "Hello, Alice! You are 30 years old."

Positional Parameters

If params is defined as an array of FuncParam objects, the func receives arguments in the order they are defined. This can be useful for functions with a fixed, small number of arguments where order is intuitive.

const addNumbers = new ToolFunc({
  name: 'addNumbers',
  description: 'Adds two numbers.',
  params: [
    { name: 'num1', type: 'number', required: true },
    { name: 'num2', type: 'number', required: true },
  ],
  func: (num1, num2) => num1 + num2,
});

addNumbers.register();
console.log(await ToolFunc.runWithPos('addNumbers', 5, 3)); // Use runWithPos for positional arguments
// Outputs: 8

Recommendation: For most use cases, defining params as an object and accessing arguments by name within your func is cleaner and less error-prone, especially as your function's parameter list grows.

🏛️ Core Architecture: Static vs. Instance

A key design principle in ToolFunc is the separation of roles between the static class and its instances:

  • The Static Class as Manager: The static side of ToolFunc (e.g., ToolFunc.register, ToolFunc.run) acts as a global registry and executor. It manages all tool definitions, allowing any part of your application to discover and run tools by name.

  • The Instance as the Tool: An instance (new ToolFunc(...)) represents a single, concrete tool. It holds the actual function logic, its metadata (name, description, parameters), and any internal state.

This separation provides the best of both worlds: the power of object-oriented encapsulation for defining individual tools and the convenience of a globally accessible service for managing and executing them.

🤝 Contributing

If you would like to contribute to the project, please read the CONTRIBUTING.md file for guidelines on how to get started.

📄 License

The project is licensed under the MIT License. See the LICENSE-MIT file for more details.