Skip to content
New

DeepSeek V4.1 Flash free for 7 days: 10M tokens a day after any top-up. Until Nov 30.

How it works

What Is MCP? The Model Context Protocol Explained

MCP is the open protocol that connects AI apps to tools and data. Learn its roles, primitives, transports and security model with a sandboxed lab.

Intermediate12 min readBy NotHoly

What MCP is

The Model Context Protocol (MCP) is an open protocol that lets an AI application talk to outside tools and data in one standard way. Before MCP, every AI app that wanted to read files, query a database or call a ticket system needed its own custom integration. MCP puts one contract between the app and the thing it connects to. An app that speaks MCP can use any server that speaks MCP, and a server written once works with every client that implements the protocol.

If you already understand tool use, you know the core loop. The model sees a list of functions, asks to call one with arguments, and receives a result. MCP does not replace that loop. It standardizes where the function list comes from, how the call travels, and what else a server can offer besides functions.

The protocol is specified in revision 2026-07-28, the revision the specification page points to when this article was written. The specification is the authority for field names and rules. This article explains the model behind it, so you can read the spec and server code with a clear picture.

Why it matters

Three things make MCP worth learning.

The first is reuse. A filesystem server, a GitHub server or a database server can be installed by any compatible host. You stop writing the same glue code for each app.

The second is discovery. A client asks a server what it offers, and the server answers with names, descriptions and input schemas. The model can then choose among capabilities that nobody hard-coded into the app.

The third is risk. Once a model can reach your files, repositories or accounts, the tool layer becomes an attack surface. Many AI security problems come from a server doing more than its description says, or from a host calling tools without asking anyone. MCP makes these questions visible. It does not answer them for you.

The roles: host, client and server

The architecture has three participants. The specification and the architecture overview use the same names.

The host is the AI application the user works in. Claude Desktop, Claude Code and an IDE with an assistant are all hosts. The host decides which servers to connect to and which tools the model may use.

The client is a component inside the host. The host creates one client per server, and each client holds a dedicated connection to exactly one server. If a host connects to three servers, it runs three clients.

The server is a program that exposes capabilities. It can run on your laptop as a child process, or on a remote machine behind a URL. Local servers usually serve one client. Remote servers often serve many.

A simple picture:

text
Host (your AI app)
 ├── Client A ── stdio ──> filesystem server (local process)
 ├── Client B ── stdio ──> database server (local process)
 └── Client C ── HTTPS ──> ticket system server (remote)

Each arrow carries JSON-RPC 2.0 messages. The protocol has two layers. The data layer defines the messages and what they mean. The transport layer defines how the messages move.

The primitives

The primitives decide what a server can offer. The specification names three server features.

Tools are functions the model can call. A tool has a name, a description, an input schema written in JSON Schema, and a handler on the server side. Listing tools uses tools/list. Running one uses tools/call. Tools are where actions happen: writing a file, sending a request, running a query.

Resources are data the application can read by URI. A resource might be a file, a database row or an API response. Resources are context. Reading one does not run an action, and the host decides how to put the content in front of the model.

Prompts are reusable message templates the server publishes, often with arguments. A user picks a prompt the way they pick a command, and the host fills it in and sends it to the model.

Clients have their own features. Elicitation lets a server ask the user for more input during an operation, such as a confirmation or a missing value. Two older client features, sampling and logging, are deprecated in revision 2026-07-28. Sampling let a server ask the host's model for a completion. The architecture overview says new implementations should integrate with model provider APIs directly, and should log to stderr on stdio or use OpenTelemetry.

Every primitive follows a pattern: a */list method for discovery, a */get or */read method for retrieval, and a call method when the primitive is an action. Lists can change at runtime. A client that opens a subscription can receive change notifications, so it refreshes without polling.

Transports

Transport is how messages travel. The architecture overview names two.

stdio starts the server as a child process. The host writes JSON-RPC messages to the server's standard input and reads responses from its standard output. There is no network port. This is the usual transport for local servers, and it is the one you use in the lab. The rule that matters most: on stdio, standard output belongs to the protocol. A stray print to stdout corrupts the stream. Diagnostics go to stderr.

