
Here's a confession: I built a whole REST API for my blog, documented it, gave it API keys with granular permissions — and then barely used it.
Not because the API was bad. Because calling it was annoying. Every interaction was a curl command with a Bearer token pasted in, JSON bodies escaped inside shell strings, and me squinting at responses trying to remember which field was isFeatured and which was featured. (Both exist, on different resources. Yes, that was a design decision I made. No, I don't want to talk about it.)
This week I finally fixed it properly: I wrapped the whole API in an MCP server. One file, one afternoon, and now my blog is something any AI client can drive natively — with typed tools, real validation, and zero shell-escaping roulette.
If you have a side project with an API, I'm here to convince you to do the same.
Quick context: what MCP actually is (in mid-2026)
Model Context Protocol started as an Anthropic open-source project in November 2024 — a standard way for AI models to discover and call external tools. The pitch was "USB-C for AI": instead of every app inventing its own plugin format, one protocol for everything.
The part nobody predicted was how fast it would become the default. By December 2025 it had been donated to the Linux Foundation's new Agentic AI Foundation, co-founded by Anthropic, OpenAI, and Block, with AWS, Google, Microsoft, Cloudflare, and GitHub backing it. As of this year we're talking 97 million monthly SDK downloads and roughly ten thousand public servers across the registries. Claude, ChatGPT, Gemini, Copilot, Cursor, VS Code — everything serious speaks it natively now.
Why should you care? Because the protocol's whole job is turning "an API I can call" into "a set of typed, self-describing tools an agent can reason about." That difference matters more than it sounds.
The itch I was scratching
My blog runs on a little CMS I built called ExploreCMS — posts, projects, and a photo gallery, all behind a REST API with key-based permissions. Solid foundation. The API even auto-revalidates the site cache on every mutation, so changes go live instantly.
But REST APIs are designed for programs you write once. Agents are different — they improvise calls on the fly. And improvising raw HTTP calls went wrong in boring, predictable ways:
- API keys kept ending up in command strings (bad) or shell history (worse)
- JSON-in-shell escaping mangled more than one request
- No validation until the server rejected something with a 400
- The agent had to remember the API shape instead of just asking
MCP fixes all four by design. Tools are declared with schemas. Inputs get validated against those schemas before the request ever leaves. Auth lives inside the server process, not in the commands. And clients can introspect everything — "show me what you can do" is a protocol-level question.
The build: one file, 19 tools
The whole server is a single Node.js file using the official TypeScript SDK (@modelcontextprotocol/sdk, currently at v1.29) with Zod for schemas — which is very much the standard pattern in 2026, and for good reason. The skeleton looks like this:
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: "tunlat", version: "1.0.0" });
From there it's just registering tools. I ended up with 19, covering the full surface of the API:
- Posts —
posts_list,posts_get,posts_create,posts_update,posts_delete - Projects — same five
- Gallery — album CRUD plus photo add/get/update/delete
Each tool declares its inputs as a Zod schema, so posts_create knows it needs a title and content, that published is a boolean defaulting to false, and that tags is an array of strings. Pass something wrong and it fails validation before my blog ever sees it.
A few small design decisions that paid off immediately:
1. Dangerous operations carry their own warnings. The tool description for gallery_delete_album literally says "WARNING: also deletes all photos inside it." Tool descriptions are part of what the client model reads when deciding how to behave — so the safety note travels with the capability. That's a genuinely underrated MCP feature.
2. The API key never leaves disk. The server reads it from a chmod 600 file at startup. Every call the client makes is just posts_create(title=..., content=...) — no credentials in arguments, no tokens in logs. Compare that to curl -H "Authorization: Bearer ..." scattered across shell history.
3. A clean() helper for updates. PATCH endpoints want only the fields you're changing, so the server strips undefined values before sending. posts_update(id, published=true) sends exactly {published: true} and nothing else. No accidental field wipes.
4. Errors come back as first-class errors. Non-2xx responses get turned into structured tool errors with the HTTP status and the server's own error message, so failures are debuggable instead of silent.
Transport was the easiest choice of the day: stdio. The server runs as a local subprocess, which means no ports, no TLS, no OAuth dance — the client spawns it, they talk over stdin/stdout, done. (Streamable HTTP exists for remote servers, but for a personal setup on one box, stdio is strictly simpler and strictly more private.)
The moment it clicked
The first time I ran a schema introspection against the live server and watched it describe all 19 tools back to me — parameters, types, descriptions, the works — the value proposition snapped into focus.
This isn't "an API wrapper." It's my blog explaining itself to anything that connects. A new client doesn't need my docs, my Postman collection, or my memory. It asks the server what it can do, and the server answers in a machine-readable contract.
Then I ran a full write cycle through it — created a draft post, published it via update, deleted it — and every call was a clean, typed function call with validated inputs. No escaping. No tokens. No 400s from a malformed body at 11 PM.
That's when I landed on the opinion in the title: this is just how CMSs should work now. WordPress figured this out — they shipped an official MCP adapter back in February. The rest of us building our own stacks get to do it in an afternoon, because the SDK does all the heavy lifting.
What I'd tell past-me
- Build the REST API first anyway. The MCP server is a thin, honest wrapper — maybe 150 lines of actual logic. All the real work (permissions, validation, cache revalidation) lives in the API. MCP rewards you for having done the boring part well.
- Put real effort into tool descriptions. They're not docs for humans — they're behavioral guidance for whatever model connects. Warnings, defaults, and "here be dragons" notes all belong in there.
- Check the registries before building anything. With ~10k public servers, whatever you're about to wrap might already exist. My blog is bespoke so I had to build mine — your Postgres, GitHub, or Slack definitely isn't.
- Stdio for personal, HTTP for shared. Don't overthink transport.
The barrier between "my project" and "my project that AI can actually use" is now officially one afternoon and one file. In 2026 there's no excuse left — and honestly, it's a fun afternoon.
So: what API-shaped thing is sitting on your machine, waiting to be wired up? Whatever it is, it's one afternoon away from being AI-native. Go spend that afternoon.



