---
title: "14 — MCP — One Plug So Any AI Talks to Any Tool"
uid: model-context-protocol
excerpt: "MCP is USB-C for AI tools: one standard plug collapses the M×N integration explosion into M+N. Written slowly, one step at a time."
date: 2026-08-12T18:35:10+0000
source: https://www.aveshina.my.id/en/blog/model-context-protocol
---

The name sounds like a networking textbook, and most explanations jumped straight into the deep end, assuming I already knew half the vocabulary. So I'm writing these notes the way I wish someone had explained MCP to me: slowly, one step at a time, with every role spelled out before it's used.

The whole thing is one sentence: **MCP is USB-C for AI tools.**

Think about what USB-C did for chargers. Before it, every phone and laptop had its own plug shape, and every gadget shipped with its own cable. You'd buy a new accessory and hope the right cable was in the box. After USB-C, one plug shape talks to everything — your laptop, your phone, your monitor, your keyboard, all through the same hole.

MCP does the same job, but for the gap between an **AI model** and the **tools** it needs to reach. An AI model is the thing that writes text for you — Claude, GPT, Gemini, and so on. A tool is anything the model could use to get its work done — reading a file, searching a database, posting to GitHub, sending a Slack message. MCP defines one standard plug so any model can talk to any tool [1].

But "one standard plug" is a claim, not an explanation. To actually understand MCP, we need to see three things, in this order, without skipping ahead:

1. The problem it solves — why "just connect them" is a mess.
2. The three roles involved — who does what.
3. One request walking through the whole system, step by step.

Let's go in that order.

## The problem: too many wires

Start from scratch. Say you're building an AI app. You picked a model — Claude, say. Now you want it to do something useful, like read a customer's order from your database.

The model can't do that by itself. Out of the box, an AI model just writes text. It has no way to reach into your database and pull out a row. To make that happen, you have to build a little bridge: a piece of code that knows how to talk to your database, on the model's behalf.

That bridge is what people call an _integration_ or a _connector_ — a custom piece of glue joining one specific model to one specific tool.

Now here's the problem. You probably don't have one model and one tool. You might have several models (Claude, GPT, Gemini, plus a few local ones), and several tools (your database, GitHub, the filesystem, Slack, a search index). For each model to reach each tool, someone has to build a bridge for that specific pair:

- Claude ↔ database: one bridge
- Claude ↔ GitHub: another bridge
- GPT ↔ database: another bridge
- GPT ↔ GitHub: another bridge
- ...and so on for every combination.

With M models and N tools, you need M × N bridges. Five models and ten tools? Fifty bridges. Every time you add one model, you write N new bridges. Every time you add one tool, you write M new ones.

This is a familiar mess. It's the same shape as editors and language servers before something called LSP fixed it (every editor had to hand-write support for every language), and the same shape as chargers before USB-C [4]. Don't worry if you haven't heard of LSP — the point is just that this problem has been solved before, with a standard plug, and MCP is the newest version of that old idea.

In practice, most teams don't build fifty bridges. They give up. They hard-wire their app to one model and two tools, because each extra bridge costs more time than it's worth. That's the exact problem MCP exists to solve.

