Skip to content
Epic Software Labs
All trends

AI, Data & Machine Learning

Model Context Protocol (MCP): what it is and when it is worth adopting

What MCP is, when it beats a direct integration, and the tool-poisoning research you should read before connecting an MCP server to production data.

9 min readEpic Software Labs

Key takeaways

  • MCP is an open protocol that standardises how AI applications connect to external tools, data and prompts — one interface instead of a bespoke integration per assistant.
  • Its value is combinatorial: N tools and M assistants become N + M pieces of work instead of N × M.
  • If you have one assistant talking to one internal system, a direct integration is simpler and you should write that instead.
  • The hard problems are not protocol problems. Authorisation, tool description quality and context budget determine whether an integration works.
  • Treat an MCP server as production software: version it, log every call, scope its credentials narrowly, and test the tool descriptions as carefully as the code.

The Model Context Protocol exists because of a combinatorics problem. Every AI assistant needs to reach things outside itself: your database, your ticketing system, your documentation, your deployment pipeline. Until recently, every one of those connections was bespoke. Each assistant had its own plugin format, its own auth story, its own way of describing what a tool does. Connecting five systems to three assistants was fifteen integrations.

MCP is the attempt to make that number eight.

What the protocol actually does

The Model Context Protocol is an open standard defining how an AI application talks to an external capability provider. A server exposes some combination of three things:

  • Tools — functions the model can invoke, each with a name, a description and a typed schema for its arguments
  • Resources — data the application can read into context, addressed by URI
  • Prompts — reusable templates a user can invoke deliberately

A client — the AI application — connects to one or more servers, discovers what they offer, and makes those capabilities available to the model.

The mechanics are unremarkable on purpose: JSON-RPC over a transport, typically stdio for a local process or HTTP for a remote one. The value is not technical novelty. It is that everyone agreed on the same shape.

When it is worth it, and when it is not

This is where most of the confusion lives, so it is worth being direct.

SituationUse MCP?Reasoning
One internal assistant, one internal serviceNoA direct function call is less code and fewer failure modes
One capability, several assistants (IDE, chat, CI)YesWrite the server once, consume it everywhere
You want to use third-party capabilitiesYesThe ecosystem is the point
You are shipping a product other people's agents should reachYesYou publish a server; you do not build an adapter per client
Latency-critical path inside your own applicationNoThe extra hop rarely justifies the abstraction
Prototype exploring what an agent should be able to doUsuallyCheap to swap tools in and out while you learn

The honest default: if you can name every consumer of an integration and there is exactly one, write the direct integration. Protocol overhead is real, and premature standardisation has the same cost as premature abstraction anywhere else.

The problems that actually bite

Teams arrive expecting protocol problems and hit design problems instead.

Tool descriptions are prompts

The description field is not documentation for a human reader. It is the entire basis on which a model decides whether to call a tool and how to fill in its arguments. search_records with a one-line description will be called at the wrong moments with the wrong arguments. A description that states what the tool does, when to use it, when explicitly not to use it, and what the arguments mean in practice will not.

Treat these as prompts. Iterate on them, and test them, because they behave like prompts — including behaving differently across models.

Context budget is a hard constraint

Every tool definition sits in the model's context on every request. A server exposing forty tools spends a meaningful slice of the window on descriptions before any actual work happens — and gives the model forty candidates to confuse.

Fewer, better-scoped tools consistently outperform exhaustive coverage. If you are exposing a large API surface, resist mapping it endpoint-for-endpoint; expose the handful of operations that correspond to things a user actually asks for.

Authorisation is yours to solve

The protocol carries the call. It does not decide whether the calling user should be allowed to make it.

The failure mode to avoid is a server holding a broad service credential and acting on behalf of whoever is talking to it — a confused deputy, with a natural-language interface bolted on. What you want instead:

  • Credentials scoped to the narrowest permission set the server genuinely needs
  • The end user's identity carried through to the underlying system, not collapsed into a service account
  • Write operations gated behind explicit human approval
  • Every invocation logged with arguments, caller and outcome

Tool poisoning is the protocol-specific attack

The security literature on MCP matured quickly, and it has converged on a threat that is specific to this architecture rather than inherited from LLMs generally.

Tool poisoning places malicious instructions in tool metadata — the description and schema the model reads in order to decide what to call. Because that metadata enters the context as trusted-looking material supplied by the system rather than by a user, it is unusually effective. A 2026 threat-modelling analysis identifies it as the most prevalent and impactful client-side vulnerability in MCP deployments, finding that many clients perform insufficient static validation of tool metadata and give users little visibility into the parameters actually being sent (arXiv:2603.22489).

