Understanding MCPs
Why are MCP's required?
Quite often that sci-fi books, movies and general entertaintment portrait LLMs as super-advanced computers capable of consciousness, with the capabilities to control software and hardware around them with a simple touch, overriding any authentication required.
This phenomenon I'd like to name it the 'anti deux-ex-machina', as the script use LLMs to justify any bit of inconsistencies. From time travel, to hacking the whole internet, LLMs are portraited as all-powerfull entities that, if let loose, would end up destroying the world we live in.
A similar phenomenon happens with CyberSecurity as a field. Access Granted or I'm hacking their mainframe are the typical buzzwords movies use to avoid investing in research to make the dialog make sense. Mr.Robot is a very nice exception to this. However, these phenomenons are probably a topic to discuss in another post.
Reality is a bit dissapointing once you understand that not too long ago, AI models were having challenges trying to classify pictures with cats and dogs. Let alone develop consciousness, or hack a system to override it's admin rights. Let's remember that, LLM are statistical prediction machines focused on predict the next tokens in a sequence. Nothing magical is happening under the hood.
Now that we remember that LLMs are text-based models, we now understand how an LLM will have major problems interacting with any applications around them.
That's where MCPs kick in. Through this protocol, we'll be able to provide an LLM model with context of the functions available, so the model can understand the situations, parameters and the results it'll get by calling these functions.
Note. as the date of writing this article, MCPs definition is still under revision and is not production-ready yet. Use with caussion.
Architecture overview
Just like many other architectures, MCP is based on a server-client relationship. MCP servers will have collection of functions available for a model to execute. In the scope of MCPs, these functions will be named tools.
The tools will be presented through an mcp endpoint on the server. We can consider these to hold the business logic to which the model will have access.
On the other hand, the MCP client will be the implementation which will have a connection between the LLM model and the MCP server. The responsibility of the MCP client will be to consume the MCP server list of tools to provide them to the model. These tools will contain a detailed description of the function signature, including name, parameters number, parameter types and return type of the function (if any). The description of the function should also have a clear specification of the intended use of the function, which use-cases could be solved by the function and the expected scenarios it should be triggered.
By definition of the protocol, a LLM model will run only one tool at the time, so it's recommended to orchestrate any multi-function as a single tool for the LLM to consume.
Challenges
Bridging a text-only model with the messy, stateful outside world sounds easy on a slide, but there are a bunch of practical gotchas the moment you turn this on:
-
Transport & session shape. MCP messages are JSON-RPC, and standard transports today are
stdio
and Streamable HTTP. Each has real trade-offs for concurrency, isolation and deployability (dev tools lovestdio
; networked deployments prefer HTTP/SSE). Pick one early and design your retry/timeout story around it. -
Tool surface area creep. It’s tempting to expose “everything the app can do.” Don’t. Every new tool is an extra attack surface and an extra decision for the model. Keep the surface tight, coarse-grained, and business-oriented (e.g.,
create_invoice_from_quote
) instead of dumping dozens of fine-grained primitives. The protocol gives you tools, resources (read-only data), and prompts as different primitives—use the right one for the job. -
Authentication & authorization. MCP doesn’t magic away auth. Servers still need to enforce identity, scoping and consent to act (user vs. service tokens, OAuth where relevant, per-tool ACLs, per-tenant scoping). Secrets management, short-lived tokens and explicit user confirmation for destructive actions are table stakes.
-
Determinism vs. real systems. Models prefer predictable, idempotent tools. Real APIs fail, paginate, time out and return partial data. Design idempotency keys, retries with backoff, and clear error codes mapped to JSON-RPC errors so the client can recover sanely (and the model can try again without duplicating side effects).
-
Long-running and streaming work. Some tasks don’t finish in one shot (exports, builds). Decide whether your “one tool call” kicks off a job and returns a handle (poll later), or streams progress/results over the chosen transport. (HTTP/SSE and
stdio
both support streaming patterns; plan for progress messages.) -
Observability & audit. You’ll want per-call logs, inputs/outputs (with redaction), latency, error rates, and a full audit trail of state-changing tools. This isn’t just for debugging; it’s critical for compliance if the model can touch customer data.
-
Client variability. Different MCP clients emphasize different primitives or transports. Claude Desktop, for instance, can connect to local servers out of the box; agent frameworks like LangChain/GraphLang ship adapters that translate MCP tools into their agent loops. Your server should be conservative and spec-faithful so it plays nicely across clients.
-
Versioning & evolution. Tool signatures change. Add
version
fields and deprecation windows, keep changes backward-compatible when possible, and publish human-readable release notes so clients can adapt incrementally. -
Data boundaries. Use resources for read-only context (files, schemas, snapshots) and tools for actions. Mixing them leads to brittle prompts and surprise write-paths in what should be safe reads.
Tool selection
So… what should you actually expose to the model? A few rules of thumb that have worked well:
-
Start from outcomes, not endpoints. Name tools after user-visible intents (“submit_expense_report”) instead of internal micro-steps (“post_line_item”, “attach_pdf”). Coarse-grained tools reduce chain length and model planning errors. (Remember: the model will call one tool at a time—compose multi-step logic inside the tool if needed.)
-
Prefer read-only resources for context. Static docs, configs, schemas and listings are better exposed as resources with stable URIs; reserve tools for actions or computations. This keeps the model’s decision simple: read from resources, do with tools.
-
Constrain with clear schemas. For each tool, give a sharp description and tight parameter schema (types, required fields, enums, bounds). Describe preconditions (“must own the invoice”), side effects (“sends email”), and postconditions (“returns the created ID”). The protocol expects tools to carry machine-readable metadata—use it to your advantage.
-
Make side effects deliberate. Add
dry_run
,confirm
, orpreview_diff
patterns for destructive operations. Return a human-readable summary of what will change and require an explicit follow-up call to apply. -
Design for idempotency. Accept an optional
request_id
/idempotency_key
. If the model retries, you won’t duplicate payments, tickets or PRs. -
Stream or stage long work. For heavy tasks, return a job handle and expose a
get_status
/get_result
tool, or stream progress chunks if your client supports it (stdio/HTTP SSE). -
Scope and least privilege. Bind tools to the current user/tenant, validate permissions server-side, and never rely on the model to “remember” not to call something. Consider per-tool rate limits and quotas.
-
Observability first. Include opaque
trace_id
s in responses, emit structured logs for each call (with redaction), and document error codes. Your future self (and the on-call) will thank you. -
Pilot with one client, keep it portable. Test with a reference client (e.g., Claude Desktop for local workflows, or an agent framework that speaks MCP) but avoid client-specific quirks. The protocol is designed to be multi-client—tools, resources and prompts are the portable primitives.
-
Document examples. For each tool: 2–3 realistic example invocations + expected outputs. Models learn from descriptions; humans learn from examples.
Reality check. MCP is evolving fast, and ecosystems around tools, resources, and prompts keep expanding. Treat your server as a product: version it, test it, and iterate as the protocol tightens.