```figure
<svg viewBox="0 0 740 340" xmlns="http://www.w3.org/2000/svg" class="my-6 w-full max-w-2xl" role="img" aria-label="Two integration patterns side by side. Left panel titled Before MCP: the M×N mess. Three AI client boxes on the left (Claude, GPT, Gemini) each connect with tangled colored lines to four tool boxes on the right (Database, GitHub, Filesystem, Slack), twelve crossing wires total. Right panel titled With MCP: M+N. The same three clients and four tools all plug into a thin vertical bar in the middle labelled MCP — one standard connection per side, no tangled wires.">
  <defs>
    <marker id="mcp-arr" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto">
      <path d="M0,0 L10,5 L0,10 z" fill="#64748b"/>
    </marker>
  </defs>
  <g font-family="ui-sans-serif, system-ui, sans-serif" text-rendering="geometricPrecision">

    <!-- LEFT panel: M×N -->
    <rect x="10" y="30" width="350" height="290" rx="10" fill="#fef2f2" stroke="#dc2626" stroke-width="1.2"/>
    <text x="185" y="52" font-size="13" font-weight="700" fill="#7f1d1d" text-anchor="middle">Before MCP — the M×N mess</text>

    <!-- clients -->
    <g font-size="10.5" font-weight="700" fill="#1e1b4b" text-anchor="middle">
      <rect x="28" y="80" width="78" height="30" rx="6" fill="#e0e7ff" stroke="#6366f1" stroke-width="1.2"/>
      <text x="67" y="99">Claude</text>
      <rect x="28" y="165" width="78" height="30" rx="6" fill="#e0e7ff" stroke="#6366f1" stroke-width="1.2"/>
      <text x="67" y="184">GPT</text>
      <rect x="28" y="250" width="78" height="30" rx="6" fill="#e0e7ff" stroke="#6366f1" stroke-width="1.2"/>
      <text x="67" y="269">Gemini</text>
    </g>

    <!-- tools -->
    <g font-size="10" font-weight="700" fill="#052e16" text-anchor="middle">
      <rect x="258" y="80" width="92" height="26" rx="6" fill="#dcfce7" stroke="#16a34a" stroke-width="1.2"/>
      <text x="304" y="97">Database</text>
      <rect x="258" y="148" width="92" height="26" rx="6" fill="#dcfce7" stroke="#16a34a" stroke-width="1.2"/>
      <text x="304" y="165">GitHub</text>
      <rect x="258" y="216" width="92" height="26" rx="6" fill="#dcfce7" stroke="#16a34a" stroke-width="1.2"/>
      <text x="304" y="233">Filesystem</text>
      <rect x="258" y="284" width="92" height="26" rx="6" fill="#dcfce7" stroke="#16a34a" stroke-width="1.2"/>
      <text x="304" y="301">Slack</text>
    </g>

    <!-- 12 tangled wires -->
    <g stroke="#f59e0b" stroke-width="1" fill="none" opacity="0.85">
      <path d="M106,95 C180,95 180,93 257,93"/>
      <path d="M106,95 C180,160 180,161 257,161"/>
      <path d="M106,95 C180,225 180,229 257,229"/>
      <path d="M106,95 C180,290 180,297 257,297"/>
      <path d="M106,180 C180,95 180,93 257,93"/>
      <path d="M106,180 L257,161"/>
      <path d="M106,180 C180,220 180,229 257,229"/>
      <path d="M106,180 C180,295 180,297 257,297"/>
      <path d="M106,265 C180,95 180,93 257,93"/>
      <path d="M106,265 C180,150 180,161 257,161"/>
      <path d="M106,265 L257,229"/>
      <path d="M106,265 C180,295 180,297 257,297"/>
    </g>
    <text x="185" y="312" font-size="10" fill="#7f1d1d" text-anchor="middle" font-style="italic">3 clients × 4 tools = 12 bespoke connectors</text>

    <!-- RIGHT panel: M+N -->
    <rect x="380" y="30" width="350" height="290" rx="10" fill="#ecfeff" stroke="#0891b2" stroke-width="1.2"/>
    <text x="555" y="52" font-size="13" font-weight="700" fill="#083344" text-anchor="middle">With MCP — M+N</text>

    <!-- clients -->
    <g font-size="10.5" font-weight="700" fill="#1e1b4b" text-anchor="middle">
      <rect x="398" y="80" width="78" height="30" rx="6" fill="#e0e7ff" stroke="#6366f1" stroke-width="1.2"/>
      <text x="437" y="99">Claude</text>
      <rect x="398" y="165" width="78" height="30" rx="6" fill="#e0e7ff" stroke="#6366f1" stroke-width="1.2"/>
      <text x="437" y="184">GPT</text>
      <rect x="398" y="250" width="78" height="30" rx="6" fill="#e0e7ff" stroke="#6366f1" stroke-width="1.2"/>
      <text x="437" y="269">Gemini</text>
    </g>

    <!-- MCP bar -->
    <rect x="525" y="78" width="50" height="204" rx="6" fill="#c7d2fe" stroke="#4f46e5" stroke-width="1.5"/>
    <text x="550" y="178" font-size="11" font-weight="800" fill="#312e81" text-anchor="middle" transform="rotate(-90 550 178)">MCP standard plug</text>

    <!-- tools -->
    <g font-size="10" font-weight="700" fill="#052e16" text-anchor="middle">
      <rect x="624" y="80" width="92" height="26" rx="6" fill="#dcfce7" stroke="#16a34a" stroke-width="1.2"/>
      <text x="670" y="97">Database</text>
      <rect x="624" y="148" width="92" height="26" rx="6" fill="#dcfce7" stroke="#16a34a" stroke-width="1.2"/>
      <text x="670" y="165">GitHub</text>
      <rect x="624" y="216" width="92" height="26" rx="6" fill="#dcfce7" stroke="#16a34a" stroke-width="1.2"/>
      <text x="670" y="233">Filesystem</text>
      <rect x="624" y="284" width="92" height="26" rx="6" fill="#dcfce7" stroke="#16a34a" stroke-width="1.2"/>
      <text x="670" y="301">Slack</text>
    </g>

    <!-- clean links -->
    <g stroke="#4f46e5" stroke-width="1.5" fill="none">
      <line x1="476" y1="95" x2="524" y2="95" marker-end="url(#mcp-arr)"/>
      <line x1="476" y1="180" x2="524" y2="180" marker-end="url(#mcp-arr)"/>
      <line x1="476" y1="265" x2="524" y2="265" marker-end="url(#mcp-arr)"/>
      <line x1="576" y1="93" x2="622" y2="93" marker-end="url(#mcp-arr)"/>
      <line x1="576" y1="161" x2="622" y2="161" marker-end="url(#mcp-arr)"/>
      <line x1="576" y1="229" x2="622" y2="229" marker-end="url(#mcp-arr)"/>
      <line x1="576" y1="297" x2="622" y2="297" marker-end="url(#mcp-arr)"/>
    </g>
    <text x="555" y="312" font-size="10" fill="#083344" text-anchor="middle" font-style="italic">3 + 4 = 7 standard connections</text>
  </g>
</svg>
```

