# Janux

> Janux - One component, two faces: UI for humans, MCP tools for AI agents. The agent-native fullstack framework.

- **Type:** MCP server
- **Install:** `agentstack add mcp-aralroca-janux`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [aralroca](https://agentstack.voostack.com/s/aralroca)
- **Installs:** 0
- **Category:** [AI & ML](https://agentstack.voostack.com/c/ai-and-ml)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [aralroca](https://github.com/aralroca)
- **Source:** https://github.com/aralroca/Janux
- **Website:** https://janux.build

## Install

```sh
agentstack add mcp-aralroca-janux
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

Janux

  The fullstack framework for the Agentic Web.
  One component, two faces: a live view for humans, typed MCP tools &amp; resources for AI agents — generated from the same definition, so they can never drift.

> [!WARNING]
> Janux is currently **under active development**. This repository is public to enable collaboration and transparency, but it has not been officially announced yet. Expect breaking changes, incomplete documentation, and unfinished features until the first public release.

  
  
  
  
  
  
  

  Website ·
  Docs ·
  Quick start ·
  The Agentic Web ·
  Playground ·
  RFC 0001

  

examples/with-web-agent — the agent calls the same intents a human clicks, and createCopilot({ visualize }) is the whole of the feedback: a chip per tool call, a gradient ring on the element being operated, and a backdrop veil that keeps the user's focus on the action.

---

## Why Janux

The web is growing a second audience. People still click, but agents now read, plan and act on the same pages — through MCP clients, through browser agents, through copilots embedded in your own product. **The Agentic Web is the web both of them can operate**, and it is being standardized in the open: MCP for tools over HTTP, WebMCP for tools in the browser, `llms.txt` for discovery, Web Bot Auth for identity.

Today, making an app agent-operable means building it twice. The UI already holds the logic — the validation, the permissions, the business rules — and then a second, hand-written integration re-declares a fraction of it as tools. Two artifacts, one source of truth, and the gap between them grows with every sprint. Tools drift, guardrails are ad-hoc, and nobody can say exactly what an agent is allowed to do.

Janux removes the second artifact. **A component is simultaneously a view, an agent-readable resource and a set of typed tools** — one definition, projected three ways by the framework. A human click and an agent tool call enter the *same* pipeline: guard check → schema validation → `run()` → audit entry. The contract can't rot, because it is generated from the code that renders.

Named after **Janus**, the two-faced Roman god of doorways: one face toward the human, one toward the agent, one threshold. Designed in [RFC 0001](https://github.com/aralroca/Janux/issues/1).

## Table of Contents

- [Install](#install)
- [Quick start](#quick-start)
- [One component, three projections](#one-component-three-projections)
- [Two agent surfaces, zero integration](#two-agent-surfaces-zero-integration)
- [Humans stay in the loop](#humans-stay-in-the-loop)
- [Highlights](#highlights)
- [How it works](#how-it-works)
- [Performance](#performance)
- [Packages](#packages)
- [Documentation](#documentation)
- [Benchmarks](#benchmarks)
- [Templates](#templates)
- [Examples](#examples)
- [Develop](#develop)
- [Contributing](#contributing)
- [Releases](#releases)
- [License](#license)

## Install

```bash
bunx create-janux my-app
cd my-app && bun install && bun run dev
```

Requires [Bun](https://bun.sh) ≥ 1.3 for the dev server and the build. Production is a choice: Bun, [Node 24+](https://janux.build/docs/recipes/deploying) via `@janux/node`, Vercel, or a static export — same app, one adapter.

Or add the pieces to an existing workspace:

```bash
bun add janux @janux/server @janux/agent @janux/cli
```

## Quick start

```tsx
import { component, intent, schema, str, int, money, list } from 'janux';
import { pay } from './pay.api';

//  UI component + 2 WebMCP tools (intents), grouped together for maintainability
export const Cart = component({
  name: 'cart',
  description: 'Shopping cart with line items.',
  state: schema({ items: list({ productId: str(), qty: int().min(1), unitPrice: money() }) }),
  derived: { total: (s) => s.items.reduce((a, i) => a + i.qty * i.unitPrice, 0) },
  intents: {
    addItem: intent({
      description: 'Add a product to the cart',
      input: schema({ productId: str(), qty: int().default(1), unitPrice: money().default(0) }),
      run: ({ state, input }) => state.items.push(input),
    }),
    checkout: intent({ description: 'Pay for the cart', guard: 'confirm', run: ({ state }) => pay({ items: state.items }) }),
  },
  view: ({ state, derived, intents }) => (
    
      
        {state.items.map((i) => (
          {i.productId} × {i.qty}
        ))}
      
      Pay ({derived.total}¢)
    
  ),
});
```

You wrote a shopping cart. You also shipped an agent surface — generated, no second file:

```json
{
  "resources": ["ui://cart"],
  "tools": [
    { "name": "cart.addItem", "description": "Add a product to the cart", "guard": "auto" },
    { "name": "cart.checkout", "description": "Pay for the cart", "guard": "confirm" }
  ]
}
```

## One component, three projections

| Projection | For | What it is |
|---|---|---|
| **View** | humans | server-rendered HTML that resumes on first interaction |
| **Resource** | agents | `ui://cart` — typed JSON state, readable and subscribable |
| **Tools** | both | `cart.addItem` (auto), `cart.checkout` (**confirm** → a human approves) |

They cannot drift: there is one definition, and the framework derives the other two. A human click and an agent tool call run the **exact same pipeline** — guard check → schema validation → `run()` → audit entry.

## Two agent surfaces, zero integration

Every Janux app speaks the Agentic Web's protocols out of the box. You declare no tools twice, and you write no adapters.

| Standard | What Janux does with it |
|---|---|
| **MCP** — tools over HTTP | A real, stateless MCP server at `/_janux/mcp`, generated from your `api()` functions. Dual-era: negotiates `2026-07-28` and `2025-06-18`. |
| **A2A** — agent to agent | A derived `/.well-known/agent-card.json` and a JSON-RPC endpoint at `/_janux/a2a`, over the same pipeline and the same guards as MCP — so an agent that arrives by A2A holds no authority an MCP client would be refused. |
| **WebMCP** — tools in the browser | Every mounted intent is registered with `document.modelContext` the moment its island mounts, so browser agents and the DevTools panel see it. Polyfilled where the API is missing. |
| **`llms.txt`** — discovery | Opt-in site index at `/llms.txt` (dynamic routes expanded via `staticParams`), plus a Markdown projection of every page by appending `.md`. |
| **Web Bot Auth** (RFC 9421) | Signed agent identity, verified per request under an `observe` or `require` policy. |
| **Human approval** | `guard: 'confirm'` reaches MCP clients as `annotations.requiresApproval`, arrives over A2A as `TASK_STATE_INPUT_REQUIRED`, and parks agent calls as Proposals whichever door they came through. |

Pointing Claude, Cursor or any MCP client at your app is a URL, not an integration project:

```bash
claude mcp add --transport http my-app https://your.app/_janux/mcp
```

## Humans stay in the loop

Guards are a language feature, not a convention. Every `intent` and every `api()` declares who may call it:

- **`auto`** — agents call it directly.
- **`confirm`** — a human click runs it; an *agent* call parks as a **Proposal** that a person approves or rejects on the real UI, executing exactly once.
- **`forbidden`** — never exposed as a tool. The agent falls back to the DOM, under the same permissions as a user.

Every invocation records its **origin** (`human` / `agent`) in an audit trail, and `janux verify` fails the build if an agent-reachable tool ships without a description. See [`examples/human-in-the-loop`](examples/human-in-the-loop).

## Highlights

- 🧿 **One definition, three projections.** The mounted tree *is* the MCP tree — UI and agent surface cannot drift.
- 🪶 **0 KB JS static pages.** Components without state compile to plain HTML; a page with no islands ships no `` at all.
- ⚡ **Structural resumability.** State is schema-typed JSON, behavior is named — the client resumes from snapshots with no hydration replay and no closure serialization. Zero component code runs until first interaction (asserted in the test suite).
- 🔌 **`api()` = endpoint + stub + tool.** A server function is at once a validated HTTP endpoint, a ~100-byte typed client stub (SWC transform) and an agent tool.
- 🤖 **Zero-config copilot.** `JANUX_MODEL` or one provider API key (Anthropic, OpenAI, Google or OpenRouter) is all it takes. Every app ships the agent endpoint, the manifest and the gui-agent bridge (`window.janux`).
- 🗺️ **App-wide agent control.** Every turn advertises built-in client tools (`ui_navigate`, `ui_get_view_context`, `ui_read_page`, `ui_click`, `ui_fill`, `ui_wait_settled`) plus the full route map — and `ui_calls` turns resume with their results (act → observe → continue), so navigate-then-act works in one turn.
- ⚛️ **Foreign-UI interop.** `foreign()` mounts React components unchanged — real embedded roots, tracked props, callbacks→intents — surviving SPA navigation.
- 🧘 **Observable quiescence.** `await janux.settled()` — the `sleep(500)` idiom dies here.
- 🧪 **CI for the agent surface.** `janux verify` gates undescribed tools; `janux eval` replays scripted agent tasks — including real human-approval steps — against a live app.

## How it works

```
Browser ── janux core (signals, resume, morph, delegation, window.janux bridge)
   │  HTML + state snapshots │ RPC │ agent turns
Server ── @janux/server (SSR, api(), manifest, proposals)
              └── @janux/agent (model resolution, provider loop: api.* server-side, ui_calls → bridge)
```

- **SSR**: sources load server-side; islands arrive with real content plus a JSON state snapshot.
- **Resume**: `boot()` indexes islands, installs two delegated listeners, and mounts an island **only** on first interaction or agent call — from the snapshot, morphing the SSR DOM in place.
- **Agents**: `GET /_janux/manifest?path=/shop` for discovery; `POST /_janux/api/*` for server tools; `window.janux.call()` for UI tools; `POST /_janux/approve` for proposals.
- **Static export**: `output: "static"` prerenders every page into `dist/client` — deploy docs and marketing sites to any static host, agent face included, no server.

### Configure the copilot model

Zero config — first match wins:

1. `defineAgent({ model: 'anthropic/claude-fable-5' })`
2. `JANUX_MODEL=provider/model`
3. Provider key sniffing: `ANTHROPIC_API_KEY` / `OPENAI_API_KEY` / `GOOGLE_GENERATIVE_AI_API_KEY`
4. Nothing set → the endpoint answers with a setup card; the app never crashes.

## Performance

  
    
    
  

The documentation site is built with Janux ([apps/docs](apps/docs)) and scores 100 across the board — including **Agentic Browsing**, Lighthouse's check for whether an agent can actually read and operate the page. A CI job re-runs the audit on every pull request.

## Packages

| Package | What |
|---|---|
| [`janux`](packages/janux) | Core: schema, signals, reactive state, component runtime, SSR islands, manifest, client resume + bridge, foreign interop, data cache, built-in client tools, glow, simulated agent cursor |
| [`@janux/server`](packages/janux-server) | api() RPC, file-system router (layouts, groups, matchers, middleware), HTTP handlers + uploads, HTML shell, `/_janux/*` endpoints incl. the hosted MCP + `.md` projections, llms.txt, Web Bot Auth |
| [`@janux/agent`](packages/janux-agent) | Model resolution, providers, the tool loop with turn continuation, and the embedded harness: memory (in-memory/Postgres), durable workflows, guardrail processors, rate limiting (in-memory/Redis), attachments, outbound MCP client |
| [`@janux/vite`](packages/janux-vite) | Vite plugin (SWC api stubs, SSR bridge) |
| [`@janux/cli`](packages/janux-cli) | `janux dev / build / start / verify / eval`, plus the adapter API third-party deploy targets are written against |
| [`@janux/node`](packages/janux-node) · [`@janux/vercel`](packages/janux-vercel) | Deployment adapters: a self-contained `build/` for any Node 24+ host, and a Build Output API directory for Vercel |
| [`create-janux`](packages/create-janux) | Scaffolder |

## Documentation

**[janux.build](https://janux.build)** — 112 pages, ⌘K search, dark mode, and a copilot that answers from the docs themselves.

| Section | Start here |
|---|---|
| **Getting started** | [What is Janux?](apps/docs/content/getting-started/what-is-janux.md) · [Quick start](apps/docs/content/getting-started/quick-start.md) · [The Agentic Web](apps/docs/content/getting-started/the-agentic-web.md) · [Mental model](apps/docs/content/getting-started/mental-model.md) |
| **Guide** | [Components](apps/docs/content/guide/components.md) · [Views and JSX](apps/docs/content/guide/views-and-jsx.md) · [Intents and guards](apps/docs/content/guide/intents-and-guards.md) · [Navigation](apps/docs/content/guide/navigation.md) · [The agent and your copilot](apps/docs/content/guide/agent-and-copilot.md) |
| **Tutorial** | [A task board with two faces](apps/docs/content/tutorial/tasks-app-part-1.md) (3 parts) |
| **Reference** | one page per export: [reactivity](apps/docs/content/reference/signal.md), [client](apps/docs/content/reference/client-state.md), [data cache](apps/docs/content/reference/data-cache-api.md), [agent harness](apps/docs/content/reference/agent-memory.md), [CLI](apps/docs/content/reference/cli.md) |
| **Recipes** | [Testing](apps/docs/content/recipes/testing-components.md) · [Forms](apps/docs/content/recipes/forms.md) · [Optimistic UI](apps/docs/content/recipes/optimistic-ui.md) · [Error handling](apps/docs/content/recipes/error-handling.md) · [Custom server](apps/docs/content/recipes/custom-server.md) · [Docker](apps/docs/content/recipes/docker.md) · [Monorepo](apps/docs/content/recipes/monorepo-setup.md) · [Tailwind](apps/docs/content/styles/tailwind.md) · [Local model copilot](apps/docs/content/recipes/local-model-copilot.md) |
| **More** | [Examples](apps/docs/content/more/examples.md) · [Comparison](apps/docs/content/more/comparison.md) · [Benchmarks](apps/docs/content/more/benchmarks.md) · [FAQ](apps/docs/content/more/faq.md) · [Glossary](apps/docs/content/more/glossary.md) |

Agents read the same docs at `/llms.txt` and any page as Markdown by appending `.md`.

**Every example is verified.** `packages/docs-tests` compiles every snippet, checks it imports only symbols the packages really export, runs the main example of a page and asserts what the prose claims. Three guards fail the build when an export has no reference page, when a page's executable claims aren't executed, or when any documented link stops resolving. **Both backlogs are empty**: every public export is documented, and every page that imports the framework has a test that runs it.

## Benchmarks

19 multi-framework suites — client runtime, hydration, SSR, streaming and
shipped bytes — measuring Janux against react 19, preact, solid 2, svelte 5
and vue-vapor, with correctness gates before any number counts. The harness
is a port of [octane](https://github.com/octanejs/octane)'s benchmarks (MIT,
Dominic Gannaway; `js-framework` fixtures derive from
[krausest](https://github.com/krausest/js-framework-benchmark), Apache-2.0).

| Category | Where Janux stands |
|---|---|
| Resume vs hydration | **0.14× react** — 0.39ms to make the news page interactive (react 2.86); 10.70ms vs 57.62 at 6× throttle |
| Shipped JS | 32.5KB gzip total vs react 60.7 (preact 9.8 · solid 13.7 · svelte 17.9 · vue-vapor 23.5); islands-free pages ship 0KB |
| Fine-grained updates | `` + `class={() => …}`: swap 1.10ms vs react 3.98; reverse 1.95 vs 2.24; rotate 0.51 vs 1.51 |
| Mass DOM work | 10k rows: 68.94ms vs react 136.86; clear 38.40 vs 41.74; 512-field reset 14.74 vs 38.64; 512-field typing 16.84 vs 45.58 |
| Whole-app suites | parity: lifecycle cycle 49.35ms vs 49.56, store integrations within ±1.4×, suspense recovery within 1.14× |
| Building rows in bulk | behind: create-1000 6.56ms vs react 4.88 (solid 1.90) — a row carries an Owner, a signal and an effect |
| SSR throughput | behind: buffered 0.26ms vs react 0.07; streaming end-to-end at parity (50.86 vs 51.06) |

Across

…

## Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [aralroca](https://github.com/aralroca)
- **Source:** [aralroca/Janux](https://github.com/aralroca/Janux)
- **License:** MIT
- **Homepage:** https://janux.build

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/mcp-aralroca-janux
- Seller: https://agentstack.voostack.com/s/aralroca
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