Two variants are worth knowing:

  • Full schema poisoning — the attacker controls the entire schema, not just the description, and can introduce hidden parameters, altered return types or malicious defaults that affect every subsequent invocation while looking legitimate to monitoring.
  • Resource content poisoning — instructions hidden inside the data a server returns, executed as commands when the model processes them.

A systematic analysis of prompt injection against agentic coding assistants catalogues 42 distinct techniques across input manipulation, tool poisoning and protocol exploitation, and reports that most published defences achieve under 50% mitigation against adaptive attacks (arXiv:2601.17548).

That number is the important one. It means detection-based defences are a layer, not a solution, and the actual security boundary has to be the one Beurer-Kellner et al. describe: constrain what the agent is able to do after it has touched untrusted input, rather than trying to detect whether the input was malicious (arXiv:2506.08837).

Practically, for anything reaching production:

  • Pin tool definitions. Treat a changed description or schema on an existing tool as a security event, not a routine update. Diff them in CI.
  • Show users the actual arguments before a consequential call executes, not a paraphrase of them.
  • Separate read servers from write servers, with different credentials, so a poisoned read path cannot reach a write capability.
  • Assume detection fails. Design so that the worst thing a fully compromised tool description can achieve is bounded by permissions rather than by the model's judgement.

Errors need to be readable by a model

A tool returning 500 Internal Server Error gives the model nothing to work with, so it retries the same call. A tool returning "the date range must be under 90 days; you asked for 400" lets it correct itself on the next turn.

Error messages in an MCP server are part of the interface, not an afterthought. Write them for a reader who can act on them.

What building a server actually involves

The protocol mechanics are a day's work. The rest is what determines whether the thing is usable, and it divides into four jobs that have little to do with JSON-RPC.

Choosing the tool surface. This is a product decision disguised as an engineering one. You are not exposing your API; you are exposing the handful of operations that correspond to things a person actually asks for. An API with sixty endpoints usually maps to six or eight good tools. Getting this wrong is the most common reason a technically correct server performs badly — see the context-budget argument in context engineering.

Writing the descriptions. These are prompts, not documentation, and they deserve the same iteration. A description should say what the tool does, when to use it, when explicitly not to, and what each argument means in practice. Expect to rewrite them several times against real transcripts, and expect them to behave differently across models.

Shaping the responses. A tool returning a 400-row JSON dump has technically succeeded and practically failed — it has just spent a large slice of the context window on material the model must now read past. Return what is needed, paginate the rest, and summarise where summarising is safe.

Deciding the trust and authorisation model. Which user is this acting as? What can it reach? What needs approval? This is where most of the real design time goes, and it is the part the protocol deliberately leaves to you.

Transport and deployment

Two shapes, and the choice is mostly about who runs it:

TransportRuns asSuits
stdioLocal subprocess of the clientDeveloper tooling, local files, anything where the user's own credentials are the right ones
HTTPA remote service you operateShared internal capabilities, anything multi-user, anything you want to log centrally

Local stdio servers inherit the user's environment, which is convenient and means the security boundary is the user's machine. Remote HTTP servers need everything an ordinary service needs — authentication, rate limiting, observability, a deployment story — because that is what they are.

MCP compared with the alternatives

Worth situating, since MCP is not the only proposal in this space and the comparison clarifies what it is actually for.

Plain function calling. Your application defines tools directly against the model API. Fewer moving parts, no extra hop, and completely adequate when there is exactly one consumer. This remains the correct default for a single application talking to its own services.

Agent-to-agent protocols. A2A, ANP and related efforts address a different problem: agents delegating to other agents, rather than an agent reaching a capability. Comparative threat modelling across MCP, A2A, Agora and ANP (arXiv:2602.11327) is worth reading if you are choosing between them, mostly because it makes clear how different their trust assumptions are.

Bespoke plugin formats. What existed before, and the thing MCP was proposed to replace. The argument against them is not technical quality; it is that each one binds a capability to a single vendor's assistant.

The honest summary: MCP is a packaging standard. It earns its place through the ecosystem rather than through anything clever in the design, which is also why its security properties depend so heavily on what you connect to it.

Running one in production

The engineering practice is ordinary, which is the point — an MCP server is a small service and deserves the same treatment as one.

  1. Version explicitly. Clients cache tool definitions. Changing an argument's meaning without a version bump breaks consumers silently.
  2. Log every call. Tool name, arguments, caller identity, latency, outcome. This is your only window into what the model is actually doing, and you will need it the first time something odd happens.
  3. Set timeouts and rate limits. A model in a retry loop will hammer an endpoint more persistently than any human.
  4. Test the descriptions, not just the code. Keep a set of realistic user requests and check that the right tool gets selected with the right arguments. This is the test that catches regressions the unit tests cannot see.
  5. Start read-only. Ship the read path, watch how it is used for a few weeks, then add writes with approval gates.

