LEDRIC

A CMS that speaks AI.

A self-hosted content engine that speaks both HTTP and MCP. Humans edit in a GUI, agents drive the same admin API, your site fetches over HTTP — all against one versioned store. Starts as one Node process and one SQLite file.

QUICKSTART VIEW ON GITHUB

🜂

WHAT IS LEDRIC?

the elevator

ledric is a self-hosted content engine designed from the ground up for the agent era. The same content store powers three distinct surfaces:

  • Humans edit through a SvelteKit admin GUI, or via a click-to-edit overlay dropped on the live site itself.
  • Agents edit through MCP — twenty structured tools that mirror the admin API, with describe_model so an LLM can learn the entire content model in a single call.
  • Sites read through a thin HTTP API or typed SDKs (TypeScript and PHP).

Everything goes through the same validation, the same versioning, the same slug-history. There is no separate "agent track" with looser rules — Claude and your marketing lead are both calling the same draft underneath.

For the longer story, read the Concepts page for the mental model, or Why ledric for honest comparisons against Contentful, Sanity, Payload, Strapi, and Directus.

🜁

HOW IT WORKS

three moving parts

A typical project has three moving parts: a content type, a daemon serving it, and a consumer that reads from it.

1. Describe a content type

Either ask Claude ("add a blog_post type with title, slug, body, cover image, and tags") or write it by hand. Either way, the schema lives in the same database as the content — no separate config files to keep in sync.

defineType("blog_post", {
  title: { type: "string", required: true, max: 160, searchable: true },
  slug:  { type: "slug", from: "title" },
  body:  { type: "markdown", searchable: true },
  cover: { type: "asset", kinds: ["image"] },
  tags:  { type: "array", of: { type: "string" } },
});

2. Edit content

Humans use the admin GUI, or click any element on the live site — the inline editor opens the right form in a drawer. Agents call draft and publish over MCP. Both paths write to the same store, with the same validation rules and the same parent_version optimistic concurrency. Drafts and published versions live side-by-side; renaming a slug retires the old one but reads keep resolving forever via 301.

3. Read from your site

Your site fetches over plain HTTP — the typed SDKs save boilerplate but they're optional. The wire shape is identical from TypeScript and PHP.

// TypeScript
import { LedricClient } from "@ledric/sdk";
const ledric = new LedricClient({ url: process.env.LEDRIC_URL! });
const posts = await ledric.find("blog_post", { limit: 10, published: true });
// PHP
use Ledric\LedricClient;
$ledric = new LedricClient(getenv('LEDRIC_URL'));
$posts  = $ledric->find('blog_post', ['limit' => 10, 'published' => true]);

For the full walkthrough — schema, seeded content, an Astro frontend, inline-editor wiring — see Build with an agent (TypeScript) or Build with an agent — PHP.

🜃

BUILT FOR AGENTS, HUMANS, AND OPS

Three constraints we don't break — everything else follows from them.

AGENTS

  • Tokens are the new bandwidth

    Every default tuned for minimal agent overhead. Flat envelopes, three response budgets (full / summary / list), structured errors with field paths and codes. The schema is the docs.

  • Agents edit differently

    draftpublish primitives with parent_version optimistic concurrency. Dry runs on schema migrations. Slug-history redirects so renames never break links — by design, not afterthought.

  • Remote MCP, your way

    Run with --public-mcp and connect claude.ai directly to your CMS as a custom OAuth connector. Or share one local daemon across Claude Code, Cursor, and the GUI with --http-mcp.

HUMANS

  • The schema is the API

    describe_model returns every type, every field, every validation rule, and a hand-written worked example — in one call. No separate docs to keep in sync, because the schema is the docs.

  • Inline editor, one script tag

    Drop /admin/inline.js on your rendered site, tag elements with data-ledric-ref, and a floating pencil opens the right form in a drawer. Editors don't need to know what a CMS is.

  • History, baked in

    Every entry is versioned. publish marks one version live; older versions stay readable. Renaming a slug retires the old one, but reads of the old slug 301-redirect to the new one. Forever.

OPS

  • One file by default

    SQLite out of the box — cp it, commit it, scp it. Postgres and MySQL are first-class storage adapters when you outgrow that, with the same schema and the same wire format.

  • Imgix-style transforms, no SaaS bill

    ?w=800&fit=crop&fm=webp&auto=format works on every asset URL. sharp/libvips does the work, transformed bytes are cached on disk, no Cloudinary bill. Stable id + per-version ref_key so caches stay correct forever.

🜏

TWO MINUTES TO RUNNING

Needs Node 22+. That's it.
# Interactive: walks you through DB path, port, MCP wiring, key minting.
npx -y ledric init

# Then:
npx ledric serve --gui
# → MCP stdio + HTTP API + admin GUI at http://127.0.0.1:3000/admin

init writes ledric.config.json, patches your .mcp.json so Claude Code picks up the server, mints admin + reader keys, and drops them in .env.local.

🜄

HOW I ACTUALLY USE IT

the story

After 25 years building bespoke content systems for clients — pre-headless-CMS days through the major players since — ledric is the data layer I'd actually want to use.

Most pages I build aren't "title, body, hero image." They're collections of sections: a hero with desktop and mobile backgrounds, a pricing table reused across three pages, a testimonials block, a CTA, a feature grid — sometimes with prose flowing around them, sometimes entirely composed of them.

A typical session against the MCP server looks like:

  1. "Go through this page design in Figma and prepare all required asset exports."
  2. "Import all assets into ledric, tagging them appropriately."
  3. "Build out the required sections, and roll them into a page."

Without ledric in the loop, Claude could write static HTML and call it a day. ledric exists because I want my marketing team to roll out updates without me — and I want them to wire up their own LLM tools to the same content store.

🜂

WHERE TO GO NEXT

the docs panel

New here? Start with the Concepts page for the mental model — types, entries, refs, versions, locales, environments. Then run through the Quickstart.

Building something? Watch an agent build a real project end-to-end in Build with an agent (TypeScript / Astro) or the PHP variant. For prompt templates that work in practice, see Agent recipes.

Going deeper? Architecture covers the internals — packages, storage adapters, asset pipeline, inline editor, process lifecycle. Schema is the field-types catalogue. MCP tools, HTTP API, and SDKs document the three surfaces. Inline editor covers the click-to-edit overlay. Remote MCP covers --http-mcp and --public-mcp.

Evaluating? Why ledric compares it honestly against Contentful, Sanity, Payload, Strapi, and Directus — including when ledric is the wrong choice. The FAQ covers the questions that come up most. The Roadmap draws the line between what's stable, what's in progress, what's planned, and what's explicitly out of scope.

Shipping? Deployment covers production shape — CDN in front of /assets, reverse proxy + TLS, env-supplied keys, backups, Postgres / MySQL. Auth covers reader and admin keys. Localization covers per-type locales and fallback chains. Assets covers the id / ref_key split and the transforms cache.

🜁

ALL THE DOCS

🜃

TRY IT IN TWO MINUTES

try it
NO SURCHARGE

All you need is Node 22 and a directory to drop a ledric.db into.