Model Context Protocol — fundamentals
MCP standardises how applications supply context and tools to LLMs. The pitch is the one that matters: M×N integrations become M+N. Without it, every AI app writes bespoke glue for every data source. With it, any MCP client talks to any MCP server.
Think “LSP for AI tooling” — the Language Server Protocol analogy is exact, and it’s the fastest way to explain MCP to an engineer.
The primitives
The three a server offers to the client:
| Primitive | Controlled by | Use for |
|---|---|---|
| Tools | the model | actions, like POST: query a DB, send a message |
| Resources | the application | context, like GET: files, records, schemas |
| Prompts | the user | templates the user invokes, like a slash command |
The distinction is about who decides, and it’s the most commonly missed part of MCP.
- A tool is invoked by the model as part of reasoning.
- A resource is selected by the client application (or user) and injected as context — the model doesn’t choose to fetch it.
- A prompt is deliberately triggered by a human.
Getting this wrong — exposing everything as tools — produces an agent with fifty tools that picks badly. Read-only context usually belongs as a resource.
The two that run the other way
Tools, resources and prompts all point from server to client. Two more point back — the server asking something of the client:
| Primitive | The server asks the client for |
|---|---|
| Sampling | an LLM completion |
| Elicitation | structured input from the user |
Sampling lets a server request a completion without holding an API key or choosing a model. A summarise-file server can ask the host to run the summarisation, so the cost, the model choice and the rate limit stay with the client — which is the point. The client is expected to keep a human in the loop, because a server that can trigger completions is otherwise a way to spend someone else’s tokens and to smuggle prompt content into the host’s model.
Elicitation lets a server pause mid-call and ask the user for a missing value with a JSON schema attached, rather than failing or guessing. A booking tool that needs a date it was not given asks for the date. Before elicitation, the alternatives were returning an error and hoping the model re-asks, or inventing a value.
Both are optional capabilities negotiated in the handshake, so a server must degrade gracefully when the client does not support them — and many do not. Treat them as a bonus path, never the only path.
A server
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("orders")
@mcp.tool()
def refund_order(order_id: str, reason: str) -> str:
"""Refund an order. Requires a reason for the audit log."""
return process_refund(order_id, reason)
@mcp.resource("order://{order_id}")
def get_order(order_id: str) -> str:
"""Read-only order details."""
return json.dumps(load_order(order_id))
@mcp.prompt()
def investigate(order_id: str) -> str:
return f"Investigate order {order_id}. Check status, payments and shipping."The docstring is the tool description the model sees. Vague docstrings produce wrong tool calls, and that’s the single highest-leverage thing to get right — the same discipline as writing a good function-calling schema. See Function calling and structured output.
Transports
| Transport | Where the server runs | Use |
|---|---|---|
| stdio | subprocess on the same machine | local tools, filesystem, developer tooling |
| Streamable HTTP | remote service | hosted servers, multi-user, production |
Streamable HTTP replaced the older HTTP+SSE transport. As of the 2026-07-28 revision the protocol core is stateless, which changes how remote servers scale — covered in The 2026-07-28 MCP revision.
The handshake, once
Worth being able to sketch, because it shows the model never speaks MCP itself:
1. client starts the server (subprocess, or connects)
2. initialize — exchange capabilities + version
3. client lists tools / resources / prompts
4. model asks for a tool; client sends the request
5. server executes, returns a result
6. client feeds the result back to the modelThe MCP client is built into the AI product — Claude Desktop, an IDE, your own agent. It mediates every call. For most backend roles you will build servers, exposing your system to AI products; writing a client is a moderate undertaking you take on only if you are building the product itself.
Why not just function calling
A fair question, and the answer is about the boundary, not the capability.
Function calling is a model API feature: you pass JSON schemas in the request, the model returns a call, your code executes it. It’s per-application and per-vendor.
MCP is a protocol between processes. The consequences:
- Reusable. One MCP server serves Claude Desktop, your IDE, your own agent, and a colleague’s app.
- Independently deployed. The server team ships without touching the AI app.
- Discoverable. A client lists tools at runtime rather than hardcoding schemas.
- Composable. Add a server, gain its tools, no code change in the client.
Underneath, MCP tools still become function-calling schemas when sent to the model. MCP doesn’t replace function calling — it standardises where the definitions come from. That’s the precise answer to the interview question.
Use plain function calling for two or three app-specific tools. Use MCP when tools are reused across applications, owned by another team, or you want an ecosystem.
And versus a framework’s tool objects
A LangChain or LlamaIndex tool is a Python object living inside your agent process. It is easier for one application, and invisible to every other one.
The two compose rather than compete: a thin MCP server can forward calls to framework tools you already have, which exposes them to any MCP client without rewriting them.
Where servers run
- Local (stdio) — full access to the user’s filesystem and environment. The trust model is “the user’s own machine”.
- Remote (HTTP) — a network service with authentication, multi-tenancy and the usual API concerns.
The security posture is entirely different between the two, and conflating them is how MCP deployments go wrong. See Building and securing MCP servers.
Related
Interview angle 5
- “What problem does MCP solve?” — the M×N integration explosion. Standardising the client-server contract turns it into M+N, so any MCP-speaking application can use any MCP server. LSP is the right analogy.
- “Tools vs resources vs prompts?” — who controls invocation. Tools are model-controlled and may have effects; resources are application-controlled read-only context; prompts are user-triggered templates. Exposing read-only context as tools bloats the tool list and degrades selection.
- “Isn’t this just function calling?” — function calling is a model API feature scoped to one application; MCP is a process-to-process protocol that makes tool definitions reusable, independently deployable and discoverable at runtime. MCP tools are still converted into function-calling schemas — it standardises their source, not the mechanism.
- “When would you not use MCP?” — a handful of app-specific tools with no reuse. The protocol, transport and server lifecycle are overhead you don’t need for three internal functions.
- “What determines whether the model calls your tool correctly?” — the name, description and parameter schema. The docstring is the prompt. Vague descriptions are the main cause of wrong or missed tool calls.