What MCP Actually Is — And What Building One for DeckDen Taught Me
How the Model Context Protocol works, what problem it solves, and what building an MCP server for DeckDen taught me about trust, scope, and error messages.
8 min read
What MCP Actually Is — And What Building One for DeckDen Taught Me
Over the last couple of weeks I built an MCP server for DeckDen. Before that, I’d heard the acronym everywhere and understood almost none of it. I knew it had something to do with AI assistants using tools, and I’d seen it called “USB-C for AI,” which sounded nice and told me nothing. Now that I’ve built one, I can finally explain what it is, why it exists, and what surprised me along the way.
What I thought MCP was
Going in, I filed MCP under “another API thing.” DeckDen already has an API and I know how to write routes, so I assumed I’d wrap a couple of endpoints, point Claude at them, and call it done. That’s not what happened. MCP isn’t a new kind of API. It’s an agreement about how an AI assistant finds out what a program can do, and how it asks that program to do it.
The problem MCP solves
Language models are good at reasoning and bad at knowing about your stuff. Your decks, your files, your tickets, your database — none of it lives inside the model. The usual fix is to give the assistant tools, a way to call out to real software and bring back real data.
The trouble is that, before MCP, every one of those connections was custom work. Claude Desktop needed its own integration with DeckDen. A code editor needed another. A different chat app needed a third, and every new assistant meant starting over. Line up a few assistants and a few tools and every pair needs its own glue code.
MCP, the Model Context Protocol, turns that multiplication into addition. It’s an open standard, introduced by Anthropic, for how an AI application talks to the programs that give it context and abilities. Build a server that speaks it once, and any assistant that speaks it can use your server. That’s the whole pitch behind the USB-C comparison: one shape of plug instead of a drawer full of cables.
How it actually works
There are three pieces, and the names are easy to mix up:
Host — the app you’re actually using, like Claude Desktop. It owns the conversation and the model.
Client — a small piece inside the host that keeps one connection open to one server.
Server — your program. It tells the client what it offers, and it does the work when asked.
What surprised me is that the model never talks to your server directly. The host sits in the middle. When it starts, the client connects to the server and asks what it can do, and the server answers with a list of tools. Each tool has a name, a plain-English description, and a schema for the inputs it accepts. The host hands that list to the model as part of the conversation.
Then you ask a question. If a tool can help, the model says which one it wants and with what arguments. The host forwards the request, the server runs it, and the result goes back to the model, which turns it into an answer. Here’s the whole trip for one question:
Underneath, the messages are JSON-RPC, a plain request-and-response format. My server talks to Claude Desktop over stdio: the host starts it as a local process on my computer, and they trade messages through standard input and output. There’s an HTTP option for servers that run somewhere else, but for a server that reads my own decks, a local process was the simplest choice, with nothing new exposed to the internet.
A server can offer three kinds of things. Tools are actions the model can call. Resources are data it can read. Prompts are ready-made prompt templates. DeckDen’s server only offers tools, two of them: list_my_decks and get_deck.
Why I built one for DeckDen
DeckDen already knew who I was and what decks I’d built. What it didn’t have was a way to let an assistant look at them without handing over my password. So the work came in two layers. First, read-only access tokens on the account page: a token only works on three read endpoints, it’s shown once, only its hash is stored, and I can disconnect it any time. Then the MCP server itself, a small TypeScript program that uses one of those tokens to read my decks and pass them to Claude.
What I learned along the way
The protocol took an afternoon to understand. Everything around it is where the real learning was:
Start with a throwaway — before touching DeckDen I built a hello-world server and poked at it in the MCP Inspector, a web page that lets you call a server’s tools by hand. Seeing the raw tool list and the raw results made the protocol click faster than any diagram did.
Scope first, code second — I settled on two read-only tools before writing anything. Every tool is something an assistant can try, so a smaller list means less to get wrong. Card questions waited, because DeckDen doesn’t store card effect text yet.
Only pass along what the assistant needs — DeckDen’s API sends more than an assistant should see, so the server runs every response through zod schemas that work as an allow-list. Whatever isn’t on the list gets dropped. No email, no image URLs, no owner info. A test feeds the real client a raw backend reply full of that stuff and checks that none of it comes out the other side.
Stdout belongs to the protocol — with stdio, the host reads everything the server prints as protocol messages, so one stray
console.logbreaks the connection. Logs go to stderr instead, and a smoke test starts the built server as a real process and checks that stdout holds nothing but protocol messages.Error messages are part of the interface — the SDK sends the message of anything a tool throws straight to the model. That meant a careless error could carry my token or a piece of a deck into the conversation. So every failure becomes one of a few fixed, friendly messages, and anything unexpected becomes a generic one, with the details going only to the server’s log and anything shaped like a token cut out first.
Fail loudly, fix nothing quietly — the config reader refuses a token with a trailing newline, quotes, or
Bearerin front of it, and it refuses a plainhttp://URL unless the host is localhost. A silent fix would hide a setup mistake, and a setup mistake is exactly what you want to hear about.
The tests that taught me the most
I wrote a lot of this server test-first, and then I checked the tests themselves by breaking the code on purpose and making sure the right one failed. The best example: I wrote a server-error check with || where I meant &&. It was true for every status code, so every request failed — and my test for a 500 still passed. A test for the case where a rule applies can’t tell a correct rule from one that’s always true. It also needs cases where the rule must not apply. The server’s suite is at 157 tests now, and I trust it a lot more than I would have if I’d only watched it go green.
Why this actually mattered
The biggest shift for me was how I think about who’s calling my code. For years I’ve written endpoints for a frontend I control. An assistant is a different kind of caller: it decides what to ask for, it reads whatever comes back, and it repeats what it read to a person. That’s why the boring decisions — read-only tokens, a short allow-list, fixed error messages — ended up being the whole design instead of polish I could add later.
It also changed how I see “AI features.” The clever part is already in the model. MCP is the plumbing that lets it reach real software, which makes the plumbing worth getting right.
What this opened up for me
Right now my server can only read decks. Next up are card tools, so an assistant can answer “how does this card work?”, but that means DeckDen has to store card effect text first. Deck building is further out, because it needs tokens that can write, and I’d rather earn that one slowly. There’s also still no rate limiting on the backend, which matters more now that an assistant stuck in a loop can send requests faster than I ever could.
If you’re a junior dev wondering whether MCP is worth your time: build the smallest server you can around something you already own. Two tools is enough. You’ll learn the protocol in an afternoon, and spend the rest of the week learning what it really means to let a machine read your app. That’s the part worth knowing.


