agentsclimarketplace

Openui forge anthropic

Skill OthmanAdi/openui-forge/.codex/skills/openui-forge-anthropic

Cross-IDE, multi-stack agent skill for OpenUI (the Open Standard for Generative UI). Adds OpenUI to existing projects across 12 backend stacks, any LLM provider, and 11 agent platforms. Scaffold, integrate, validate.

Install
npx -y skills add OthmanAdi/openui-forge --skill openui-forge-anthropic

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 20 stars20 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

OpenUI generative UI with Anthropic Claude SDK backend. Stream conversion to OpenAI NDJSON format.

SKILL.md

5.9 KB, as published. Nobody here has run it

OpenUI Forge — Anthropic

Build generative UI apps with OpenUI + Anthropic Claude. Converts Anthropic streaming events to OpenAI-compatible NDJSON.

Activation Triggers

  • "openui anthropic", "openui claude", "openui sonnet"
  • "generative ui claude", "claude streaming ui"

Prerequisites

  • Node.js >= 22 (24 LTS recommended), React >= 18.3.1 (19+ recommended)
  • ANTHROPIC_API_KEY environment variable set
  • Next.js project (App Router recommended)

Quick Start

  1. Install dependencies:
npm install @openuidev/react-ui @openuidev/react-headless @openuidev/react-lang lucide-react zod @anthropic-ai/sdk
  1. Add the CSS import to app/layout.tsx:
import "@openuidev/react-ui/components.css";
  1. Create the API route and frontend page below
  2. Run npm run dev and test

Full Code

Backend: app/api/chat/route.ts

The backend streams from Anthropic and converts each event into OpenAI-compatible SSE chunks that openAIAdapter() expects (data: {json}\n\n lines, terminated by data: [DONE]).

import { openuiChatLibrary } from "@openuidev/react-ui/genui-lib";
import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic();

export async function POST(req: Request) {
  const { messages } = await req.json();

  const systemPrompt = openuiChatLibrary.prompt({
    preamble: "You are a helpful assistant that generates interactive UIs.",
    additionalRules: ["Always use Stack as root when combining multiple components."],
  });

  // ANTHROPIC_MODEL alternatives: claude-opus-4-8, claude-haiku-4-5, claude-fable-5
  const stream = client.messages.stream({
    model: process.env.ANTHROPIC_MODEL ?? "claude-sonnet-4-6",
    max_tokens: 4096,
    system: systemPrompt,
    messages,
  });

  const encoder = new TextEncoder();
  const readableStream = new ReadableStream({
    async start(controller) {
      const id = `chatcmpl-${Date.now()}`;
      for await (const event of stream) {
        if (
          event.type === "content_block_delta" &&
          event.delta.type === "text_delta"
        ) {
          const chunk = {
            id,
            object: "chat.completion.chunk",
            choices: [
              {
                index: 0,
                delta: { content: event.delta.text },
                finish_reason: null,
              },
            ],
          };
          controller.enqueue(
            encoder.encode(`data: ${JSON.stringify(chunk)}\n\n`)
          );
        }
      }
      const done = {
        id,
        object: "chat.completion.chunk",
        choices: [{ index: 0, delta: {}, finish_reason: "stop" }],
      };
      controller.enqueue(encoder.encode(`data: ${JSON.stringify(done)}\n\n`));
      controller.enqueue(encoder.encode("data: [DONE]\n\n"));
      controller.close();
    },
  });

  return new Response(readableStream, {
    headers: { "Content-Type": "text/event-stream" },
  });
}

Frontend: app/chat/page.tsx

"use client";
import { FullScreen } from "@openuidev/react-ui";
import { openuiChatLibrary } from "@openuidev/react-ui/genui-lib";
import {
  openAIAdapter,
  openAIMessageFormat,
} from "@openuidev/react-headless";

export default function ChatPage() {
  return (
    <FullScreen
      componentLibrary={openuiChatLibrary}
      streamProtocol={openAIAdapter()}
      messageFormat={openAIMessageFormat}
      apiUrl="/api/chat"
    />
  );
}

The backend emits SSE (data: {json}\n\n). Pair it with openAIAdapter() on the frontend — openAIReadableStreamAdapter() is for NDJSON (no data: prefix) and will silently produce no output here.

Component Creation

import { defineComponent } from "@openuidev/react-lang";
import { z } from "zod";

export const StatusCard = defineComponent({
  name: "StatusCard",
  description: "Displays a status with label and color indicator",
  props: z.object({
    label: z.string().describe("Status label text"),
    status: z.enum(["ok", "warning", "error"]).describe("Current status level"),
  }),
  component: ({ props }) => {
    const colors = { ok: "#22c55e", warning: "#eab308", error: "#ef4444" };
    return (
      <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
        <span style={{ width: 10, height: 10, borderRadius: "50%", background: colors[props.status] }} />
        <span>{props.label}</span>
      </div>
    );
  },
});

System Prompt Generation

npx @openuidev/cli generate ./src/lib/library.ts --out src/generated/system-prompt.txt

Or at runtime: openuiChatLibrary.prompt({ preamble: "...", additionalRules: [...] }).

Validation Checklist

  • ANTHROPIC_API_KEY is set in .env.local
  • CSS import present in root layout
  • Backend converts Anthropic content_block_delta events to OpenAI-compatible SSE chunks
  • Final chunk has finish_reason: "stop" and ends with data: [DONE]
  • Frontend uses streamProtocol={openAIAdapter()} and openAIMessageFormat
  • componentLibrary={openuiChatLibrary} prop passed to FullScreen

Error Patterns

ErrorCauseFix
401 from AnthropicMissing or invalid API keySet ANTHROPIC_API_KEY in .env.local
Stream hangsMissing [DONE] sentinel or controller.close()Ensure final chunk and [DONE] are sent
Garbled outputNot wrapping in data: ... SSE formatEach chunk must be data: {json}\n\n
Components render as textLibrary not passed to FullScreenAdd componentLibrary={openuiChatLibrary} prop
Nothing renders, no errorUsed openAIReadableStreamAdapter() (NDJSON) on SSE stream, or adapter= prop (silently ignored)Use streamProtocol={openAIAdapter()}
max_tokens requiredAnthropic API requires explicit max_tokensAlways set max_tokens (e.g., 4096)

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.