New the agent layer — channels, connections, durable turns →

Build agents
into real apps.

June is the React framework where an agent is a feature, not a separate runtime. Drop an agent/ directory into your app: its tools are your server actions, its channels are Slack, Crisp, or HTTP, and every turn is durable — checkpointed, resumable, one Durable Object per session at the edge.

$ npm create june@latest my-app
Get startedWhy June →
#ops · slackChannel · streamed
D
Danasupport
@ops refund order #4812 — arrived broken, photos in thread
⚙ get_order{ id: 4812 }✓ $84.00 · delivered
⚙ read_skill{ name: "refunds" }✓
refund_order · awaiting approval
Refund $84.00 to card •• 4242 for order #4812?
ApproveDeny
resume.by = @maya (manager)·turn resumed as Dana
J
opsagent
Refunded $84.00 to •• 4242. I've replied to the customer and noted it on the ticket.
● done·3 tool calls·parked 41s·ctx.user = U04DANA

01 The agent layer

The directory is the manifest.

No agent framework to adopt, no config to assemble. June discovers agent/, mounts it in dev, and compiles it for the edge on june build. Presence is the API.

agent/agent.ts
→
Name, model, per-surface policy. A plain object — surfaces.slack.denyTools hides a tool from one channel.
agent/instructions.md
→
The system prompt, as a file you can diff. instructions.<source>.md appends to it — or replaces it — on one surface.
agent/tools/*.ts
→
Each file is a defineAction() — the same one your UI calls and /mcp serves. A raw tool when it needs the turn itself, e.g. to ask a human.
agent/skills/*.md
→
Loaded on demand through an automatic read_skill tool — long procedures stay out of the prompt until needed.
agent/channels/*.ts
→
Inbound edges. slackChannel, crispChannel, httpChannel, or your own defineChannel.
agent/connections/*.ts
→
Outbound tool sources. A remote MCP server, an OpenAPI spec, Google Drive — credentials resolved server-side per call.
Agents overview →The agent/ directory reference →

02 No glue layer

One action. Four callers. One gate.

No second permission system. A defineAction() is your button's server action and an /mcp tool for anyone else's agent, automatically. Export it from agent/tools/ and your own agent calls the very same one.

run(input, ctx) — ctx.user is whoever is really asking
your UI

A server action behind a button or form.

your agent

A tool in agent/tools/, scoped to the speaker a channel resolved.

/mcp

An MCP tool for any external agent holding a user's credential.

WebMCP

A browser tool, registered with navigator.modelContext.

agent/tools/refund_order.tsts
import { defineAction } from "@junejs/core/agent";
import { db } from "@junejs/db";

export default defineAction({
  id: "refund_order",
  description: "Refund a delivered order.",
  input: { type: "object", properties: { id: { type: "number" } } },
  requiresPrincipal: true,  // hidden from anonymous turns
  async run(input, ctx) {
    // the one gate: a button, the agent, and /mcp all land here
    if (!ctx.user?.canRefund) throw new Error("forbidden");
    return db.orders.refund(input.id);
  },
});

03 Channels & connections

Meet people where they work. Reach what they use.

Channels bring turns in and resolve who is speaking. Connections take tools out — the agent never holds a token. Each is one file.

in channels/

slackChannelStreams its reply, shows a thinking line, approval buttons, drops redeliveries.
crispChannelCustomer chat — plus private notes only your operators see.
httpChannelPOST /message for your own UI, a CLI, or a cron.
defineChannelAnything else with a webhook.
Channels →
agent/

One agent, many surfaces. Each surface can append or replace the instructions and deny tools.

tools = your actionsctx.user per speakeranthropic()

out connections/

MCP serverAny remote MCP server; its tools arrive as conn__tool.
OpenAPIPoint at a spec — operations become tools.
Google DriveRead and save files, on the user's own linked account.
auth(ctx)Credentials resolve server-side per call, never in the prompt.
Connections →

04 Durable turns

A turn is a process, not a request.

Real work outlives an HTTP request. June persists each turn as it runs and streams its events, so an agent can wait on a person, survive a restart, and still answer in the right thread.

running

Crash-proof

Every model call and tool result is checkpointed. Redeliver a turn after a crash and it replays: finished steps are skipped, not re-run.

parked

Human in the loop

A tool calls ctx.requestInput() and the turn parks for an answer — minutes or days — then picks back up. Slack renders it as Approve / Deny.

replaced

Superseded cleanly

Opt in to replace, and a newer message cancels the unfinished turn instead of queueing behind it.

initiated

Agent goes first

An agent can start a turn itself — a reminder, a follow-up — through the same log and channels.

june dev
NativeRuntime · SQLiteMounted automatically when agent/ exists, on Bun or Node. SQLite in memory by default — fast to iterate, reset on restart.
deploy
Workers · one Durable Object per sessionjune build generates the DO class and the wrangler binding. Nothing to wire.
Durable turns →Run & deploy →

05 Try it on this site

This site is a June app. Ask it.

The box calls search_site on this site's own /mcp — a defineAction() in app/actions.ts, the same tool Claude or any agent gets. Or point one at it:

$ curl -X POST https://june.build/mcp \
    -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
›↵
This box is an MCP client. It calls search_site — a defineAction() on this site — exactly as an external agent would.
same tool at /mcpmap at /llms.txt

06 The foundation

Underneath: a framework that already speaks agent.

The agent layer stands on the same core that serves your pages — server-first React where every route is readable by people and machines alike.

route()

One definition, four surfaces.

A page's default export is the view; named exports configure the rest. Nothing drifts, because nothing is duplicated — append .md to any URL on this site.

text/htmlstreamed RSC
.mdyour authored bytes
.jsonthe loader data
/mcpevery action
discovery

llms.txt, sitemap, API catalog.

Derived from the route graph. On by default — june.config.ts exists to turn things off.

llms.txt →
data

Ambient db, plain SQL.

import { db }, scoped per request. Writes auto-invalidate; reads auto-batch.

Data model →
rsc + islands

Zero client JS by default.

Interactivity is an explicit island. Navigation is the browser's — Speculation Rules, View Transitions.

Islands →
web standards

fetch(Request) → Response.

That is the framework. Bun-first toolchain, runtime-agnostic core, deploys to Workers.

Web Standards →
59ms
dev cold start
73ms
HMR flight
0
client JS by default
8.8×
fewer D1 queries
Every number traces to a named run →

07 Honest status

0.0.x preview. APIs will change — we'll say which.

The shape is settled; the surface is still moving. Calibrate with the stability & roadmap page.

Stable
  • routes & projections
  • defineAction()
  • /mcp, llms.txt, discovery
Changing
  • the agent layer (agent/, channels, connections)
  • data layer
  • auth
Experimental
  • native Rust+V8 runtime
  • live RSC

08 FAQ

Questions people (and agents) ask first.

The same answers are in /index.md and in this page's FAQPage JSON-LD.

What is June?

An open-source (MIT) React framework for apps that serve people and AI agents at once. One route renders HTML for people and also answers as Markdown and JSON; an action you define with defineAction() and a description is also an MCP tool at /mcp and, when its id is URL-safe, a POST /api/<id> operation in /openapi.json.

How is June different from a chatbot builder or an agent framework?

June is not a hosted chatbot or a separate agent runtime. The agent lives inside your app: an agent/ directory declares it, its tools are your app's own actions exported into agent/tools/, and the same authorization check covers a person clicking a button and an agent calling the tool.

Which npm packages are June's?

@junejs/core (the framework), @junejs/server and @junejs/cli; start a new app with npm create june my-app. The june package and the @june/* scope on npm are unrelated projects.

Where can I deploy a June app?

Cloudflare Workers (the default), Vercel and Deno Deploy ship today, and a static export works on any file host such as GitHub Pages. Self-hosting on Bun or Node serves the same pipeline; a production june start command is on the roadmap.

Do agent turns survive a restart or a redeploy?

With a persistent store, yes: every turn is recorded step by step, so a process backed by a mounted SQLite file or by Durable Objects on Workers resumes where it left off. The default in-memory store keeps turns only while the process runs.

Is June ready for production?

June is a 0.0.x preview. Routes and projections, defineAction(), and the /mcp and discovery surfaces are stable; the agent layer (agent/, channels, connections), the data layer and auth are still changing. The stability page at june.build/docs/stability tracks each surface.

Your app is the agent.

Start with a page. Add an agent/ directory when you need one. Deploy both with one command.

$ npm create june@latest my-app
Read the docs