# For developers

> Content and design as files in git, plugins in plain PHP, and one ability system behind the admin, the CLI, the REST API and MCP. MIT licensed.

## Plain files. Plain PHP.

A CMS you can read to the bottom.

[Get started](https://lettern.org/download)

[Read the docs](https://docs.lettern.org)

See it on GitHub

### CLI

Terminal

```plain
$ php cms run entries/create collection=pages title=About
$ php cms run entries/publish id=01m7c2q4
$ php cms build --strict
$ php cms links:check
$ php cms plugin enable letterncms/forms
$ php cms token:create you@studio.com --scope='entries/*'
```

### REST

REST

```plain
curl https://your-site.com/api/v1/abilities/entries/list \
  -H "Authorization: Bearer cms_…" -G -d collection=posts

curl https://your-site.com/api/v1/batch \
  -H "Authorization: Bearer cms_…" \
  -d '{"calls": [{"ability": "entries/publish", "input": {"id": "01m7c2q4"}}]}'

curl https://your-site.com/api/v1/content/posts?sort=-date&limit=10
```

### MCP

MCP

```plain
claude mcp add --transport http cms https://your-site.com/mcp

# Signs in with OAuth, acts as you, with your permissions.
# Tools:     entries.create, entries.publish, design.update, …
# Resources: cms://site/design, cms://collections, cms://blocks
# Prompts:   Build a page, Write a blog post, Audit the site
```

## Your site is a folder.

Commit it, branch it, review it like code.

Project structure

### pages/home.md

content/collections/pages/home.md

```plain
---
title: Home
status: published
---

::: box {"id":"hero","props":{"tag":"section"}}
:::: heading {"id":"word","props":{"text":"Ondine","level":"1"}}
::::

:::: paragraph {"id":"line","props":{"text":"Fish from the morning boat."}}
::::

:::: button {"id":"book","props":{"label":"Book a table","url":"/book"}}
::::
:::
```

### dishes.yaml

content/collections/dishes.yaml

```yaml
title: Dishes
singular: Dish
icon: fork-knife
driver: files
sort: position
fields:
  - { handle: price, type: text, label: Price }
  - { handle: course, type: select, label: Course }
  - { handle: description, type: textarea, label: Description }
  - { handle: photo, type: media, label: Photo, max: 1 }
  - { handle: available, type: toggle, label: On the menu }
```

### tokens.json

content/design/tokens.json

```json
{
  "tokens": {
    "color": {
      "background": { "$value": "#d9432a" },
      "surface": { "$value": "#fbf1e3" },
      "foreground": { "$value": "#fbf1e3" },
      "accent": { "$value": "#ecb54a" }
    },
    "font": {
      "display": { "$value": ["Instrument Serif", "serif"] }
    }
  }
}
```

### roles.yaml

config/roles.yaml

```yaml
roles:
  client:
    label: Restaurant team
    description: Changes dishes, prices and photos.
    permissions:
      - admin.access
      - entries.dishes.view
      - entries.dishes.edit
      - entries.dishes.publish
      - media.view
      - media.upload
```

## Write an action once. It’s everywhere.

Validated, permission-checked and logged.

Abilities

ShopPlugin.php

```php
$this->cms()->abilities()->register('shop/price-update', [
    'label' => 'Update a price',
    'description' => 'Set the price of a product, in cents.',
    'permission' => 'shop.prices',
    'input' => [
        'type' => 'object',
        'properties' => [
            'id' => ['type' => 'string'],
            'cents' => ['type' => 'integer', 'minimum' => 0],
        ],
        'required' => ['id', 'cents'],
    ],
    'annotations' => ['idempotent' => true],
    'handler' => fn (array $input) =>
        Product::setPrice($input['id'], $input['cents']),
]);
```

In the admin

@letterncms/sdk

run('shop/price-update', { id, cents })

Over HTTP

REST

POST /api/v1/abilities/shop/price-update

In the terminal

CLI

php cms run shop/price-update id=p1 cents=1200

In your AI tool

MCP

shop.price-update

## Nothing exotic underneath.

If you know these, you know it.

PHP 8.4

Any host

Laravel

The kernel

React

The admin

MCP

First-party plugin

## Start reading.

It’s all documented, down to the hooks.

### Your first plugin

A setting, an ability, a panel and a widget, in one folder.

### Hooks

Every action and filter, listed.

### Block Markdown

Pages as plain text, for files and agents.

### REST API

Every ability over HTTP, and content without a token.

### Admin extensions

Add screens and panels in React.

### Deployment

From shared hosting to git and environments.

## MIT licensed. Read all of it.

[See it on GitHub](https://github.com/letterncms)

[Read the docs](https://docs.lettern.org)

---
Source: https://lettern.org/for/developers · Updated 2026-10-30
