"MCP vs function calling" reads like a choice between two competitors. It is a choice between two layers. Function calling is the primitive: the mechanism by which a model emits a structured request to run a tool. MCP — the Model Context Protocol — is a protocol built on top of that primitive: a standard way to discover, describe, and serve tools so that any client can use them. Asking which one to use is like asking whether to use HTTP or a REST API. One is how the request travels; the other is how the endpoint is organized.
Definitions, without the hype
Function calling is a model capability. You send the model a list of tool definitions — each a name, a description, and a JSON Schema for its arguments — alongside the conversation. When the model decides a tool would help, it emits a structured call: the tool's name and arguments matching the schema. Your application executes the function, appends the result to the conversation, and the model continues. The model never runs anything; it only asks. Everything else — the executor, the error handling, the decision to obey — is your application code.
MCP standardizes the layer around that exchange. An MCP server is a service that exposes tools (plus resources and prompts) over a defined protocol — JSON-RPC over stdio or HTTP. Any MCP-capable client can connect to it, list its tools, and offer them to whatever model that client drives. The tool definitions no longer live inside one application; they live in a server that many applications can share.
The dependency runs one way: an MCP client still delivers the server's tools to the model as function-calling definitions, and the model still emits ordinary tool calls. MCP servers execute via function calling under the hood. There is no version of MCP that bypasses the primitive.
The architecture, in prose
Plain function calling is a two-party arrangement. Your application holds the tool definitions, hands them to the model, receives the calls, and executes them. The wiring is inline: add a tool, and you edit the application. If a second application wants the same tool, you copy the definition and the handler, and now there are two of them to keep in sync.
MCP inserts a discovery-and-serving layer between the tools and their consumers. The tools move out of the application into a server; the application becomes a client that asks the server what tools exist. Add a tool to the server and every connected client sees it. Fix a tool's schema and every client gets the fix. The shape is exactly the one API teams know: inline code becomes a shared service the moment it has a second consumer.
That framing also gives you the decision rule. A shared service is overhead for one consumer and leverage for many. Count your consumers.
When we reached for MCP
We run this pattern in production at two scales, and both had the same trigger: multiple consumers. A financial-analytics platform we work on exposes 22 MCP hubs with more than 850 tools — token resolution, asset search, swap quotes, oracle and gas data — consumed by multiple agents and sessions rather than one hardcoded application. An IoT platform we built runs one TypeScript server on the official MCP SDK, 42 tools across 5 domains, serving user-scoped and admin-scoped clients.
The protocol layer was the easy part in both. As the full write-up in inside an MCP tool server puts it: if you have shipped a REST API, you can ship a working MCP server in a day. The engineering that actually took time — semantic tool selection over an 850-tool catalog, per-tool caching, loading versioned skills before tools — is work the protocol neither causes nor solves. It becomes unavoidable at catalog scale no matter how the tools are served.
What MCP bought was the doorway: one schema that any client and any trained model can speak, so tools written once serve every agent in the portfolio. Where MCP sits relative to the other layers of an AI system — knowledge in, actions out, control flow — is mapped in MCP vs RAG vs agents.
Security: the part the acronym hides
A tool call executes as someone, whichever layer serves it. Two notes from running this in production:
Prompt injection is an execution problem, not a politeness problem. Any text the model reads — a retrieved document, a tool result, a user message — can try to steer it into calling tools it should not. Asking the model to behave is not a control. The stronger guarantee is structural: on the IoT platform, regular users connect to a user-scoped MCP server and admins to an admin-scoped one, and the user server does not contain the admin tools at all. An injection cannot invoke a tool that was never in the catalog. Plain function calling can enforce the same rule — filter the definitions you hand the model per caller — but MCP's server boundary gives the rule a natural enforcement point, and a shared server makes getting it right once pay off everywhere.
Shared tools mean shared blast radius. The flip side of "fix a schema once, every client gets the fix" is that a badly scoped credential or an over-permissive tool is now exposed to every client too. An MCP server needs the same governance as any shared internal API: authenticated callers (the IoT platform puts a JWT on every tool call), permission decisions per tool at design time, and versioned schemas so a change does not silently break the other consumers.
None of this should scare you off the protocol. It is the standard cost of promoting inline code to shared infrastructure — you pay it exactly when the sharing is worth it.
The decision checklist
Choose plain function calling when all of these hold:
- One application, talking to one model provider
- A handful of tools, owned by the same team that owns the app
- No other agent, client, or team needs those tools
Inline definitions are the whole job. The protocol overhead buys you nothing until a second consumer appears — the same reason the LangChain vs LlamaIndex comparison concludes that frameworks earn their weight only when their abstractions match problems you actually have.
Reach for MCP when any of these appear:
- A second consumer: another agent, another client app, another team wanting the same tools
- Off-the-shelf clients you do not control need your tools (or you want third-party MCP servers in your agent)
- Tool governance is becoming real work: auth scoping, versioned schemas, one place to audit what agents can do
And whichever you pick, remember which layer you picked it for. The model still emits function calls either way. MCP just decides how many doors those calls can knock on — and who holds the keys.