Streamable HTTP runs the server behind an HTTP endpoint. The client sends messages with HTTP POST, and the server can stream responses or notifications back using Server-Sent Events. This is the transport for remote servers. Because it is network-facing, it brings the usual HTTP problems: authentication, TLS, origin checks and rate limits. The architecture overview recommends OAuth for obtaining tokens.

The data layer stays the same across both. A tool call looks the same whether it crossed a pipe or a socket.

How a session runs, step by step

Here is the sequence for a host connecting to one local server. The exact messages depend on the protocol revision, so check the spec before you build against it.

  1. The host starts the server process with the command configured for it. For a stdio server, that is the command line in the host's configuration.
  2. The client can send a discovery request, server/discover, which every server must implement. Calling it is optional, because each request carries what the server needs. The server answers with its supported protocol versions, its capabilities and its identity. In revision 2026-07-28, every request carries the protocol version and client capabilities in a _meta field. The protocol is stateless: each request carries what the server needs to process it, so the server does not rely on what happened earlier in the connection.
  3. The client calls tools/list, resources/list and prompts/list for the features the server declared.
  4. The host gives the model the tool definitions. The model decides when to call one and with which arguments.
  5. The host sends tools/call. The server runs its handler and returns a result with a content array, which can hold text, images or embedded resources. A failed call returns an error result that the model can read.
  6. If the server's tool list changes, a client that subscribed with subscriptions/listen receives a notifications/tools/list_changed message, and it can request the list again.

The host sits between steps 4 and 5. It is the component that asks you for approval, if it asks at all. That point comes back in the security section.

The security model

MCP itself does not enforce security. The specification says so in its trust and safety section, and it lists the principles implementors must build in. Read the list as a checklist for your own setup.

Users must explicitly consent to and understand the data access and operations a server performs. Hosts must obtain explicit consent before exposing user data to a server, and before invoking any tool. The specification also says tool descriptions, including annotations, are untrusted unless they come from a server you trust. A server can say a tool is read-only. That is a claim in a string, and a buggy or malicious server can make it false.

In practice this means four habits.

Treat a server as code you run. A stdio server is a program on your machine with the permissions of your user account. Installing one is like installing any package. Read its source or at least its README, check who publishes it, and pin the version you run. The reference servers in the official repository are a good starting point, but read what each one does before you enable it.

Scope access. Give the server only the folders, accounts and scopes it needs. The filesystem server in the lab takes an allowed directory as an argument, and everything outside it is refused. Remote servers should get the narrowest OAuth scopes available, and tokens you can revoke.

Require approval for actions. Read-only tools and tools that write, delete, send or spend are different categories. Configure the host to ask before any state-changing tool runs, and read the arguments before you approve.

Watch what comes back. Tool results and resource contents are data. They can contain text that looks like instructions to the model, copied from a web page or a file. The host should not treat that text as an order, and neither should you. This is the root of prompt injection through tools, which gets its own article.

Hands-on: the filesystem server in a sandbox

This lab uses the official reference filesystem server from the modelcontextprotocol/servers repository, and the official MCP Inspector. You do not need a model, an API key or a paid account. You need Node.js. The Inspector requires Node 22.19.0 or newer, so use a current LTS release. You will create a folder, start the server with that folder as its only allowed directory, and check what the server lets you do.

Run these commands in the terminal where you want the lab. On Git Bash, Linux or macOS:

bash
mkdir mcp-sandbox && echo 'hello from the sandbox' > mcp-sandbox/note.txt

On PowerShell, the same result comes from:

powershell
New-Item -ItemType Directory mcp-sandbox | Out-Null
Set-Content -Path mcp-sandbox\note.txt -Value 'hello from the sandbox'

Start the Inspector with the server as its target:

bash
npx @modelcontextprotocol/inspector npx -y @modelcontextprotocol/server-filesystem ./mcp-sandbox

The Inspector prints a local URL that carries a token for this launch, and it opens that URL in your browser. Keep the terminal open, because the server runs as a child process of the Inspector for as long as you use it. The first launch can take a while because npx downloads the packages. If the connection fails while packages are still downloading, wait a minute and try again. In CLI mode, --connect-timeout raises the limit, and the commands below use it.

Connect and list tools

In the web page, click Connect. Then open the tab that lists the server's tools and run the list action. Labels change between versions, so look for the tab and the button that show tools. You should see the filesystem tools. On the release I tested, the reference server exposed sixteen tools, including list_directory, read_text_file, write_file and list_allowed_directories. read_file is still there and marked deprecated in favor of read_text_file.

