MCP Anti-Patterns: What Not to Build and Why
Two MCP design anti-patterns — One-to-One API Mirroring and Overloaded Tools — reliably produce agents that behave unpredictably and users who stop trusting them.
Why Anti-Patterns Matter
MCP anti-patterns are seductive because they seem like the fastest path to shipping. Mirror the existing REST API — done in an afternoon. Build one flexible tool that handles every case — no need to design a full tool set. Both approaches produce agents that behave unpredictably and users who stop trusting them.
Anti-Pattern 1: One-to-One API Mirroring
One-to-one API mirroring is building an MCP tool for every endpoint of an existing REST API. If the Salesforce API has 300 endpoints, this approach creates 300 MCP tools.
Why it seems right: It's fast. The API is already defined. You're just wrapping it.
Why it fails: The LLM can't distinguish between 300 tools with similar names and overlapping purposes. The naming conventions of REST APIs are designed for developer consumption, not LLM reasoning. Tools named POST /opportunity, PATCH /opportunity/{id}, and PUT /opportunity/{id}/stage don't help an agent understand when to create vs. update vs. advance a deal.
The result is an agent that calls the wrong endpoint, sends malformed payloads, and fails in ways that are hard to debug. Users experience this as the agent being unreliable or "dumb." The actual problem is that the tool design forced the LLM to reason at the wrong level of abstraction.
The fix: Design tools at the business action level. create_opportunity, advance_opportunity_stage, and close_opportunity are three tools that replace 15 API endpoints with three clear intents.
Anti-Pattern 2: Overloaded Tools
An overloaded tool is a single tool that does many different things based on parameter combinations. The LLM has to figure out which parameter combination produces the desired behavior — and it will guess wrong in unpredictable ways.
Example of an overloaded tool:
A tool named process_request with parameters: action (approve/reject/escalate/query), request_id, comment, escalation_target, query_field. This is four tools pretending to be one. The approve action uses request_id and comment. The query action uses request_id and query_field. The escalate action uses all three. The LLM has no reliable way to know which parameters are relevant for which action.
Why teams build this: To reduce the number of tools in the tool set, or because the underlying system has a single "process" endpoint that the MCP tool mirrors.
Why it fails: LLMs select tools based on names and descriptions. An overloaded tool's description can't accurately convey four different behaviors in a way the LLM can reliably act on. Parameter combinations create a combinatorial space the LLM navigates by guessing.
The fix: Split overloaded tools into single-purpose tools. approve_request, reject_request, escalate_request, and query_request_status are four tools with clear names, clear parameters, and clear behaviors. The tool set is larger but the agent is dramatically more reliable.
A Third Anti-Pattern: Undocumented Failure Modes
A tool that can fail — because the target system is unavailable, because the input is invalid, because the user lacks permission — but doesn't return a structured error response forces the LLM to guess what went wrong. The agent either retries indefinitely, reports a nonsensical error, or silently drops the failed action.
Every MCP tool must have a defined failure response format. At minimum: a success flag, an error code the agent can act on, and a human-readable message. See the Error Handling extension for the full pattern.