Max Narditdata & ai systems
← Articles
8 mindev

MCP vs API for AI agents: the API catalog is a directory, and the choice is who sits in the middle

An API catalog does not compete with an MCP server: it can list one. With that settled, the choice between MCP and a plain HTTP API described by OpenAPI comes down to whether a host application integrates your service for the model, and whether anyone has already decided to put you in front of it.

Contents

This site has an API catalog at /.well-known/api-catalog. Since 8 October, the first thing it lists is the site’s MCP server.

That one entry undoes the question most “MCP vs API” comparisons start from. An API catalog and an MCP server are not two answers to the same problem: the catalog is a directory, and an MCP server is one of the things a directory can point at. Compare the things it points at, and there is a real choice left. My read is that it is usually framed around the wrong variable. The protocol matters less than whether a host application integrates your service for the model, and whether someone has already decided to put you in front of it. Those two turn out to be one condition seen from two sides.

What an RFC 9727 API catalog does and does not do

RFC 9727, a Proposed Standard from June 2025, defines a well-known URI that returns a linkset, served as application/linkset+json. Each entry has an anchor, the endpoint, and relations attached to it. service-desc points at a machine-readable description, typically an OpenAPI file. service-doc points at documentation for humans. The stated goal is the automated discovery of a publisher’s public API endpoints.

That is all it does. The RFC does not tell a client how to authenticate or how to call anything. It answers “what is here” and hands the “how” to whatever the links lead to.

The format itself comes from RFC 9264, and it has one detail that is easy to get wrong: the relation type is the name of the member, and its value is an array of targets with an href. Here are this site’s two entries in that shape, trimmed to the two RFC 9727 relations:

json
{
  "linkset": [
    {
      "anchor": "https://max.nardit.com/mcp",
      "service-desc": [{ "href": "https://max.nardit.com/.well-known/mcp/server-card.json" }],
      "service-doc": [{ "href": "https://max.nardit.com/api-docs/mcp.md" }]
    },
    {
      "anchor": "https://max.nardit.com/api/md",
      "service-desc": [{ "href": "https://max.nardit.com/api-docs/openapi.json" }],
      "service-doc": [{ "href": "https://max.nardit.com/api-docs/markdown-for-agents.md" }]
    }
  ]
}

That is not what I shipped. The catalog I wrote in April, and the one served on 18 October, puts each link in a links array as an object with a rel field, a member RFC 9264 does not define. A parser that follows the RFC finds anchors with no relations it recognizes. The file is valid JSON, it carries the right content type, and a human reading it understands it immediately, which is exactly why nothing told me. The directory is the cheapest piece of the arrangement to write, and apparently also the easiest one to get wrong without a sound.

The second anchor is an HTTP API with an OpenAPI 3.1 description. The first is an MCP server whose service-desc is a server card, in the shape of an earlier proposal; its successor, SEP-2127, is now Final and recommends a different location for the card. The catalog does not care which protocol sits behind an anchor, which is why asking whether an API catalog is “enough” instead of MCP compares a directory with one of its entries.

OpenAPI and MCP: what each one hands the caller

The two entries are not the same data in two wrappers either. The OpenAPI file describes one operation: give it a path, get the page back as Markdown. The MCP server exposes three read-only tools: contact details, the product list, and a search over article titles and summaries that returns canonical URLs.

An OpenAPI description is reading material for whoever makes the call. A script or an agent with a fetch tool reads it, composes the HTTP request itself and parses what comes back. The agent’s runtime still applies its own rules to that fetch, but they are its rules for HTTP in general. Your service did not negotiate anything with the runtime, and nobody had to configure it in advance: an agent that found the catalog while browsing can use the markdown API with nothing but the ability to make a GET request.

MCP hands the caller something else: a host. The 2026-07-28 specification names three roles, hosts (the LLM applications), clients (connectors inside the host) and servers. The server talks to the host, and the host decides what reaches the model. It receives your tool names, descriptions and schemas in a typed list and presents them to the model, either all at once, which is still the common case and the cost in tokens I have written about, or on demand, when the host defers them. And the host is supposed to put a person in the loop. The spec’s tool safety principle is short:

Tools represent arbitrary code execution and must be treated with
appropriate caution.
...
Hosts must obtain explicit user consent before invoking any tool

