Building better MCP servers
MCP servers are everywhere now. GitHub, Atlassian. Plenty are quite badly built though, often little more than a thin wrapper over an API, firing back a wall of data without sparing a thought for the context window it's filling.
I think the issue is the pressure to ship one. Companies feel they need an MCP server, so they bolt one on regardless of whether it's any good, and some simply expose their entire REST API wholesale, so a trivial query comes back as 15,000 tokens. Which rather misses the point. A traditional API hands you raw data, an MCP server I'd say should be handing you an outcome.
So what separates a good one from a wrapper? Most of it comes down to respecting the context window. It's a limited resource, and it doesn't behave like a normal API constraint, every token in your response eats into it and a bloated response makes the model worse. A bit like asking for a phone number and being read the whole address book. I've seen a CRM return 50+ fields per user object, internal timestamps nobody asked for, base64-encoded avatars, the lot, when all anyone wanted was an email address. I'd return only what's needed by default and put the extras behind optional parameters. GraphQL fits this quite nicely, since the client picks exactly what it wants.
From there, I'd build tools around what the user is actually trying to do rather than around your API, so not one tool per endpoint. My BGG MCP works this way. Instead of exposing the raw endpoints, the rules resolver combines several APIs into one tool that searches the forums and gives a direct answer, with no chain of tool calls to bounce through and no polluting the context along the way.
How you shape the response matters too. XML tags give the model more to grab onto than plain JSON, and I like to bake instructions into the response itself, telling the model how to read what it got back and nudging it towards a sensible next step or tool call.
The same thinking applies to errors. A generic status code is no use to a model. An error should really be a proper, context-aware response that says what went wrong, why it matters, and what to try instead.
There are also three newer parts of the protocol worth knowing about, because they let you build something more interesting than a wrapper.
- Sampling lets the server ask the host's model to do some reasoning on its behalf, so it can offload a bit of thinking without shipping its own API key or model.
- Roots define the trusted working directories, which keeps the server from wandering off and trashing something it shouldn't.
- Elicitation lets the server pause and ask the user for structured input mid-run, so it can get clarification when there's more than one sensible option.
The MCP servers worth having, I think, are the ones designed around what your users are actually trying to get done with an agent, rather than thrown together because everyone else has one. Of course, the wrapper is quicker to ship, and I can see the appeal. If all you wanted was raw data though, you already had an API for that anyway.