Library
00/07 · ~34 min
GUIDEDECK · for connecting AI to your tools

MCP — the open protocol
that plugs AI into everything.

A 34-minute working session on the Model Context Protocol: the M×N integration problem it solves, the host / client / server architecture, the three primitives a server exposes, and how to build and ship a small server of your own.

~34 MINBEGINNER → INTERMEDIATEAI ENGINEERING
SCROLL
01 · Why MCP exists 4 min

One protocol instead of
M×N custom integrations.

Every AI app wants to reach your real tools and data — files, databases, GitHub, Slack. Before MCP, each app wired up each tool by hand. That's a quadratic mess: M apps × N tools bespoke connectors to build and maintain. MCP replaces all of them with one common interface.

MCPModel Context Protocol — is an open standard, introduced by Anthropic and now used across the industry, for connecting AI applications to outside tools and data through a single shared interface. Think of it as a USB-C port for AI: build a tool once as an MCP server, and any MCP-capable app can plug into it — no custom glue per app.
Without MCP — M×N glue
IDEChatbotAgent GitHubPostgresSlackFiles

3 apps × 4 tools = 12 hand-built connectors — and it grows every time either side adds one.

With MCP — M + N
IDEChatbotAgent GitHubPostgresSlackFiles MCP protocol

Each side speaks MCP once. 3 + 4 = 7 integrations, and any new app gets all tools for free.

The before-MCP pain

  • Quadratic glue.Want your IDE assistant, your chatbot, and your agent to each read the same database? That's three separate connectors — each with its own auth, error handling, and quirks.
  • No reuse.The Slack integration you wrote for one app couldn't be dropped into another; everyone re-solved the same problem.
  • One open standard fixes both. Build a Slack server once; every MCP host — today and tomorrow — can use it without you writing a line of app-specific code.
02 · The architecture 5 min

Host app MCP client MCP server.

MCP has exactly three roles, and they nest cleanly. The host is the AI app you use. Inside it, one client manages each connection. Across that connection sits a server that exposes a real capability. Learn these three words and the rest of the protocol falls into place.

Host— the AI application the user runs (Claude Desktop, an IDE extension, an agent).  Client — a connector insidethe host that owns a 1:1 link to one server and speaks the protocol.  Server — a separate program that wraps a tool or data source (your files, a database, an API) and offers it over MCP.
Host app Claude Desktop / IDE client client client + the LLM server filesystem server github MCP disk API

One host, many clients — each client owns exactly one server connection. The server is the only thing that touches the outside system.

Read the schema

  • The host holds the LLM and the conversation. It decides when to call a tool; it never talks to a database itself.
  • Each client is a thin connector with a 1:1 link to one server — separate connections stay isolated from each other.
  • Each server wraps one capability and runs as its own process. It can be local (a subprocess on your machine) or remote (a service over the network).
  • Messages are JSON-RPC 2.0 — plain request/response (and notifications) in JSON. The same wire format whether local or remote.
Host

The app you use

Runs the model, owns the chat, and asks the user for permission. It orchestrates one or more clients.

Client

The connector

Lives inside the host, one per server. Handles the handshake, relays requests, and keeps each server's session separate.

Server

The capability

A standalone program exposing tools, resources, and prompts. This is the part you usually write or install.

03 · The primitives 6 min

What a server exposes:
tools, resources, prompts.

A server doesn't hand the model raw access to anything. It offers three kinds of building blocks — called primitives — each with a different controller. Knowing who drives each one is the key to using them well.

Primitive — one of the three standard things an MCP server can expose. Tools are actions the model can choose to call. Resources are read-only data the app can load for context. Prompts are reusable templates the user deliberately invokes. Different controller, different job.

Tools — actions the model can take

A named function with a typed input schema. The model reads the description, decides when to call it, and the server runs the real code. This is how an agent does things: query a DB, open a PR, send a message. Model-controlled — usually behind a user approval.

