If you understood what MCP is, you have the big picture. This page is about what's actually inside an MCP. It turns out there are only five pieces, and once you can name them, the whole protocol feels obvious. Nothing here requires a coding background, but we'll look at a little bit of code because the structure is easier to see than to describe.
Everything in MCP is one of these five things. If you see a new MCP concept, it's going to be one of these, or a way they relate to each other.
Let's walk through each one with enough detail that you could explain it to a friend over coffee.
A tool is an action. Specifically, it's a function the AI can ask to run. When the model decides it wants to use a tool, it names the tool and fills in some arguments, and the tool does its thing and hands back a result.
Every tool has three parts:
Here's what a tool looks like written out. Don't worry about the syntax; just see the shape.
{
"name": "search_email",
"description": "Search the user's email inbox. Use when the user asks about recent messages or needs context from email.",
"inputSchema": {
"type": "object",
"properties": {
"query": { "type": "string", "description": "Search query" },
"limit": { "type": "integer", "default": 20 }
},
"required": ["query"]
}
}
That's it. Name, description, arguments. If someone hands this tool description to a model, the model can decide to call search_email with a query string and get back email results. The server on the other side actually talks to Gmail and returns real data.
Almost every useful thing an agent does in the world is done through a tool call. If you want your agent to be able to do something new, you usually give it a new tool.
A resource is a piece of data the AI can read. Not run, not execute, just read. A file. A row in a database. A log entry. The Wikipedia page for "Paris."
Every resource has an address (called a URI, which is a fancy term for "a string that uniquely identifies a thing"). The address tells the client where to find the content. Some examples:
file:///Users/sam/notes.md, a file on your computerpostgres://orders/row?id=123, a specific row in a databasehttps://company.slack/channel/general, a Slack channelThe server says "here are the resources I know about," the client shows them to the user or the model, and when someone wants to read one, the server hands back the content.
You might be asking: if a tool can return a file too, why have a separate concept of resources? Fair question. The difference is that resources are listable and addressable in advance. A tool is a function the model decides to call in the moment. A resource is a thing the user (or the client) can browse, pin, and select before the agent even starts running. If you want a user to say "focus on these three documents," resources are how you structure that. Tools are for the agent to drive; resources are more for the human to curate.
A prompt, in the MCP sense, is a pre-written template of instructions that a server offers to the client. The idea is that writing good prompts is a skill. Instead of every user of a tool writing their own prompt from scratch, the maker of the server can package up well-tested prompts and ship them along with the tools.
Here's an example:
{
"name": "summarize_thread",
"description": "Summarize a conversation thread",
"arguments": [
{ "name": "thread_id", "required": true }
]
}
Your Slack MCP server might ship with this summarize_thread prompt baked in. The client (like Claude Code) can display this as a command or menu item. You click it, pick a thread, and the server hands back a fully-formed prompt like "Please summarize the following Slack thread, focusing on decisions made and open questions. Thread contents: [pasted content]." The model then runs on that prompt.
You don't see prompts as often as tools, partly because not every server ships them and partly because they're a newer concept. But when you find a server with well-crafted prompts, they can be a huge time-saver; you're borrowing someone else's prompt-engineering work.
A server is the "offering" side of an MCP connection. It's a program, running somewhere, that says "here are the tools, resources, and prompts I expose." When a client connects, the server tells it what's available and stands ready to fulfill requests.
Two important things to know about servers:
Where they run. Some servers run locally, as a small program on your machine, spawned by the client when it starts up. These are called stdio servers because they talk over standard input/output pipes. Other servers run remotely, as HTTP endpoints, reachable over the network. These are useful when the server needs access to cloud data (like a company's central database) or when it's shared by multiple users.
Their lifecycle. Every server goes through the same cycle with a client: initialize (say hi and announce what's available), list capabilities (what tools, resources, and prompts it offers), handle requests (execute tools, return resources), and shut down when done.
The key thing: writing an MCP server is easy. The protocol is open. The spec is published. Most languages have libraries. A useful server can be 50-100 lines of code. This is why hundreds of MCP servers already exist, and why you can build your own in an afternoon. For more on that, see Building servers.
The client is the other side of the connection: the program that connects to servers and makes their capabilities available to a model.
A client has a LOT of responsibilities. Among them:
Claude Code, Claude.ai, Cursor, Zed, and many custom agent frameworks are all MCP clients. Most of the "user experience" you see when working with MCP is really the client's doing. The server is anonymous plumbing underneath. See Clients for a deeper look at what makes a good one.
Here's the traffic pattern, at the level that will stick in your head:
Notice something important: the model never talks to the server directly. The client is always the middleman. This is intentional and it's one of the most important design choices in MCP.
Why does this matter? Because it means the client is the single place where permissions get checked. The model can want to send an email all it wants; the client is the one that decides whether to let that tool call through to the server or pause and ask the user first. If the model could talk to the server directly, there would be no chokepoint to enforce safety rules. By inserting the client as middleman, every call goes through a point where rules can be applied.
If I said: "I want my AI agent to be able to pull the latest issue from my GitHub repo and draft a reply," can you now sketch out what parts of MCP you'd need?
You'd need: a server that speaks MCP and knows how to talk to GitHub (the GitHub MCP, which exists). That server would expose tools like get_issue and list_issues. A client (like Claude Code) would connect to it, discover those tools, and make them available to the model. The model would decide when and how to call them. It might also expose GitHub issues as resources so you could pin one into context before you even start. That's the whole picture.
If that sketch made sense, the anatomy is clicking.
Once the pieces are in your head, two natural questions follow: "How do servers and clients actually talk to each other?" and "How do I build one?" Those are the next two pages.
Transports covers the three ways (stdio, SSE, HTTP) a client and server can connect, and when to use which. Building servers walks through how to make your own.
Andrej Karpathy - Intro to Large Language Models (1 hour)