Model Context Protocol (MCP) In Depth
RAG gives a model access to information. But there is a limitation RAG does not solve on its own: LLMs cannot act on the world — they cannot look up a live balance, let alone file a request. The Model Context Protocol (MCP), introduced by Anthropic in November 2024, is the open standard designed to connect AI applications with external tools and context sources.
What is MCP?
MCP is, at its core, a shared language that lets an LLM-powered application talk to external systems in a standardized way.
The building-concierge analogy: imagine you are an executive who wants to know how many vacation days you have left. You cannot walk straight into the HR database — you do not know the internal system, you do not have credentials, you do not know which queries to run. Instead, you call the concierge (the MCP server), who does have access: they understand what you need, they know which system to query, they have the right credentials, and they return the information in a format you understand. The concierge is not the one who has the information — they are the one who knows where and how to get it, and they can execute authorized actions on your behalf.
In technical terms, MCP uses JSON-RPC 2.0 messages and defines primitives for discovering and invoking tools, accessing resources, and obtaining prompt templates. The model can propose a call, but it is the host — through the MCP client — that sends the protocol messages and applies the authorization policies.
The current standard transports are stdio (a local subprocess) and Streamable HTTP (a network-accessible service). The old HTTP+SSE transport from the 2024 specification was replaced and has been deprecated since March 2025; SSE can still appear inside Streamable HTTP to stream responses, but it is no longer the name of the recommended transport for new implementations. MCP is model-agnostic: it does not force you to use a particular LLM provider.
The architecture: three main actors
- Host: the application that runs the LLM — our HR chat app, an IDE, or a chatbot. It contains one or more MCP clients and decides which capabilities to expose to the model.
- MCP Client: lives inside the host. It keeps a connection to a server, translates requests into the protocol, and manages their lifecycle.
- MCP Server: exposes tools, resources, and prompts connected to an external system. In our case we might have an HR server (vacation balance), a Calendar server (team availability), and a Policies server (the same RAG, exposed as a tool or a resource).
The full flow, with our scenario
Question: “how many vacation days do I have left THIS YEAR, and can I take next week off?”
Step 1 — Tool discovery: the LLM understands it needs data it does not have in its pretraining or in RAG. Through the MCP client, the host discovers what is available:
// Request
{"jsonrpc": "2.0", "id": 1, "method": "tools/list", "params": {}}
// Relevant part of the response
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"tools": [
{
"name": "get_vacation_balance",
"description": "Gets the available balance",
"inputSchema": {"type": "object", "properties": {"employee_id": {"type": "string"}}}
},
{
"name": "get_team_calendar",
"description": "Checks team availability",
"inputSchema": {"type": "object", "properties": {"team_id": {"type": "string"}}}
},
{
"name": "request_vacation",
"description": "Submits a request",
"inputSchema": {"type": "object", "properties": {"employee_id": {"type": "string"}}}
}
]
}
}
Step 2 — Invoke the first tool:
{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"get_vacation_balance","arguments":{"employee_id":"emp_12345"}}}
The server returns the structured result: Ana García has 18 days allocated, 6 used, and 12 available.
Step 3 — Invoke the second tool:
{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"get_team_calendar","arguments":{"start_date":"2026-09-01","end_date":"2026-09-07","team_id":"equipo_456"}}}
The result indicates high availability, one person absent out of a maximum of three allowed, and that the request is viable.
Step 4 — Answer with confirmation pending: “You have 12 days available. For next week, the team has high availability. You can request those days. Do you want me to file the request now?”
Notice: the LLM does not submit the request automatically — it asks for confirmation first. Read-only queries can be configured with less friction; actions with real effects should require explicit human confirmation according to their risk. MCP makes it possible for the model to propose request_vacation, but having the host execute it without asking is a separate design decision — and a bad idea in most cases.
Step 5 — Execution, after confirmation:
{"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"name":"request_vacation","arguments":{"employee_id":"emp_12345","start_date":"2026-09-01","end_date":"2026-09-07"}}}
The server responds with status enviada, request number VAC-2026-8473, and the submission date.
MCP vs. RAG
| RAG (Chapter 5) | MCP (this chapter) | |
|---|---|---|
| Kind of answer | “How many days do I get based on seniority?” (general rule) | “How many days do I have left?” (personal, live data) |
| Origin of the data | Document vectorized ahead of time | System queried in real time |
| Can act | Not on its own — it retrieves context | Can expose tools that execute actions |
| Updates | Requires reindexing when the document changes | Each query can get the current state |
MCP does not replace RAG — they usually live together. RAG implements retrieval over general policies; MCP can standardize access to that retriever and also to user-specific data or actions.
Security in MCP
User consent: it is never assumed for a sensitive action — “Do you want me to file the request?” [Yes] [No], never “I have filed your request” without having asked. The degree of confirmation should match the impact and reversibility of each tool.
Data privacy — authorization on the server:
def get_vacation_balance(requested_employee_id, authenticated_user):
if requested_employee_id != authenticated_user.id:
return {"error": "Not authorized to look up another employee's balance"}
return db.execute(
"SELECT * FROM vacaciones WHERE empleado_id = ?",
(requested_employee_id,),
).fetchone()
The trusted identifier should be derived from the authenticated session whenever possible, not from text generated by the model.
Server and tool security: the host must not automatically trust an unknown MCP server. Mitigations: a registry or allowlist of servers, provenance review, pinned versions, least-privilege credentials, and sandboxing when the server runs locally.
Supply-chain security: if the system the server connects to has a vulnerability, that vulnerability can also become reachable through the assistant. The server must never pass dynamic queries generated by the LLM straight to the database. Tools should be bounded operations with typed, validated parameters and parameterized queries.
Prompt injection and exfiltration: tool results are untrusted data; they can contain malicious instructions. The host must separate data from instructions, limit which tools can be chained, and ask for approval before sharing sensitive information with another server.
Auditing: every relevant action should log user, server, tool, allowed arguments, result, approval, and timestamp, applying appropriate minimization and retention. In regulated environments, these controls are often necessary to demonstrate traceability.
Local vs. remote, managed vs. self-hosted
| MCP Server | Data sensitivity | Starting point |
|---|---|---|
| Vacation balance (HR) | Very high | Self-hosted or a private managed service with contractual and technical controls |
| Team calendar | Medium | Internal or an approved provider with OAuth and least privilege |
| General policies (RAG) | Low | Remote managed can be reasonable |
| Official public holidays | Public | Third-party remote managed |
Sensitivity does not automatically force you to run everything locally: a remote platform can be valid if it satisfies residency, encryption, isolation, auditing, and contractual obligations. What must not happen is sending sensitive data to an unevaluated third party, or granting broader permissions than necessary.
MCP is not memory
It is easy to confuse MCP with “memory” because both connect the LLM to external information — but they are concepts at different levels. Memory answers “what does the system know or remember?”; RAG is a common implementation of semantic memory.
MCP answers “how does this application exchange capabilities and context with an external system?”. It can carry access to tools, resources, and prompts. Looking up a vacation balance is a tool over a live datum; exposing a collection of policies can be a resource or a search tool. That is why MCP is not a type of memory: it is a standardized interface that can also connect the agent to a memory.
Practical implementation
(Note: MCP SDKs evolve fast — verify the exact syntax against the current official documentation before using this code in production; the general structure stays stable.)
The example uses the Python SDK 2.x API documented in August 2026.
Basic MCP server (Python):
from mcp.server import MCPServer
server = MCPServer("mcp-rrhh-server")
@server.tool()
async def get_vacation_balance(employee_id: str) -> dict:
"""Gets the available balance of an authorized employee."""
# In production: obtain the authenticated identity and authorize on the server.
return {
"empleado": "Ana García",
"asignados_anio": 18,
"usados": 6,
"disponibles": 12,
}
@server.tool()
async def request_vacation(
employee_id: str,
start_date: str,
end_date: str,
) -> dict:
"""Submits a request after the host confirms the action."""
return {
"estado": "enviada",
"numero_solicitud": "VAC-2026-8473",
}
if __name__ == "__main__":
server.run() # stdio by default
MCP client (invocation from the host):
import asyncio
from mcp import Client, StdioServerParameters
from mcp.client.stdio import stdio_client
async def main() -> None:
params = StdioServerParameters(
command="python",
args=["servidor_mcp.py"],
)
async with Client(stdio_client(params)) as client:
tools = await client.list_tools()
print([tool.name for tool in tools.tools])
result = await client.call_tool(
"get_vacation_balance",
{"employee_id": "emp_12345"},
)
print(result)
if __name__ == "__main__":
asyncio.run(main())
For a new remote server, the standard alternative is server.run(transport="streamable-http", port=3001), accompanied by proper authentication and authorization.
MCP in the current ecosystem (2026)
MCP went from being one lab’s proposal to a project with shared governance and adoption across providers in a little over a year:
- November 2024: Anthropic publishes MCP as an open standard.
- March 2025: OpenAI announces adoption in its products. Support was available that month in Agents SDK; Responses API and ChatGPT were announced then as coming next, not as already available.
- May 2025: OpenAI adds remote MCP servers to the Responses API. Microsoft takes MCP integration in Copilot Studio to general availability.
- During 2025: Gemini, Cursor, Microsoft Copilot, Visual Studio Code, and other products incorporate MCP. In September, ChatGPT enables full read-and-write MCP tool support through Developer Mode.
- December 9, 2025: Anthropic donates MCP to the Agentic AI Foundation, a directed fund under the Linux Foundation, co-founded by Anthropic, Block, and OpenAI, with support from Google, Microsoft, AWS, Cloudflare, and Bloomberg.
- December 2025: in that same official announcement, Anthropic reports more than 97 million monthly downloads combined of the Python and TypeScript SDKs. The primary source does not place that figure for the first time in March 2026; by then it had already been published.
- August 26, 2026: OpenAI retires the Assistants API and directs the migration to Responses API and Conversations API. Responses includes MCP among its tools, but the migration is not exclusively “toward MCP”.
- Frameworks such as LangChain, LlamaIndex, LangGraph, CrewAI, and AutoGen offer MCP integrations, although their configuration and degree of support vary; there is no single universal “default” layer for everyone.
MCP’s original promise — an open standard for connecting AI applications with tools and context sources without creating a distinct integration for every combination — became a widely adopted option. That does not remove the need to verify version compatibility, security, or the concrete capabilities of each client.
Chapter summary
- MCP is a JSON-RPC 2.0 protocol for connecting AI applications with external tools, resources, and prompts.
- Three-actor architecture: Host (contains the LLM), MCP Client (inside the host), and MCP Server (connected to external systems).
- MCP and RAG live together: RAG implements retrieval; MCP can standardize how that retrieval and other capabilities are exposed.
- Security is critical: consent proportional to risk, authorization on the server, validation, least privilege, and auditing.
- MCP is not a type of memory — it is an interface that can connect tools, data, and also memory systems.
- Since December 2025, MCP is part of the Agentic AI Foundation under the Linux Foundation, with shared governance.
In the next chapter (Agentic flows, 6) we will look at how memory, tools, and degrees of autonomy combine into a single working agent.
LinkedIn nelson.zepeda@simov.io SIMOV LABS
Found an error? Suggest a correction