That figure is the whole idea in one picture. On the left, every model is wired to every tool by hand — the tangle. On the right, everything plugs into one standard bar in the middle. The payoff: add one tool, and you write one adapter instead of one per model. **M×N collapses into M+N.**

## The three roles

Now we need names for the parts. Every explanation of MCP uses these three words, and it's easy to get lost if they're dropped on you all at once. Take them one at a time — and hold onto them, because the step-by-step walkthrough below depends on them.

**The host** is the app the _human_ is actually using. If you're chatting in the Claude desktop app, that app is the host. If you're coding in an editor with an AI extension, the editor-plus-extension is the host. It's the thing with the buttons and windows. It's also where the important decisions happen — which tools you're allowed to use, and what needs your approval — because it's the app you're running.

**The server** is a small program that wraps one tool in the MCP shape. There's an MCP server for your database, one for GitHub, one for the filesystem, and so on. Each server does two jobs: it _advertises_ what it can do ("I can run a query, I can list tables"), and it _answers_ requests when asked ("here's the result of that query"). Think of a server as a translator: it takes a plain tool that doesn't speak MCP, and teaches it to speak MCP.

**The client** is the code that lives _inside_ the host and does the talking on the model's side. It's the plug itself. When the model wants to use a tool, the client is the piece that sends the request over to the server. One app can hold several clients — one for each server it's connected to.

So the shape to remember is: **the host runs, the client plugs in, the server wraps the tool.** The human sits at the host, the plug sits in the middle, and the wrapped tool sits on the other end [1][3].

The language these three speak is called **JSON-RPC**. Don't let the name scare you — it just means "a standard way to send a request and get a reply, formatted as JSON." MCP's designers picked it instead of inventing a new transport, because language servers already used it [4]. It's boring plumbing, and that's the point.

## One request, step by step

Now let's watch a single request travel through the whole system. This is the part most explanations skip, and it's the part that makes everything else click. We'll use a concrete example: you type "find the customer named Alice."

**Step 1 — You ask the host.** You type the question into the app (the host). That's the only step you, the human, ever do yourself.

**Step 2 — The host starts the server.** The host already knows which servers it's connected to. It makes sure the database server is running, and the client — the plug inside the host — connects to it.

**Step 3 — The server introduces itself.** As soon as it connects, the server tells the client what it can do: "I'm a database server. I can run a SQL query, list tables, read a schema." This is the _advertisement_ step. Now the model, through the client, knows what's on the menu.

**Step 4 — The model decides to call a tool.** The model reads your question and thinks, "I need to search the database." It writes out a request: "call the query_db tool, with the SQL SELECT * FROM customers WHERE name = 'Alice'." Notice the key detail: **the model doesn't run anything.** It just _proposes_ the call.

**Step 5 — The host checks permission.** The model's request lands at the host. This is where safety lives. The host checks whether the model is allowed to touch this tool right now. If you've set it to ask first, the host shows you a small prompt: "Allow this query?" You approve, and only then does the request move on.

**Step 6 — The server does the work.** The client sends the request to the server over JSON-RPC. The server runs the query against the real database, gets the result — Alice's row — and sends it back, again over JSON-RPC.

**Step 7 — The result becomes context.** The result arrives back at the host, which hands it to the model as fresh input. The model reads it and writes your answer: "Alice is customer #42, in Jakarta." You see the reply.

That's the whole trip. Seven steps, and each one is simple on its own. **The model proposes, the host decides, the server executes, and the result flows back.** Once you've traced one request, MCP stops feeling mysterious.

