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

@fractal-os/plugin

v0.0.105

Published

The official SDK and plugin toolkit for the Fractal. Build and publish custom tools, toolsets, collections, views, and event hooks for autonomous AI agents.

Downloads

1,169

Readme

@fractal-os/plugin

The official Fractal Plugin SDK — a developer toolkit for building extensions, tools, and integrations on top of the FractalOS.

Build, extend, and publish autonomous agent capabilities, persistent data schemas, user interface panels, custom error diagnostics, and system hook interceptions with a fluent, type-safe builder API.


Key Features

| Builder | Purpose | |---------|---------| | FractalTool | Define callable actions with input schemas and async handlers | | FractalToolset | Group related tools under connection configurations | | FractalCollection | Define reactive data persistence schemas with lifecycle hooks | | FractalView | Construct reactive UI trees rendered inside the Fractal workspace | | FractalHook | Intercept and modify pipeline events (e.g., PreToolUse, SessionStart) | | FractalError | Create diagnostic error registries with actionable CTAs |


Installation

# Using bun (recommended)
bun add @fractal-os/plugin

# Using npm
npm install @fractal-os/plugin

# Using pnpm
pnpm add @fractal-os/plugin

Peer Dependencies

This package requires [email protected] as a peer dependency (schema validation across builders):

bun add zod

Direct Dependencies (installed automatically)

Installing @fractal-os/plugin also pulls:

| Package | Why | |---------|-----| | ai / @ai-sdk/provider | Public types for chat / model surfaces (UIMessage, language models, …) | | @igniter-js/collections | Native collection / view builders under the hood | | @igniter-js/core | Shared Igniter contracts | | @standard-schema/spec | Schema interop for withSchema() |

You do not need to install these manually — they ship as direct dependencies of the plugin.

Requirements

  • Bun: >=1.3 (recommended) or Node.js: >=22
  • TypeScript: >=5.5 (strongly recommended for type safety)

TypeScript Support

All builders are written in TypeScript and export full type definitions out of the box.

import { FractalTool, type FractalContext } from "@fractal-os/plugin";
import { z } from "zod";

// Full type inference on input
const tool = FractalTool.create("search")
  .withSchema(z.object({ query: z.string() }))
  .withHandler(async ({ input, fractal }) => {
    // `input` is fully typed: { query: string }
    // `fractal` provides workspace context, env vars, config
    const results = await performSearch(input.query);
    return { results };
  });

API Reference

1. Defining a Tool (FractalTool)

Tools represent callable actions that AI agents use to interact with the system or external APIs.

import { z } from "zod";
import { FractalTool } from "@fractal-os/plugin";

const GreetSchema = z.object({
  name: z.string().describe("The name of the user to greet")
});

export default FractalTool.create("greet")
  .withDescription("Greets a user by name professionally")
  .withSchema(GreetSchema)
  .withHandler(async ({ input, fractal }) => {
    const config = await fractal.config.get();
    return `Hello, ${input.name}! Welcome, ${config.user.name}.`;
  })
  .build();

FractalTool Methods

| Method | Description | |--------|-------------| | .create(name) | Initialize a new tool with a unique name | | .withDescription(text) | Set the documentation description for AI agents | | .withSchema(schema) | Attach a Zod schema for input validation | | .withHandler(fn) | Define the async execution logic | | .withRules(rules) | Attach cognitive policy rules (optional) | | .build() | Validate and compile the tool definition |


2. Grouping Tools in a Toolset (FractalToolset)

Toolsets bundle related tools under a shared connection configuration.

import { FractalToolset } from "@fractal-os/plugin";
import greetTool from "./greet.tool";
import searchTool from "./search.tool";

export default FractalToolset.create("utilities")
  .withDescription("Essential workspace helper tools")
  .withConnection({ type: "custom" })
  .addTool(greetTool)
  .addTool(searchTool)
  .build();

MCP Server Integration (Auto-Discovery)

For MCP-compatible servers, tools are discovered automatically — no need to define them individually:

export default FractalToolset.create("postgres")
  .withDescription("PostgreSQL database tools via MCP")
  .withConnection({
    type: "mcp-server::stdio",
    command: "npx",
    args: ["-y", "@modelcontextprotocol/server-postgres"],
    env: { DATABASE_URL: process.env.DATABASE_URL! }
  })
  .build();

