Bottom line: MCP is an important technology, but as of May 2025, it's not ready for production deployment. It's immature, the documentation is poor, and it doesn't have the security features it needs. Unless your business has a compelling and immediate need for it, wait a while before starting experimentation.
I've been hearing a lot about MCP and how much of a game-changer it is, but there are three problems with most of the articles I've read:
- They don't explain the what and the how very well.
- They're either too technical or too high-level.
- They smell too strongly of hype.
In this blog post, I'm going to dive into the why at a business level and do some of the how at a more technical level. This is going to be a hype free zone.
What problem are we trying to solve?
AI systems need to access data, but data is accessed in a huge number of ways, making it harder for an AI to connect and use data. MCP is a way of presenting the 'same' interface for all data types.
There are many different data sources, for example: JSON files, CSV files, XML files, text files, different APIs, different database types, and so on. In any computer language, there are different ways of connecting to these data sources. Here are two Python code snippets that illustrate what I mean:
- You use different Python libraries to access different data sources.
- The API is different.
- In some cases, the way you use the API is different (e.g. some sources use paging, others don't).
This is bad enough if you're a programmer writing code to combine data from different sources, but it's even worse if you're an AI. An AI has to figure out what libraries to use, what data's available, whether or not to use paging, etc. In other words, different data source interfaces make life hard for people and for AIs.
There's a related problem, often called the NxM problem. Let's imagine there are M data sources and N LLMs. Each LLM has to create an interface to each data source, so we get a situation that looks like this:
This is a huge amount of duplication (NxM). What's worse is if a data source changes its API (e.g, an AWS API update) we have to change N LLMs. If we could find someway of standardizing the interface to the data sources, we would have one set of code for each LLM (M) and one set of code for each data source (N), transforming this into an N+M problem. In this new world, if a data source API is updated, this just means updating one wrapper. Can we find some way of standardizing the interfaces?
(In the old days, this was a problem for hardware too. Desktop PCs would have a display port, an ethernet port, a printer port, and so on. These have pretty much all been replaced with USB-C ports. Can we do something similar in software?)
Some background
There has been a move to consolidate the interface to different sources, but it's been very limited. In the Python world, there's a database access library that lets you connect to most databases using the same interface, but that's about it. Until now, there just hasn't been a strong enough motivation for the community to work out how to provide consistent data access.
I want to go on two slight tangents to explain ideas that are important to MCP. Without these tangents, the choice of name is hard to understand, as are the core ideas.
At the end of the 1970's, Trygve Reenskaug was working at Xerox Parc on UI problems and came up with the Model-View-Controller abstraction. The idea is, a system can be divided into conceptual parts. The Model part represents the business data and the business logic. There's a code interface (API) to the Model that the View and Controller use to access data and get things done.
The Model part of this abstraction corresponds to the data sources we've been talking about, but it generalizes them to include business logic (meaning, doing something like querying a database). This same abstraction is a feature of MCP too. Sadly, there's a naming conflict we have to discuss. Model means data in Model-View-Controller, but it's also part of the name "large language model" (LLM). In MCP, the M is Model, but it means LLM; the data and business logic is called Context. I'm going to use the word Context from now on to avoid confusion.
Let's introduce another key idea to understand MCP, that of the 'translation' or 'interface' layer. This is a well-known concept in software engineering and comes up a number of times. The best known example is the operating system (OS). An OS provides a standardized way of accessing the same functionality on different hardware. The diagram below shows a simple example. Different manufacturers make different disk drives, each with a slightly different way of controlling the drives. The operating system has a translation layer that offers the same set of commands to the user, regardless of who made the disk drive.
Languages like Python rely on these translation layers to work on different hardware.
Let's summarize the key three ideas before we get to MCP:
- There's been very little progress to standardize data access functionality.
- The term Context refers to the underlying data and functionality related to that data.
- Translation layer software allows the same operations to work on different machines.
What MCP is
MCP stands for Model Context Protocol. It's a translation layer on top of a data source that provides a consistent way of accessing different data sources and their associated tools. For example, you can access database data and text files data using the same interface.
- The Model part of the acronym refers to the LLM. This could be Claude, Gemini, GPT, DeepSeek or one of the many other Large Language Models out there.
- Context refers to the data and the tools to access it.
- Protocol refers to the communication between the LLM and the data (Context).
Here's a diagram showing the idea.
What's interesting about this architecture is that the MCP translation layer is a server. More on this later.
In MCP terminology, users of the MCP are called Hosts (mostly LLMs and IDEs like Cursor or Windsurf, but it could be something else). Hosts have Clients that are connectors to Servers. A Host can have a number of Clients; it'll have one for each data source (Server) it connects to. A Server connects to a data source and uses the data source's API to collect data and perform tasks. A Server has functions the Client uses to identify the tasks the Server can perform. A Client communicates with the Server using a defined Protocol.
Here's an expended diagram providing a bit more detail.
I've talked about data sources like XML files etc., but it's important to point out that a data source could be Github, Slack, or Google Sheets or indeed any service. Each of these data sources has their own API and the MCP Server provides a standardizes way of using it. Note that the MCP Server could do some compute intensive tasks too, for example running a time-consuming SQL query on a database.
I'll give you an expanded example for how this all works, let's say a user asks the LLM (either standalone or in a tool like Cursor) to create a Github repo:
- The Model, via its MCP Client, will ask the MCP Server for a list of capabilities for the Github service.
- The MCP Server knows what it can do, so it will return a list of available actions, including the ability to create a repo.
- The MCP Client will pass this data to the LLM.
- Now the Model knows what Github actions it can perform and it can check it can do what the user asked (create a repo).
- The LLM instructs its MCP Client to create the repo, which it in turn passes the request to the MCP Server, which in turn formats the request using the Github API. Github creates the repo and returns a status code to the MCP Sever, which in turn informs the Client which in turn informs the Host.
This is a lot of indirection, but it's needed for the whole stack to work.
This page: https://modelcontextprotocol.io/docs/concepts/architecture explains how the stack works in more detail.
How it works
How to set up the Host and Client
To understand the Host and Client set up, you need to understand that MCP is a communications standard (the Protocol part of the name). This means, we only have to tell the Client small amounts of information about the Server, most importantly, it's location. Once it knows where the Server is, it can talk to it.
In Cursor (a Host), there's an MCP setting where we can tell Cursor about the MCP Servers we want to connect to. Here's the JSON to connect to the Github MCP Server:
In this example, the line "mcp/github" is the location of the GitHub MCP server.
Setup is similar for LLMs, for example, the Claude desktop.
I'm not going to explain the above code in detail (you should look here for details of how the Client works). You should note a couple of things:
- It's very short.
- It's terse.
- It has some security (the Personal Access Token).
How to set up the MCP Server
- Resources. They expose data to your Host (e.g. the LLM) and are intended for light-weight and quick queries that don't have side effects, e.g. a simple data retrieval.
- Tools. They let the Host tell the Server to take an action. They can be computationally expensive and can have side effects.
- Prompts. These are templates that standardize common interactions.
- Roots and Sampling. These are more advanced and I'm not going to discuss them here.
These are implemented in code using Python function decorators, a relatively new features of Python.
Regardless of whether it's Prompts, Tools, or Resources, the Client has to discover them, meaning, it has to know what functionality is available. This is done using discovery functions called list_resources, list_prompts, and of course list_tools. So the Client calls the discovery functions to find out what's available and then calls the appropriate functions when it needs to do something.
Resources
Note the use of async and the decorator. The async allows us to write efficient code for tasks that may take some time to complete.
Tools
Here's an example of two tool functions. As you might expect by now, the first function lets the Client discover which tools it can call.
The second function is a function the Client can call once the Client has discovered it.
Here, the code is calling out to an external API to retrieve the weather for a city. Because the external API might take some time, the code uses await and async. This is a tool rather than a resource because it may take some time to complete.
Prompts
You can read more about how prompts work in the documentation: https://modelcontextprotocol.io/docs/concepts/prompts#python
Messages everywhere
The whole chain of indirection relies on JSON message passing between code running in different processes. This can be difficult to debug. You can read more about MCP's message passing here: https://modelcontextprotocol.io/docs/concepts/transports
Documents, tutorials, and YouTube
At the time of writing (May 2025), the documentation for MCP is very sparse and lacks a lot of detail. There are a few tutorials people have written, but they're quite basic and again lack detail. What this means is, you're likely to run into issues that may take time to resolve.
There are videos on YouTube, but most of them have little technical content and seem to be hyping the technology rather than offering a thoughtful critique or a guide to implementation. Frankly, don't bother with them.
Skills needed
This is something I've hinted at in this blog post, but I'm going to say it explicitly. The skill level needed to implement a non-trivial MCP is high. Here's why:
- The default setup process involves using uv rather than the usual pip.
- The MCP API makes extensive use of function decorators, an advanced Python feature.
- The Tools API uses async and await, again more advanced features.
- Debugging can be hard because MCP relies on message passing.
The engineer needs to know about function decorators, asynchronous Python, and message passing between processes.
Where did MCP come from?
MCP was released by Anthropic in November 2024. After a "slowish" start, it's been widely adopted and has now become the dominant standard. Anthropic have open-sourced the entire protocol and placed it on GitHub. Frankly, I don't see anything usurping it in the short term.
Security and cost
This is a major concern. Let's go back to this diagram:
There could be three separate companies involved in this process:
- The company that wants to use the LLM and MCP, we'll call this the User company.
- The company that hosts the LLM, we'll call this the LLM company.
- The company that hosts the data source, we'll call this the Data company.
The User company starts a job that uses an LLM in the LLM company. The job uses computationally (and $ costly) resources located at the Data company. Let's say something goes wrong, or the LLM misunderstands something. The LLM could make multiple expensive calls to the data source through the MCP Server, racking up large bills. Are there ways to stop this? Yes, but it takes some effort.
The other concern is a hacked remote LLM, Remember, the LLM has the keys to the kingdom for your system, so hackers really could go to town, perhaps making rogue calls to burn up expensive computing resources or even writing malicious data.
There are a number of other concerns that you can read more about here: https://www.pillar.security/blog/the-security-risks-of-model-context-protocol-mcp and here: https://community.cisco.com/t5/security-blogs/ai-model-context-protocol-mcp-and-security/ba-p/5274394
The bottom line is, if you're running something unattended, you need to put guard rails around it
Complexity - everything is a server?
As I've stated, this is a very complex beast under the hood. The LLM will run in its own process, the MCP Server will run in its own process, and maybe the underlying data sources will too (e.g. a web-based resource or a database). If any of these processes fail, then the whole system fails. If a system fails, the developers have to debug which of all these servers failed first. Inter-process communication is harder than simple procedure calls which means debugging is too.
All of the examples I've seen on the web have been relatively simple. I'm left wondering how complex it would be to develop a robust system with full debugging for something like a large-scale database. I'm not sure I want to be first to find out.
How can I get started?
- The project home page: https://modelcontextprotocol.io/introduction
- The Python SDK on GitHub: https://github.com/modelcontextprotocol/python-sdk
I couldn't find tutorials or articles that are good enough for me to recommend. That of itself is telling.
Where we stand today
MCP was released in November 2024 and it's still an immature standard.
- Security in particular is not where it needs to be; you need to put guard rails up.
- Documentation is also sorely lacking and there are very few good tutorials out there.
- Debugging can be very hard, the message passing infrastructure is more difficult to work with than a simple call stack.
Sadly, the hype machine has really got going and you would think that MCP is ready for prime time and immediate deployment - it's not. This is definitely an over-hyped technology for where we are now.