PullMD Part 7: The MCP Server
On the evening of March 2, in the very first concept chat, there is half a sentence: “MCP later.” After that, nothing was added to it for seven weeks. In my head the matter was ticked off at some point anyway.
On April 25 I asked Claude:
“How do I set it up in Claude Code or other AI agents, I think it’s an MCP right?”
It wasn’t. It was an HTTP interface with a skill next to it, and the two together did the job well enough.
My first reaction was accordingly: then the skill is enough. So build a help page, offer a ZIP, done. That’s in Part 6.
Why I Started Half an Hour Later After All
Then, while writing the instructions, I came up with the argument that turned the whole thing around:
“So quite concretely: the MCP would be good, because we already have a server running anyway and users then wouldn’t have to handle skills, and when something changes on the server all clients automatically have the new code… or what speaks for/against it?”
That’s the entire point. The server is up regardless. If it offers the tools itself, nobody has to download a ZIP, unpack it and put it in the right place. And when I change something, everyone has it immediately, without anyone updating anything.
The Two Routes to an MCP Server
What matters here is how the agent reaches the server. There are two routes.
-
In the local variant, the client starts the server as a process on your own machine. Which means: every user installs something first.
-
In the remote variant, the client talks over HTTP to a server that sits somewhere. The user enters an address, nothing more.
Only the second one solves my problem. With the first, the advantage from the argument above would have been gone right away, because then, instead of handling a skill, you handle a local installation.
The Skill Stays Anyway
That was the question I put to myself at the time: if the MCP server exists, can the skill go?
Up to a point, yes. An MCP server runs without a skill too, and every tool comes with a description that also says when to reach for it. For read_url it said from the start, in essence: whenever you would otherwise fetch raw HTML.
Only, rules that concern several tools fit badly into the description of a single one. The skill had exactly those:
- don’t fetch GitHub addresses through the service, that’s what
ghis for - endpoints that return JSON, likewise not
- and if the service isn’t answering right now, fall back to the built-in fetch
The skill stayed, and one reason for that still holds today. Not everyone wants to or can wire in an MCP server, and anyone who stays with the normal interface only gets that behaviour through the skill.
The other justification we talked ourselves into back then doesn’t hold up. We took these rules for something that doesn’t fit into a tool description, and that simply isn’t true.
The description of read_url today begins with exactly that: prefer the service over the built-in fetch, and not only once that one fails. Anyone who writes the descriptions properly doesn’t need the skill for it at all. That was probably already the case on April 25, only neither Claude nor I noticed.
The Three Tools
Three were proposed at first:
read_urlfetches an address as markdown, that’s the main thingget_sharereads the live endpoint from Part 6, which had existed for two hourslist_recentshows what has run through most recently
I then asked whether there were further candidates. Back came the whole interface, sorted by usefulness for an agent.
Two more would have been worth it, but each needed an endpoint that didn’t exist yet. A search across the titles and addresses of the stored pages, and a statistic about the extractions. The search ended up in there as a fourth proposal, the statistic only as a possibility for later.
Four others fell through again in the same answer. An archive query, because list_recent with a larger number does the same. A statistic about the storage in use, because it says nothing about the content. And delete_entry and delete_all, with the reasoning that a tool schema gives the model too little context to decide safely what may go.
I didn’t contradict that, but had a simpler reason: I just didn’t need those two. What would go there is only an entry in the cache anyway, and the service fetches it again on the next call.
My answer was short: the three are plenty.
What Spoke Against It
For the sake of completeness, because there was certainly something against it.
A client simply addressing an MCP server over HTTP instead of starting it itself was still fairly new back then. Claude Code, Claude Desktop and Cursor could already do it, other programs not necessarily. Anyone on an older version could only start the server locally, and that is exactly what I wanted to avoid.
The server also takes on a second job. When the MCP part jams, agents see an error. The normal interface keeps running independently of that, but it’s one more place that can break.
And anything that doesn’t speak MCP gets nothing out of it. For those, the box to copy stays on the help page.
The Address on the Help Page
One small thing at the end, which only came up while writing this down. The help page carries the command for the setup, and inside it there is an address.
My address. Which, for anyone running the service themselves, is exactly the wrong one.
So the base address became an environment variable. It is substituted into the help page and written into the ZIP, and both are created anew when the container starts.
Why that of all things became important on that day is in Part 8.
Part 7 of 15 in the PullMD series.
← PullMD Part 6: Setup for Agents | PullMD Part 8: From My Tool to an OSS Project →