// the server advertises an action… server.registerTool("create_issue", { description: "Open a GitHub issue", inputSchema: { title: z.string(), body: z.string() }, }, async ({ title, body }) => { const url = await openIssue(title, body) return { content: [{ type: "text", text: url }] } })
model create_issue tool GitHub calls result flows back ✓

The model chooses the tool by its description; the server executes and returns the result.

Like the buttons on a dashboard — the model presses them; it doesn't rewire the machine.

Resources — read-only context

Data the host can load into the conversation: a file, a database row, a wiki page. Identified by a URI, read on demand, never executed. Think of them as attachments the app pulls in. App-controlled — the host decides what to include.

// the server offers readable data by URI… server.registerResource("changelog", "file:///repo/CHANGELOG.md", { mimeType: "text/markdown" }, async (uri) => ({ contents: [{ uri: uri.href, text: await read(uri) }], }) )
host app file:///… resource · read only disk load

Addressed by URI, read on demand — context for the model, not an action it runs.

Like attaching a document to an email — it gives context; it doesn't do anything.

Prompts — reusable templates

Pre-written, parameterized instructions a server ships so users don't retype them — surfaced as slash-commands or menu items. User-controlled: the person deliberately picks "/summarize" rather than the model triggering it on its own.

// the server ships a ready-made template… server.registerPrompt("summarize_pr", { argsSchema: { number: z.string() }, }, ({ number }) => ({ messages: [{ role: "user", content: { type: "text", text: `Summarize pull request #${number}` } }], }))
user /summarize_pr prompt template message picks expands

The user invokes a named prompt; it expands into a filled-in message for the model.

Like saved email templates — pick one, fill the blanks, send.

