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.
🜂
ledric is a self-hosted content engine designed from the ground up for the agent era. The same content store powers three distinct surfaces:
describe_model so an LLM can learn the entire content model in a single call.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.
🜁
A typical project has three moving parts: a content type, a daemon serving it, and a consumer that reads from it.
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" } },
});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.
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.
🜃
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.
draft → publish primitives with parent_version optimistic concurrency. Dry runs on schema migrations. Slug-history redirects so renames never break links — by design, not afterthought.
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.
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.
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.
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.
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.
?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.
🜏
# 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.
🜄
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:
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.
🜂
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 you need is Node 22 and a directory to drop a ledric.db into.