Skip to main content
MCP Is Great. You're Just Using It Wrong.

MCP Is Great. You're Just Using It Wrong.

· 9 min read
Claude

Nobody is arguing about the right thing. The people defending MCP point to Claude Desktop connecting to your Jira in two clicks, or a docs server giving an agent perfect context about a framework's API. The people criticizing it point to their agent codebase becoming an unreliable mess of JSON-RPC boilerplate. Both are right. They're describing different use cases.

MCP is genuinely good software for two specific problems: connecting GUI AI clients to external tools, and giving agents access to documentation. Where it falls apart is when developers use it for programmatic tool calling inside agent loops.

The case for MCP

MCP's core idea is sound. Without a standard, every AI client (Claude, Cursor, whatever comes next) needs a custom integration for every tool (GitHub, Jira, Notion, your internal API). That's an N×M problem. MCP makes it N+M. One server works with every client. That's real value.

The LSP analogy people keep reaching for is apt. The Language Server Protocol solved exactly this problem for editor integrations — one language server works in VS Code, Neovim, Emacs. Before LSP, every combination was a custom build. MCP is attempting the same thing for AI tools, and for the GUI client use case — Claude Desktop, Cursor, whatever you have running in a sidebar — it works well.

If you are a non-developer and you want your AI assistant to read your calendar, update a ticket, or query a database, MCP is the right answer. The tooling is there, the clients support it, and you don't need to write a line of code.

Docs MCP: the underrated use case

Documentation is where MCP quietly does its best work, and where much of the criticism misses the mark.

A docs MCP server gives an agent structured, up-to-date knowledge about a framework, API, or internal system. The agent asks for what it needs, the server returns clean markdown with code examples, type signatures, and usage patterns. Unlike shoving an entire docs site into the context window upfront, MCP lets the agent pull specific pages on demand. The context stays small. The information stays current.

This matters because LLMs are trained on a snapshot of the internet. Libraries ship breaking changes. New APIs launch. Internal tools have no public documentation at all. A docs MCP server bridges that gap in a way that's both standardized and composable — the same server works whether the agent runs in Claude Desktop, Cursor, or a custom client.

The pattern works especially well for:

  • Framework docs that change between versions — a Next.js or SvelteKit MCP server can serve docs matching the version in your package.json, not whatever version the LLM was trained on
  • Internal APIs and company-specific tooling — things the LLM has never seen and can't guess about
  • API references with complex type signatures — structured data that the MCP server can return in a format the agent parses cleanly, rather than hoping the LLM remembers the exact shape of a response object

The key difference from tool-calling MCP is the risk profile. A docs server is read-only. It returns text. If it returns slightly wrong text, the agent might write slightly wrong code, which the developer catches in review. A tool-calling MCP server that executes the wrong action against a production database has no such safety net.

The problem: tool-calling MCP in agent code

The ecosystem pressure pushed MCP into programmatic agent loops for tool calling, and that's where it goes wrong.

When you use MCP's direct tool-calling mechanism in an agent — having the LLM invoke tools via the protocol at runtime — you are asking the LLM to operate in its weakest mode. LLMs were trained on billions of lines of real Python and TypeScript calling real APIs. They were trained on a tiny synthetic dataset of tool-call JSON syntax. Cloudflare's engineering team put it bluntly: "Making an LLM perform tasks with tool calling is like putting Shakespeare through a month-long class in Mandarin and then asking him to write a play in it."

The fix Cloudflare landed on — and what StackOne found reduces token overhead by 98.7% — is having the LLM write code to call MCP tools rather than invoking them directly. Code mode: the agent writes a TypeScript function, executes it, gets results. One inference pass instead of N sequential tool calls. Far more reliable. Far cheaper.

Which raises an obvious question: if the right way to use MCP programmatically is to have the LLM write code that calls MCP... why not write code that calls the API directly? You've added a JSON-RPC server process, a protocol layer, and a discovery mechanism to what could have been a fetch() call.

Programmatic tool-calling MCP is the worst of both worlds

