TL;DR — I built and released ghost-content-mcp: an open-source (MIT), read-only MCP server that exposes any Ghost site's content to AI assistants. Hybrid BM25 + local semantic embeddings, nine tools, configuration down to two environment variables. You can already see it working on this site: it powers Mataken, the chat widget in the corner. And if MCP is new to you: it's a standard protocol that lets an AI model interact with external systems — here the external system is a blog, but the same mechanism works for databases, APIs, file systems, home automation. Web content is just my use case; the range is enormous. Below: what it is, what I built, and why I believe in it — with reservations.

For about thirty years the web had one front door: the browser. You typed a URL, a page rendered, a human read it. Everything we built — layout, SEO, that tasteful drop shadow — assumed a pair of eyes on the other side.

There's a second front door now. More and more, people don't open ten tabs; they ask an assistant. "What's a good zero-knowledge backup setup?" "Does that site have anything on Docker forensics?" If your content can't be read by the assistant, it's invisible to everyone who arrives that way — no matter how nice your drop shadow is.

This is the story of the second front door I built for MojaLab. It's also an honest argument about why it matters, and where I'd push back on my own enthusiasm — because I promised myself this wouldn't turn into a brochure.

First: what is an MCP server?

MCP stands for Model Context Protocol. The official analogy — the USB-C port for AI — is fine, but you've read it ten times already. As a Roman by adoption, let me tell you an older one, by about two thousand years.

Walk on a Roman road — in Pompeii, in Ostia — and look down: the paving stones carry parallel ruts, worn by centuries of wheels. They're the same distance apart everywhere, about 1.4 metres, because Roman carts shared the same gauge. It wasn't a law: it was a standard that emerged from use, and the empire built its killer feature on top of it. Even Pompeii's pedestrian crossings — those big raised blocks that kept you out of the mud — have the gaps between the stones calibrated to the carts' wheelbase. If your cart was to standard, you passed. The result: eighty thousand kilometres of paved roads on which goods, news and people moved from one end of the known world to the other. "All roads lead to Rome" isn't poetry — it's the network effect of an open standard.

Now fast-forward two thousand years, onto rails. In the nineteenth century the governments and empires building the first railways each made, on their own, the opposite choice: different gauges — sometimes for engineering reasons, sometimes on frankly military grounds (on an incompatible network, a foreign army's trains don't run). The Tsarist empire adopted broad gauge, Spain the Iberian one, the Australian colonies three different gauges simply because they hadn't talked to each other. The bill came due, and it came due for posterity: to this day, at Europe's eastern border they swap the bogies under the carriages, Spain spent a century and a half stitching itself back to the continental network, Australia spends billions on it. None of these choices says anything about the peoples who inherited them — it says everything about the logic of closed-network thinking: incompatibility feels like a defence, and becomes a perpetual tax on whoever comes next.

