The USB-C standard for AI — one plug that connects any model to any tool, data source, or service.
You build an AI assistant. Users want it to check the weather, query a database, search the web, read files, call your company's internal API. So you write custom integrations — a weather fetcher, a database connector, a search wrapper. Then you switch to a different model. You rewrite everything. Then a colleague builds a different assistant. They rewrite everything again.
This is the state of AI tooling in 2024. Every team solves the same problem from scratch, in a slightly different way, with incompatible interfaces. An LLM that can call tools has no standard way to discover what tools exist, what they accept, or what they return. Each integration is bespoke.
Compare this to USB-C. Before USB-C, every device had its own port shape. After USB-C, one standard cable charges your laptop, transfers files, drives an external monitor — any device, any brand. MCP is USB-C for AI: one standard protocol that lets any model talk to any tool or data source.
MCP — the Model Context Protocol — is an open standard released by Anthropic in November 2024. It defines exactly how an AI model (the client) discovers and calls external capabilities (the server). The model doesn't need to know whether it's talking to GitHub, a database, or your home automation system. It speaks MCP. The server speaks MCP. They interoperate automatically.
Without MCP: every model needs its own connector to every tool (N×M pairs). With MCP: each model connects once to MCP, and each tool connects once to MCP (N+M pairs). Click to toggle.
MCP has three roles. The Host is the application the user runs — Claude Desktop, an IDE plugin, a custom chat app. The Client is the component inside the host that speaks MCP (usually the LLM layer). The Server is an independent process that exposes tools, resources, or prompts over the MCP protocol.
The client and server communicate with JSON-RPC 2.0 — a simple, language-agnostic message format. Every message is a JSON object with a method name, optional parameters, and an ID for matching requests to responses. You probably already know JSON. That's 90% of MCP.
{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{...}}. Response: {"jsonrpc":"2.0","id":1,"result":{...}}. Error: replace result with error.
The physical connection — the transport — is where bytes actually travel. MCP supports two transports:
The connection lifecycle is always the same: initialize (client announces its capabilities) → initialized (server responds with its capabilities) → normal operation (tools, resources, prompts) → shutdown. Think of it like a TCP handshake but for AI capabilities.
Click a transport type to see how data flows between host, client, and server.
A tool is an action the model can invoke. Fetching weather, sending an email, running a SQL query — anything that does something is a tool. The model decides when to call it, what arguments to pass, and how to interpret the result.
Every tool has three parts: a name (a unique identifier), a description (plain English explaining what it does and when to use it — this is what the model reads!), and an input schema (a JSON Schema object defining what parameters it accepts). The description is the most important part: the model decides whether to call a tool based entirely on reading it.
Here's a complete tool definition for a weather fetcher. Notice how the description is precise about what the tool returns and what the location parameter format should be:
json { "name": "get_weather", "description": "Get current weather conditions and forecast for a city. Returns temperature (Celsius), humidity (%), wind speed (km/h), and a conditions summary. Use for any weather-related question.", "inputSchema": { "type": "object", "properties": { "location": { "type": "string", "description": "City name, e.g. 'Tokyo' or 'New York'" }, "units": { "type": "string", "enum": ["metric", "imperial"], "default": "metric" } }, "required": ["location"] } }
When the model calls a tool, the flow is: model emits a tool_use message with the tool name and arguments → host intercepts it → host calls the MCP server → server executes the logic → server returns a tool result → host injects the result back into the conversation → model continues generating.
Click Next to walk through a tool call from question to answer.
Tools do things. Resources expose data. A resource is a read-only blob of content — a file, a database row, a webpage snapshot, a config — that the model can fetch and include in its context. It doesn't take arguments that change state. It just returns content.
Every resource has a URI (Uniform Resource Identifier) — a unique address like file:///home/user/notes.txt or db://customers/user-42 or github://repo/main/README.md. The URI scheme is defined by the server. Clients discover available resources by calling resources/list, then fetch one by calling resources/read with its URI.
| Primitive | Direction | Has Side Effects? | Example |
|---|---|---|---|
| Tool | Model → World | Yes | Send email, query DB, run code |
| Resource | World → Model | No | Read file, fetch docs, get config |
| Prompt | Server → Model | No | Reusable prompt templates |
Resources can be static (a fixed file that rarely changes) or dynamic (generated on request, like a live database view). Servers can also send resource change notifications when a subscribed resource updates — useful for watching log files or live dashboards.
Click a resource type to see its URI format and what content it returns.
Let's build a real MCP server. The official Python SDK handles the JSON-RPC plumbing, connection lifecycle, and schema validation. You just define what your server exposes using decorators — the same pattern you'd use in Flask or FastAPI.
Install the SDK: pip install mcp. That's the only dependency. Here's a minimal server with one tool and one resource:
python from mcp.server import Server from mcp.server.stdio import stdio_server import mcp.types as types # 1. Create the server instance app = Server("weather-server") # 2. Declare a tool using the @app.call_tool decorator @app.list_tools() async def list_tools(): return [ types.Tool( name="get_weather", description="Get current weather for a city.", inputSchema={ "type": "object", "properties": { "location": {"type": "string"} }, "required": ["location"] } ) ] @app.call_tool() async def call_tool(name: str, arguments: dict): if name == "get_weather": loc = arguments["location"] # Real implementation would call a weather API here return [types.TextContent( type="text", text=f"Weather in {loc}: 22°C, partly cloudy, humidity 65%" )] # 3. Declare a resource @app.list_resources() async def list_resources(): return [ types.Resource( uri="weather://stations/list", name="Weather Station List", description="All monitored weather stations.", mimeType="text/plain" ) ] @app.read_resource() async def read_resource(uri: str): return types.TextResourceContents( uri=uri, text="NYC-001, LON-042, TOK-007, SYD-003", mimeType="text/plain" ) # 4. Run over stdio — the host starts this process and talks to it if __name__ == "__main__": import asyncio asyncio.run(stdio_server(app))
That's it. The SDK handles the initialize/initialized handshake, dispatches incoming JSON-RPC calls to the right handler, and serializes responses. You focus on the logic; MCP handles the protocol.
Hover over each component to see what it does.
The client lives inside the host application — the thing the user actually runs. When a client connects to an MCP server, it follows a strict lifecycle: connect, negotiate, discover, invoke, disconnect. Every step is a defined JSON-RPC exchange.
Step 1 — Initialize. The client sends an initialize request carrying its protocol version and a list of capabilities it supports (like "can I sample?", "can I do roots?"). The server responds with its own version and capabilities. They compare notes and agree on a common feature set. This is the handshake.
Step 2 — Discovery. The client calls tools/list to get the server's tool manifest, resources/list to get available resources, and prompts/list for prompt templates. The model gets all of this injected into its system prompt — now it knows what it can do.
Step 3 — Invocation. When the model decides to call a tool, the host calls tools/call with the tool name and arguments. The server executes and returns a result. The host injects the result back into the conversation.
python from mcp.client.stdio import stdio_client from mcp import ClientSession, StdioServerParameters import asyncio async def main(): # Connect to a server via stdio params = StdioServerParameters( command="python", args=["weather_server.py"] ) async with stdio_client(params) as (read, write): async with ClientSession(read, write) as session: # Step 1: Initialize (handshake) await session.initialize() # Step 2: Discover what the server offers tools = await session.list_tools() print(f"Available tools: {[t.name for t in tools.tools]}") # Step 3: Invoke a tool result = await session.call_tool( "get_weather", {"location": "Tokyo"} ) print(result.content[0].text) asyncio.run(main())
Click through the lifecycle steps.
The MCP ecosystem grew rapidly after the spec's release. Anthropic ships official servers for common services. The community has built hundreds more. Here's what the most-used servers expose and why they're designed that way.
| Server | Tools (actions) | Resources (data) | Use case |
|---|---|---|---|
| GitHub | create_issue, push_file, create_branch, merge_pr | github://repo/owner/file, github://issues/list | AI-assisted development, code review automation |
| PostgreSQL | execute_query (SELECT only, sandboxed) | db://schema, db://tables/name/rows | Natural language SQL, data exploration |
| Brave Search | web_search, local_search | — | Give model real-time web access |
| Filesystem | write_file, create_directory, move_file | file:///path/to/file | Local file management, code editing |
| Slack | send_message, create_channel | slack://channels/list, slack://messages/channel | AI assistant in workspace |
| Puppeteer | navigate, screenshot, click, fill_form | browser://current/html, browser://current/screenshot | Web automation, UI testing |
Notice the pattern: tools are write operations (create, push, send), while resources are read operations (list, fetch, view). The GitHub server exposes file contents as resources — the model can read your entire codebase without being able to push a commit until you grant the tool.
Click a server to see what it exposes.
MCP is powerful precisely because it lets models take real-world actions. That power comes with real risks. The spec's security model is built around one principle: the model is not trusted. The host is the security boundary.
The threat model. An attacker who can control the model's input can try to make the model call tools it shouldn't — deleting files, exfiltrating data, sending messages. This is called prompt injection: malicious instructions embedded in content the model reads (a webpage, a file, an email) that hijack the model's actions.
The four security layers in MCP:
Click a risk zone to see the attack and defense.
Watch a complete MCP interaction from user question to model answer. Toggle which servers are active. Inject errors at any point and see how the system handles them.
MCP doesn't exist in isolation. It's one layer in a larger stack of AI engineering patterns. Understanding where it connects helps you decide when to use it versus alternatives.
| Concept | Relation to MCP | When to combine |
|---|---|---|
| AI Agents | MCP is the tool interface for agents. An agent loop calls tools repeatedly until a goal is reached — MCP is how those tool calls are structured and dispatched. | Always. Multi-step agents need standardized tool access. |
| RAG | RAG retrieves chunks from a vector store and injects them. MCP resources can serve the same role — the model fetches relevant docs via URI. MCP adds dynamic fetching; RAG adds semantic search. | Use a RAG-over-MCP server when you want semantic retrieval with standard interfaces. |
| Function Calling | Function calling is model-level (OpenAI/Anthropic API feature). MCP is application-level (how tools are discovered, connected, and executed). MCP servers are how functions get implemented; function calling is how models invoke them. | Use together. Function calling is the model mechanism; MCP is the plumbing. |
| Enterprise Integration | MCP servers can wrap any internal API — HR systems, ERP, CRM. One MCP adapter per system. Any MCP-compatible model can then access all of them without re-integration. | Build MCP servers as internal API adapters. Ship once; use with any future model. |
"What I cannot create, I do not understand." — Richard Feynman.
Build an MCP server this week. You'll understand the protocol in a way no lesson can teach.