Model Context Protocol (MCP)
An open standard from Anthropic (Nov 2024) for connecting AI assistants to external systems — tools, data, prompts. Replaces “every model integrates with every tool separately” with “any model speaks MCP, any tool speaks MCP.” Spreading quickly through 2025-2026.
The problem MCP solves
Without MCP, every AI product (Claude Desktop, Cursor, Continue, OpenAI custom GPTs, Bedrock agents) needs bespoke integrations with every external system (GitHub, Slack, your internal API, your DB). N × M integrations.
With MCP: an MCP server wraps one system. An MCP client lives inside the AI product. Any client talks to any server through the same wire protocol. N + M instead of N × M.
The three abstractions
MCP servers expose three primitive types:
| Primitive | Meaning | Example |
|---|---|---|
| Resources | read-only data the model can fetch | a file’s contents, a DB row, a URL |
| Tools | actions the model can invoke | create_issue, run_query, send_email |
| Prompts | templated prompts the user can pick | “review this PR,” “summarize this doc” |
Resources are passive (the model reads them). Tools are active (the model calls them with args, gets a result). Prompts are user-driven (the user picks “Review PR”; the prompt is then sent to the model with placeholders filled).
Transport
MCP defines the wire protocol; transports carry it:
| Transport | When |
|---|---|
| stdio | local server, AI product spawns it as a subprocess. Simple, common for desktop apps (Claude Desktop, Cursor). |
| SSE | HTTP-based, server pushes streams over Server-Sent Events. Used for remote / cloud servers. |
| streamable HTTP | newer, HTTP with bidirectional streaming. Replacing SSE. |
The protocol layer is identical; only the transport changes. A server can support multiple.
Quick example — Python MCP server
# Using the official Anthropic Python SDK
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("my-app")
@mcp.tool()
def add_numbers(a: int, b: int) -> int:
"""Add two numbers."""
return a + b
@mcp.resource("file://config.json")
def get_config() -> str:
"""Application config."""
return open("/etc/myapp/config.json").read()
@mcp.prompt()
def code_review(language: str) -> str:
"""Generate a code review prompt for the given language."""
return f"Review this {language} code for bugs, security, and style:"
if __name__ == "__main__":
mcp.run() # stdio by default
Running this and pointing Claude Desktop at it → Claude can now call add_numbers, read the config resource, and use the code_review prompt.
Server lifecycle
1. Client launches server (stdio: subprocess; HTTP: connect to URL)
2. Initialize handshake — exchange capabilities + protocol version
3. Client lists available resources/tools/prompts
4. Model decides to use one → client sends request to server
5. Server executes, returns result
6. Client passes result back to model
7. Model continues generating
The model never directly talks to MCP. The MCP client (built into Claude Desktop, Cursor, etc.) mediates.
Why MCP and not function calling?
OpenAI / Anthropic function calling already lets a model call tools. Differences:
| Function calling | MCP | |
|---|---|---|
| Defined where | in the LLM API request | in a separate server |
| Discoverable | no — client must register them | yes — client discovers from server |
| Portable | tied to one model API | model-agnostic |
| Process model | in-process | separate process / remote |
| Stateful resources | no | yes (resources concept) |
| User-pickable prompts | no | yes |
Function calling is the right primitive for “call my own backend in response to a request.” MCP is the right primitive for “expose this system to any AI product.”
For an internal microservice with one consumer (your app’s LLM), function calling is simpler. For a tool intended to be used across many AI products, MCP.
Where MCP servers run
| Pattern | Typical |
|---|---|
| Local subprocess (stdio) | Claude Desktop / Cursor + filesystem, GitHub, Slack |
| Local HTTP server | dev tools with web UI |
| Remote managed server | enterprise integrations (Jira, Salesforce, vendor SaaS) |
There’s a growing public ecosystem (https://github.com/modelcontextprotocol/servers) with reference servers for GitHub, GitLab, filesystem, Postgres, Brave Search, Google Drive, etc.
Security model
MCP doesn’t sandbox tools — calling a tool runs whatever code the server wants. The trust model:
- stdio servers run as subprocesses with the user’s privileges. Same model as installing a CLI tool. Trust the publisher.
- HTTP servers are network services. Use TLS, auth headers, per-user tokens. Standard API security.
- Resource ACLs are the server’s job — MCP doesn’t enforce who can read what.
Risks:
- Prompt injection in resource content → tool calls the model wouldn’t otherwise make. Mitigation: don’t auto-execute tools with side effects; require user confirmation for “destructive” tools.
- Malicious server published to a registry → arbitrary code execution. Mitigation: trust verified servers; review code before installing.
Treat MCP servers like browser extensions: powerful, only install from trusted sources.
Building an MCP server — checklist
- Identify the system to wrap (your DB, internal API, vendor SaaS).
- Decide which capabilities map to tools (writes, actions) vs resources (reads, lookups).
- Authenticate: pass tokens via env vars (stdio) or headers (HTTP).
- Validate tool args with Pydantic — don’t trust the model’s inputs.
- Return structured JSON results (not natural language) — model integrates them better.
- Set tool descriptions carefully — the model picks tools by description.
- Test with
npx @modelcontextprotocol/inspectoror the AI product’s debug mode.
Building an MCP client — when
Usually you don’t. You use Claude Desktop / Cursor / Continue / similar as the client. If you’re building your own AI product and want to consume third-party tools, you can implement an MCP client via the SDK; that’s a moderate undertaking.
For a typical Python backend role, you’ll build servers (exposing your system to AI products), not clients.
MCP vs LangChain tools
LangChain tools are Python objects with a specific class shape. They live inside your LangChain agent process.
MCP servers are separate processes / network services that any MCP client can call.
If you’re building one app: LangChain tools are easier. If you’re building a reusable integration: MCP exposes it to any AI product, not just your specific LangChain agent.
You can also expose LangChain tools through an MCP server: write a thin MCP server that forwards tool calls to LangChain. Best of both.
Common patterns
Pattern 1: read-only DB MCP server
Resources: each table is a resource. Tool: query(sql) with read-only credentials. Resource for table schema discovery.
Pattern 2: action-oriented internal tool
Tools: create_ticket, assign_ticket, close_ticket. No resources. Used by AI agents to manage a workflow.
Pattern 3: SaaS-vendor wrapper
Tools that wrap the vendor’s REST API; resources that expose entities. Vendor cares about API rate limits → MCP server respects them. Cleaner than the AI product calling the raw API.
Common gotchas
- Tool description matters. Model picks tools by description; vague descriptions → wrong choice. Be specific.
- JSON args, not free-form. Models sometimes hallucinate args. Validate with Pydantic at the boundary; reject malformed.
- Streaming results for long tools. stdio MCP supports streaming partial results — use it for tools that take >5s.
- Stateless preferred. Resources should be fresh on each fetch; don’t keep server state across invocations unless required.
- Error responses. Return errors as part of the result content (with a clear message), not by raising. The model can recover.
Interview angle
- “What is MCP and what problem does it solve?” — Anthropic’s open protocol for connecting AI products to external systems. Replaces N×M custom integrations with N+M (one server per system, one client per AI product). Resources + Tools + Prompts as primitives.
- “MCP vs function calling — when each?” — function calling: in-process, model-specific, single-app. MCP: separate process, model-agnostic, reusable across AI products. Function calling for your own backend; MCP for tools others should consume.
- “What are the three MCP primitives?” — Resources (read-only data), Tools (actions with side effects), Prompts (user-pickable templates). Resources are passive; Tools are active; Prompts are user-initiated.
- “How does MCP transport work?” — same protocol, three transports: stdio (subprocess, local), SSE (HTTP push), streamable HTTP (newer). Stdio for desktop apps; HTTP for remote servers.
- “Security model?” — MCP doesn’t sandbox. Servers run with their host process’s privileges. Trust comes from the source (verified publisher, your own code). Treat MCP servers like browser extensions: powerful, only install from trusted sources. Mitigate prompt-injection-driven tool calls with confirmation on destructive actions.
- “How would you expose your company’s internal API via MCP?” — Python
FastMCPserver. Auth via env-var token. Tools for writes (create, update); resources for reads. Validate args with Pydantic. Deploy as a managed HTTP service so multiple AI products can use it; or distribute as a Docker container that users run locally. - “Why does MCP matter for senior backend engineers?” — increasingly, “exposing my service to AI assistants” is a real product requirement. MCP is the emerging standard. Knowing how to build a clean MCP server is the same as knowing how to build a clean REST API for the AI-product audience.