The spec’s tools page adds a hard rule for annotations: clients must consider them untrusted unless they come from trusted servers. It says nothing as firm about the description one field over, which I have argued deserves the same standing as any outside author.

So both paths run through an agent runtime. The difference is whether your service is integrated into it. With OpenAPI your service is a document the agent reads. With MCP it is a set of tools the host has accepted, presents to the model and governs with its own consent policy.

MCP 2026-07-28 made the server cheap: the cost sits with the host

Under the 2025 revisions, every connection opened with an initialize handshake that set up a connection-scoped session. HTTP session IDs were already optional by the November 2025 revision, but the handshake was not. The current spec drops it. Its base protocol lists “stateless, self-contained requests” with per-request capability negotiation, and on the Streamable HTTP transport each message is an HTTP POST to a single endpoint, answered with a JSON object or a stream scoped to that request.

The server on this site is one route handler on a serverless function. It answers POST, returns 405 to GET, keeps no sessions and needs no auth because the data is public. It also still answers the older initialize handshake, as the spec’s backward-compatibility section describes. Writing the server is not the expensive part anymore.

What did not get cheaper sits on the host’s side. Someone has to discover your server and permit it, by hand in a client’s config or through a registry. Many hosts still load every definition into context up front, and deferring is the host’s choice, not yours. The overview’s principles say the host must get consent; the tools page softens that to a human in the loop who SHOULD be able to deny a call, and leaves the interaction to the host. In practice it is a prompt on first use, a per-tool allowlist, or a confirmation each time. For an already approved, low-risk lookup, repeated prompts add little. They are not worthless even then: my search tool takes a free-text query, and a query can carry something private from the user’s session to a server they never looked at.

Where “MCP is for agents, REST is for code” goes wrong

The usual one-line split says MCP is the interface for AI agents and a REST API is the interface for programs. Both halves miss.

An agent with a web fetch tool is a REST client. It can read an OpenAPI file, or just request a page with Accept: text/markdown, and for public content that is the shortest path there is. It needs no configuration and carries no tool list from you in its context. If your callers are agents that wander in from search or from a link, an MCP server is something they cannot use until a host that speaks MCP has found it and been allowed to connect. The catalog can point at the server and a server card can describe it. Neither one connects anything.

This is where I part ways with a line I wrote last week. Comparing the MCP endpoint with in-page WebMCP tools, I called it the one an agent can reach today. Against WebMCP that holds. Against a markdown API that needs no client at all, it is the second path, not the first.

MCP, for its part, serves hosts more than agents, and through them the people who run those hosts and decided to connect your service. That decision is what MCP buys you: once made, your tools are available in that client without the model having to find you first, typed and governed by the host. The audience is narrower than “agents” and more committed.

So the split I would use is not agent against code. It is a stranger against an installed integration. A stranger needs a readable, discoverable endpoint. An installed integration can afford a host in the middle and gets real value from it as soon as there is a credential to hold or an action worth asking about.

WebMCP in the open tab: the third door, and the one this site needs least

There is a third caller: an agent driving a browser tab, where a page can register tools for the session it is already in. The post linked above covers how WebMCP registration works now, including the part where my in-page script still looks for an object the current draft no longer defines, so in a current browser that door exposes nothing. The point here is narrower. WebMCP fits when the useful state lives in the tab: a cart, a half-filled form, a logged-in view. A public site that gives everyone the same answer has no such state.

Do you need an MCP server? The first question to answer

Before choosing a protocol, answer one question: who has already decided to use you?

If the honest answer is nobody yet, and your callers would be agents that find you while fetching the web, build the plain path first. Make the content readable over HTTP, describe it with OpenAPI if there is more than one operation, and list it in an API catalog so a crawler can see what exists. Agents with ordinary HTTP access can then use it without preloading a schema of yours; what they fetch still costs them context, but only when they fetch it.

If there are people or teams who will connect you on purpose, inside a client they already use, MCP starts to earn its place, and it earns more the closer you are to what a host is good at: a credential the host can hold for the user, a write action where the consent prompt is doing its job, or a tool the work reaches for often enough to keep in context.

And if you build both, list both in the catalog, then check it with a parser that follows RFC 9264 rather than with your own eyes. It is the one piece that asks nothing of the caller, and mine said nothing to a strict parser for six months before I looked.

01 / Discussion

No comment section here — all discussions happen on X.

Max Nardit@mnardit

02 / More articles