## What else the plug can carry

The walkthrough above used a _tool_ — the model asked the server to do something. But MCP's standard plug can carry four different kinds of things. Slow down here, because this is where the shape of the protocol really shows.

- **Tools** — things the _model_ asks to run. query_db, create_issue, read_file. The model decides to call one mid-conversation, the host runs it, and the result comes back as context. This is the one we just traced, and it's the most common kind [3].
- **Resources** — data the _app_ attaches for the model, without the model asking. File contents, database rows, log lines. Think of it as the app quietly handing the model some reading material up front, so the model has context before it even starts.
- **Prompts** — ready-made instruction templates the server ships. "Summarize this repo," "Draft a release note." The host shows them in its menu; you click one and it's filled in for you. Pre-baked, not invented on the spot.
- **Sampling** — the reverse direction. Normally the model talks to the server. With sampling, the _server_ asks the _model_ to write some text. This lets a server-side workflow borrow the model's brain without you going back and forth. The server requests, the host approves, the model writes [1].

The sentence that finally made all four stick for me: **tools go out, resources come in, prompts are pre-baked, sampling borrows the model back.** Four primitives, four directions, one wire.

```figure
<svg viewBox="0 0 560 220" xmlns="http://www.w3.org/2000/svg" class="my-6 w-full max-w-xl" role="img" aria-label="Four MCP primitives and their direction of flow. A central box labelled MCP server. An arrow going OUT to the right labelled Tools — model invokes a function. An arrow coming IN from the left labelled Resources — application attaches data. A small box labelled Prompts sitting on top of the server — pre-baked templates the host surfaces. A dashed arrow going back INTO the server labelled Sampling — server borrows the client's model.">
  <defs>
    <marker id="mcp-o" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto">
      <path d="M0,0 L10,5 L0,10 z" fill="#4f46e5"/>
    </marker>
    <marker id="mcp-i" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto">
      <path d="M0,0 L10,5 L0,10 z" fill="#0891b2"/>
    </marker>
  </defs>
  <g font-family="ui-sans-serif, system-ui, sans-serif" text-rendering="geometricPrecision">

    <!-- Resources IN (left) -->
    <rect x="20" y="120" width="120" height="40" rx="8" fill="#ecfeff" stroke="#0891b2" stroke-width="1.5"/>
    <text x="80" y="138" font-size="12" font-weight="700" fill="#083344" text-anchor="middle">Resources</text>
    <text x="80" y="153" font-size="9.5" fill="#475569" text-anchor="middle">data IN · app-controlled</text>

    <!-- Prompts (top) -->
    <rect x="220" y="20" width="120" height="40" rx="8" fill="#fce7f3" stroke="#db2777" stroke-width="1.5"/>
    <text x="280" y="38" font-size="12" font-weight="700" fill="#500724" text-anchor="middle">Prompts</text>
    <text x="280" y="53" font-size="9.5" fill="#475569" text-anchor="middle">pre-baked templates</text>

    <!-- Server center -->
    <rect x="200" y="120" width="160" height="60" rx="10" fill="#e0e7ff" stroke="#4f46e5" stroke-width="1.8"/>
    <text x="280" y="146" font-size="13" font-weight="800" fill="#1e1b4b" text-anchor="middle">MCP server</text>
    <text x="280" y="164" font-size="10" fill="#475569" text-anchor="middle">JSON-RPC · capabilities</text>

    <!-- link prompts to server -->
    <line x1="280" y1="60" x2="280" y2="119" stroke="#db2777" stroke-width="1.2" stroke-dasharray="3 3"/>

    <!-- Tools OUT (right) -->
    <rect x="420" y="120" width="120" height="40" rx="8" fill="#dcfce7" stroke="#16a34a" stroke-width="1.5"/>
    <text x="480" y="138" font-size="12" font-weight="700" fill="#052e16" text-anchor="middle">Tools</text>
    <text x="480" y="153" font-size="9.5" fill="#475569" text-anchor="middle">function OUT · model-invoked</text>

    <!-- Sampling (bottom, dashed back in) -->
    <rect x="200" y="185" width="160" height="30" rx="8" fill="#fef9c3" stroke="#ca8a04" stroke-width="1.5" stroke-dasharray="4 3"/>
    <text x="280" y="204" font-size="11" font-weight="700" fill="#422006" text-anchor="middle">Sampling · server borrows the model</text>

    <!-- arrows -->
    <line x1="140" y1="140" x2="199" y2="140" stroke="#0891b2" stroke-width="1.5" marker-end="url(#mcp-i)"/>
    <line x1="360" y1="140" x2="419" y2="140" stroke="#4f46e5" stroke-width="1.5" marker-end="url(#mcp-o)"/>
    <line x1="280" y1="185" x2="280" y2="181" stroke="#ca8a04" stroke-width="1.5" marker-end="url(#mcp-i)" transform="translate(0,-2)"/>
  </g>
</svg>
```