Connection Types

| Type | Use Case | |------|----------| | mcp-server::stdio | Local MCP server subprocess | | mcp-server::http | Remote MCP server over HTTP/SSE | | rest-api | Standard REST API integration | | cli | Command-line tool wrapper | | custom | In-memory JavaScript/TypeScript handlers |


3. Creating a Collection (FractalCollection)

Collections model local files as typed records with reactive lifecycle hooks.

import { z } from "zod";
import { FractalCollection } from "@fractal-os/plugin";

const UserSchema = z.object({
  id: z.string(),
  username: z.string(),
  role: z.enum(["admin", "developer", "user"]),
  createdAt: z.string().optional()
});

export default FractalCollection.create("users")
  .withSchema(UserSchema)
  .withPatterns(["data/users/*.json"])
  .onCreated(async ({ value, fractal }) => {
    const config = await fractal.config.get();
    console.log(`User ${value.username} created for ${config.user.name}`);
    return value;
  })
  .onUpdated(async ({ newValue, previousValue, fractal }) => {
    await fractal.workspace.core.collections.get("audit").create({
      data: { action: "user_updated", user: newValue.username },
    });
    return newValue;
  })
  .build();

Lifecycle Hooks

Public hook context omits Igniter internals (context, manager). Authors only receive document fields plus fractal:

| Hook | Trigger | Context | |------|---------|---------| | onCreated | After a new record is persisted | { value, fractal } — returns Document \| false | | onUpdated | After an existing record is modified | { newValue, previousValue, fractal } — returns Document \| false | | onDeleted | Before a record is removed | { value, fractal } — returns boolean | | onRead | When a record is retrieved | { value, fractal } — returns Document \| false | | onList | When records are queried | { values, fractal } — returns Document[] \| false |

Complete CRM Example

// A real-world collection with auto-timestamps and enrichment
export default FractalCollection.create("customers")
  .withPatterns(["data/customers/*.json"])
  .withSchema(z.object({
    name: z.string(),
    email: z.string().email(),
    phone: z.string().optional(),
    createdAt: z.string().optional(),
    updatedAt: z.string().optional(),
  }))
  .onCreated(async ({ value }) => ({
    ...value,
    createdAt: new Date().toISOString(),
    updatedAt: new Date().toISOString()
  }))
  .onUpdated(async ({ newValue }) => ({
    ...newValue,
    updatedAt: new Date().toISOString()
  }))
  .build();

4. Creating a Workspace View (FractalView)

Views render json-render specs (shadcn catalog) inside the Fractal workspace.

import { FractalView } from "@fractal-os/plugin";

export default FractalView.create("recent-activities")
  .withTitle("Recent Activities")
  .withDescription("Displays recent actions performed in the active workspace")
  .withData(async (ctx) => {
    const activities = await ctx.workspace.core.collections.get("activities").findMany({
      take: 20,
      orderBy: { createdAt: "desc" },
    });
    return {
      columns: ["Activity", "Status"],
      rows: activities.map((a) => [a.title, a.status]),
      stats: { total: String(activities.length) },
    };
  })
  .withSpec({
    root: "page",
    elements: {
      page: {
        type: "Stack",
        props: {
          direction: "vertical",
          gap: "md",
          align: null,
          justify: null,
          className: null,
        },
        children: ["heading", "table"],
      },
      heading: {
        type: "Heading",
        props: { text: "Recent Activities", level: "h1" },
        children: [],
      },
      table: {
        type: "Table",
        props: {
          columns: { $state: "/columns" },
          rows: { $state: "/rows" },
          caption: null,
        },
        children: [],
      },
    },
  })
  .build();

Dashboard View with KPI Cards

