---
name: vamo-design-studio
description: Generate on-brand Vamo assets (pages, PNGs, PDFs) from a prompt or a component tree.
---

# Vamo design studio

Two HTTP services turn structured input into on-brand Vamo assets. This file is
the whole interface. Fetch it, then call the service that fits your need.

## When to use which service

- You are an app that renders Svelte: do not use these services. Consume the git
  mirror of `packages/design`. See CONSUMING.md in the repo.
- You want a finished asset and are happy to let a model do the layout: use the
  studio agent. One tool, one prompt.
- You want full control of the layout: use the render plane. You author the
  component tree; it renders exactly what you send.

Secrets are held by the operator. Use the placeholders below verbatim; never
guess or embed a real secret.

---

## Studio agent

Base URL: `https://vamo-design-agent.bewanted.workers.dev`
Model: `@cf/moonshotai/kimi-k2.6`. Retries up to 3 times to produce a valid tree.

### POST /task

Header: `Authorization: Bearer <AGENT_SECRET>`

Request body:

```json
{
  "prompt": "A one-page pitch sheet for Ada Lovelace, a senior systems engineer.",
  "data": { "name": "Ada Lovelace", "gemScore": 87, "languages": ["Rust", "C"] },
  "format": "page"
}
```

- `prompt`: string, required.
- `data`: any JSON, optional. The facts to lay out.
- `format`: one of `page`, `png`, `pdf`, `pitch-kit`. Default `page`.

Success response:

```json
{
  "ok": true,
  "format": "page",
  "pageUrl": "https://vamo-design-render.bewanted.workers.dev/p/ab12cd",
  "assets": { "page": "https://vamo-design-render.bewanted.workers.dev/p/ab12cd" },
  "tree": { "component": "stack", "props": { "gap": "lg" }, "children": [] }
}
```

Failure response (after 3 attempts):

```json
{ "ok": false, "error": "could not produce a valid asset", "feedback": "..." }
```

`png` and `pdf` return the page URL and a note that browser rendering is not
configured until the operator sets that secret.

### MCP registration

The agent speaks MCP at `/mcp` with one tool, `generate_asset`, taking the same
`prompt`, `data`, `format` fields.

```bash
claude mcp add --transport http vamo-design-studio \
  https://vamo-design-agent.bewanted.workers.dev/mcp \
  --header "Authorization: Bearer <AGENT_SECRET>"
```

---

## Render plane

Base URL: `https://vamo-design-render.bewanted.workers.dev`
Header on everything except page retrieval: `Authorization: Bearer <RENDER_SHARED_SECRET>`

### Endpoints

- `POST /render`: tree in, hosted page out. Returns `{ id, url, html_bytes }`.
  Pages live 24 hours in KV.
- `GET /p/:id`: the hosted page. Public, no auth.
- `POST /capture`: tree or url in, binary out. `format` is `png`, `jpeg`, or
  `pdf`. Width presets: `a4`, `letter`, `og`, `slide-16x9`.
- `GET /registry`: the full component manifest with props and enums.

### MCP tools (at /mcp)

- `list_components`: every renderable component with category, description, props.
- `render_page`: compose a tree into a stored page. Returns a URL.
- `capture_png`: render a tree or url and capture a PNG. Returns a URL.
- `capture_pdf`: render a tree or url and capture a PDF. Returns a URL.
- `create_pitch_kit`: render a multi-page kit in one call. Returns a manifest of
  `{ name, url, captureUrl }` per page.

### ComponentNode format

A tree is a single node. A node is:

```json
{ "component": "kebab-name", "props": {}, "children": ["node or text"] }
```

- `component`: a kebab-case name from the registry. Nothing else is renderable.
  Unknown names are rejected with the list of valid names.
- `props`: enum and string values. Enum props must use a listed value verbatim.
- `children`: an ordered array of child nodes and plain text strings. This is the
  default slot only; there are no named slots.
- No-children rule: components flagged no-children in the registry render nothing
  from `children`. All their content goes in `props`.

Full valid example:

```json
{
  "component": "stack",
  "props": { "gap": "lg" },
  "children": [
    { "component": "masthead", "props": { "title": "Ada Lovelace", "meta": "Systems engineer" } },
    { "component": "section", "props": { "label": "Scores" }, "children": [
      { "component": "stat-tile", "props": { "label": "GemScore", "value": "87", "tone": "accent" } },
      { "component": "stat-tile", "props": { "label": "Velocity", "value": "88th", "tone": "neutral" } }
    ] },
    { "component": "section", "props": { "label": "Evidence" }, "children": [
      { "component": "signal-chip", "props": { "label": "Rust", "trust": "github" } },
      { "component": "signal-chip", "props": { "label": "Top 3%", "trust": "score" } }
    ] }
  ]
}
```

---

## Voice law (binding for every string you write into an asset)

1. No em dashes.
2. No "not X, it's Y" contrast constructions.
3. No branded aphorisms or slogans.
4. Do not write about the reader. Give imperative instructions; describe the thing.
5. No throat-clearing.
6. No infomercial claims. Real numbers only.
7. No emoji.

Em dashes are also stripped in code, so do not rely on that; write clean copy.

---

## Hard limits

- Depth: 12 levels maximum.
- Size: 500 nodes maximum.
- Retries: the studio agent tries up to 3 times, feeding each rejection back to
  the model, then returns `ok: false`.
- Captures: `capture_png`, `capture_pdf`, and pitch-kit `captureUrl` return
  not_configured until the operator sets the browser rendering token. Page URLs
  work regardless.