## Why it's worth understanding and not just using

The thing that turned MCP from "another thing I install" into "a thing I actually want to understand" was noticing how much it borrows from older, boring, successful designs. The host/client/server split mirrors LSP, which is why every editor now talks to every language server. The "introduce yourself" step mirrors how HTTP Allow headers and OpenAPI specs describe what an endpoint can do. And the choice to ride JSON-RPC instead of inventing a transport is the same pragmatic call LSP made [4]. MCP isn't exotic; it's a pattern that has solved this exact class of problem before, pointed at the new boundary that AI models created.

That lineage is also why I stopped trusting the roadmap's framing of MCP as "a rulebook for how to pack context into a prompt" [2]. MCP does standardize how context _flows_ between server and model, but the heart of it is the **integration contract** — the standard plug — not the prompt-packing format. Getting that distinction right matters for the next decision: when to reach for MCP, and when not to.

## How I use this

The practical habit these notes left me with is a one-question gate before I wire anything into an AI app: _is there an MCP server for this already?_ The official registry and the community ecosystem have grown fast — filesystem, Postgres, GitHub, Slack, Sentry, Playwright, Puppeteer, and more are already there [1][4]. If one exists, I point my client at it and skip the glue. If none exists, I weigh whether what I'm exposing is general enough that writing a small MCP server — rather than a one-off function-call stub — is worth it. Because once it's a server, every MCP-speaking client can use it for free. That compound payoff, not any single feature, is the whole point of agreeing on the plug.

## References

[1] Anthropic, "Model Context Protocol — Introduction," modelcontextprotocol.io, 2024. [Online]. Available: [https://modelcontextprotocol.io/introduction](https://modelcontextprotocol.io/introduction)

[2] "Model Context Protocol (MCP)," Frontend roadmap, roadmap.sh. [Online]. Available: [https://roadmap.sh/frontend/mcp](https://roadmap.sh/frontend/mcp)

[3] DeepLearning.AI, "MCP: Build Rich-Context AI Apps with Anthropic," 2025. [Online]. Available: [https://www.deeplearning.ai/short-courses/mcp-build-rich-context-ai-apps-with-anthropic/](https://www.deeplearning.ai/short-courses/mcp-build-rich-context-ai-apps-with-anthropic/)

[4] G. Li, "The Ultimate Guide to MCP," guangzhengli.com, 2025. [Online]. Available: [https://guangzhengli.com/blog/en/model-context-protocol](https://guangzhengli.com/blog/en/model-context-protocol)

[5] "modelcontextprotocol," GitHub repository. [Online]. Available: [https://github.com/modelcontextprotocol/modelcontextprotocol](https://github.com/modelcontextprotocol/modelcontextprotocol)

```quiz
Q: With 4 AI clients and 5 tools, how many bespoke connectors does the M×N integration pattern require?
- 9
- 20
- 4
correct: 1
explain: M×N: 4 clients × 5 tools = 20 hand-rolled connectors. Each new client or tool adds a whole row or column of glue.

Q: MCP's role split is fixed as three parts. Which set is correct?
- model, tokenizer, prompt
- client, host, server
- client, gateway, cache
correct: 1
explain: MCP defines the client (the plug inside the app), the host (the app the human runs, managing clients and permissions), and the server (the tool wrapped in MCP shape).

Q: A model decides mid-conversation to call query_db. Which MCP primitive is this?
- a Resource
- a Tool
- a Prompt
- Sampling
correct: 1
explain: Tools are model-invoked functions. Resources are pulled by the application; Prompts are pre-baked templates; Sampling is the server borrowing the client's model.

Q: What does MCP actually standardize over the wire?
- the prompt's text formatting only
- the integration contract between an AI client and a tool/data source
- the model's training data distribution
correct: 1
explain: MCP is an integration protocol (JSON-RPC, capability advertisement) — a standard plug so any client talks to any server. It does standardize context flow as a side effect, but its soul is the integration contract.

Q: The roadmap blurb frames MCP as "a rulebook for packing context into a prompt." What's wrong with that framing?
- nothing — it's exactly right
- it misses the point: MCP's core is the standard integration plug, collapsing M×N into M+N
correct: 1
explain: Context does flow over MCP, but that's a consequence. The actual problem it solves is the per-pair integration explosion — the same shape LSP and USB-C fixed for their domains.
```
