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

agentfn

v1.0.9

Published

Lightweight agentic AI SDK for building tool-using, workflow-driven AI agents.

Readme

agentfn

npm version

The world's lightest agentic AI SDK for building tool-using, workflow-driven AI agents.


✨ Features

  • Agentic AI: Create agents that reason, use tools, and adapt to user needs.
  • Tool Registration: Register any function, API, or workflow as a tool.
  • Schema Validation: Automatic parameter validation for safe, robust tools.
  • Workflows: Chain tools and logic for multi-step automation.
  • promptTool: Instantly turn prompts into tools—no code required.
  • Streaming: Get partial/final responses as they're generated.
  • Custom Hooks: onStart, onFinish, onError for every tool.
  • Memory & Context: Plug in your own memory for persistent agents.
  • Agentic Modes: Switch between creative, balanced, or precise reasoning.
  • Type-safe Response Formats: Markdown, HTML, JSON, plaintext.

🚀 Install

npm install agentfn

🏁 Quick Start

import { Client, Agent, RESPONSE_TYPE } from 'agentfn';
import { AzureOpenAiAdapter } from 'agentfn/dist/azureOpenAiAdapter';

const client = new Client();
const aiAdapter = new AzureOpenAiAdapter({
  endpoint: 'https://your-azure-endpoint',
  apiKey: 'sk-...',
  deployment: 'gpt-4',
  apiVersion: '2024-05-01-preview'
});

client.registerTool({
  name: 'add',
  description: 'Adds two numbers.',
  schema: { type: 'object', properties: { a: { type: 'number' }, b: { type: 'number' } }, required: ['a', 'b'] },
  type: 'function',
  execute: async ({ a, b }) => ({ sum: a + b })
});

    const agent = new Agent({
  name: 'MathBot',
  tools: client.getLocalTools(),
  responseType: RESPONSE_TYPE.MARKDOWN,
  aiAdapter
});

(async () => {
  const result = await agent.process(client, 'What is 2 + 2? Use your add tool.');
  console.log(result);
})();

Output:

{
  "answer": "The sum of 2 and 2 is 4.",
  "messages": [
    { "role": "user", "content": "What is 2 + 2? Use your add tool." },
    {
      "role": "assistant",
      "content": null,
      "tool_calls": [
        {
          "id": "call_...",
          "function": { "name": "add", "arguments": "{\"a\":2,\"b\":2}" }
        }
      ]
    },
    {
      "role": "tool",
      "tool_call_id": "call_...",
      "content": "{\"sum\":4}"
    },
    {
      "role": "assistant",
      "content": "The sum of 2 and 2 is 4."
    }
  ]
}

🧠 Registering Powerful Tools

Any Function or API

client.registerTool({
  name: 'weather',
  description: 'Gets the weather for a city.',
  schema: { type: 'object', properties: { city: { type: 'string' } }, required: ['city'] },
  type: 'function',
  execute: async ({ city }) => {
    // Call your weather API here
    return { forecast: 'Sunny in ' + city };
  }
});

promptTool: Turn Any Prompt Into a Tool

import { promptTool, RESPONSE_TYPE } from 'agentfn';

client.registerTool({
  name: 'summarize',
  description: 'Summarizes any text.',
  schema: { type: 'object', properties: { text: { type: 'string' } }, required: ['text'] },
  type: 'function',
  execute: promptTool({
    prompt: ({ text }) => `Summarize this: ${text}`,
    responseType: RESPONSE_TYPE.MARKDOWN
  })
});

🛠️ Workflows: Chain Tools and Logic

import { Workflow } from 'agentfn';

const wf = new Workflow()
  .step(async (ctx) => {
    ctx.weather = await client.callTool('weather', { city: 'London' });
  })
  .step(async (ctx) => {
    ctx.summary = await client.callTool('summarize', { text: ctx.weather.forecast });
  });

const ctx = await wf.run({ client });
console.log(ctx.summary);

🤖 Streaming Responses

for await (const msg of agent.processStream(client, [
  { role: 'user', content: 'Summarize this: The quick brown fox jumps over the lazy dog.' }
])) {
  if (msg.role === 'assistant') {
    console.log('Partial:', msg.content);
  }
  if (msg.isFinal) {
    console.log('Final:', msg.content);
  }
}

🦾 Advanced: Custom Memory, Tool Policies, and More

Custom Memory Module

const memory = {
  store: {},
  async save(key, value) { this.store[key] = value; },
  async load(key) { return this.store[key]; }
};

const agent = new Agent({
  // ...
  memory
});

Custom Tool Use Policy

const agent = new Agent({
  // ...
  toolUsePolicy: (tool) => tool.name !== 'delete_all_data'
});

📝 promptTool: The Fastest Way to Make an AI Tool

client.registerTool({
  name: 'translate',
  description: 'Translates text to French.',
  schema: { type: 'object', properties: { text: { type: 'string' } }, required: ['text'] },
  type: 'function',
  execute: promptTool({
    prompt: ({ text }) => `Translate this to French: ${text}`,
    responseType: RESPONSE_TYPE.JSON,
    outputSchema: {
      type: 'object',
      properties: { translation: { type: 'string' } },
      required: ['translation']
    },
    sample: { translation: 'Bonjour!' }
  })
});

// If the AI returns { translation: "Bonjour!" }, it passes validation.
// If the AI returns a string or an object without 'translation', an error is thrown.

🧩 Using the sample Option

You can provide a sample output (object or array) when creating a prompt tool. This helps the AI by showing a concrete example, and the SDK will validate your sample against the outputSchema:

client.registerTool({
  name: 'weather_json',
  description: 'Returns weather as JSON.',
  schema: { type: 'object', properties: { city: { type: 'string' } }, required: ['city'] },
  type: 'function',
  execute: promptTool({
    prompt: ({ city }) => `Get the weather for ${city}.`,
    responseType: RESPONSE_TYPE.JSON,
    outputSchema: {
      type: 'object',
      properties: { forecast: { type: 'string' }, tempC: { type: 'number' } },
      required: ['forecast', 'tempC']
    },
    sample: { forecast: 'Sunny', tempC: 25 }
  })
});
  • If your sample does not match the schema, the SDK will log a warning in the console, but will not throw an error.
  • If your sample is valid, it will be included in the prompt as an example for the AI.
  • You can also provide an array of samples for more complex tools.
// Example with an invalid sample (logs a warning):
client.registerTool({
  name: 'bad_sample',
  description: 'Example with invalid sample.',
  schema: { type: 'object', properties: {}, required: [] },
  type: 'function',
  execute: promptTool({
    prompt: 'Say hello',
    responseType: RESPONSE_TYPE.JSON,
    outputSchema: { type: 'object', properties: { answer: { type: 'string' } }, required: ['answer'] },
    sample: { wrongField: 123 } // This will log a warning
  })
});

🧪 Example: Agentic Reasoning Chain

client.registerTool({
  name: 'web_search',
  description: 'Searches the web.',
  schema: { type: 'object', properties: { query: { type: 'string' } }, required: ['query'] },
  type: 'function',
  execute: async ({ query }) => {
    // Call your web search API
    return { results: [{ title: '...', url: '...' }] };
  }
});

client.registerTool({
  name: 'send_email',
  description: 'Sends an email.',
  schema: { type: 'object', properties: { to: { type: 'string' }, subject: { type: 'string' }, body: { type: 'string' } }, required: ['to', 'subject', 'body'] },
  type: 'function',
  execute: async ({ to, subject, body }) => {
    // Send email logic
    return { sent: true };
  }
});

// Now the agent can search, summarize, and email in one reasoning chain!

Author

Tochi Mba


License

MIT