Getting Started
Keryx is the fullstack TypeScript framework for MCP and APIs. It's the spiritual successor to ActionHero, rebuilt from scratch on Bun with Zod for validation, Drizzle for the ORM, and a Vite + React frontend. The core ideas are the same — transport-agnostic actions, built-in background tasks, strong typing between frontend and backend — but the tooling and scope are different.
Write your controller logic once, and it works as an HTTP endpoint, WebSocket handler, CLI command, background task, and MCP tool — all at the same time. Enable MCP_SERVER_ENABLED=true and AI agents can discover and call those same actions as MCP tools.
The name Keryx (κῆρυξ) comes from ancient Greek, meaning "herald" or "messenger" — the person who carried proclamations between gods and mortals. It fits: your actions are the message, and Keryx delivers them across every transport.
Prerequisites
You'll need these running locally:
- Bun (latest)
- PostgreSQL
- Redis
Installation (macOS)
# install bun
curl -fsSL https://bun.sh/install | bash
# install postgres and redis
brew install postgresql redis
brew services start postgresql
brew services start redisCreate a New Project
bunx keryx new my-app
cd my-app
cp .env.example .env
createdb my-app
createdb my-app-test
bun installThe generated .env points DATABASE_URL at a database named after your project, and DATABASE_URL_TEST at <project>-test — so create both before starting the server. keryx new prints these same steps when it finishes.
The keryx new command will prompt you for a project name and optional features (database, Redis server, example action). You can also skip prompts with --no-interactive:
bunx keryx new my-app --no-interactiveWithout Postgres or Redis
Postgres and Redis are what Keryx is built for, and what you should run in production. For a quick prototype or a single-process tool, you can skip them:
bunx keryx new my-app --no-db --no-redis--no-db leaves DATABASE_URL empty, so the app runs without a database. --no-redis sets REDIS_URL="memory://", so Redis runs inside your process — background tasks, scheduled jobs, PubSub, and sessions keep working, but everything lives in one process and disappears on restart. See Running Without Postgres or Redis for the trade-offs. Moving to the real thing later is just setting DATABASE_URL and REDIS_URL.
Run the Dev Server
bun devThat's it. The backend will start with hot reload — edit a file, save it, and see the change immediately.
Project Structure
A new Keryx project looks like this:
my-app/
├── actions/ # Transport-agnostic controllers
├── channels/ # WebSocket PubSub channels
├── drizzle/ # Generated migration SQL (not created with --no-db)
├── initializers/ # Lifecycle components (DB, Redis, etc.)
├── middleware/ # Action and channel middleware
├── schema/ # Drizzle ORM table definitions (not created with --no-db)
├── index.ts # Sets api.rootDir, re-exports framework types
├── keryx.ts # CLI entry point
├── .env.example # Environment variable template
└── package.jsonActions, initializers, and channels are auto-discovered from their directories — just drop in a .ts file and the framework picks it up.
Generate Components
Use the CLI generator to scaffold new components with boilerplate:
bunx keryx generate action user:create
bunx keryx g initializer cache # "g" is a shorthand aliasThis creates the component file and a matching test file. See the CLI guide for all generator types and options.
What's Next
- Actions — the core concept. Everything is an action.
- Initializers — how the server boots up and connects to services
- Tasks — background jobs and the fan-out pattern
- Configuration — environment-based config with per-env overrides
- Building for AI Agents — expose your actions as MCP tools for Claude, Cursor, and other AI clients
- Caching — Redis-backed caching helpers and invalidation patterns
- Testing — real HTTP requests against a real server, no mocks
- Deployment — running Keryx in production
- Coming from ActionHero — what changed, and how concepts map across