export default FractalView.create("analytics")
  .withTitle("Analytics Dashboard")
  .withData(async (ctx) => {
    const users = await ctx.workspace.core.collections.get("users").findMany({});
    const tasks = await ctx.workspace.core.collections.get("tasks").findMany({
      where: { status: "finished" },
    });
    return {
      columns: ["ID"],
      rows: [],
      stats: {
        totalUsers: String(users.length),
        completedTasks: String(tasks.length),
      },
    };
  })
  .withSpec({
    root: "page",
    elements: {
      page: {
        type: "Stack",
        props: {
          direction: "vertical",
          gap: "md",
          align: null,
          justify: null,
          className: null,
        },
        children: ["heading", "metrics"],
      },
      heading: {
        type: "Heading",
        props: { text: "Analytics", level: "h1" },
        children: [],
      },
      metrics: {
        type: "Grid",
        props: { columns: 2, gap: "md", className: null },
        children: ["users", "tasks"],
      },
      users: {
        type: "Card",
        props: {
          title: "Total Users",
          description: { $state: "/stats/totalUsers" },
          maxWidth: null,
          centered: null,
          className: null,
        },
        children: [],
      },
      tasks: {
        type: "Card",
        props: {
          title: "Completed Tasks",
          description: { $state: "/stats/completedTasks" },
          maxWidth: null,
          centered: null,
          className: null,
        },
        children: [],
      },
    },
  })
  .build();

5. Intercepting Events with Hooks (FractalHook)

Hooks intercept operations on the Fractal pipeline, enabling safety checks, audit logging, or result modification.

import { FractalHook } from "@fractal-os/plugin";

export default FractalHook.create("safety-guard")
  .onType("PreToolUse")
  .withHandler(async (event) => {
    const cmd = event.input.toolInput?.command || "";

    // Block dangerous commands
    if (cmd.includes("rm -rf") || cmd.includes(":(){ :|:& };:")) {
      throw new Error(
        "Destructive terminal commands are forbidden in this workspace."
      );
    }

    // Log all tool invocations for audit (Igniter Collections namespace API)
    await event.fractal.workspace.core.collections.get("audit").create({
      data: {
        tool: event.input.toolName,
        args: event.input.toolInput,
        timestamp: new Date().toISOString(),
      },
    });

    return event.output;
  })
  .build();

Supported Event Types

| Event Type | Description | Use Case | |------------|-------------|----------| | SessionStart | Agent session begins | Inject session-wide context or setup | | UserPromptSubmit | User submits a prompt | Filter or enrich user input | | PreToolUse | Before tool invocation | Authorization, safety checks, input validation | | PostToolUse | After tool completes | Audit logging, result transformation | | PostToolUseFailure | Tool execution fails | Error recovery, retry logic, alerting | | SubagentStart | Subagent spawns | Resource allocation, permission scoping | | SubagentStop | Subagent finishes | Result collection, cleanup | | Stop | Session stops | Final audit, resource cleanup | | PreCompact | Context compaction | Preserve critical context before compaction | | GenericHook | Fallback for custom events | Extensibility for future event types |


6. Dynamic Custom Errors (FractalError)

Define diagnostic error codes with actionable Call-To-Action (CTA) buttons that both developers and agents can execute.

import { z } from "zod";
import { FractalError } from "@fractal-os/plugin";

const MissingEnvSchema = z.object({
  key: z.string().describe("The missing environment variable name")
});

export const WorkspaceErrors = FractalError.create()
  .addError("ENV_VARIABLE_MISSING", {
    status: 400,
    schema: MissingEnvSchema,
    message: ({ issue }) =>
      `Required environment variable "${issue?.key}" is not set.`,
    cta: ({ issue }) => ({
      description: "Set the missing environment variable in your config.",
      commands: [
        {
          command: `fractal config set ${issue?.key}="<value>"`,
          description: "Configure the environment key"
        }
      ]
    })
  })
  .addError("NOT_FOUND", {
    status: 404,
    schema: z.object({ id: z.string() }),
    message: ({ issue }) => `Resource "${issue?.id}" not found.`,
    cta: ({ issue }) => ({
      description: "Verify the resource ID and try again.",
      commands: [
        { command: `fractal get ${issue?.id}`, description: "Look up the resource" }
      ]
    })
  })
  .build();

// Usage
throw new WorkspaceErrors({
  code: "ENV_VARIABLE_MISSING",
  issue: { key: "API_TOKEN" }
});

Merging Multiple Error Registries

import { AuthErrors } from "./auth.errors";
import { ValidationErrors } from "./validation.errors";

export const AppErrors = FractalError.create()
  .merge(AuthErrors)
  .merge(ValidationErrors)
  .build();

Context API

All handlers receive a Fractal context object with access to:

