Blu for developers

Claude Code plugin · v1.2.1

/plugin marketplace add https://developers.blu-ce.horizonmedia.com/plugins/claude-code/marketplace.json
/plugin install blu@blu

Download blu-1.2.1.zip

Next.js (App Router) app: @blu/next-adapter

The same codebase keeps building and running with next build / next start, and also builds as one federated Blu module with build:blu (Vite). The app's own pages, layouts, components and server actions run inside Blu. The adapter supplies the Blu versions of next/link, next/navigation and next/image, and sends server actions over HTTP with the Blu token.

A complete worked example lives in the Blu repository: teams/team-d/README.md and teams/team-d/BLU_INTEGRATION.patch (a stand-in for HorizonMedia's Unified app). Read it if the app is shaped similarly.

Files to add

File What
blu.module.json the manifest, inline: "remote": { "name": "<federation name>", "exposed": "./App" }, remoteEntry: https://<app>/blu/mf-manifest.json
vite.blu.config.ts export default bluModule({ name: "<federation name>", tailwind }) from @blu/next-adapter/vite (pass the app's Tailwind config if it uses Tailwind). Output goes to public/blu/, served by the app's own Next server.
blu/App.tsx what app/layout.tsx does, inside Blu: the app's providers, top bar and guards around {page} (see below)
blu/server.ts (or similar) createBluNextServer({ moduleId, issuer, jwksUrl, apiUrl, roleMap, allowedOrigins }) from @blu/next-adapter/server, configured per environment from env vars
app/api/blu/actions/route.ts where the app's server actions arrive from inside Blu
app/api/mcp/route.ts optional: Concierge tools (createMcpWebHandler from @blu/sdk/server, see server.md)

package.json: add @blu/sdk, @blu/next-adapter (install.md), vite as a dev dependency, and "build:blu": "vite build -c vite.blu.config.ts". Add public/blu/ to .gitignore. Run build:blu in the deployment build after next build. A running next start only serves files that existed when it started, so restart it after build:blu.

blu/App.tsx: the root layout inside Blu

/// <reference types="vite/client" />
import { useHost } from "@blu/sdk";
import { BluNextModule } from "@blu/next-adapter/client";
import css from "../app/globals.css?inline";
import { Providers } from "../app/providers";
import { AppTopBar } from "../components/AppTopBar";

export default function AppInBlu() {
  const blu = useHost();                     // user, client, roles from Blu
  return (
    <BluNextModule
      css={css}
      pages={import.meta.glob("../app/**/page.tsx", { eager: true })}
      layouts={import.meta.glob("../app/*/**/layout.tsx", { eager: true })}
      exclude={["/login", "/admin"]}        // pages that stay standalone-only
    >
      {(page) => <Providers user={sessionFromBlu(blu)}><AppTopBar />{page}</Providers>}
    </BluNextModule>
  );
}

Server side

Code that changes

Keep these few and small:

Where Change
Header components hide the app's own client switcher, user menu and theme toggle inside Blu: if (useOptionalHost()) return null (Blu's header has them)
fetch("/api/…") in client code moduleFetch("/api/…") from @blu/next-adapter/client (plain fetch standalone; the app's own server, with the token, inside Blu)
Images or files the app serves only to signed-in users (<img src="/api/…">) useModuleAssetUrl(src) (and preloadModuleAsset for preloads)
<Image src="/logo.svg"> nothing: the adapter loads root-relative images from the app's own server
next/font, next-themes not used inside Blu; Blu's page owns fonts and theme. Don't render ThemeProvider in blu/App.tsx.

Search the client code for fetch(, "/api/, src="/, window.location and document.documentElement to find what needs attention.