MCP Fundamentals: How AI Apps Standardize External Capabilities
A structured overview of the Model Context Protocol—Host / Client / Server, Tools / Resources / Prompts, transport and lifecycle—and how it differs from Function Calling.
---
MCP Fundamentals: How AI Apps Standardize External Capabilities
Large language models are getting better at doing things: reading files, querying databases, calling APIs, running workflows. But every new capability often means another one-off adapter for each Host—IDE, desktop assistant, or custom agent. As tools multiply, integration cost and security boundaries spiral out of control.
MCP (Model Context Protocol) exists for exactly that: let AI applications connect to external tools, data, and workflows in a uniform way, instead of writing a plugin protocol for every Host.
This article starts from the definition, walks through participants, the two-layer architecture, the three primitives, lifecycle, and a typical call flow, clarifies how MCP relates to Function Calling, and closes with security principles and a practical learning path.
What Is MCP?
One-line definition
MCP is an open protocol that lets AI applications discover and call external capabilities (tools, resources, prompt templates) through a standardized Client ↔ Server conversation, instead of inventing a separate plugin protocol per Host.
It is often described as: the “USB standard” for plugging external capabilities into AI apps.
What it covers—and what it doesn’t
| MCP covers | MCP does not cover |
|---|---|
| How Client ↔ Server exchange context and capabilities (JSON-RPC messages, primitives, transport) | How the Host picks a model, writes the agent loop, or builds UI |
| Semantics of Tools / Resources / Prompts | Business logic itself (querying DBs, reading files—that’s the Server author’s job) |
| Cross-cutting features: capability negotiation, notifications, progress | Replacing Function Calling on the model side (“I want to call a tool”) |
A useful mnemonic:
Function Calling = how the model expresses “call this tool”; MCP = how tools/data are wired into the application in a standard way.
Why it’s worth learning
- Reusable: one Server can be consumed by Claude Desktop, Cursor, VS Code, and your own Host.
- Clear boundaries: Host / Client / Server roles separate concerns and make security edges easier to draw.
- De facto ecosystem: many official and community Servers; multiple vendor Hosts already integrate it.
- Fits agent engineering: if you already have tool calls, workflows, or multi-agent setups, MCP is the standard answer for the tool-integration layer.
Participants: Host / Client / Server
MCP uses a Host ↔ (many) Clients ↔ (many) Servers structure.
┌──────────────────────── MCP Host (AI app) ─────────────────────────┐
│ e.g. Cursor / Claude Desktop / VS Code / your own agent │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Client 1 │ │ Client 2 │ │ Client 3 │ ← one Client per Server │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
└────────┼─────────────┼─────────────┼───────────────────────────────┘
│ │ │
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────────┐
│ Server A │ │ Server B │ │ Server C │
│ local FS │ │ local DB │ │ remote SaaS │
│ (stdio) │ │ (stdio) │ │ (HTTP) │
└──────────┘ └──────────┘ └──────────────┘
| Role | Metaphor | Responsibility |
|---|---|---|
| Host | Command center | The AI app users face; creates Clients, drives the LLM, decides when tool results re-enter the conversation |
| Client | Translator / dedicated line | Embedded in the Host; maintains a connection to one Server, handles handshake and message routing |
| Server | Capability provider | Exposes Tools / Resources / Prompts; may be a local subprocess or a remote service |
Three facts worth memorizing:
- One-to-one connection: one Client instance maps to one Server connection; N Servers means N Clients.
- “Server” does not mean “must be remote”: a local stdio subprocess is still an MCP Server.
- Local vs remote is mainly decided by the Transport layer.
Two layers: Data Layer + Transport Layer
┌─────────────────────────────────────────┐
│ Transport Layer (outer) │
│ stdio | Streamable HTTP (+ optional SSE) │
│ connection, framing, auth │
├─────────────────────────────────────────┤
│ Data Layer (inner) │
│ JSON-RPC 2.0: lifecycle, primitives, │
│ notifications, tool calls │
└─────────────────────────────────────────┘
| Layer | Question it answers | Key points |
|---|---|---|
| Data Layer | “What do we say?” | JSON-RPC 2.0; initialize, tools/list, tools/call, notifications… |
| Transport Layer | “How do we ship it?” | stdio pipes, or HTTP POST (optionally streaming); same JSON-RPC payload |
When learning MCP, prioritize Data Layer primitives first; you can swap transports later without changing semantics.
Transports
Two mainstream options (per the official docs):
| Transport | Shape | Typical use | Traits |
|---|---|---|---|
| stdio | Host spawns a subprocess; JSON-RPC over stdin/stdout | Local files, local git, local DB | No network surface; lifecycle owned by Host; best for beginners |
| Streamable HTTP | Server listens on HTTP; Client POSTs, optional SSE streaming | Shared remote capabilities, multi-client | Standard HTTP auth (Bearer / API Key / OAuth, etc.) |
On SSE: early designs used “SSE-only” remote transport; newer specs center on Streamable HTTP, with SSE as part of streaming or a historical path. For learning and selection, remember:
Local → stdio; remote → Streamable HTTP.
Server primitives: Tools / Resources / Prompts
This is the part worth memorizing first. A Server may implement one, two, or all three.
| Primitive | Metaphor | What it is | Usually triggered by | Typical methods |
|---|---|---|---|---|
| Tools | Executable actions | Functions with JSON Schema inputs (query DB, write file, call API) | The model deciding mid-conversation | tools/list → tools/call |
| Resources | Readable material | URI-addressed data (file content, schema, config snapshot) | App/user injecting context, or the model reading via Host | resources/list → resources/read |
| Prompts | Preset workflow templates | Parameterized prompts / few-shot templates | The user picking a template in Host UI | prompts/list → prompts/get |
How to tell them apart
| Question | More like |
|---|---|
| Does it change the outside world or run computation? | Tool |
| Is it only “inject this data into context”? | Resource |
| Do you want a one-click fixed script / flow for users? | Prompt |
The same product often uses all three—for example a “database assistant”:
- Tool:
query_sql - Resource:
db://schema(table structure) - Prompt:
analyze-slow-query(analysis template with few-shots)
Tool metadata (what Client / LLM use to choose)
Each tool returned by tools/list typically includes:
| Field | Role |
|---|---|
name | Stable unique call key |
title | Short human-facing name |
description | “When to use this” for the model (poor wording means the model never calls it) |
inputSchema | JSON Schema: types, required fields, enums |
tools/call usually returns a content array (multiple text / image / resource segments, etc.), which the Host turns into a model-readable tool result.
Client-side capabilities: Sampling / Elicitation / Roots / Logging
Beyond what Servers “offer outward,” the protocol also lets a Server ask the Client / Host to do something (capabilities must be declared at handshake).
| Capability | Direction | Use |
|---|---|---|
| Sampling | Server → Client | Server asks the Host-side LLM to complete (Server isn’t tied to one model SDK) |
| Elicitation | Server → Client | Server requests extra user info or confirmation (form / dialog) |
| Roots | Client ↔ Server | Bounds the filesystem / URI space a Server may touch |
| Logging | Server → Client | Structured logs for Host debugging and monitoring |
For beginners: master Server Tools first; leave Sampling / Elicitation for later.
Lifecycle and capability negotiation
Under the common stateful session model, a connection usually starts with a handshake:
Client Server
| |
|──── initialize (version, |
| capabilities, clientInfo)──►|
|◄─── result (version, |
| capabilities, serverInfo)───|
|──── notifications/initialized ─►|
| |
|──── tools/list ───────────────►|
|◄─── tools[] ───────────────────|
|──── tools/call ───────────────►|
|◄─── content[] ─────────────────|
The handshake settles three things:
- Protocol version negotiation (mismatch → disconnect)
- Capabilities declaration (tools?
list_changednotifications? …) - Identity info (
clientInfo/serverInfofor debugging)
Only then come discovery and calls. Methods not declared in capabilities must not be assumed available.
The spec continues to evolve (session models and HTTP deployment shapes may shift). Learn with “handshake + primitives + transport” as the spine; when coding, pin an SDK / protocol version and read its changelog.
A typical call (mental walkthrough)
With an LLM embedded in the Host:
- Host starts, creates a Client per configured Server, completes
initialize. - Client calls
tools/list; Host merges tools from all Servers into one tool table for the model. - User asks a question → model emits a tool call (Function / Tool Calling).
- Host maps the tool
nameto the right MCP Client →tools/call. - Server runs business logic and returns
content. - Host writes the result back into the conversation; the model continues to a final answer.
Who calls the LLM? By default, the Host. Servers usually don’t embed a model; if they need one, they use Sampling (advanced).
Notifications and utilities
| Capability | Notes |
|---|---|
| Notifications | e.g. notifications/tools/list_changed: tool list changed; Client should tools/list again (no id, no response required) |
| Progress | Progress reports for long tasks |
| Cancellation / Errors | Cancel and standard error reporting |
| Tasks (evolving) | Long-running task handles and deferred results; shape varies by spec version |
Dynamic tool lists (permission changes, hot-loaded plugins) rely on notifications; tiny static Servers can skip them at first.
MCP vs Function Calling vs A2A
| Concept | Layer | Problem it solves |
|---|---|---|
| Function / Tool Calling | Model ↔ Host | How the model structurally says “call which function with which args” |
| MCP | Host / Client ↔ Server | How external capabilities are discovered and invoked in a standard way (reusable across apps) |
| A2A and similar agent protocols | Agent ↔ Agent | How multiple agents coordinate (a different layer from “plug in tools”) |
As a diagram:
User ↔ Host (with LLM)
│ Tool Calling (model decides what to call)
▼
MCP Client ──JSON-RPC──► MCP Server (executes / provides data)
Without MCP you can still register local functions as tools inside the Host. With MCP, the same tools can be consumed by many Hosts, and Servers can evolve and deploy independently.
Security boundaries (read before writing a Server)
MCP can open arbitrary data access and code-execution paths. Security is first-class:
| Principle | Practice tips |
|---|---|
| Least privilege | Expose only necessary tool actions; confirm dangerous ops (Elicitation / Host policy) |
| Roots / sandbox | Filesystem Servers must restrict accessible roots |
| Remote auth | Use OAuth / tokens for Streamable HTTP; never put secrets in the prompt |
| Trust boundaries | Servers don’t see each other; Host owns isolation and user consent |
| Descriptions are attack surface | Tool description and return content can steer the model—guard against prompt injection |
Tooling and ecosystem entry points
| Name | Use |
|---|---|
| MCP Specification | Authoritative protocol definition |
| Official SDKs | TypeScript / Python, etc.—hide JSON-RPC boilerplate |
| MCP Inspector | Graphical debugging: connect to a Server, exercise list / call—highly recommended for beginners |
| Reference Servers | Official / community examples (filesystem, git, browser, …) |
A practical local learning path:
Use Inspector as the Client → write a minimal stdio Server yourself → then build a Host that includes an LLM.
Practice roadmap: how to really learn it
Once concepts are clear, practice by keeping the product shell fixed and swapping only the MCP core—don’t pile on model and orchestration complexity too early. A strong exercise is a local Docs assistant:
- Fixed corpus (e.g. a set of Markdown docs)
- Fixed capability semantics:
list_docs/search_docs/read_doc - Progressively deepen the MCP kernel, not the business logic
A five-stage progression:
| Stage | Core question | How to verify |
|---|---|---|
| A · stdio + Tools | Does handshake / list / call close the loop? | Both MCP Inspector and a minimal Host can call through |
| B · Resources / Prompts | Are boundaries vs Tools clear? | Paths that inject a resource or apply a prompt work |
| C · Streamable HTTP | How does remote transport differ from stdio lifecycle? | Reachable over pure HTTP, no subprocess |
| D · Multi-language parity | Same semantics across stacks (e.g. Python / TypeScript)? | Same inputs and capabilities; only the implementation language differs |
| E · LLM Host tool loop | Does the model pick tools reliably? When do multi-round loops stop? | Tool hit rate, termination reason, call rounds |
Shared constraints that help:
- Keep the UI simple (even plain HTML); focus on an MCP timeline (
initialize/tools/list/tools/call) - Retrieval can stay naive keyword search—this track trains protocol + Host, not RAG depth
- Prepare a few “golden questions” per stage (expected path, whether a tool should be called) for regression
Cheat sheet
- Command center = Host, dedicated translator = Client, capability provider = Server
- Actions = Tools, readable data = Resources, templates = Prompts
- Local pipe = stdio, remote HTTP = Streamable HTTP
- What we say = Data / JSON-RPC, how we ship = Transport
- Model “wants to call” = Tool Calling, standardized “plugged in” = MCP
- Server borrows Host’s brain = Sampling, Server asks the user = Elicitation
Closing
MCP does not replace model-side Tool Calling, nor agent-to-agent collaboration protocols. It fills the middle layer: how external capabilities are discovered, negotiated, and invoked in a standard way.
A sensible onboarding order:
- Internalize Host / Client / Server and the three primitives
- Close the loop with stdio:
initialize → tools/list → tools/call - Add Resources / Prompts and HTTP transport
- Wire a real LLM Host tool loop—and keep security and least privilege first
When you can sketch the path “model decides what to call → Host routes to which MCP Client → Server executes and returns content,” you already hold the backbone of MCP.