All articles

Running an MCP server in production: three things I got wrong first

Everyone's writing about MCP. Far fewer have run one in production.

I built and maintain the MCP server at Navoy — a NestJS service that exposes our travel APIs as typed tools our AI agents call. 10 tools, three external providers behind them, a year in production.

Three things I got wrong first, in case they save you the detour:

1. The tool description IS the API.

I wrote descriptions like documentation: what the tool does. Wrong audience. The model isn't reading to understand — it's reading to decide. The lift came from being prescriptive about when to call it, not just what it does. "Search hotels by city and dates" is a label. "Call this when the user has a destination and dates and needs availability" is a trigger.

2. Scope your tools, and fail closed.

Not every caller should see every tool. I filter tools/list by scope — a search-only token never learns the trip-mutation tools exist. And an unrecognized token gets nothing, in every environment, including dev. The version where dev was permissive is the version that eventually ships.

3. Shape responses for a model, not a browser.

Our upstream APIs return everything. An agent doesn't need everything — it needs the fields that inform the next decision, and it pays for each token twice: once reading, once reasoning. Trimming payloads improved answers and cost. Same for errors: an error message isn't a log line, it's an instruction. "Invalid request" is a dead end. "No availability for those dates — try a wider range" is a retry.

The honest limit: I build the tool layer agents consume, not the agents themselves. Those are a different service and a different problem.

MCP is a small spec. The hard part isn't the protocol — it's designing an interface for a caller that reasons instead of reading docs.

Building with MCP? What's surprised you?