interface FractalContext {
  /** Environment variable access (e.g., fractal.env.get("API_KEY")) */
  env: {
    get(name: string): string | undefined;
  };
  /** Typed config manager — async get / update of the persisted Fractal config */
  config: {
    get(): Promise<FractalConfig>;
    update(params: FractalConfigUpdateInput): Promise<FractalConfigUpdateResult>;
  };
  /** Full workspace runtime — services, collections manager (`core`), store, path helper */
  workspace: FractalWorkspaceRuntime;
}

The fractal.workspace runtime exposes the complete Fractal service layer:

| Service | Access Pattern | Purpose | |---------|---------------|---------| | Toolsets | fractal.workspace.toolsets.list(...) | Discover and call tools | | Collections (definitions) | fractal.workspace.collections.get("name") | Manage custom collection definitions | | Core (documents) | fractal.workspace.core.collections.get("name") | CRUD documents (create / findMany / …) | | Views | fractal.workspace.views.render("id") | Render workspace views | | Events | fractal.workspace.events.publish(...) | Publish and query events | | Tasks | fractal.workspace.tasks.create(...) | Create and manage tasks | | Memories | fractal.workspace.memories.create(...) | Persist knowledge records | | Agents | fractal.workspace.agents.list() | Manage agent configurations | | Skills | fractal.workspace.skills.install(...) | Install community skills | | Files | fractal.workspace.files.read(...) | File system operations | | Chats | fractal.workspace.chats.create(...) | Chat session management | | Templates | fractal.workspace.templates.render(...) | Render LiquidJS templates | | Instructions | fractal.workspace.instructions.create(...) | Define behavioral rules | | Routines | fractal.workspace.routines.create(...) | Scheduled automations | | Projects | fractal.workspace.projects.create(...) | Project management | | Marketplace | fractal.workspace.marketplace.search(...) | Community discovery |


Complete Skill Example

A full Fractal skill using all six builders — a "Task Manager" plugin:

skills/task-manager/
├── SKILL.md
├── .fractal/manifest.json
├── toolsets/
│   └── task-tools.toolset.ts    ← FractalToolset with "create-task" + "list-tasks" tools
├── collections/
│   └── tasks.collection.ts      ← FractalCollection with auto-timestamps
├── views/
│   └── task-board.view.ts       ← FractalView with kanban-style table
├── hooks/
│   └── task-audit.hook.ts       ← FractalHook logging all tool executions
└── errors/
    └── task.errors.ts           ← FractalError registry for task-specific errors

Troubleshooting

"Cannot find module 'zod'"

Ensure zod is installed as a direct dependency:

bun add zod

The plugin lists zod as a peerDependency, so you must install it separately.

TypeScript errors with builders

This package requires TypeScript >=5.5. Upgrade if you see errors:

bun add -d typescript@latest

Collection hooks not firing

Ensure the collection is registered in your workspace configuration. Collections are auto-discovered from skill folders — verify the collection file exports the result of .build():

// ✅ Correct — hooks will fire
export default FractalCollection.create("users").withSchema(...).onCreated(fn).build();

// ❌ Wrong — hooks won't fire (missing .build())
export default FractalCollection.create("users").withSchema(...).onCreated(fn);

View not appearing in workspace

Views must be explicitly enabled in the workspace settings. Check the Fractal workspace preferences panel, or verify the view is in a skill folder with proper .fractal/manifest.json.

"Fractal collection context is not available"

This error occurs when a FractalCollection or FractalView runs outside a properly initialized Fractal workspace. Ensure FRACTAL_WORKSPACE_ID is set and the workspace is resolved.

Toolset connection fails

For mcp-server::stdio connections, verify the command exists and args are correct. For rest-api and mcp-server::http, ensure the url is reachable and any required headers are set.


Building from Source

# Clone the repository
git clone https://github.com/tryfractal/fractal.git
cd fractal/@packages/plugin

# Install dependencies
bun install

# Build CJS, ESM, and DTS typings
bun run build

# Watch mode for development
bun run dev

Distribution

The package ships dual CJS + ESM builds with full TypeScript declarations:

| File | Purpose | |------|---------| | dist/index.js | ESM entry (import) | | dist/index.cjs | CJS entry (require) | | dist/index.d.ts | TypeScript declarations | | README.md | Consumer-facing manual | | AGENTS.md | Agent-facing operational manual | | CHANGELOG.md | Version history (breaking changes + migration notes) |


License

MIT License — see LICENSE.

Copyright (c) 2026 tryfractal


Links