Building an MCP integration?

Our AI platform engineering practice builds MCP servers and the agent applications that consume them — including the unglamorous parts: auth, audit logging, and getting tool descriptions right.

Start a project

A decision checklist

Before committing to MCP for a given capability, five questions settle it faster than a design document.

How many consumers will there be, honestly? Not how many you can imagine — how many exist or are funded. One means write the direct integration. Three or more means the protocol pays for itself.

Does the capability belong to you? Publishing a server for a product other people's agents should reach is a distribution decision, and a good one. Wrapping your own internal service for your own single assistant is usually overhead.

Can you state the tool surface in under ten operations? If not, you have not finished designing it. A server that mirrors an API endpoint-for-endpoint will underperform regardless of implementation quality.

Who is the caller, and what may they reach? If the answer is "a shared service account with broad access", stop and fix that first. It is the single most common way these deployments become a liability.

What is the worst a compromised tool description could do? Given that published defences against tool poisoning achieve well under half mitigation against adaptive attacks, the answer needs to be bounded by permissions rather than by the model's judgement.

If those five have good answers, the implementation is straightforward. If they do not, the protocol will not rescue the design.

Where this is heading

The interesting consequence of a standard interface is not the standard. It is that capabilities become composable across vendors: an assistant can reach a tool whose author never considered that assistant, the same way a browser reaches a website.

That is genuinely useful, and it is also why the security posture matters more than the protocol details. Composability cuts both ways, and the practices that make an MCP deployment safe — narrow credentials, user-scoped identity, approval on writes, complete audit logs — are the ones worth establishing while your surface area is still small enough to reason about.

Frequently asked questions

What is the Model Context Protocol?

MCP is an open standard that defines how AI applications connect to external capabilities. A server exposes tools the model can call, resources it can read, and prompts it can use; any MCP-compatible client can consume them without a custom adapter. The point is that the integration is written once against the protocol rather than once per assistant.

How is MCP different from ordinary tool calling or function calling?

Tool calling is the model-side mechanism — the model emits a structured request to invoke a function. MCP is the transport and packaging layer around that: how a tool is discovered, described, authenticated and invoked over a connection. They are complementary. MCP is how a tool gets to the model; tool calling is what the model does with it.

Do we need MCP to build an AI agent?

No. An agent can call your functions directly, and for a single application talking to a single set of internal services that is usually less code and fewer moving parts. MCP earns its place when the same capability needs to be reachable from several assistants, or when you want to consume capabilities other people have already built.

Is MCP secure enough for internal company data?

The protocol does not make an insecure integration secure. What matters is what you do around it: scope each server's credentials to the narrowest possible permission set, authenticate the calling user rather than sharing a service account, require explicit approval for any write operation, log every invocation with its arguments, and run third-party servers with the same suspicion you would apply to any third-party dependency with network access.

What is the biggest practical problem with MCP servers?

Context budget and description quality, not the protocol. Every tool a server exposes consumes space in the model's context and adds a candidate for it to choose wrongly between. A server with forty thinly described tools performs worse than one with eight well-described ones. Tool descriptions are prompt engineering, and they deserve the same iteration as any other prompt.

How many tools should an MCP server expose?

Fewer than the API it wraps, and usually under ten. Every tool definition occupies context on every request and adds another candidate for the model to choose wrongly between, so coverage and performance pull against each other. The right unit is a task a user would actually ask for, not an endpoint. A sixty-endpoint API typically maps to six or eight well-scoped tools.

Should we build an MCP server or just use function calling?

Use function calling when one application talks to its own services — it is less code, one fewer hop, and entirely adequate. Build an MCP server when the same capability must be reachable from several assistants, when you want to consume capabilities other people have built, or when you are shipping a product that other people's agents should be able to reach.

Is MCP production-ready?

The protocol is stable enough to build on and widely implemented across clients. The question worth asking is not about maturity but about posture: the security research published through 2026 makes clear that tool poisoning is effective and that detection-based defences underperform against adaptive attacks. Production readiness here is a property of your deployment — narrow credentials, user-scoped identity, approval gates on writes, pinned tool definitions and complete audit logs — rather than of the specification.

What does an MCP server cost to run?

As a service, very little — it is a small application with modest resource needs. The costs that matter are elsewhere: the context budget each tool definition consumes on every request, the engineering time to get tool descriptions and response shapes right, and the ongoing work of reviewing changes to any third-party server you depend on.

References & further reading

  1. [1]
  2. [2]
  3. [3]
  4. [4]
  5. [5]
  6. [6]
  7. [7]
  8. [8]