HEADCODE CMS
Docs

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

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.

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

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` 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.

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

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.

The source code lives in headcodecms/headcodecms. For installation work, start with the install contract first:

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.

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

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.

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

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.

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

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.

import { TextSection, renderTextMarkdown } from './text'import { FaqSection, renderFaqMarkdown } from './faq'const sectionComponents = {  text: TextSection,  faq: FaqSection,}const sectionMarkdownRenderers = {  text: renderTextMarkdown,  faq: renderFaqMarkdown,}

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.

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.

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.

{  "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"      }    }  }}

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.

NEXT_PUBLIC_SITE_URL=http://localhost:3000NEXT_PUBLIC_HEADCODE_VERSION=autoNEXT_PUBLIC_HEADCODE_DRAFT_HOSTS=headcode.localhostALLOWED_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 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.

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` for installation. Start with headcode/config.ts and headcode/sections.ts for customization.

Headcode CMS Docs - Headcode CMS Docs