MCP is the attempt to make the Roman choice in the world of AI agents: to tell everyone, in public, how to build a cart that can travel any road (whether this particular cart — MCP — or a cousin ends up winning is another question; I'll come back to it). A client (the assistant) connects to a server (your tools and data). The server exposes tools: named functions with typed inputs — search_all(query), get_post(slug) — that the model can choose to call. On its own, a language model is a brain in a jar — smart, well-read, and cut off from your stuff. The protocol is the shared gauge: the model doesn't need to know how your site is wired, it drops its wheels into the ruts and asks.

And note that "site" is just this article's case. The external system on the other end of the cable can be anything: a database to query, a file system, a ticketing system, your home automation, a company API. MCP knows nothing about blogs — it only defines how a model discovers and calls tools. What the tool actually does is your business, and that's why the range of possibilities is so wide. Here I use it for the thing I cared about: making content legible.

Concretely: without an MCP server, ask an assistant "what does MojaLab say about zero-knowledge backups?" and it will either shrug or, worse, confidently invent an article that doesn't exist. With a server in front of the site, it calls search_all("zero knowledge backup"), gets the actual articles already ranked by relevance, and answers from them — with links you can click.

(A note for the pedants, who are welcome on this blog: the legend that standard railway gauge descends directly from Roman ruts is, well, a legend. Stephenson took the gauge from the Tyneside mining tramways. But the resemblance is no accident: both derive from the same physical constraint — the width of a cart drawn by two horses side by side. Some standards aren't decreed: they're rediscovered. Keep that in mind when we talk about protocols.)

The use case: Mataken (yes, the cat in the corner)

If you're reading this on the site, there's a little chat widget down in the corner — Mataken. Go ahead, ask it something about MojaLab. I'll wait.

Under the hood, Mataken talks to an MCP server that has indexed every published post and page. When you ask a question, the server searches the content, hands the best matches to the model as context, and the model answers grounded in what's actually here — not in whatever it half-remembers about a site with a similar name.

Here are the two doors, drawn:

flowchart TB
    L1["👤 Reader in a browser"]
    L2["👤 Reader who asks<br/>an assistant"]

    subgraph SECOND["Door 2 — agentic"]
        W["Mataken widget 🐱"]
        BE["Mataken backend<br/><i>(orchestration)</i>"]
        LLM["Cloud model<br/><i>(generates the answers)</i>"]
        MCP["<b>ghost-content-mcp</b><br/>BM25 + embeddings index<br/><i>(local inference in the container)</i>"]
    end

    GHOST["Ghost site — MojaLab"]

    L1 -->|"Door 1 — HTML"| GHOST
    L2 --> W
    W <-->|"question / answer<br/>(WebSocket, streaming)"| BE
    BE <-->|"prompt + context"| LLM
    BE <-->|"MCP tool calls<br/>search_all, get_post, …"| MCP
    MCP -.->|"Content API (read-only)<br/>refresh: ~10 min TTL<br/>or /refresh webhook"| GHOST

The dashed arrow is indexing — asynchronous, on its own, every ten minutes or on a webhook. The solid arrows are the question-answer loop: the widget talks only to the backend, which orchestrates the cloud model and the tool calls to the MCP. Note where inference sits: the embeddings inside the container, the generation on the cloud model — as promised a few lines up.

That's the specific case. The general one is bigger, and it's the interesting part: you can make any site legible to an agent. Let it search, summarize, relate, and cite your content accurately instead of hallucinating around it. It's retrieval-augmented generation (RAG), but pointed at your own site, over a clean protocol anyone's assistant can speak.

What I actually built

A read-only content-discovery MCP server for Ghost. Read-only and public by design: its whole job is to answer "what does this site say about X?" well — not to manage the blog, create posts, or touch anything. (There are already several fine Ghost MCP servers for the management side; this one deliberately does the opposite, smaller, safer thing.)

What's in the box:

  • Hybrid search — classic keyword ranking (BM25) fused with local semantic embeddings, so a natural-language question matches the right article even when it shares no exact words with it. The embedding model runs inside the container: embedding inference is fully local, no external calls for indexing. (Mataken's chat, on the other hand, uses a cloud model to generate its answers — to be precise: your questions to the widget do leave the box; the indexed content doesn't.)
  • Nine tools — search everything, search posts, "more like this," a one-call site summary, read a full post or page as Markdown, list tags, and so on.
  • Zero-config identity — it reads the site's name and description from Ghost's own settings, so it works on any Ghost instance with two environment variables.
  • A self-updating index — an in-memory index with a ~10-minute TTL refresh: you publish in Ghost and the content shows up on its own. For instant updates there's an internal POST /refresh endpoint to wire to a Ghost webhook.

An honesty box, because you'd notice anyway: this was built by an agent. Claude, pair-programming with me over a long stretch of evenings. Some people will close the tab right there, and I get it. I'd rather you keep going. It ships with 43 tests. It survived a genuine production incident — and since we're among technical people, here it is in detail.

The "something uncomfortable": building the embeddings index means loading the ONNX model and processing batches of documents at the same time. On one of the cheap 4 GB VPSes I usually run, with no swap (mea culpa!), the RAM spike during the rebuild killed the container — and since the index rebuilds every ten minutes, it wasn't a one-off crash but a standing appointment, which is the nastiest kind of bug. The scars are in the repo: a configurable EMBEDDINGS_BATCH_SIZE (smaller batches, lower RAM peak), an explicit memory limit in the docker-compose, and a healthcheck that at least makes failure visible instead of silent. "Written by an AI" is a fact about process, not a verdict on whether the thing works. Judge the artifact.

It's open source — ghost-content-mcp on GitHub, MIT licensed. Point it at your own Ghost with two env vars and you've got the second front door too.

The argument — and my reservations

Here's the claim I actually want to put on the table: making content legible to agents is becoming as important as making it legible to humans in a browser.

The case for it is real. It's an accessibility argument at heart — the same content, delivered in the modality your audience actually uses, and that modality is shifting. HTML is a lousy channel for a machine: navigation chrome, cookie banners, ads, JavaScript-rendered text, and no signal about what matters. Giving an agent clean, ranked, intent-shaped access is the same instinct that gave us sitemaps and RSS, carried a step further.

But I said no brochure, so here's where I push back on my own thesis:

"As important" is too strong — for now. For almost every site on earth, humans in browsers are still nearly all of the value. Agent traffic is real but early. The honest version of the claim isn't "equal today"; it's "this is where access is heading, and the cost of being early is low." It's an investment, not yet a parity.

The disintermediation trap is real. If the agent answers perfectly from your server, the reader never arrives. No brand, no serendipity, no newsletter signup, no community — just your content, consumed somewhere else, sometimes misattributed. We've seen this film before: RSS, AMP, Google's answer boxes all traded reach for someone else's convenience. An MCP that hands over the full text is the extreme cut of it. So the design decision that matters isn't "give the agent everything" — it's "give it enough to answer, and a reason and a link to come back." Attribution and links-back aren't a nicety here; they're the entire economic argument.

Then why does my server hand over the full text via get_post anyway? Because for MojaLab the trap doesn't spring: I don't monetize here. I experiment, I build things I want to share, and if they're useful to someone I'm happy — I make my money elsewhere. For me the channel, human or agentic, barely matters. And let's be honest: even if I did care, trying to impose terms on an agent today would be wishful thinking. But this is a choice, made possible by the fact that my business model is "none." If your site lives on subscriptions, ads, or leads, the same design decision has a price — and you should make it with your eyes open. That's why the server supports #noindex: tag a post and it drops out of search while staying reachable by direct slug. The choice is yours, and above all it's configurable.

And there's protocol risk. MCP is one protocol, not the web standard for this. Betting your whole agent strategy on it is betting on a single horse. The durable version of the idea is protocol-agnostic: keep your content structured and machine-legible. Like the cart gauge and the railway gauge, the right standards aren't decreed — they emerge from real constraints, and sometimes they're rediscovered. MCP is the best incarnation of that today, not the last word.

Where I land: I believe the direction, not the hype. The web is growing a second consumption layer, and content that's only browser-legible is going quietly dark to it. For an audience like this one — technical, curious, the kind of people who already ask an assistant before they open a tab — building the second door early is worth it, with eyes open about disintermediation and lock-in. The healthiest framing isn't humans versus agents. It's this: your content should be first-class through whatever door someone uses to reach it — and increasingly, one of those doors is an agent.

Try it

Ask Mataken something on this page and watch it cite real articles. If you run Ghost, clone ghost-content-mcp, give it your site URL and a content key (a read-only key Ghost generates in two clicks), and open your own second front door. And if you think I've got the argument wrong — that "as important" is either too strong or not strong enough — that's exactly the conversation I want to have. The comments below, after all, are a door too.