Running an MCP Server as a Distribution Channel for Your Content
Why a content site should expose an MCP server, which tools to ship, how discovery and authentication work, how to gate paid tools, and the honest limits of the channel.
- An MCP server turns your content from something agents scrape into something agents call. The client keeps the connection; you keep the interface.
- Ship five tools before anything clever: list, get, search, bulk-get, and an access/pricing tool. That covers almost every question an agent asks.
- Reuse your JSON layer. If you already publish an index, per-item and bulk endpoints, the MCP server is a thin adapter over them — not a second content system.
- Gate paid tools with the same entitlement check and the same payment-required payload your HTTP 402 gate returns, from one shared code path.
- Discovery is the weak link: a server nobody lists is a server nobody connects to. Registry presence and a
.well-knowndescriptor do more for adoption than extra tools.
Most of the agent-ready website is about being read well. This chapter is about being called — publishing your content as tools an agent can invoke, rather than pages it has to fetch and parse.
What changes when content becomes a tool call
A crawl-and-parse pipeline puts all the work on the agent: find the site, fetch pages, extract, guess structure, hope the format did not change. A tool call inverts it. You declare what is available and what each call returns; the model reads those declarations and picks. The practical consequences:
- No parsing ambiguity. You return structured results, not a page to interpret.
- No crawl budget. A search tool answers in one call what a crawl needs dozens for.
- Persistent presence. A client that has connected your server has you available in every future conversation — closer to being installed than to being indexed.
- You keep the interface. Renaming a URL breaks scrapers silently; a tool contract is explicit and versionable.
The protocol-level comparison with plain function calling — when a hosted tool definition beats a protocol server — is in MCP vs function calling.
The five tools to ship first
Resist a large surface. These five answer almost everything:
| Tool | Arguments | Returns |
|---|---|---|
list_items |
none | Metadata for everything: id, title, description, tags, updated, and the HTML/Markdown/JSON URLs |
get_item |
id, optional api_key |
One item in full (or a payment-required payload if gated) |
search_items |
query, optional limit |
Ranked metadata only — never bodies |
get_corpus |
none | Everything free, in one response, for priming a long context |
get_access_info |
none | What is free, what costs money, how to pay |
Design notes that matter more than the list:
- Tool descriptions are prompt text. The model chooses between your tools by reading them. Say what the tool returns and when to prefer it: "Returns metadata only — call get_item for the body." Vague descriptions produce wrong tool choices, which read to the user as your server being broken.
- Search returns metadata, not bodies. Otherwise one query floods the context and the agent stops using you.
- Always include the URLs. Every result should carry the canonical HTML, Markdown and JSON URLs of the item, so an agent can cite you properly and a human can follow up.
- Keep the big one honest. A
get_corpustool that returns a large payload should say so in its description; agents budget context.
Once the basics work, the protocol has more to offer than tools — resources, prompts, sampling and elicitation each fit different jobs, and choosing correctly avoids reimplementing them badly: see MCP primitives.
Build it over your JSON layer
The mistake to avoid is a second content pipeline. If you followed JSON API design for agents, the server is an adapter:
// One data module → HTTP endpoints AND MCP tools
server.tool("list_items", "Metadata for every item. No arguments. Returns ids, titles, descriptions, tags, update dates and variant URLs — call get_item for bodies.",
{}, async () => json(items.map(toMetadata)));
server.tool("get_item", "Fetch one item by id. Free items return the full body; gated items return a payment-required object unless a valid api_key is supplied.",
{ id: z.string(), api_key: z.string().optional() },
async ({ id, api_key }) => {
const item = items.find((i) => i.id === id);
if (!item) return json({ error: "not_found", hint: "Call list_items for valid ids." });
if (!item.premium) return json(toFull(item));
// SAME entitlement check and SAME payload as the HTTP 402 gate.
const tier = await tierOf(api_key, env);
return tier ? json(toFull(item)) : json(build402Body(item.id, env));
});
The invariant worth enforcing in review: the HTTP gate and the MCP tool call the same entitlement function and the same payment-payload builder. Two implementations of one commercial policy will diverge, and the divergence will be discovered by a buyer.
For transport and framework choices, and the difference between a local stdio server and a remote HTTP one, see building an MCP server.
Authentication, only where you need it
A public, read-only server over free content needs no authentication, and that is a feature: connection friction is the main thing standing between you and adoption. Introduce auth only for what genuinely requires it — paid tools or per-user actions — and use the established OAuth path for remote servers rather than inventing a scheme. The details, including why an API-key argument on a tool is acceptable for simple entitlement checks but not for user identity, are in MCP server authentication.
For paid tools, the simplest arrangement that works: an optional api_key argument, validated against the same store your HTTP gate uses, with an explicit payment-required payload (price, checkout URL, how to retry) when it is missing or insufficient. An agent that receives that payload can complete the purchase and call again in the same session.
Discovery is the actual bottleneck
Tool quality gets the attention; discovery decides whether anyone connects. Three things move the needle, in order:
- Registry presence. Being listed where clients and users look for servers is worth more than any additional tool.
- A
.well-knowndescriptor on your domain declaring your remote endpoint, so a client that knows your domain can find your server without a directory. - Copy-paste configuration in your docs. The exact JSON block a user pastes into a client. Sounds trivial; it is the difference between "interesting" and "connected".
What agents and users actually evaluate when choosing between servers — and therefore what to make visible — is the subject of finding and evaluating MCP servers.
The honest limits
- The spec moves. Revisions land regularly and clients lag. Pin what you support, state it, and test against real clients rather than the spec text alone.
- Client behaviour varies. Tool-selection quality differs across clients and models; a tool that works in one may be ignored in another. Descriptions carry more weight than schemas here.
- It is not a traffic channel. Agents calling your tools do not generate page views, and may not produce visible referrals at all. Measure it as usage, not as sessions — MCP calls are one of the outcome classes worth logging separately in measuring AI agent traffic.
- It does not replace being crawlable. Most agents will never connect to your server; they will fetch your pages. MCP is additive to Markdown variants and llms.txt, never a substitute.
Is it worth it?
Yes, on one condition: your JSON layer exists first. Then the server is a small adapter over data you already publish, and the upside is a distribution channel where you define the interface instead of hoping a scraper survives your next redesign. If your data layer does not exist yet, build the endpoints — they pay for themselves, and the MCP server becomes an afternoon rather than a project.
Frequently asked questions
- Why would a content site run an MCP server instead of just a JSON API?
- Because of who does the integration work. A JSON API needs someone to write client code; an MCP server is connected once in a client and is then available in every conversation, with tool descriptions the model reads itself. For a content business it converts "developers could integrate us" into "agents can use us now".
- Does an MCP server replace my API or my llms.txt?
- No, it is a third front door for the same content. llms.txt serves crawl-and-read agents, JSON endpoints serve programmatic clients, MCP serves tool-using clients. All three should be generated from one source so they cannot disagree.
- Do I need authentication?
- Not for free content — a public read-only server can be connectionless and unauthenticated, which maximises adoption. You need it the moment a tool returns paid content or acts on behalf of a user, and remote MCP servers have an established OAuth-based path for that case.
- Is it worth the maintenance?
- If your JSON layer already exists, the server is a small adapter and the ongoing cost is low. If you would be building the data layer from scratch to support it, build the JSON endpoints first — they are useful on their own, and the MCP server then comes almost free.