George Forger's guide to building MCP servers that hold up
George Forger shares hard-won lessons from building 10 MCP servers in a week, focusing on tool descriptions, error messages, input validation, and rate-limit handling as the pillars of a well-built server.
Score breakdown
Treat every error string and tool description as LLM-facing copy — not developer documentation — to prevent silent failures, crashed connections, and hallucinated parameters in production MCP servers.
- 01George Forger built 10 MCP servers in one week and distills the lessons into this guide.
- 02Tool descriptions should tell the LLM what the tool does, what it returns, when to use it, and what the parameters mean — not just restate API docs.
- 03Error messages returned from a failed tool are treated as prompts by the LLM, so they should include actionable recovery guidance (e.g., 'Rate limited — wait 30 seconds and retry').
George Forger frames an MCP server as a bridge between an LLM and an external service, and argues that the details of that bridge — not the concept — are where things go wrong. His central thesis is that the tool description is more important than the tool implementation itself, because the LLM decides whether and when to call a tool based solely on that description. He contrasts a bad description ("Get token price") with a good one that specifies what the tool returns, when to use it, and what the parameters mean — writing it, he says, "as if you're explaining to a smart colleague when they should use this function."
For input validation, he recommends returning a helpful error as a tool response rather than throwing an exception, noting that thrown exceptions can crash the MCP connection.
The post covers a recommended project layout with one file per tool, a shared `api-client.ts` for retry and rate-limiting logic, and a `validation.ts` helper. On error handling, Forger makes the point that error messages are effectively prompts to the LLM: a message like "Error 500" leaves the LLM stuck, while "Rate limited — wait 30 seconds and retry" gives it actionable guidance. For input validation, he recommends returning a helpful error as a tool response rather than throwing an exception, noting that thrown exceptions can crash the MCP connection. Finally, he demonstrates using `p-queue` with an `intervalCap` of 10 requests per 60,000 ms to proactively throttle API calls and avoid hitting free-tier rate limits when an LLM fires multiple tool calls in rapid succession.
Key facts
- 01George Forger built 10 MCP servers in one week and distills the lessons into this guide.
- 02Tool descriptions should tell the LLM what the tool does, what it returns, when to use it, and what the parameters mean — not just restate API docs.
- 03Error messages returned from a failed tool are treated as prompts by the LLM, so they should include actionable recovery guidance (e.g., 'Rate limited — wait 30 seconds and retry').
- 04Bad input should be returned as a helpful tool-response error, not thrown as an exception — thrown exceptions can crash the MCP connection.
- 05LLMs hallucinate parameters (e.g., passing 'BTC' where a CoinGecko ID like 'bitcoin' is required), so server-side validation is essential.
- 06The recommended architecture uses one file per tool, a shared `api-client.ts` with retry and rate-limiting, and a `validation.ts` helper.
- 07Rate limiting is handled proactively with `p-queue` (e.g., `intervalCap: 10` per 60,000 ms) to avoid hitting free-tier API limits when the LLM makes rapid successive calls.
Topics
Summary and scoring are generated automatically from the original article. We always link back to the publisher and never republish images or paywalled content. Last processed Jun 7, 2026 · 12:45 UTC. How this works →