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

keryx

v0.15.4

Published

The fullstack TypeScript framework for MCP and APIs — write one action, serve HTTP, WebSocket, CLI, background tasks, and MCP tools.

Readme

Keryx

Test

What is this Project?

This is a ground-up rewrite of ActionHero, built on Bun. I still believe in the core ideas behind ActionHero — it was an attempt to take the best ideas from Rails and Node.js and shove them together — but the original framework needed a fresh start with Bun, Zod, Drizzle, and first-class MCP support.

The big idea: write your controller once, and it works everywhere. A single action class handles HTTP requests, WebSocket messages, CLI commands, background tasks, and MCP tool calls — same inputs, same validation, same middleware, same response. No duplication.

That includes AI agents. Every action is automatically an MCP tool — agents authenticate via built-in OAuth 2.1, get typed errors, and call the same validated endpoints your HTTP clients use. No separate MCP server, no duplicated schemas.

One Action, Every Transport

Here's what that looks like in practice. This is one action:

export class UserCreate implements Action {
  name = "user:create";
  description = "Create a new user";
  inputs = z.object({
    name: z.string().min(3),
    email: z.string().email(),
    password: secret(z.string().min(8)),
  });
  web = { route: "/user", method: HTTP_METHOD.PUT };
  task = { queue: "default" };

  async run(params: ActionParams<UserCreate>) {
    const user = await createUser(params);
    return { user: serializeUser(user) };
  }
}

That one class gives you:

HTTPPUT /api/user with JSON body, query params, or form data:

curl -X PUT http://localhost:8080/api/user \
  -H "Content-Type: application/json" \
  -d '{"name":"Evan","email":"[email protected]","password":"secret123"}'

WebSocket — send a JSON message over an open connection:

{ "messageType": "action", "action": "user:create",
  "params": { "name": "Evan", "email": "[email protected]", "password": "secret123" } }

CLI — flags are generated from the Zod schema automatically:

./keryx.ts "user:create" --name Evan --email [email protected] --password secret123 -q | jq

Background Task — enqueued to a Resque worker via Redis:

await api.actions.enqueue("user:create", { name: "Evan", email: "[email protected]", password: "secret123" });

MCP — exposed as a tool for AI agents automatically:

{
  "mcpServers": {
    "my-app": {
      "url": "http://localhost:8080/mcp"
    }
  }
}

Same validation, same middleware chain, same run() method, same response shape. The only thing that changes is how the request arrives and how the response is delivered.

That's it. The agent can now discover all your actions as tools, authenticate via OAuth, and call them with full type validation.

Key Components

  • MCP-native — every action is an MCP tool with OAuth 2.1 auth, typed errors, and per-session isolation
  • Transport-agnostic Actions — HTTP, WebSocket, CLI, background tasks, and MCP from one class
  • Zod input validation — type-safe params with automatic error responses and OpenAPI generation
  • Built-in background tasks via node-resque, with a fan-out pattern for parallel job processing
  • Strongly-typed frontend integrationActionResponse<MyAction> gives the frontend type-safe API responses, no code generation needed
  • Drizzle ORM with auto-migrations (replacing the old ah-sequelize-plugin)
  • Companion Vite + React frontend as a separate application (replacing ah-next-plugin)

Why Bun?

TypeScript is still the best language for web APIs. But Node.js has stalled — Bun is moving faster and includes everything we need out of the box:

  • Native TypeScript — no compilation step
  • Built-in test runner
  • Module resolution that just works
  • Fast startup and an excellent packager
  • fetch included natively — great for testing

Project Structure

  • root — a slim package.json wrapping the workspaces. bun install and bun dev work here, but you need to cd into each workspace for tests.
  • packages/keryx — the framework package (publishable)
  • example/backend — the example backend application
  • example/frontend — the example Vite + React frontend
  • docs — the documentation site

Quick Start

Create a new project:

bunx keryx new my-app
cd my-app
cp .env.example .env
bun install
bun dev

Requires Bun, PostgreSQL, and Redis. See the Getting Started guide for full setup instructions.

Developing the framework itself

If you're contributing to Keryx, clone the monorepo instead:

git clone https://github.com/actionhero/keryx.git
cd keryx
bun install
cp example/backend/.env.example example/backend/.env
cp example/frontend/.env.example example/frontend/.env
bun dev

Production Builds

bun compile
# set NODE_ENV=production in .env
bun start

Databases and Migrations

We use Drizzle as the ORM. Migrations are derived from schemas — edit your schema files in schema/*.ts, then generate and apply:

cd example/backend && bun run migrations
# restart the server — pending migrations auto-apply

Actions, CLI Commands, and Tasks

Unlike the original ActionHero, we've removed the distinction between actions, CLI commands, and tasks. They're all the same thing now. You can run any action from the CLI, schedule any action as a background task, call any action via HTTP or WebSocket, and expose any action as an MCP tool for AI agents. Same input validation, same responses, same middleware.

Web Actions

Add a web property to expose an action as an HTTP endpoint. Routes support :param path parameters and RegExp patterns — the route lives on the action itself, no separate routes.ts file:

web = { route: "/user/:id", method: HTTP_METHOD.GET };

WebSocket Actions

Enabled by default. Clients send JSON messages with { messageType: "action", action: "user:create", params: { ... } }. The server validates params through the same Zod schema and sends the response back over the socket. WebSocket connections also support channel subscriptions for real-time PubSub.

CLI Actions

Enabled by default. Every action is registered as a CLI command via Commander. The Zod schema generates --flags and --help text automatically:

./keryx.ts "user:create" --name evan --email "[email protected]" --password password -q | jq

The -q flag suppresses logs so you get clean JSON. Use --help on any action to see its params.

Task Actions

Add a task property to schedule an action as a background job. A queue is required, and frequency is optional for recurring execution:

task = { queue: "default", frequency: 1000 * 60 * 60 }; // every hour

MCP Actions

When the MCP server is enabled (MCP_SERVER_ENABLED=true), every action is automatically registered as an MCP tool. AI agents and LLM clients (Claude Desktop, VS Code, etc.) can discover and call your actions through the standard Model Context Protocol. Actions can also be exposed as MCP resources (URI-addressed data) and prompts (named templates) via mcp.resource and mcp.prompt.

Action names are converted to valid MCP tool names by replacing : with - (e.g., user:create becomes user-create). The action's Zod schema is converted to JSON Schema for tool and prompt parameter definitions.

To exclude an action from MCP tools:

mcp = { tool: false };

To expose an action as an MCP resource or prompt:

// Resource — clients fetch this by URI
mcp = { tool: false, resource: { uri: "myapp://status", mimeType: "application/json" } };

// Prompt — clients invoke this as a named template
mcp = { tool: false, prompt: { title: "Greeting" } };

OAuth 2.1 with PKCE is used for authentication — MCP clients go through a browser-based login flow, and subsequent tool calls carry a Bearer token tied to the authenticated user's session.

Fan-Out Tasks

A parent task can distribute work across many child jobs using api.actions.fanOut() for parallel processing. Results are collected automatically in Redis. See the Tasks guide for full API and examples.

Coming from ActionHero?

Keryx keeps the core ideas but rewrites everything on Bun with first-class MCP support. The biggest changes: unified controllers (actions = tasks = CLI commands = MCP tools), separate frontend/backend applications, Drizzle ORM, and MCP as a first-class transport.

See the full migration guide for details.

Production Deployment

Each application has its own Dockerfile, and a docker-compose.yml runs them together. You probably won't use this exact setup in production, but it shows how the pieces fit together.

Documentation

Full docs at keryxjs.com, including: