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.
#4812 — arrived broken, photos in thread01 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.
surfaces.slack.denyTools hides a tool from one channel.instructions.<source>.md appends to it — or replaces it — on one surface.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.read_skill tool — long procedures stay out of the prompt until needed.slackChannel, crispChannel, httpChannel, or your own defineChannel.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.
A server action behind a button or form.
A tool in agent/tools/, scoped to the speaker a channel resolved.
An MCP tool for any external agent holding a user's credential.
A browser tool, registered with navigator.modelContext.
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/
One agent, many surfaces. Each surface can append or replace the instructions and deny tools.
out 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.
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.
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.
Superseded cleanly
Opt in to replace, and a newer message cancels the unfinished turn instead of queueing behind it.
Agent goes first
An agent can start a turn itself — a reminder, a follow-up — through the same log and channels.
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"}'
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.
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.
llms.txt, sitemap, API catalog.
Derived from the route graph. On by default — june.config.ts exists to turn things off.
llms.txt →Ambient db, plain SQL.
import { db }, scoped per request. Writes auto-invalidate; reads auto-batch.
Zero client JS by default.
Interactivity is an explicit island. Navigation is the browser's — Speculation Rules, View Transitions.
Islands →fetch(Request) → Response.
That is the framework. Bun-first toolchain, runtime-agnostic core, deploys to Workers.
Web Standards →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.
- routes & projections
- defineAction()
- /mcp, llms.txt, discovery
- the agent layer (agent/, channels, connections)
- data layer
- auth
- 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.