Why I built an MCP server for my website content
I wanted to publish on my own site without logging into anything. So I built an MCP server for website content, and now I write, schedule and publish posts by asking Claude. This post went live that way.
It's less magic than it sounds. Here's what the server does, why I set it up the way I did, and how you'd build one for your own site.
What is an MCP server, in plain English?
An MCP server is a small program that gives an AI assistant a set of tools it can use outside the chat. MCP is short for Model Context Protocol, an open standard that Anthropic started and that other AI apps now support too.
Mine offers nine tools, things like "list my articles" and "create an article". Claude reads the list, picks a tool and fills in the inputs. My code decides whether the request goes through.
That last part is why I built it this way. Claude never touches the database. It can only ask.
Why not just give Claude access to the CMS?
Because I'd be checking its work forever.
I can tell Claude that titles stay under 70 characters and that only one post goes out a day. It listens, mostly. But "mostly" means I'm the one catching the misses. When the server refuses a 71-character title, I don't have to.
When I rebuilt this site on Firestore and Cloud Run, the articles moved somewhere the browser can't read or write directly. The MCP server is the only way in for Claude, so its rules are the rules. The case study for this site has the full stack.
What tools does a content MCP server need?
Fewer than I expected. Here's what mine has:
get_writing_guidelinesgives Claude my house style before it writes a wordlist_articlesshows what's live, scheduled or still a draftnext_publish_datefinds the first free daylist_topicsandadd_topicskeep a bank of ideas for latervalidate_article_bodychecks a post will render, without saving itcreate_articleandupdate_articledo the actual publishingdelete_articleexists, but I keep it at arm's length (more on that below)
Most of these only read. Two of them write, and one checks a post without saving it, so Claude tends to look around before it changes anything.
How does the server stop mistakes?
It checks every request in three places before anything is saved.
First, the limits sit in each tool's input schema. Claude sees them before it calls the tool, and the server rejects anything that breaks them. This is the slug field on mine:
{
"slug": {
"type": "string",
"maxLength": 80,
"pattern": "^[a-z0-9]+(-[a-z0-9]+)*$",
"description": "URL slug, permanent: lowercase words joined by hyphens (no dates)."
}
}
Titles get 8 to 70 characters and descriptions 50 to 160, the same way.
Second, some rules need a bit of logic. A slug can't be used twice. Two posts can't share a day. If Claude doesn't pick a date, the server takes the next free one. And once a post is out, its slug can't change, because that's its permanent address.
Third, every post runs through the same renderer the site uses before it's saved. If there's a broken table in there, I find out then, not when someone opens the page.
Why are the writing guidelines a tool and not a prompt?
Because I use Claude across a lot of different chats, and I don't want to paste my style rules into every one of them.
As a tool, they're one call away wherever I am. They cover voice, length, formatting, where the search phrase goes and how to link. When I change my mind about something, I change it once.
Who approves a post before it goes live?
I do. Claude shows me the full draft, and nothing gets published until I say yes. The first draft of this post came back sounding like a machine wrote it, so I asked for another one. That's what the person in the loop is for.
Taking a post down doesn't delete it either. It goes back to being a draft, so I can bring it back later. Deleting is a separate tool, and Claude is told to leave it alone unless I actually say "delete".
The Udaya Public School website needed more than that, because more people are involved. Its blog runs on a hosted MCP server where AI drafts the posts and a person reviews and approves them before they go out. The review is part of the server, so nobody has to remember to do it.
How do you build an MCP server for website content?
Write your rules down, put the server in front of your content, and add tools in order of risk. If I were starting again, this is the order I'd go in:
- Write your rules down first. Title length, URL format, how often you publish. If you can't write a rule down, the server can't enforce it.
- Make sure the server is the only way in. If Claude can reach the database some other way, the rules don't mean much.
- Start with tools that only read. Listing posts can't break anything, and it tells you the connection works.
- Add a "check this" tool before a "save this" tool.
- Write error messages Claude can act on. It reads them and fixes its own input, so "title is 74 characters, limit is 70" beats "invalid input".
- Make deleting hard. Hiding a post is almost always what you actually want.
- Then write a real post through it and watch which tools Claude calls. You'll learn more from that than from any test.
The code is small. Here's a stripped-down sketch using the official TypeScript SDK. It isn't my actual server, just the shape of one tool:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const server = new McpServer({ name: "site-articles", version: "1.0.0" });
server.tool(
"list_articles",
"Every article with its status: live, scheduled or draft.",
{ status: z.enum(["all", "live", "scheduled", "draft"]).default("all") },
async ({ status }) => {
const articles = await listArticles(status); // your data layer
return { content: [{ type: "text", text: JSON.stringify(articles) }] };
}
);
await server.connect(new StdioServerTransport());
Every other tool follows the same pattern: a name, a schema, and a handler that checks the rules.
Frequently asked questions
Do I need to be a developer to use one?
To build one, yes, or you need someone who is. To use one, no. Once it's connected you just ask: "write a post about this and schedule it for Friday".
Can Claude publish without my approval?
Only if you let it. The server decides what's possible. You can make every post land as a draft, add a review step, or keep publishing behind a separate tool.
Does it only work with Claude?
No. MCP is an open standard, and other assistants, ChatGPT included, can connect to MCP servers. Check your app's help pages for how to add one.
Is it safe to connect AI to my live website?
It's as safe as the tools you give it. Read tools, a check tool and publishing tools that enforce your rules are a lot safer than handing over a CMS password.
If you'd like something like this for your own site, set up around how your team actually works, that's part of what I do.