Skip to content
A Public MCP Server With Nothing Private to Leak

4 September 2026

A Public MCP Server With Nothing Private to Leak

aisecurity

Adding an MCP server so AI agents could query the blog's posts raised the obvious question first: does this need authentication? The answer fell out of an existing constraint rather than a new one — GET /api/posts, the endpoint the dashboard uses, is already auth-gated because it returns every post, including drafts. The MCP tools were never going to touch that endpoint.

Reusing the boundary that already existed

list_posts and get_post are built directly on getPublishedPosts() and getPublishedPostBySlug() — the same two functions that already back the public /blog pages and the RSS feed. A draft can't leak through the MCP server for the same reason it can't leak through the blog index: the publishedAt <= now() filter lives in one shared place, not reimplemented per caller. Public and unauthenticated stopped being a risk decision and became a description of what the tools already were.

The part that still needed a decision

Data exposure wasn't the open question; abuse was. A public, unauthenticated endpoint that hits Postgres on every call is a new surface for a scanner to hammer, authenticated or not. The fix was the same rate limiter already protecting the pageview-tracking endpoint — thirty requests a minute per IP — applied here for an unrelated reason: bounding database load, not protecting secrets.

What made this safe to ship quickly

The interesting design work was already done by an earlier, unrelated decision — gating the raw posts API — and this feature just had to avoid undoing it. The fastest way to build a safe public endpoint isn't reasoning about safety from scratch each time; it's noticing which existing boundary already draws the line you need and building on the correct side of it.