Run list_allowed_directories first. It tells you what the server will accept. The result shows one path, the absolute location of mcp-sandbox. Nothing else is reachable.

Call list_directory and read_text_file

Select list_directory and pass . as the path, which means the allowed directory itself. The result should list one entry: [FILE] note.txt.

Select read_text_file and pass note.txt as the path. The result should contain the text you wrote, hello from the sandbox.

Notice the path. It is note.txt, not ./mcp-sandbox/note.txt. The server resolves relative paths against its allowed directory, not against the folder where you launched the Inspector. I made this mistake on my first attempt. The server returned a "no such file" error for a path it had resolved to mcp-sandbox\mcp-sandbox\note.txt. The error message showed me which path the server had actually used, which is why you should read error text closely.

Try a path outside the sandbox

Create a file in the parent folder, one level above mcp-sandbox:

bash
echo 'this file is outside the sandbox' > outside.txt

Back in the Inspector, call read_text_file with the path ../outside.txt. Expect an error result, not the file contents. On my machine the server returned this text, with the path shortened:

text
Access denied - path outside allowed directories: ...\mcplab\outside.txt not in ...\mcplab\mcp-sandbox

Then try an absolute path to the same file. The result should be the same refusal. The check runs in the server against the resolved path, so the request fails however you spell it.

This is what scoping looks like. The server is code running on your machine with your user's permissions, and it still cannot read the parent folder, because it was started with one allowed directory and enforces that list. Try a few more paths if you like, such as .. alone or a folder that does not exist, and compare the error text.

What I observed

I ran the same calls with the Inspector's CLI mode, which sends the same JSON-RPC messages as the web client:

bash
npx @modelcontextprotocol/inspector --cli --connect-timeout 120000 npx -y @modelcontextprotocol/server-filesystem ./mcp-sandbox --method tools/call --tool-name read_text_file --tool-arg path=note.txt

The reply came back as JSON, and the text content was hello from the sandbox followed by a newline. The same command with --tool-name list_allowed_directories showed the single allowed path. The ../outside.txt call returned the refusal shown above. The package versions on the day of the run were @modelcontextprotocol/inspector 2.14.0 and @modelcontextprotocol/server-filesystem 2.2.0. The server printed Secure MCP Filesystem Server running on stdio to stderr, which is the stdio rule in action. Its stdout stayed reserved for protocol messages.

I ran this on Windows 11 with Git Bash and Node 24, and I did not use the web page for these runs. Your versions and paths will differ, and the tool list can change between releases.

Common mistakes

Treating MCP as a model feature. MCP is a protocol between a host and a server. The model sees only what the host passes along. The same server can behave differently in two hosts if one shows tool descriptions more clearly or asks for approval in a different place.

Confusing a published server with an audited one. Installing a server runs its code. A package in a public registry says nothing about its safety.

Giving write access by default. Start read-only. Add write scopes only when a task needs them.

Printing to stdout in a stdio server. Logs and debug prints written to stdout break the protocol. Use stderr.

Trusting tool annotations. A tool marked read-only still runs whatever code the server contains.

Building from a summary of the spec. Revisions change method names and deprecations. Check the revision your SDK implements, and read the spec for exact field names before you write code.

Expecting the Inspector to audit. The Inspector shows what the server returns to the client. It is a debugging view, not a security review.

How to go further

Read the architecture overview first, then the server concepts and client concepts pages. They explain the primitives with more examples. After that, read the specification's base protocol and security sections, since they set the rules every server and host must follow.

Open the reference filesystem server's source. It is short, and it shows how a handler checks paths and returns errors. Then write a small server of your own with one tool that returns a fixed string, using an official SDK, and inspect it with the Inspector before you add anything that touches your files.

Keep the lab folder small and disposable, and delete it when you finish. The next article in this track, what is prompt injection in LLM applications, covers the attack that tool results make possible. For the background this one assumes, start with what are AI agents and tool use.

Sources

Sources

  1. Model Context Protocol specification (revision 2026-07-28)
  2. Model Context Protocol architecture overview
  3. Model Context Protocol reference servers
  4. MCP Inspector documentation

More in AI agents for security