Skip to main content
MCP Deep Dive

Why MCP Exists

Ravinder··6 min read
MCPModel Context ProtocolAIArchitectureLLM Tooling
Share:
Part 1 of 10
Why MCP Exists

Every team building an AI-powered product eventually hits the same wall: you wire up a custom tool, it works beautifully with one model, and then six months later you want to switch models, add a second agent, or let a third-party system call the same tool — and everything falls apart. The glue code you wrote is load-bearing, undocumented, and completely model-specific.

This is the problem the Model Context Protocol (MCP) was designed to solve.

The Pre-MCP World Was a Mess of Bespoke Glue

Before MCP, connecting an LLM to an external capability meant one of a few bad options:

  • Hardcoded function schemas embedded in system prompts, duplicated across every model integration.
  • Vendor-specific plugin formats (OpenAI plugins, Anthropic tool_use blocks) that locked you to one provider.
  • RAG pipelines with no standard interface — every retrieval system invented its own protocol.

The result: each AI feature was a snowflake. Onboarding a new model meant rewriting the integration layer. Adding observability meant patching every connector by hand.

# The pre-MCP reality: hardcoded tool definitions per model
openai_tools = [
    {"type": "function", "function": {"name": "get_customer", "parameters": {...}}}
]
anthropic_tools = [
    {"name": "get_customer", "input_schema": {...}}  # Different format, same capability
]
gemini_tools = [...]  # Yet another format

This is not a tooling problem. It is an architectural problem. Without a shared protocol, every integration point becomes a proprietary API that cannot be reused.

Why Custom Plugin Systems Don't Scale

Most teams respond by building an internal plugin framework. It sounds reasonable — define your own standard, enforce it across the team. But this approach consistently fails for three reasons:

1. Capability negotiation is hard to get right. Which tools does this agent support? What version of the schema? A custom framework rarely answers these questions in a machine-readable way, so you end up with runtime failures and hand-rolled version checks.

2. Lifecycle management gets ignored. When does a tool session start? When does it end? Who cleans up stale connections? Custom plugins usually assume a stateless HTTP call and break the moment you need streaming, background jobs, or persistent sessions.

3. Security is bolted on. Auth is solved per-tool rather than at the protocol level, producing inconsistent scoping, audit gaps, and token leakage.

graph TD A[LLM Agent] -->|proprietary format A| B[Weather Tool] A -->|proprietary format B| C[Database Tool] A -->|proprietary format C| D[Calendar Tool] E[Second LLM Agent] -->|different proprietary format| B E -->|different proprietary format| C E -->|different proprietary format| D style A fill:#f9a825,color:#000 style E fill:#e53935,color:#fff style B fill:#1e88e5,color:#fff style C fill:#1e88e5,color:#fff style D fill:#1e88e5,color:#fff

Every arrow in that diagram is custom code. Add a third agent and you multiply the connectors. This is O(n×m) integration complexity.

MCP Collapses N×M to N+M

MCP introduces a clean separation: servers expose capabilities, clients consume them, and a shared protocol defines how they talk. Any MCP client can connect to any MCP server. The diagram becomes a hub:

graph TD A[LLM Agent A] --> P[MCP Protocol Layer] B[LLM Agent B] --> P C[LLM Agent C] --> P P --> X[MCP Server: CRM] P --> Y[MCP Server: Database] P --> Z[MCP Server: Calendar] style P fill:#43a047,color:#fff style A fill:#f9a825,color:#000 style B fill:#f9a825,color:#000 style C fill:#f9a825,color:#000 style X fill:#1e88e5,color:#fff style Y fill:#1e88e5,color:#fff style Z fill:#1e88e5,color:#fff

Add a new agent: it speaks MCP, it gets all servers for free. Add a new tool server: every agent gets access without touching agent code. This is the core value proposition — not magic, just a well-designed protocol boundary.

What MCP Actually Specifies

MCP is a JSON-RPC 2.0-based protocol that defines:

  • Initialization handshake — server announces its protocol version and capabilities; client acknowledges.
  • Three primitives — Tools (callable actions), Resources (readable data), and Prompts (reusable prompt templates).
  • Transport negotiation — stdio for local processes, SSE or HTTP for remote servers.
  • Lifecycle events — connect, initialize, shutdown, with explicit teardown.
// What a minimal MCP initialization looks like on the wire
const initRequest = {
  jsonrpc: "2.0",
  id: 1,
  method: "initialize",
  params: {
    protocolVersion: "2024-11-05",
    capabilities: {
      tools: {},
      resources: { subscribe: true }
    },
    clientInfo: {
      name: "my-agent",
      version: "1.0.0"
    }
  }
};

The protocol is intentionally minimal. It does not prescribe how you implement tools, what transport you must use, or how you handle auth beyond a token-passing convention. That restraint is deliberate — it makes the spec implementable across languages and runtimes in days, not months.

The Philosophical Shift: Tools as Services, Not Code

The deepest change MCP drives is conceptual. Before MCP, a "tool" was typically a function in the same process as the agent — a Python function decorated with @tool, registered in the LLM's function list. The tool and the agent were tightly coupled at the code level.

MCP treats tools as out-of-process services with a defined contract. The agent never sees the implementation. It only sees the schema. This means:

  • Tools can be written in any language.
  • Tools can be hosted by different teams or companies.
  • Tools can be versioned and deployed independently.
  • Tools can enforce their own auth, rate limits, and audit logging.

This is how mature service architectures work. MCP applies the same principle to AI tool integration.

Where This Series Goes

Understanding why MCP exists is step one. The rest of this series goes deeper:

  • Post 2 covers the full anatomy of an MCP server — how the handshake works in practice.
  • Post 3 dissects the three primitives and when to reach for each.
  • Posts 4–5 cover transport and auth.
  • Posts 6–7 are hands-on builds — a SaaS integration and a legacy system wrapper.
  • Posts 8–10 cover testing, observability, and distribution.

If you have ever written the same tool definition twice for two different models, MCP is the refactor you have been waiting for.

Key Takeaways

  • Custom per-model tool integrations create O(n×m) connector complexity that compounds with every new agent or capability.
  • Internal plugin frameworks repeatedly fail on capability negotiation, lifecycle management, and security — not because teams are bad engineers, but because these are genuinely hard protocol problems.
  • MCP reduces integration complexity to O(n+m) by defining a shared protocol layer between agents and tool servers.
  • MCP is JSON-RPC 2.0 with three primitives (Tools, Resources, Prompts), explicit lifecycle management, and transport negotiation.
  • The key conceptual shift is treating tools as out-of-process services with versioned contracts, not co-located functions.
  • Adopting MCP is an architectural decision, not just a library choice — it changes how teams own and deploy capabilities.
Part 1 of 10
Share: