A website is pretty much a human right. You almost don't exist if you're not on the web in some form.
Not a controversial opinion. If your business doesn't show up online in 2026, it barely exists. But after 20+ years building web projects, it stopped being the building that bothered me. It's the maintaining.
The CMS problem
WordPress powers a third of the web. I don't know if that's impressive or depressing anymore.
It's not that WordPress is bad. When it launched, it genuinely solved something. But it was built for a time when "content" meant blog posts and "dynamic" meant a PHP page hitting MySQL. A dashboard made sense then, because there was no other way for a non-developer to touch a page.
Today most content changes are nothing. Fix a menu label. Update a price. Add a post. For that, you don't need a database, a plugin directory, or an update cycle that quietly breaks your theme. You need to find the content, change it, and leave.
The block idea
I'd been sitting on a different approach for years. What if a CMS just parsed the site into named, reusable blocks of markup? Define a block once, reference it wherever it's needed.
A header. A footer. A pricing table. Any piece of the site can be a block, and a block can hold anything: plain HTML, a component, a dynamic snippet. Change a global block and it updates everywhere it appears. Leave a block local to a single page and it stays there.
What you get is consistency without the loss of control. No wrestling with a WYSIWYG editor that wraps your HTML in extra divs.
There's no database. No dependencies beyond a standard PHP install. You deploy by copying files to a server.
A block looks like this:
<!-- CMS:BLOCK name=hero start -->
<section class="hero">
<h1>Your heading here</h1>
<p>Supporting text that can be anything.</p>
</section>
<!-- CMS:BLOCK name=hero end -->
Every block lives in the page file. Global or page-specific is controlled by one attribute: custom=1. Without it, a block is global — change it anywhere and the CMS syncs that update to every page with a matching block name. Add custom=1 and the block stays local; global updates skip it.
<!-- CMS:BLOCK name=hero custom=1 start -->
<section class="hero hero--about">
<h1>This heading only appears on this page</h1>
</section>
<!-- CMS:BLOCK name=hero end -->
No separate template directory, no include system to maintain. The CMS reads through all page files to know what blocks exist and which ones have local overrides.
Where AI came in
The block structure was right. What was slow was building templates. Each new page meant writing structure from scratch, wiring blocks, keeping things consistent. The overhead didn't disappear, it just moved.
Then I looked at MCP — Model Context Protocol — which gives an LLM structured access to external tools: search, read, create, update. When I saw what MCP needed and what the block architecture already provided, it was obvious.
Named, discrete, searchable, editable blocks are exactly what an AI can navigate. It can loop through every block on the site, find the one it needs by name or content, and change it in a single call. You don't have to guess at the page structure or figure out where the navigation lives: the CMS gives it the map.
How it works
The CMS generates a connection string: an MCP endpoint and tool manifest. You paste it into Claude, ChatGPT, Gemini, or anything that supports MCP tools. From that point the AI has controlled, auditable access to your site.
You don't open an admin panel or track down a template. You just describe what you want:
"Update the pricing section on the homepage — starter plan is now €49/month."
The LLM connects, reads the site's block structure, finds the right section, checks the current content, and makes the change. You see what changed and confirm it.
Global blocks work the same way. Tell the AI to update the navigation and every page using that nav block gets updated. You don't have to know which pages share a header. That's the CMS's job.
What you can tell it to do
- Update content anywhere on the site, even if you can't remember which page it's on
- Change the nav and have it propagate everywhere on its own
- Add a blog post that matches the format of existing ones
- Replace a missing image with something from an open library, or generate one with AI
- Find every instance of a phrase across all pages and change them at once
There are two interfaces. The classic GUI: open any page directly and edit it like a normal page builder. And the AIGUI — the AI interface — where you describe what you want and the LLM finds and changes it for you. Nothing forces you to use one over the other, but you don't have to go looking for things anymore.
Blog and collections
Any repeating content type works the same way: blog posts, news, case studies, job listings, team profiles, product FAQs.
You define a collection type and a template block for how entries should look. To add something new, describe it. The LLM writes the content to match what's already there, creates the block, and adds it to the index.
Posts are JSON files in /cms/content/blog/. Publishing one generates the page at the expected URL. If you want to add a new post through an LLM, it calls the create_post tool with the fields you give it:
{
"slug": "new-product-launch",
"title": "We launched something new",
"content": "<p>Post HTML here</p>",
"excerpt": "Short summary for listings",
"status": "draft",
"author_id": "dev-team",
"categories": ["news"],
"seo": {
"title": "...",
"description": "..."
}
}
Scheduling, editing, and unpublishing all work the same way. How posts look is controlled by the blog template page — it uses the same block system as everything else, nothing special about it.
Setup
Copy the files to your server and run /cms/install.php. It generates the MCP token, writes the config file, creates an admin account. No database, no composer — just PHP 8.3 and you're done.
Content lives as flat files: PHP files for pages (one directory per page), JSON for posts. You can read them, back them up, or version control them without any tooling. Before anything gets published, the CMS saves a snapshot automatically — you can restore from the admin panel or through an MCP call.
If you want to see it in action or set it up for your site, get in touch.