Skip the Builder: Hook Claude to Your Own Site With MCP and Ship Landing Pages in Minutes
Michael Sacca•The AI landing page builder pitch goes like this: leave your current site, move onto our platform, and our AI will make pages for you. It's a subscription and a migration wrapped in one demo.
But if you already have a site — a static host, a CMS, a folder of HTML that deploys on push — the builder isn't solving your problem. Your problem is that pages take you time. Writing the copy, matching the layout, committing, deploying. The bottleneck is your own hands, not your hosting.
There's a third option that almost nobody talks about: keep your site exactly where it is, and give Claude a tool that publishes to it. That's what MCP is for, and building the server is an afternoon, not a quarter.
What MCP actually gives you here
MCP — Model Context Protocol — is the standard that lets Claude call tools: read files, hit APIs, run functions. We've written before about how MCP started as a tool-use protocol and became the publishing protocol. This post is the concrete version: you write a server that exposes a single tool, something like publish_page, and suddenly Claude can take a brief and put a live URL on the internet.
No template gallery. No platform lock-in. Your page lands in your own repo, on your own domain, with your own analytics already wired up.
The anatomy of the server
An MCP server is just a program that declares tools and handles their invocations. You need three things:
- A tool definition — name, description, and input schema. This is the contract Claude reads to know what it can do.
- A handler — the code that actually writes the page somewhere.
- A publish step — whatever turns a file on disk into a live URL.
Here's the smallest useful version, using the official TypeScript SDK:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { writeFile, mkdir } from "fs/promises";
const server = new McpServer({ name: "site-publisher", version: "1.0.0" });
server.tool(
"publish_page",
"Publish an HTML landing page to the live site. Returns the public URL.",
{
slug: z.string().describe("URL slug, e.g. 'spring-promo'"),
html: z.string().describe("Complete HTML document for the page"),
},
async ({ slug, html }) => {
const dir = `site/pages/${slug}`;
await mkdir(dir, { recursive: true });
await writeFile(`${dir}/index.html`, html, "utf-8");
return {
content: [{ type: "text", text: `Published at /pages/${slug}/` }],
};
}
);
server.run({ transport: new StdioServerTransport() });
Point Claude Desktop (or Claude Code) at this server in your config, and the tool appears in its toolkit. Claude writes the HTML, calls publish_page, and your deploy pipeline does the rest.
The publish step is where your stack decides
The write-to-disk version above is the simplest case — it works if your site is a static build or a plain folder. Adapt the handler to whatever you actually run:
- Git-based deploys (Netlify, Vercel, Cloudflare Pages, GitHub Pages): have the handler commit the file and push. Claude asks for the page; your existing pipeline ships it. This is my default recommendation because you get version history for free — which matters more than people expect. We've argued your landing pages should be as disposable as your ad creative, and disposable only works if old versions survive somewhere.
- A headless CMS: the handler becomes an API call to create an entry. Same shape, different endpoint.
- A traditional server: an SFTP or SSH call, or a small HTTP endpoint on your side that accepts the HTML. Keep it behind auth — a tool that publishes to your production site should never be wide open.
The point is that the MCP layer stays identical in all three. Only the handler changes.
Guardrails worth building on day one
Letting an LLM write to production sounds reckless until you add three cheap constraints:
Template in, HTML out. Don't ask Claude to invent layout and structure every time. Keep a branded template in your repo, and have the handler inject Claude's copy and sections into it. You get message-match and speed without surrendering your brand. This is the same principle behind message-matching every ad group to its own page — the content varies per campaign, the shell shouldn't.
A slug allowlist. Restrict where pages can land. site/pages/* and nothing else. Claude can't overwrite your homepage by accident.
A dry-run flag. Add a confirm parameter that defaults to false. The tool returns the file path and a diff instead of publishing, and you flip it to true once you've eyeballed the output. After a week of trust-building, most people flip it off and never look back.
Why this beats the builder decision entirely
The comparison posts — mine included — frame it as builder versus builder. But the real fork in the road is: do you want pages on your infrastructure, published by your AI, or do you want to rent someone else's?
Wiring MCP to your own site means:
- Your domain, your analytics, your pixels — nothing new to install or re-verify.
- Your existing deploy pipeline becomes your safety net: rollback is a git revert.
- The tool generalizes. The same server pattern that publishes a landing page can publish a blog post, a pricing tweak, or a localization variant. You're building a publishing interface, not buying a page factory.
And when a campaign needs a variant now, the loop is: brief Claude, Claude calls the tool, URL is live before your ad editor finishes reading the brief. That time-to-live-URL number — the only metric that matters when an ad group is failing — collapses from days to minutes.
Start smaller than you think
The version worth shipping this week is embarrassingly simple: one tool, one template, one directory. Publish a single test page, delete it, publish a real one. Once the pipe works, the fancy stuff — variant generation, QA checks, pulling briefs from your ad account — layers on top of a foundation you already trust.
The middleman tool was only ever necessary because your site couldn't talk to your AI. Now it can.