What is MCP (Model Context Protocol) and How It Works

Clint Adair / Unsplash
Every AI assistant hits the same wall: it knows a lot about the world up to its training cutoff date, but it knows nothing about your files, your database, or your company's internal API. The Model Context Protocol (MCP) was born to close exactly that gap. Since Anthropic released it as an open standard in late 2024, it has effectively become a kind of USB-C for connecting language models to external tools and data.
What is MCP
The Model Context Protocol is an open protocol based on JSON-RPC 2.0 that defines a standardized way for a language model (or the application hosting it) to connect to external data sources and tools. Instead of each AI assistant reinventing its own way to read a file, query a database, or trigger an automation, MCP provides a common interface that any server can implement and any client can consume.
In practice, this means that someone building an integration—for example, a connector for GitHub or Google Drive—does that work once, in the form of an MCP server. Any compatible application, such as Claude Desktop, Claude Code, an IDE, or a custom agent, can then use that integration without writing any code specific to it. The protocol organizes communication into three building blocks: tools (actions the model can execute), resources (data the model can read), and prompts (reusable instruction templates), all described in a format the model interprets at runtime.
The Problem MCP Solves
Before MCP, connecting an LLM to an external system usually meant writing a custom integration: a plugin here, a custom function calling there, an agent wrapped in specific API wrappers. Each AI application had its own way of exposing tools, and each tool needed to be adapted for each application. If a company maintained 5 internal tools and wanted them to work across 3 different assistants, the result was 15 separate integrations to maintain, each with its own authentication scheme and request format.
This is the classic "M times N" problem: M tools multiplied by N applications yields M×N integration bridges. MCP flattens this equation to M+N. Each tool becomes an MCP server, built once; each application becomes a compatible MCP client, also built once; and any combination between the two sides works without extra effort. It's essentially the same reasoning behind the Language Server Protocol, created years earlier so code editors no longer needed to implement native support for each programming language separately.
Protocol Architecture
MCP follows a client-server architecture with three well-defined roles:
- Host: the application the end user interacts with directly, such as Claude Desktop, an IDE with built-in AI, or a custom agent.
- MCP Client: a component inside the host that maintains an individual, isolated connection to a specific MCP server.
- MCP Server: a separate process, local or remote, that exposes tools, resources, and prompts over an external system.
A single host can run multiple MCP clients simultaneously, each connected to a different server: one for the file system, another for Slack, another for a Postgres database. This one-client-per-server rule is intentional because it ensures isolation. A failure or crash in one server does not bring down the connection to others.
The diagram below summarizes this design:
Communication between client and server happens over two main transports. For local servers, the default is stdio, the process's standard input and output, simple and fast because it requires no network. For remote servers, the transport is HTTP with Streamable HTTP, the evolution of the older Server-Sent Events transport, which allows hosting an MCP server in the cloud and serving multiple clients simultaneously.
The MCP Client
The client lives inside the host and handles three things: opening and maintaining the connection to a server, translating the model's decisions into JSON-RPC messages, and returning the server's responses back into the conversation context. In practice, the client also handles the initial handshake, the initialize step where client and server exchange protocol version and supported capabilities, as well as the discovery process, where it asks the server what tools, resources, and prompts it offers.
A subtle detail is that the client doesn't decide on its own when to use a tool. That decision is up to the model: the client provides the LLM with the list of available tools, formatted as part of the conversation context, and the model itself chooses, based on what the user asked, whether and which tool to call. The client only executes the technical call after the model has decided.
The MCP Server
On the other side, the server is the one that actually knows how to interact with the external system, be it a database, a third-party API, or the local file system. A well-built MCP server exposes its capabilities in a self-descriptive way: each tool comes with a name, a natural language description, and a schema, usually JSON Schema, that defines what parameters it accepts.
This self-description is what makes MCP pluggable. The server doesn't need to know which model will consume it, and the client doesn't need any code specific to that server beyond speaking the protocol. Today, there are hundreds of MCP servers published by companies like GitHub, Stripe, Cloudflare, and Sentry, along with a large ecosystem of community servers for databases, productivity tools, and internal services.
MCP Tools
Tools are the most used type of capability in MCP: actions the model can execute that typically have side effects or return computed results, like checking the weather, creating a Trello card, or running a SQL query. The protocol defines two more capability types worth knowing:
- Resources: data the server exposes for reading, such as the contents of a file, a database row, or the result of a query. Unlike tools, resources usually have no side effects; they are just context the host can attach to the conversation.
- Prompts: reusable instruction templates the server provides, like a ready-made script for reviewing a pull request or summarizing a support ticket, which the user can invoke directly.
Each tool is described by an object with name, description, and inputSchema. It is this description, written in natural language but structured, that allows the model to understand what the tool does without anyone needing to program explicit rules for when to use it.
Complete Flow of an MCP Call
To understand how all this connects in practice, it's worth following the lifecycle of a real call as it travels between client and server via JSON-RPC 2.0.
First, during connection, the client asks what tools the server offers:
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/list"
}
The server responds with the full list, including each tool's schema:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"tools": [
{
"name": "get_weather",
"description": "Returns the current weather forecast for a city",
"inputSchema": {
"type": "object",
"properties": {
"city": { "type": "string" }
},
"required": ["city"]
}
}
]
}
}
This catalog is fed to the model as part of the available context. When the user makes a request the model interprets as requiring the get_weather tool, the client assembles and sends a tools/call request:
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "get_weather",
"arguments": { "city": "São Paulo" }
}
}
The server executes the real logic behind the tool, in this case likely a call to an actual weather API, and returns the result in the same JSON-RPC format:
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"content": [
{ "type": "text", "text": "23°C, clear sky in São Paulo" }
]
}
}
The client injects this result back into the conversation, and the model uses the information to formulate the final response to the user. From the perspective of someone chatting with the assistant, all of this happens transparently: they only see the question and the answer, unaware that a full protocol negotiation took place behind the scenes.
MCP vs Traditional API
A common question arises: if every external tool already has a REST API, why not use that API directly? The answer lies in the difference between integrating a system for a human developer to read the documentation and integrating a system for a model to discover and use on its own, in real time.
| Aspect | Traditional REST API | MCP |
|---|---|---|
| Capability discovery | External documentation (Swagger/OpenAPI), read by a human | Client asks the server in real time which tools exist (tools/list) |
| Integration per system | A dedicated connector for each API | A single standard protocol for any MCP server |
| Communication format | HTTP + JSON, schema varies by provider | Standardized JSON-RPC 2.0 |
| Context for the model | Developer manually decides what to expose to the LLM | Server describes tools in a way the model itself decides what to use |
| Transport | Almost always HTTP | stdio (local) or HTTP with Streamable HTTP (remote) |
| Reuse across AI apps | Low, each app reimplements the integration | High, any compatible host uses the same server |
In practice, MCP does not replace the REST API behind the server; it is a standardization layer on top of it. An MCP server for GitHub, for example, likely calls the GitHub REST API internally. The difference is that the model never needs to know that: it only sees tools with consistent names and schemas, no matter which API is behind them.
Practical Example: A Simple MCP Server
To bring the protocol to life, a minimal MCP server in Python using the official SDK with the FastMCP class can be written in just a few lines:
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("weather-server")
@mcp.tool()
def get_weather(city: str) -> str:
"""Returns the current weather forecast for a city."""
# the actual call to a weather API would go here
return f"23°C, clear sky in {city}"
if name == "main":
mcp.run(transport="stdio")
This server exposes a single tool called get_weather. The @mcp.tool() decorator automatically generates the JSON schema from the function signature and docstring, so there's no need to write JSON Schema by hand. When this script runs, it listens via stdio, ready for any MCP client—such as a locally configured Claude Desktop—to connect and discover the tool automatically.
This example is intentionally simple, but the same pattern applies to more complex servers: the business logic (querying a database, calling a paid API, triggering an automation) goes inside the function body, and the SDK handles all the protocol, handshake, and serialization.
Security in MCP
Giving a language model direct access to tools that read files, write to databases, or call third-party APIs raises an obvious question: what ensures this doesn't get out of control? The protocol defines some safeguards, but the ultimate responsibility is shared between the server, the client, and whoever configures the system.
At the server level, best practice is to apply the principle of least privilege: an MCP server for the file system, for example, should restrict access to specific directories, never the entire disk. Remote servers using HTTP transport typically require authentication via OAuth 2.1, and the protocol specification covers this in detail, precisely because misconfigured servers are the most obvious attack vector.
At the client and host level, the most visible security layer for the end user is human consent: hosts like Claude Desktop ask for confirmation before executing sensitive tools, such as writing a file or sending a message, especially the first time a tool is used. There is also the risk of prompt injection via resources, when malicious content embedded in a document the model reads tries to manipulate the model into performing an inappropriate action. Servers that expose content from untrusted sources, such as web pages or received emails, deserve extra attention from both the server builder and anyone deciding to connect such a server to an agent with broad permissions.
Limitations of MCP
MCP solves the standardization problem, but it is not a magic bullet for everything. It's worth knowing the real limits before deciding to build on it.
First, the protocol is still relatively new, released in late 2024, and the ecosystem, though growing fast, still has gaps: not every popular tool already has a mature, officially maintained MCP server. Second, more tools available at once means more tokens spent describing those tools in the conversation context, which can increase call costs and, in extreme cases, compete for context space with the rest of the conversation. Third, the protocol alone does not solve the model's reliability problem: it can still choose the wrong tool, assemble incorrect arguments, or misinterpret a result returned by the server, so careful orchestration remains the responsibility of the agent builder.
Finally, authentication and access governance in corporate environments—who can connect which server, with what permissions, audited in what way—is still a maturing space, with significant work happening in the community and among companies adopting the protocol in production.
FAQ
Is MCP exclusive to Anthropic and Claude?
No. MCP is an open protocol with a public specification, and other AI companies and agent frameworks have already announced support for it, even though it originated within Anthropic.
Do I need to know how to program to use an MCP server?
To use an already-built server, such as one for file access or Google Drive, inside an app like Claude Desktop, no. Programming is only necessary for those who want to build a new server from scratch.
Does MCP replace function calling?
Not exactly. Function calling is the model's ability to choose and format a tool call. MCP uses that ability under the hood but adds the discovery, standardization, and transport layer between client and server that function calling alone does not define.
Can an MCP server run in the cloud?
Yes. Remote servers use HTTP transport and can be hosted normally, serving multiple clients simultaneously, with authentication via OAuth.
The real gain of MCP is not technical in the sense of "yet another API"; it is organizational: it transforms a problem that used to grow multiplicatively—each tool for each application—into a problem that grows additively. For anyone building AI agents today, understanding this protocol is no longer optional. It is already the closest piece of infrastructure to a de facto standard for connecting models to everything that exists outside the model itself.
This content was created and reviewed by our team (iatoskill.com), if you find any issues, please reach out to us


