← back to blog

MCP servers in production: lessons from six months

Model Context Protocol is powerful but the ecosystem is young. How I'm using MCP for HubSpot, Google Workspace, and custom data sources — and what broke along the way.


I’ve been running MCP (Model Context Protocol) servers in production for about six months now — for my own consulting workflow and for a couple of client projects. The protocol is genuinely useful, but the gap between “works in a demo” and “works reliably at scale” is real.

Here’s what I’ve learned.

What MCP is (briefly)

MCP is a protocol for giving LLMs access to external tools and data sources through a standardized interface. Instead of writing custom function-calling code for every integration, you run MCP servers that expose tools and resources that any MCP-compatible client can use.

Think of it as USB for AI — a universal connector that lets models plug into your systems.

What I’m running in production

HubSpot MCP server

Used for: Pulling CRM data into AI-assisted email drafting and meeting prep. Before a client call, I can ask Claude to summarize recent interactions, open deals, and last touchpoints — all pulled live from HubSpot.

What works: Read operations are solid. Searching contacts, pulling deal history, reading notes — reliable and fast.

What breaks: Write operations need careful guardrails. I accidentally updated a deal stage during testing because the model interpreted “move this forward” as an instruction to change the CRM record. Now all write operations require explicit confirmation.

Google Workspace MCP server

Used for: Calendar awareness, email search, and document retrieval. The model can check my availability, find relevant email threads, and pull context from Google Docs.

What works: Calendar queries and email search. “What’s on my calendar tomorrow” and “find the last email from [client]” are fast and reliable.

What breaks: Google’s API rate limits are aggressive. During heavy usage, the MCP server hits 429s and the model gets confused by the error responses. I added retry logic with exponential backoff, which helped, but the model still sometimes interprets “rate limited” as “no results found.”

Custom data source MCP server

Built for a client: Exposes internal patient eligibility data through a read-only MCP interface. Care coordinators use it through a Claude-powered chat interface to check patient program eligibility.

What works: Everything, actually. This is the cleanest MCP implementation I’ve built, because the scope is narrow (read-only, one data source, structured queries) and we control both sides.

What breaks: Nothing yet. Narrow scope is the secret to reliable MCP servers.

Lessons learned

1. Read-only servers are 10x easier to get right

The moment you add write operations, you need confirmation flows, undo mechanisms, audit logs, and careful prompt engineering to prevent unintended mutations. If you can get away with read-only, do it.

2. Error handling is the whole game

MCP servers that return clean error messages when things go wrong are dramatically more useful than servers that return raw exceptions. The model can work with “No contacts found matching that query” — it cannot work with a Python traceback.

Every MCP tool should have:

  • Clear error messages in plain English
  • Distinction between “no results” and “something went wrong”
  • Rate limit awareness (return “try again in X seconds” not a generic error)

3. Test with adversarial prompts

Users will ask the model to do things your MCP server doesn’t support. “Delete all my emails” when you only expose search. “Update the patient record” when you only expose reads. Your server needs to handle these gracefully, and your model needs instructions about what’s in and out of scope.

4. Logging everything is non-negotiable

Every MCP tool call gets logged: the tool name, the arguments, the response, and the latency. When something goes wrong (and it will), these logs are the only way to diagnose whether the problem is the model, the MCP server, or the downstream API.

5. The ecosystem is still young

Community MCP servers vary wildly in quality. I’ve had better results building custom servers for specific use cases than trying to use generic community servers and work around their limitations. The protocol itself is solid — the implementations are catching up.

Where MCP is headed

I’m bullish on MCP as a standard. The protocol solves a real problem — integrating LLMs with external systems without writing bespoke code for every combination of model and tool. The patterns are still being established, but the direction is right.

For teams considering MCP: start with one read-only server for one specific use case. Get that working reliably before expanding scope. The temptation to build a “universal MCP layer” that connects everything is strong — resist it. Narrow, reliable, and well-tested beats broad and fragile every time.