The Bespoke Integration Tax
A company ships three AI features — a support assistant, a sales copilot, and internal search — and runs five internal services: orders, CRM, billing, docs, and inventory. Each feature integrates each service it needs with bespoke code: its own auth handling, its own retry logic, its own error parsing. Three agents times five services means fifteen integrations to build, and when the orders API changes, every integration breaks separately.
This is the bespoke integration tax, and it scales as N x M. The Model Context Protocol (MCP) reframes the problem: each service exposes one MCP server, and each feature embeds one MCP client. Now the math is N + M — three clients plus five servers — and each integration exists exactly once.
Without MCP — N x M bespoke integrations:
Support ──┬── orders-api ──┐
├── crm-api ─────┤
└── billing-api ─┤
Sales ────┬── orders-api ──┤ 3 agents x 5 services
├── crm-api ─────┤ = 15 integrations
└── docs-api ────┤
Search ───┬── docs-api ────┤
└── inventory-api┘
With MCP — N + M:
Support ── MCP client ─┐
Sales ──── MCP client ─┼── MCP server (orders)
Search ─── MCP client ─┘ MCP server (crm)
MCP server (billing)
MCP server (docs)
MCP server (inventory)
3 clients + 5 servers = 8 components, each built once
The protocol does not make the integrations faster. It makes them single-sourced: one server per service that any MCP-capable client can use.
Clients, Servers, Tools, and Resources
MCP has four roles, and confusing them is the most common source of misunderstanding.
- Host: the application the user touches — an IDE, a CLI, a desktop app. It contains the LLM and the agent loop.
- Client: lives inside the host, maintains one connection to one server, and speaks JSON-RPC on the host's behalf.
- Server: a thin wrapper around your service. It exposes capabilities and nothing else — no model, no agent loop.
┌──────────────────────────┐ ┌──────────────────────────┐
│ Host │ │ MCP Server │
│ (IDE, CLI, desktop app) │ │ (your service) │
│ ┌────────────────────┐ │ │ ┌────────────────────┐ │
│ │ MCP Client │◄─┼─────────┼─►│ Tools (actions) │ │
│ └────────────────────┘ │ stdio │ │ Resources (data) │ │
│ │ or │ │ Prompts (templates)│ │
│ ┌────────────────────┐ │ HTTP │ └────────────────────┘ │
│ │ LLM + agent loop │ │ │ │
│ └────────────────────┘ │ │ │
└──────────────────────────┘ └──────────────────────────┘
Servers expose three primitives, and the distinction matters:
| Primitive | Who invokes it | Example |
|---|---|---|
| Tools | The model, mid-conversation | get_order, issue_refund |
| Resources | The client, on demand | config://orders |
| Prompts | The user, by selection | Summarize this ticket |
Tools are functions the model can call. Resources are data the client reads — files, configs, database records — addressed by URI. Prompts are reusable templates the host offers the user. Tools drive agent behavior; resources feed it; prompts structure it.
The Handshake and Message Flow
Every MCP session opens with a handshake, and the handshake is a capability negotiation, not an authentication exchange. Both sides declare what they support before any tool is called.
Client Server
│ │
│── initialize ──────────────────────►│ protocol version + capabilities
│◄─ result: protocolVersion, caps ────│
│── notifications/initialized ───────►│ (no response expected)
│ │
│── tools/list ──────────────────────►│
│◄─ [tool definitions + schemas] ─────│
│ │
│── tools/call {name, arguments} ────►│
│◄─ content + isError ────────────────│
│ │
│── resources/read {uri} ────────────►│
│◄─ resource content ─────────────────│
The messages are JSON-RPC 2.0. The initialize request:
{"jsonrpc": "2.0", "id": 1, "method": "initialize", "params": {"protocolVersion": "2024-11-05", "capabilities": {}}}
A tool call:
{"jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": {"name": "get_order", "arguments": {"order_id": "ORD-42"}}}
The server's initialize result declares which primitives it supports — tools, resources, prompts, sampling — and the client declares its own capabilities in return. After notifications/initialized, the session is live: the client lists tools, calls them, and reads resources. Requests carry an id and get responses; notifications carry none and get none. That single distinction keeps the protocol stateless per message.
Transports and When MCP Pays Off
MCP runs over two transports, and the choice is about where the server lives.
stdio spawns the server as a local subprocess and speaks JSON-RPC over stdin and stdout. It fits CLI tools and desktop apps: no ports, no auth headers, no deployment. The server dies with the host process, which is a feature for single-user tools and a limitation for anything shared.
HTTP (streamable HTTP) runs the server as a remote endpoint. It fits shared services that many agents call: proper auth, scaling, and monitoring, at the cost of operational burden.
| Transport | Best for | Watch out for |
|---|---|---|
| stdio | Local CLI tools, desktop apps, single-user | Server dies with the host process |
| HTTP | Shared services, multi-tenant, remote agents | Auth, scaling, deployment burden |
MCP pays off when one server must serve many different agents. A billing service wrapped once becomes callable by the support assistant, the sales copilot, and the internal search tool without any of them knowing how billing works. It does not pay off for a single client talking to a single service — there, a direct API call is simpler and one less protocol to debug.
The protocol also composes with everything around it: the tools a server exposes can be registered in a tool registry, and the selection problem across many servers is exactly tool selection under many options. Building your own server is the next topic.
Quiz
1. What integration problem does MCP solve?
2. What is the difference between an MCP tool and an MCP resource?
3. What is the purpose of the initialize handshake?
4. Which transport fits a shared service called by many remote agents?
Flashcards
Question
What integration problem does MCP solve?
Click to reveal answer
Answer
N agents x M services needs N x M bespoke integrations. MCP makes it N + M: one server per service, one client per agent.
Question
What are the three MCP primitives?
Click to reveal answer
Answer
Tools (model-invoked actions), resources (data the client reads), and prompts (reusable prompt templates).
Question
What happens during the initialize handshake?
Click to reveal answer
Answer
Client and server exchange protocol versions and capabilities, so both know what the other supports before any tool call.
Question
When does the stdio transport fit?
Click to reveal answer
Answer
Local, single-user setups: CLI tools and desktop apps where the server runs as a subprocess of the host.
Question
When does adopting MCP pay off?
Click to reveal answer
Answer
When one server must serve many different MCP-capable agents. For a single client-service pair, a direct API call is simpler.
Revision Notes
Key Takeaways
- 1. MCP reduces N x M bespoke integrations to N + M: one server per service, one client per agent
- 2. Tools are model-invoked actions; resources are data the client reads; prompts are user-selected templates
- 3. The initialize handshake negotiates capabilities before any tool is called
- 4. stdio fits local single-user tools; HTTP fits shared remote services
- 5. MCP pays off when one server serves many agents, not for a single client-service pair
Interview Tips
- • Open with the N x M math — it is the clearest way to show why the protocol exists
- • Distinguish tools from resources precisely; mixing them up signals shallow understanding
- • Walk the message flow: initialize, capability negotiation, tools/list, tools/call
- • Give the honest boundary: a single integration does not need MCP
Cheat Sheet
MCP Overview Cheat Sheet
The problem: N agents x M services = N x M bespoke integrations. MCP makes it N + M.
Roles:
- Host: the app (IDE, CLI, desktop) containing the LLM and agent loop
- Client: one per server connection, speaks JSON-RPC
- Server: thin wrapper around your service, no model inside
Primitives:
| Primitive | Invoked by | Example |
|---|---|---|
| Tools | The model | get_order |
| Resources | The client | config://orders |
| Prompts | The user | Summarize this ticket |
Message flow:
initialize → notifications/initialized → tools/list → tools/call → resources/read
Messages are JSON-RPC 2.0: requests carry an id and get responses; notifications carry none and get none.
Transports:
- stdio: local subprocess, stdin/stdout, single-user
- HTTP: remote endpoint, shared services, multi-tenant
When it pays off: one server serving many agents. Single pair → direct API call is simpler.
Continue Learning
Before You Start
- No prerequisites listed.