# Install and build with Headcode CMS

The recommended path is agentic: give your coding agent the start.md contract, let it inspect the target project, then install, configure, and verify Headcode CMS with you in the loop.

## Agent-first installation

For most projects, do not start by copying commands manually. Start by giving your coding agent the installation contract:

[https://headcodecms.com/start.md](https://headcodecms.com/start.md)

`start.md` tells the agent how to inspect the target app, which setup questions to ask, which secrets never to invent, when to run Convex/Auth setup, and how to verify the install.

### New project

```bash
Read https://headcodecms.com/start.md then install Headcode CMS in this directory.
```

### Existing site

```bash
Read https://headcodecms.com/start.md. Inspect this Next.js project first, preserve the existing design where possible, then install Headcode CMS and stop before any destructive file conflict.
```

### Verify install

```bash
Use the Headcode CMS start.md checklist to verify the site, admin login, Convex env vars, Markdown output, and MCP route. Report what works and what still needs a human secret or decision.
```

This is the preferred install path for Codex, ChatGPT, Claude, Claude Code, Cursor, OpenCode, and similar tools. The agent should ask for real project choices and secrets instead of guessing.

## What the agent should do

A good Headcode install is not just a package command. The agent should first understand the target project, then install the CMS in a way that matches that project.

The agent should:

1. Inspect the directory: empty folder, existing Next.js app, shadcn app, or custom site.
2. Read [`start.md`](https://headcodecms.com/start.md) and the repository instructions before changing files.
3. Ask only for missing setup choices: Convex project, admin emails, Resend sender/key, local URL, test login, MCP tokens, and draft/live hosts.
4. Install the shadcn registry item `headcodecms/headcodecms/headcode`.
5. Run Convex setup before Convex Auth so `CONVEX_DEPLOYMENT` and generated files exist.
6. Set server secrets in Convex environment variables, not in `NEXT_PUBLIC_*` values.
7. Verify the public site, admin login, Markdown output, and MCP route.

The important rule: the agent can do the mechanical work, but it should stop when a human decision or secret is required.

## Manual installation, secondary path

Manual installation is useful when you want to understand the moving parts or when an agent gets stuck. Use the same order as `start.md`: install the app files first, then initialize Convex, then configure auth and environment variables.

### 1. New app

```bash
pnpm dlx shadcn@latest init --preset bbVJxYW --base base --template next --pointer
```

### 2. Add CMS

```bash
rm -f app/page.tsx app/layout.tsx
pnpm dlx shadcn@latest add headcodecms/headcodecms/headcode
pnpm install
```

### 3. Convex/Auth

```bash
pnpm convex dev
pnpm dlx @convex-dev/auth
```

### 4. Env vars

```bash
pnpm convex env set ALLOWED_ADMIN_EMAILS "admin@example.com"
pnpm convex env set AUTH_RESEND_KEY "re_..."
pnpm convex env set AUTH_RESEND_FROM "Headcode <admin@example.com>"
pnpm convex env set SITE_URL "http://localhost:3000"
```

### 5. Run

```bash
pnpm dev
open http://localhost:3000
open http://localhost:3000/admin/login
```

Skip the new-app command if the target project is already a Next.js/shadcn app. If files conflict, inspect and merge instead of overwriting blindly.

`pnpm dlx @convex-dev/auth` creates `JWT_PRIVATE_KEY` and `JWKS`. Follow any manual instructions it prints, and keep those values in Convex environment variables.

## Project map

Headcode is intentionally small enough for a developer or agent to read. Start with these files when changing behavior:

- `headcode/config.ts` lists collections, globals, allowed sections, and defaults.
- `headcode/sections.ts` defines section fields and validation shape.
- `headcode/fields.ts` contains reusable field helpers.
- `headcode/defaults.ts` contains starter content used before CMS data exists.
- `app/(site)/_sections/` renders sections as HTML and Markdown.
- `app/(admin)/` contains the authenticated editor.
- `app/(mcp)/[transport]/route.ts` exposes MCP tools.
- `convex/services.ts` is the service boundary for site, admin, and MCP.
- `convex/section_validations.ts` validates section JSON before it leaves the backend.

Most changes follow the same pattern: update config or sections, adjust defaults, render HTML and Markdown, then verify admin and MCP still understand the data.

## Source links

The source code lives in [headcodecms/headcodecms](https://github.com/headcodecms/headcodecms). For installation work, start with the install contract first:

- **[start.md installation contract](https://github.com/headcodecms/headcodecms/blob/main/public/start.md)**
- [Repository root](https://github.com/headcodecms/headcodecms)
- [Installation guide](https://github.com/headcodecms/headcodecms/blob/main/docs/installation.md)
- [Architecture notes](https://github.com/headcodecms/headcodecms/blob/main/ARCHITECTURE.md)
- [CMS configuration folder](https://github.com/headcodecms/headcodecms/tree/main/headcode)
- [CMS config](https://github.com/headcodecms/headcodecms/blob/main/headcode/config.ts)
- [Section definitions](https://github.com/headcodecms/headcodecms/blob/main/headcode/sections.ts)
- [Field definitions](https://github.com/headcodecms/headcodecms/blob/main/headcode/fields.ts)
- [Default content](https://github.com/headcodecms/headcodecms/blob/main/headcode/defaults.ts)
- [Public section renderers](https://github.com/headcodecms/headcodecms/tree/main/app/%28site%29/_sections)
- [Admin app](https://github.com/headcodecms/headcodecms/tree/main/app/%28admin%29)
- [MCP route](https://github.com/headcodecms/headcodecms/blob/main/app/%28mcp%29/%5Btransport%5D/route.ts)
- [Convex backend](https://github.com/headcodecms/headcodecms/tree/main/convex)
- [Registry definition](https://github.com/headcodecms/headcodecms/blob/main/registry.json)

For most tasks, open the folder link first. Use deep links when you need the exact implementation.

## Configuration

`headcode/config.ts` is the content model entry point. It tells Headcode which collections and globals exist, which sections each entry can use, and which default sections should appear in a fresh install.

Use collections for repeatable content: docs pages, legal pages, blog posts, products, locations, or case studies.

Use globals for singletons: home, header, footer, navigation, pricing, settings, or `llms.txt`.

The same config is used by the public site, admin UI, Convex validation, defaults, and MCP tools. If a section is not listed in config, editors and agents should not add it there.

## Configuration example

This is the shape a small documentation site might use. The public site fetches entries from Convex services and renders the configured section list.

### headcode/config.ts

```typescript
import { code, footer, header, hero, llmsTxt, meta, snippet, text } from './sections'

export const headcodeConfig = {
  collections: [
    {
      slug: 'docs',
      label: 'Docs',
      path: '/docs',
      sections: [meta, hero, text, snippet, code],
    },
    {
      slug: 'pages',
      label: 'Pages',
      path: '/pages',
      sections: [meta, hero, text],
    },
  ],
  globals: [
    {
      slug: 'home',
      label: 'Home',
      path: '/',
      sections: [meta, hero, text],
    },
    {
      slug: 'header',
      label: 'Header',
      sections: [header],
    },
    {
      slug: 'footer',
      label: 'Footer',
      sections: [footer],
    },
    {
      slug: 'llms',
      label: 'llms.txt',
      path: '/llms.txt',
      sections: [llmsTxt],
    },
  ],
} as const
```

### prompt.txt

```text
Add a products collection to this Headcode CMS project.

Use existing section and field patterns.
Update headcode/config.ts, add only the missing section definitions, create default content when useful, and add public renderers if the new section needs custom HTML or Markdown.
Do not bypass convex/services.ts.
```

If you are adding a new content area, start in config, then add sections and renderers only when the existing ones are not enough.

## Sections

A page is an ordered list of sections. Sections are the reusable page blocks that humans edit and agents update through MCP.

Common default sections:

- `meta`: title and description.
- `hero`: first page section and CTAs.
- `text`: Markdown rich text.
- `snippet`: copyable commands and prompts.
- `code`: syntax-highlighted examples with filenames.
- `imageText` and `image`: media sections.
- `header`, `footer`, and `llmsTxt`: shared globals.

When adding a section, keep four files in sync: field definition, default data, public HTML rendering, and Markdown rendering. A good section is narrow and reusable, not an entire page hidden in one field.

## Section example

This example adds a small FAQ section. Notice that the field definition, public HTML, and Markdown output are all predictable for an AI tool to follow.

### headcode/sections.ts

```typescript
import { z } from 'zod'
import { TextField } from './fields'

export const faq = {
  name: 'faq',
  label: 'FAQ',
  icon: 'circle-help',
  fields: [
    {
      name: 'items',
      label: 'Questions',
      type: 'array',
      schema: z.array(
        z.object({
          question: z.string().min(1),
          answer: z.string().min(1),
        }),
      ),
      fields: [
        TextField({
          name: 'question',
          label: 'Question',
        }),
        TextField({
          name: 'answer',
          label: 'Answer',
        }),
      ],
    },
  ],
} as const
```

### app/(site)/_sections/faq.tsx

```tsx
import type { SectionData } from '@/headcode/types'

type FaqData = SectionData<'faq'>

export const FaqSection = ({ data }: { data: FaqData }) => (
  <section className="mx-auto w-full max-w-3xl px-6 py-16">
    {data.items.map((item) => (
      <article key={item.question} className="border-t py-6">
        <h2 className="text-xl font-semibold">{item.question}</h2>
        <p className="mt-3 text-muted-foreground">{item.answer}</p>
      </article>
    ))}
  </section>
)

export const renderFaqMarkdown = (data: FaqData) =>
  data.items
    .map((item) => '## ' + item.question + '\n\n' + item.answer)
    .join('\n\n')
```

After adding the renderer, register it in the site section map. If the section should be editable by agents, make sure its fields validate cleanly and have obvious labels.

## Fields

Fields are the editable values inside a section. Headcode field helpers live in `headcode/fields.ts` and pair editor metadata with Zod validation.

Use existing field helpers first: text, textarea, rich text, image, link, icon, boolean, select, and array fields.

Array fields are important for agent-friendly content. They keep lists, cards, pricing rows, FAQ items, and navigation items structured instead of burying them in one rich text blob.

Only add a new field type when the existing helpers cannot represent the data. If you add one, also add the admin field component and validation path.

## Field examples

Use existing field helpers when possible. If a new field type is needed, also add its admin field component and keep the data shape easy for agents to produce.

### headcode/sections.ts

```typescript
import { LinkField, RichtextField, TextField } from './fields'

export const callout = {
  name: 'callout',
  label: 'Callout',
  icon: 'message-square',
  fields: [
    TextField({
      name: 'eyebrow',
      label: 'Eyebrow',
    }),
    RichtextField({
      name: 'content',
      label: 'Content',
    }),
    LinkField({
      name: 'action',
      label: 'Action',
    }),
  ],
} as const
```

### image-field-data.json

```json
{
  "image": {
    "imageId": "jn7..."
  },
  "alt": "Screenshot of the Headcode admin UI",
  "caption": "Images are stored in Convex and referenced from section data."
}
```

Prefer explicit field names over clever abstractions. Good labels help humans in the admin UI and help AI tools generate valid section JSON.

## Admin UI

The admin UI lives in `app/(admin)/`. It is the human editing surface for entries, sections, images, drafts, and publishing.

Editors can sign in, browse globals and collections, add or reorder sections, edit fields, upload images, manage draft/live versions, and publish intentionally.

The admin UI should stay simple. It is not the only editing surface; MCP clients can use the same content model through tools. Keep admin behavior aligned with services and validation so both humans and agents edit the same data safely.

## Frontend website building

The public website lives in `app/(site)/` and uses the Next.js App Router. The site should feel like a real custom website, not like a generic CMS preview.

Server Components fetch CMS content through Convex service queries. Keep fetching simple and let Convex function caching do the first layer of work before adding custom Next.js caching.

Project-specific design belongs in the site layer: section renderers, layout, CSS, and visual composition. The CMS config should describe editable data; the frontend decides how that data becomes a website.

## Frontend section rendering

HTML and Markdown renderers should stay close together. Markdown should be rendered from validated section data, not scraped from rendered HTML.

### app/(site)/_sections/index.tsx

```tsx
import { TextSection, renderTextMarkdown } from './text'
import { FaqSection, renderFaqMarkdown } from './faq'

const sectionComponents = {
  text: TextSection,
  faq: FaqSection,
}

const sectionMarkdownRenderers = {
  text: renderTextMarkdown,
  faq: renderFaqMarkdown,
}
```

### app/(site)/docs/page.tsx

```tsx
import { fetchHeadcodeEntry } from '../_lib/content'
import { RenderSections } from '../_sections'

export default async function DocsPage() {
  const entry = await fetchHeadcodeEntry({
    collection: 'docs',
    slug: 'docs',
  })

  return <RenderSections sections={entry.sections} />
}
```

If an AI tool adds a section, ask it to update both the visual renderer and the Markdown renderer. This is one of the most important Headcode conventions.

## Markdown output and llms.txt

Headcode assumes every important public page should be understandable as Markdown.

The designed HTML page is for humans. The Markdown page is for AI tools, search-adjacent workflows, documentation ingestion, and quick source-of-truth reading.

The Markdown route should use the same dispatcher as `?md` and `Accept: text/markdown`. It should call page-local `md.ts` renderers, and those renderers should call section Markdown functions.

`/llms.txt` is a CMS-backed global. It should explain what the site is, link to important Markdown pages, and describe how agents should use the site. Keep it concise, accurate, and updated when important routes or docs change.

Do not generate Markdown by scraping HTML. Use validated section data directly so the output stays stable, compact, and predictable.

## Convex services and validation

Convex is the backend for Headcode CMS. The public site, admin UI, and MCP route all go through `convex/services.ts`.

This boundary is important. Services handle:

- reading globals and collection entries,
- ensuring default globals exist,
- creating and renaming entries,
- adding, updating, duplicating, deleting, and reordering sections,
- image registration and metadata updates,
- draft/live publishing operations,
- version-aware content fetching,
- authorization checks,
- and parsed, validated section data.

Shared validators live in `convex/schema_validators.ts`. Section JSON validation lives in `convex/section_validations.ts`. Do not duplicate schema shapes across services, database helpers, and MCP tools.

Section data is stored as JSON strings in Convex. Services return parsed and validated data. This keeps storage flexible while still giving the frontend and agents a safe typed shape.

## MCP

Headcode includes an MCP server so authorized AI clients can inspect and edit CMS content directly.

The route lives at `app/(mcp)/[transport]/route.ts`. Access is controlled by bearer tokens in `ALLOWED_MCP_TOKENS`, and normal MCP edits should target draft content. Publishing must stay a separate explicit action.

Useful MCP operations include reading versions, listing entries, reading sections, updating section fields, adding entries, reordering sections, uploading images, and publishing only when the user asks for it.

For local development, use different hostnames for draft and live clients when version routing is host-based. Do not fake draft/live by adding custom service arguments.

## MCP prompts

Use prompts that separate inspection, editing, image upload, and publishing. This reduces accidental live changes.

### Inspect

```bash
Use the Headcode MCP server. Call headcode_get_version, list entries, then read the docs entry. Summarize the current content model and do not make changes.
```

### Edit draft

```bash
Use the Headcode MCP server to update the draft docs page. Add one text section and one code section. Keep the existing section order unless the requested change requires moving sections. Do not publish.
```

### Image

```bash
Upload this image with headcode_upload_image_from_url, then update the target image section with the returned imageId. Add useful alt text and keep draft content unpublished.
```

### Publish

```bash
Publish the current draft to live with headcode_publish. Before publishing, show me the entries and sections that will change and ask for confirmation.
```

For local binary image uploads, create an upload URL, POST the file, then register the uploaded image. For remote images, prefer `headcode_upload_image_from_url` because it validates and creates metadata server-side.

## MCP client configuration

Exact client config differs between tools, but the important parts are the endpoint URL and bearer token.

### mcp.json

```json
{
  "mcpServers": {
    "headcode-draft": {
      "url": "https://draft.example.com/mcp",
      "headers": {
        "Authorization": "Bearer hc_dev_token"
      }
    },
    "headcode-live": {
      "url": "https://example.com/mcp",
      "headers": {
        "Authorization": "Bearer hc_live_token"
      }
    }
  }
}
```

### .env.local

```dotenv
ALLOWED_MCP_TOKENS=hc_dev_token
NEXT_PUBLIC_HEADCODE_VERSION=auto
NEXT_PUBLIC_HEADCODE_DRAFT_HOSTS=draft.example.com,headcode.localhost
```

Live and draft MCP clients should usually point to different hosts. Version routing follows the request host; do not invent separate service arguments for live versus draft.

## Authentication

Admin authentication uses Convex Auth with Resend magic links by default.

Core production variables:

- `JWT_PRIVATE_KEY` and `JWKS` from Convex Auth setup.
- `SITE_URL` for the trusted auth URL.
- `AUTH_RESEND_KEY` and optional `AUTH_RESEND_FROM` for magic links.
- `ALLOWED_ADMIN_EMAILS` for the admin allow-list.

Set these in Convex environment variables. Do not put server secrets into `NEXT_PUBLIC_*` values. Use development test login only locally, never in production.

## Draft, live, and publishing

Headcode has draft and live versions. Editors and agents normally change draft content. Publishing promotes the current draft to live and creates a new draft from it.

This gives small teams a clear workflow: agents can prepare changes, humans can review them, and publishing remains intentional.

Version selection can be configured with `NEXT_PUBLIC_HEADCODE_VERSION=live|draft|auto`. With `auto`, hosts listed in `NEXT_PUBLIC_HEADCODE_DRAFT_HOSTS` read draft content; other hosts read live content.

MCP clients should follow the same host routing. A draft MCP client should point at the draft host, and a live client should point at the live host.

## Environment example

Keep browser-safe values public and secrets server-side. Convex environment variables are set with `pnpm convex env set`, not by adding them to a client bundle.

### .env.local

```dotenv
NEXT_PUBLIC_SITE_URL=http://localhost:3000
NEXT_PUBLIC_HEADCODE_VERSION=auto
NEXT_PUBLIC_HEADCODE_DRAFT_HOSTS=headcode.localhost
ALLOWED_MCP_TOKENS=hc_dev_token
```

### Convex environment

```bash
pnpm convex env set SITE_URL "http://localhost:3000"
pnpm convex env set ALLOWED_ADMIN_EMAILS "you@example.com"
pnpm convex env set AUTH_RESEND_KEY "re_..."
pnpm convex env set AUTH_RESEND_FROM "Headcode CMS <login@example.com>"
pnpm convex env set ALLOWED_MCP_TOKENS "hc_dev_token"
```

Use different tokens for local, draft, and production contexts. Rotate tokens if they are shared with the wrong client or appear in logs.

## Images

Images are stored in Convex Storage and referenced from section data with an `imageId`.

The image table stores metadata such as filename, content type, dimensions, storage ID, alt text, and blur placeholder data. Services hydrate image fields so public renderers can use the image safely.

For admin users, the image manager handles upload and metadata editing. For MCP clients, prefer `headcode_upload_image_from_url` when the source is a URL. It validates the image, generates metadata and blur data server-side, stores the asset in Convex, and returns `{ imageId }`.

If an MCP client must upload a local binary, the flow is:

1. call `headcode_create_image_upload_url`,
2. upload the file with HTTP `POST` to the returned URL,
3. call `headcode_register_uploaded_image` with the returned `storageId`,
4. update the target section with the new `imageId`.

Always include useful alt text. It improves accessibility, search context, and agent-readable page summaries.

## Developer workflow

For most Headcode development tasks, use this loop:

1. Read `AGENTS.md` and `ARCHITECTURE.md` when the change crosses app areas.
2. Inspect `headcode/config.ts` and `headcode/sections.ts` before changing content shapes.
3. Update defaults when the starter site should change.
4. Render both HTML and Markdown for new public sections.
5. Keep backend access behind `convex/services.ts`.
6. Verify validation, public rendering, Markdown output, admin editing, and MCP behavior.

A good Headcode change is easy for a developer to review and easy for an agent to explain.

## Developer task prompts

These prompts are useful when working with a coding agent on an existing Headcode project.

### Add section

```bash
Add a testimonials section to this Headcode CMS project. Use existing field helpers, add the section to the correct collections in headcode/config.ts, create default content if useful, render HTML and Markdown in app/(site)/_sections, and verify section validation.
```

### Customize site

```bash
Customize the public Headcode site for this brand. Keep the content model intact, use existing section data, improve the site renderers and CSS only where needed, and keep Markdown output stable for AI tools.
```

### MCP edit

```bash
Use the Headcode MCP server to update the draft homepage copy. First inspect the current entry and section types. Then update only the requested sections. Do not publish.
```

### Debug auth

```bash
Debug Headcode admin authentication. Check Convex Auth configuration, SITE_URL, Resend variables, ALLOWED_ADMIN_EMAILS, proxy protection, and friendly handling for disallowed email addresses. Do not expose secrets in the response.
```

Agents work best when the prompt states the boundary, the files to inspect, the desired output, and whether publishing is allowed.

## What to remember

Headcode CMS is small on purpose. The important mental model is:

- config says what exists,
- sections define editable structure,
- Convex stores and validates the content,
- the public site renders HTML and Markdown,
- the admin UI edits content for humans,
- MCP edits content for agents,
- publishing is explicit.

Start with [`start.md`](https://headcodecms.com/start.md) for installation. Start with `headcode/config.ts` and `headcode/sections.ts` for customization.