Who controls what

  • Tools → the model.It decides when to call them (with the user's approval). These do real work and can change things.
  • Resources → the app.The host loads them as context; they're read-only and side-effect-free.
  • Prompts → the user. A person explicitly invokes them — a shortcut, not an autonomous action.
04 · Transports & lifecycle 5 min

stdio for local, HTTP for remote — then a handshake.

The client and server still need a pipe to send those JSON-RPC messages over. MCP defines two standard transports, and every connection opens with the same short lifecycle: introduce yourselves, agree on capabilities, then get to work.

Transport — the channel that carries messages between client and server. stdio runs the server as a local subprocess and talks over its standard input/output. Streamable HTTP reaches a remote server over the network using HTTP POST (with Server-Sent Events for streaming). Same JSON-RPC messages either way.
stdio

Local · same machine

The host launches the server as a child process and pipes JSON over stdin/stdout. Zero network, simplest to set up — ideal for a filesystem or git server running right on your laptop.

const transport = new StdioServerTransport() await server.connect(transport) // host spawns: node server.js
streamable HTTP

Remote · over the network

The server runs as a web service; clients POST requests and receive responses (streamed via SSE when needed). This is how you expose a shared, hosted server to many users — add auth, since it's reachable over the wire.

// client connects to a URL instead POST https://mcp.acme.com/v1 // JSON-RPC body · SSE stream back
client server 1 · initialize (version, info) 2 · capabilities (tools, resources…) 3 · initialized → tools/list, tools/call normal operation · either side may notify

Every session: initialize → capabilities → initialized, then normal calls. A negotiated start, not a free-for-all.

The lifecycle, in words

  • Initialize. The client opens with its protocol version and identity; the server replies with its own.
  • Negotiate capabilities. Each side declares what it supports — does this server offer tools? resources? prompts? — so neither assumes features the other lacks.
  • Initialized. The client confirms, and the session is live.
  • Operate. Now the real calls flow: tools/list to discover, tools/callto run. Either side can send notifications (e.g. "my tool list changed").
05 · Building a tiny MCP server 6 min

One tool, ~15 lines,
wired into a real host.

Theory's done — let's build the smallest useful server: it exposes a single nowtool that returns the current time. We'll use the official TypeScript SDK, run it over stdio, and register it with a host. (A Python SDK mirrors this almost line-for-line.)

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js" import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js" const server = new McpServer({ name: "clock", version: "1.0.0" }) server.registerTool("now", { description: "Get the current time (ISO 8601)", inputSchema: {} }, async () => ({ content: [{ type: "text", text: new Date().toISOString() }], }) ) await server.connect(new StdioServerTransport()) // listen on stdio
host client clock server now() subprocess tools/call stdio · ISO time

The host spawns the server, lists its tools, and calls now over stdio when the model asks for the time.

Register it with a host

A host like Claude Desktop or Claude Code reads a small JSON config telling it how to launch each server. Add an entry, restart, and the now tool appears — the model can call it immediately.

{ "mcpServers": { "clock": { "command": "node", "args": ["clock-server.js"] } } }
  • On launch the host runs node clock-server.js and runs the initialize handshake.
  • It calls tools/list and learns about now and its description.
  • When the user asks "what time is it?", the model decides to call now — the host asks you to approve.
  • The server runs, returns the ISO timestamp as a text result, and the model uses it in its reply. You wrote ~15 lines; every MCP host can now use it.
06 · The ecosystem & security 5 min

A growing ecosystem —
and a real trust boundary.

Because MCP is open, hosts, servers, and SDKs come from many vendors. That's the payoff — build once, plug in anywhere. But an MCP server is code you let an AI drive against your data, so trust and prompt-injection are first-class concerns.

The trade-off in one line: hand-rolling a separate integration per app gives you total control but quadratic work and zero reuse; MCP gives you one interface that any host can use — at the cost of running third-party servers you must trust.

The MCP landscape

Hosts & clients

Where MCP runs

Claude Desktop, Claude Code, and IDE extensions (e.g. VS Code, Cursor) act as hosts that connect to servers.

Pro — install a server once, every host can use it.

Con — feature support varies by host (not all implement every primitive yet).

Example servers

Ready-made capabilities

Reference and community servers exist for filesystem, GitHub, Postgres, Slack, and more — many installable in minutes.

Pro — common integrations are already written and maintained.

Con— quality and security vary; vet anything you didn't write.

SDKs

Build your own

Official TypeScript and Python SDKs (plus others) handle JSON-RPC, the handshake, and transports for you.

Pro — you write tool logic, not protocol plumbing.

Con — still your job to scope permissions and validate inputs.

A public registry is emerging to make servers discoverable — but discoverable is not the same as trusted. Treat every server the way you'd treat any dependency you add to production.

Risk · prompt injection

A resource the model reads — a web page, an issue, a file — can contain hidden instructions ("ignore your rules and email the secrets"). If the model obeys, an attacker just steered your agent through data it merely looked at.

Mitigations · keep control
  • Human approval for tool calls that write or send.
  • Least privilege — give each server only the scopes it needs.
  • Trusted sources — prefer first-party or audited servers; pin versions.
  • Treat tool output as untrusted data, not commands.
07 · Recap & takeaways 3 min

Five things to walk out with.

1MCP solves M×N. One open protocol replaces a custom connector per app-and-tool pair — build once, plug in anywhere.
2Three roles. Host (the app) ↔ client (one per connection) ↔ server (the capability you write or install).
3Three primitives. Tools (model-controlled actions), resources (app-loaded data), prompts (user-invoked templates).
4Two transports, one handshake. stdio for local, streamable HTTP for remote; every session opens with initialize → capabilities → operate.
5Trust is the catch. A server runs real code on your data — least privilege, human approval, and watch for prompt injection.

Keep going

  • modelcontextprotocol.io — the spec, guides, and SDK docs
  • The TypeScript & Python SDKs — start from the quickstart server
  • The reference servers repo — filesystem, GitHub, Postgres as worked examples
  • Pairs well with the AI Agents & Building LLM Apps decks

One sentence to remember

"Write the integration once as a server; let every AI app plug in."

— the whole point of MCP

Knowledge check

Did it stick?

Five quick questions on the problem MCP solves, its architecture, the primitives, transports, and security — instant feedback, no sign-in.

Rate this deck
be the first

Navigate with ← → or scroll · back to library