The Model Context Protocol is the USB-C moment for AI tools. Here is what it actually is, how it works, and where it breaks.
Two years ago I built the same thing six times. A client would say "connect our LLM assistant to our CRM," and I would sit down and write the plumbing: a function schema for the model, a Python adapter for the CRM API, a loop that executed calls and fed results back, plus authentication, retries, and logging. Six different clients, six different CRMs, six different codebases — and every single one was the same engineering problem wearing a different logo.
When Anthropic released the Model Context Protocol in late 2024, I remember reading the spec on a flight and realizing it was aimed directly at that exact pain. Not a tool, not a library — a standard for how AI applications connect to the data and systems around them. The kind of thing that only becomes obviously necessary after you have written the same glue code enough times to feel it in your hands.
So what is MCP, actually, and does it live up to the hype? Let me break it down the way I had to, for myself.
The Problem MCP Solves
Before MCP, connecting an AI model to a tool meant an N×M integration problem. For every model/framework you use (your app, your agent, your IDE assistant) and every system you want it to reach (a database, a ticketing API, a file system), you write custom code. Your OpenAI assistant has one integration for the CRM, one for email, one for the ticketing system — each hand-built, each with its own auth, its own schema, its own error handling. Every new model vendor means redoing all of it. Every new tool means writing it all again.
MCP inverts the topology. Instead of N×M integrations, you get N+M: each tool exposes itself once, behind the protocol, and every client that speaks the protocol can use it without knowing anything about the underlying system. It is the difference between a charger with a proprietary port for every device and a USB-C port on everything.
This is the core insight and the reason the protocol matters: MCP standardizes the interface between AI applications and the tools they use, the way SQL standardized the interface between applications and databases. Once a tool speaks MCP, it works with every client that speaks MCP — no per-client rewrite.
The Vocabulary in Three Words
MCP has a small, learnable vocabulary, and that is a feature. Everything you will ever do with it is one of three things:
- Servers are the tools. A server wraps a system — a database, a file system, a web service — and exposes its capabilities over MCP.
- Clients are the AI applications. Your app, your agent framework, your IDE, a chat assistant. A client connects to one or more servers and presents their capabilities to the model.
- Hosts are where the user lives — the application process that owns the client and the model, like Claude Desktop or a code editor. The host is the product; clients and servers are the plumbing.
And the servers expose exactly three kinds of capability:
- Tools — things the model can invoke. Read a row, send an email, run a query. These are function calls with JSON schemas, exactly like the tool-calling you already use — just standardized.
- Resources — data the model can read. Files, database rows, documentation, context you want available without a function call.
- Prompts — reusable templates a user or app can trigger, pre-written instruction blocks for common tasks.
That is the whole model. Tools for acting, resources for reading, prompts for starting. Everything else in the spec is transport, security, and bookkeeping.
Resources and prompts get less attention than tools, so let me give them their due. A resource is how a server hands the model context without making it call a function first — say the latest campaign brief, a set of policy documents, or a database schema. The client can fetch a resource and inject it into the model's context automatically, which is exactly the retrieval step in a RAG system, standardized. A prompt is a reusable template the user or client can trigger — "summarize this ticket," "write a PR description from these changes." The server supplies the template and the required parameters; the client fills them and runs it. Both capabilities exist so that servers can contribute not just actions but context and workflows, and they matter more than the marketing suggests, because they are the difference between a tool that needs the model to know what to ask for and a system that brings the right context to the model on its own.
How the Conversation Actually Works
Here is the shape of a real MCP exchange, end to end:
┌────────────┐ 1. connect + initialize ┌────────────┐
│ │ ─────────────────────────▶ │ │
│ MCP Client │ 2. list tools/resources │ MCP Server │
│ (your app)│ ◀───────────────────────── │ (a tool) │
│ │ 3. model picks a tool │ │
│ │ ─── tool call ───────────▶ │ │
│ │ 4. server executes + │ │
│ │ 5. result streams back ◀── │ │
└────────────┘ └────────────┘
The lifecycle is deliberate. First the client and server handshake and negotiate versions and capabilities. Then the client asks what the server can do and gets back a list of tools with their schemas — this list is what the model sees and decides against. When the model calls a tool, the client forwards the request, and the server can stream progress and partial results as it works (long-running tools report progress so the user is not staring at a spinner for thirty seconds).
The two transports matter for deployment, so choose deliberately:
- stdin/stdout transport runs the server as a child process — the client spawns it. Simple, local, no ports to expose, ideal for a code editor running a file-system tool.
- Streamable HTTP transport runs the server as a network service — the client calls it over HTTP. This is how you give a tool to many clients, and how a cloud app reaches a shared backend.
I default to this rule: local, single-user, dev-time tools on stdio; anything shared, remote, or user-facing on HTTP.
A Minimal MCP Server
The protocol is JSON-RPC 2.0 under the hood, and there are official SDKs in TypeScript, Python, and others that handle the protocol so you only write the tool logic. Here is the smallest server worth reading — a TypeScript server that exposes one tool that reads the latest rows from a Postgres table:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const server = new McpServer({
name: "sales-pipeline",
version: "1.0.0",
});
server.tool(
"latest_deals",
"Return the most recently updated deals from the pipeline.",
{ limit: z.number().int().min(1).max(100).default(10) },
async ({ limit }) => {
const rows = await db.query(
`SELECT id, name, amount, updated_at
FROM deals
ORDER BY updated_at DESC
LIMIT $1`, [limit]);
return {
content: [{ type: "text", text: JSON.stringify(rows) }],
};
}
);
const transport = new StdioServerTransport();
await server.connect(transport);
That is a complete tool, exposed through a standard interface, callable by any MCP client. The same server file can be wired to an HTTP transport with two lines of changes, and suddenly every MCP-capable app in your organization can talk to your sales pipeline without you writing a single integration for any of them. That is the payoff, made concrete.
A practical note on testing a server before wiring it into an app: the SDKs ship a lightweight inspection tool (for the TypeScript SDK it is npx @modelcontextprotocol/inspector) that connects to your server and lets you list tools, inspect their schemas, and invoke them interactively. I run that before connecting any client — it catches the two bugs that otherwise burn an afternoon: a malformed JSON schema the client parses differently than you expected, and a tool that works in a unit test but times out on the real transport. Test the server in isolation first; debug against a live client second.
The Progress and Streaming Bits
Two details in the protocol are easy to miss and matter in production. First, tool calls are asynchronous: the server can return progress notifications while a long task runs, so a client can show "processing, 40% done" instead of an indefinite spinner. Second, tool results can include structured content, not just text — a result can carry an image, a resource, or structured data, which means a code-review tool can return a diff and a weather tool can return a chart. Design your server results with this in mind: return the structured thing the client can render, not a formatted string you force the model to re-parse.
Why This Changes Everything (The Honest Case)
The strategic argument is not about today's code; it is about the compounding effect of a standard:
- One integration, N consumers. You write the tool once. It works with every MCP client that ships — desktop assistants, IDEs, agent frameworks, your own apps. The six-CRMs problem collapses into six server modules, not six bespoke systems.
- Tools become composable. Since every server exposes schemas and descriptions the same way, an agent can assemble a workflow from heterogeneous systems — read from the CRM server, write to the email server, look up in the database server — without custom glue between each pair.
- The ecosystem snowballs. Every new MCP server released is a tool available to every client. The network effect is real, and it is the same dynamic that made package managers and USB-C win: participants stop building adapters because the standard absorbed the cost.
- The agent loop stops being bespoke. The protocol gives agents a uniform way to discover tools, call them, receive structured results, and stream progress. The "agentic" half of the stack — the reason I wrote six tool-adapters — becomes commodity plumbing.
Where It Still Breaks (Production Reality)
I am optimistic, but I am also the person who will get called when it misbehaves, so here is the honest list of problems:
-
Security is now your problem, per server. MCP gives you a uniform connection, not a security model. Every server you run is a new code path with its own permissions. A "file system" server with full read/write access is a prompt-injection blast radius — if the model is tricked into calling a destructive tool, the server executes it. Treat every tool result as untrusted data, scope permissions narrowly, and require human confirmation on mutating tools. I have seen teams ship an MCP server with
writeFileenabled and no confirmation. Do not be that team. - The protocol is young, and the ecosystem is uneven. Server quality varies wildly; some tools ship malformed schemas or sloppy error handling, and you will debug them like any dependency. Version negotiation exists but not every client implements the latest spec — expect mismatch headaches for a while.
- Auth is not a solved standard. The spec has grown authorization models, but in practice much of the ecosystem runs servers with no auth at all, or leans on host-level OAuth. For anything remote, you are still wiring up tokens yourself.
- Latency and context cost. Every tool description and schema the client fetches occupies model context. A client connected to thirty verbose servers can burn thousands of tokens just announcing its tools. Keep descriptions tight and only connect the servers the task needs.
- It is a standard, not a silver bullet. MCP will not make a bad agent good. It standardizes the adapter layer. The hard parts — retrieval quality, evaluation, guardrails — are untouched, and they are still where most systems fail.
When NOT to Use MCP
For a single app talking to a single API, MCP is overhead. If your LLM needs one endpoint and one function schema, just call the API directly — a protocol layer adds process, transport, and debugging surface with zero benefit. MCP pays off when the integration is reused: multiple clients, multiple tools, a shared backend, or an ecosystem where you want to publish a capability. The rule: adopt MCP when you would otherwise write the same adapter twice. Otherwise, a direct call is the right call.
The Practitioner's Checklist
If you are wiring an application or agent to MCP, work through this list:
- [ ] Started by listing every external system the model needs, and whether the integration will be reused by more than one client
- [ ] Chosen the transport: stdio for local/single-user, Streamable HTTP for shared/remote
- [ ] Exposed capabilities as the right primitives: tools for actions, resources for data, prompts for templates
- [ ] Written tool names and descriptions as contracts the model will actually read
- [ ] Validated inputs (zod) and returned structured, serializable content
- [ ] Scoped permissions per server — no broad file-system or mutation access without confirmation
- [ ] Treated all tool results as untrusted data for prompt-injection purposes
- [ ] Auth resolved for any remote server (tokens, not "trusted network")
- [ ] Measured context overhead from tool lists and trimmed verbose servers
- [ ] Checked version compatibility between your client SDK and server SDK
The Bottom Line
I am not usually early to standards — I have watched enough of them die to stay skeptical. MCP is different because it solves a problem I have personally paid for, repeatedly, and because it is already shipping in the tools people actually use. The moment your agent needs to reach outside itself — a database, a ticketing system, a file system, a browser — is the moment a protocol for that boundary stops being theoretical and starts saving you the same six weeks I spent, six times over.
Build one server. Point one client at it. You will feel the difference the first time a second client connects without you writing a single line of integration.
*Gulshan Yad
Top comments (0)