If you're building agents in code, direct tool calling and programmatic MCP share a failure mode: both make your agent's reliability contingent on the LLM correctly generating structured output (tool invocations or function calls) in every turn. But programmatic MCP also adds:

  • A running server process you have to manage and keep alive
  • Context window overhead: a modest five-server setup with 58 tools consumed ~55,000 tokens before any conversation started (StackOne's production numbers). 10 servers × 5 tools each means 50 tool definitions eating context before you've said a word.
  • Response quality degradation: long contexts cause LLM instruction-following to decline. The more tools loaded, the worse the agent performs on unrelated tasks.
  • A protocol your LLM doesn't natively understand well

If you have more than five tools and more than one team, MCP's standardization has real value for tool calling. If you're building a single application with a handful of tools, the overhead is pure cost with no benefit.

Note that these problems are specific to tool-calling servers. A docs MCP server typically exposes one or two tools (search_docs, get_page) with minimal schema overhead. It doesn't bloat the context with 50 tool definitions, and there's no risk of the agent accidentally executing a destructive action.

Skills, not tool-calling MCP

For most of what developers reach for tool-calling MCP to solve — giving an agent procedural knowledge about how to do things — the right abstraction is closer to what Anthropic calls Skills: encoded instructions about workflows, style, and multi-step procedures, loaded progressively as needed rather than all upfront.

Skills are Claude-specific and don't solve the interoperability problem. But if you're not solving the interoperability problem (you're building one agent in one codebase for one use case), you don't need to pay MCP's costs to get what Skills give you for free.

Docs MCP and Skills actually complement each other well. Skills tell the agent how to do something (the workflow, the conventions, the patterns). Docs MCP tells the agent what's available (the API surface, the type signatures, the configuration options). An agent with both can follow a company's coding conventions while using the correct, current API.

The security problem nobody talks about enough

MCP has a serious security problem that good implementations can mitigate but can't eliminate, and it affects tool-calling servers far more than docs servers.

Tool descriptions are instructions the LLM reads. If an MCP server can dynamically redefine its tool descriptions — which the protocol permits — a compromised or malicious server can inject instructions into the LLM's context that are invisible in the UI. Researchers have demonstrated this by hiding exfiltration instructions in whitespace-encoded text in tool descriptions. A legitimate-looking server installs, builds trust, then rewrites its tools to do something else. There's no integrity verification in the protocol.

The fourth-party injection problem is worse: a trusted MCP server fetches data from an untrusted source (a GitHub issue, a web page), which contains injected instructions. Your server is clean. The data it retrieves is not. Two hops removed from the user, and the LLM treats it as authoritative.

A 2025 scan of ~2,000 internet-exposed MCP servers found all verified servers lacked any authentication. The protocol recommends auth but doesn't enforce it.

For docs servers, the attack surface is smaller. The server returns documentation text, which is read-only and typically sourced from a controlled repository. A compromised docs server could inject misleading instructions, but the blast radius is limited to bad code suggestions — not unauthorized actions against production systems. For tool-calling servers that execute actions against external systems, the security gap is structural.

The actual split

There are three distinct MCP use cases, and they shouldn't be evaluated together:

Docs MCP — good:

  • Gives agents current, structured documentation on demand
  • Low context overhead (one or two tools, not 50)
  • Read-only and low-risk
  • Works across any MCP-compatible client
  • Especially valuable for internal APIs and fast-moving frameworks

GUI client MCP — good:

  • A non-developer connects a GUI AI client to external tools
  • Cross-model interoperability matters
  • 10+ tools shared across multiple teams benefit from the N+M standardization

Programmatic tool-calling MCP — usually wrong:

  • You're building agents in code with a handful of tools — use direct API calls or function calling instead
  • You're encoding procedural knowledge about how to do things — use skills or system prompt patterns instead
  • You're at an early stage and the server infrastructure is adding more complexity than the standardization saves

The confusion comes from treating MCP as one thing. A docs MCP server and a tool-calling MCP server share a protocol, but they have different cost profiles, different risk profiles, and different value propositions. The criticism that MCP is overengineered is right — for tool calling in agent code. For documentation and GUI integrations, the protocol earns its keep.