Open source / Deno / Agent first

Hazelnut

An open-source, agent-first backend framework for Deno, under active development. Declare your resources and access rules in TypeScript, then expose selected operations over HTTP and MCP.

APIs and behaviour may change between releases. Rough edges remain.

Declaration preview01 / note

defineResource

note

owner_id
titlebodyowner_id
note.resource.tsMCP
mcp: {
  list: {
    describe: "List notes.",
    shape: ["id", "title"]
  }
}

HTTP routes and MCP tools are selected separately.

One resource.
Explicit exposure.

Hazelnut derives TypeScript types and Postgres tables from resource declarations at boot. HTTP operations, MCP tools, response columns and row rules are declared explicitly.

01 / A declaration

One resource.
Two interfaces.

A note resource with owner-scoped reads and separate HTTP and MCP operations.

note.resource.tsResource → HTTP / MCP
defineResourcenote
titlebodyowner_id

rowPolicy owner_id

HTTP Application

listfind

id · title · owner_id

MCP Agent

list

id · title

Resource fields + owner row rule
schema: z.object({
  title: z.string(),
  body: z.string(),
  owner_id: z.string(),
}),
features: { timestamps: true, versioning: false },
rowPolicy: "owner_id",

Fragments from the note example below.

  1. Resource

    Fields and ownership.

    The note declares title, body and owner_id, with an owner row rule, timestamps enabled and versioning disabled.

  2. HTTP

    Owner-scoped HTTP reads.

    HTTP exposes list and find using the row rule. Both return id, title and owner_id; the body field is omitted.

  3. MCP

    One selected MCP tool.

    MCP exposes only list, with a description and a result shape of id and title. find remains available over HTTP.

02 / Capabilities

What it brings together

  1. MCP / Exposure

    Letting AI agents in

    Apps expose MCP at /mcp. Only operations explicitly exposed to MCP become tools, with descriptions and a caller-specific catalog. Read tools must restrict rows or be declared public; destructive tools can ask the host for human confirmation. Agent credentials have a separate rate limit.

  2. Declarations / Verification

    Code written by a coding agent

    Permissions, scheduled jobs and other supported features use explicit declarations, giving coding agents a consistent structure. Boot checks and the verify step catch structural configuration errors; business rules belong in application tests.

  3. Policies / Data

    Your users' data

    Access is closed by default. Permissions control writes; row rules scope access for callers and tenants. The framework supports field encryption and key rotation. Sensitive-field declarations support masking in audit trails, logs and events, and filtering in responses.

  4. Events / Workflows

    Work after the request

    Events are recorded in the same transaction as the change, then delivered by a relay. Repeatedly failing deliveries enter a dead-letter queue for redrive. Scheduled jobs coordinate dispatch across replicas, and durable workflows record progress for resuming after an interruption.

  5. Migrations / Deployment

    Deployment and operations

    Schema changes are previewed and applied as a separate release step. The serve command derives Deno permission grants from the app's declarations, and the runtime supports multiple replicas.

  6. Product / AI

    The rest of a product

    Email and password login, status state machines, full-text and vector search, per-field translations, live updates, signed outbound webhooks and a typed TypeScript client are also included. The separate @hazelnut/ai package adds declared model calls with output validation and a token budget.

Build

Build your setup brief

Describe your app to generate a setup prompt for your coding agent.

Brief

What are you building?

Your answers aren’t saved or sent.

Project starting point

Lowercase letters, numbers, - or _. Example: notes-api.

Additional details (optional)

Prompt

Your prompt

Project brief
Setup mode: New project
App name (project folder): [add an app name]

What it should do:
[describe what the app should do]

For a new project, clarify anything essential before scaffolding.
Choose a valid app name before scaffolding.

Hazelnut is under active development. Before writing code, read the public agent instructions, quickstart and handbook:
https://github.com/Hazelnut-17/hazelnut/blob/main/AGENTS.md
https://github.com/Hazelnut-17/hazelnut/blob/main/core/docs/QUICKSTART.md
https://github.com/Hazelnut-17/hazelnut/blob/main/core/docs/README.md
https://github.com/Hazelnut-17/hazelnut/blob/main/core/docs/agent-door.md

Use documentation that matches the project's resolved framework version. Follow the documented scaffold for a new project; inspect the existing instructions and pinned dependencies for an existing project. Do not invent APIs or bypass a CLI refusal.

Clarify missing requirements, data ownership and access rules before implementing them. Make HTTP and MCP exposure explicit. If agent operations are unspecified, ask which are needed before exposing tools.

Use the documented Deno permission grants, and keep dependency pins and the lockfile. Implement a small working slice, with tests for application behaviour and permitted and denied access. After schema changes, generate and review migrations, then run the project's documented CI task.

Report the commands and results, what works, blockers, assumptions and remaining limitations. Passing checks alone should not be described as production readiness.

Enter an app name.

04 / In code

A resource declaration.

Note resource

An owner-scoped note with two HTTP reads and one MCP tool.

note.resource.tsTypeScript
import { defineResource } from "hazelnut";
import { z } from "zod";

export const note = defineResource({
  name: "note",
  schema: z.object({
    title: z.string(),
    body: z.string(),
    owner_id: z.string(),
  }),
  features: { timestamps: true, versioning: false },
  rowPolicy: "owner_id",
  http: {
    list: { policy: "policy", columns: ["id", "title", "owner_id"] },
    find: { policy: "policy", columns: ["id", "title", "owner_id"] },
  },
  mcp: {
    list: { describe: "List notes.", shape: ["id", "title"] },
  },
});

The resource must be registered in the app config. Its owner rule returns no rows for unauthenticated list calls. Every resource must declare whether it keeps versions.

Start locally

Requires Deno 2. The local quickstart runs an in-memory Postgres, with no external database or API key to configure.

TerminalDeno 2
deno run --allow-read --allow-write=. --allow-env \
  --allow-run=deno,deno.exe,git --allow-net \
  jsr:@hazelnut/core/cli new my-app
cd my-app

On Windows, use a bare --allow-run in place of the named list. The scaffold writes a starter project with tests and a Dockerfile.

Follow the quickstart (opens in new tab)

05 / Open source

Packages and source

View on GitHub (opens in new tab)
@hazelnut/core
JSR

The framework: runtime, command-line tool and the verify step.

@hazelnut/ai
JSR

Declared model calls for business logic.

Built on
Deno 2 · PostgreSQL 16+

Hono for HTTP, Zod 4 for schemas, Drizzle for the database layer.

License
Apache-2.0
Status
Under active development

0.x releases may change APIs and behaviour, including changes that require app updates.

Bug reports and questions: GitHub issues.