--- title: 'MCP vs Function Calling: Protocol or Primitive?' excerpt: 'Function calling is the primitive; MCP is the protocol that makes tools portable across agents. When each is enough, from systems we ship.' date: '2026-07-14' lastModified: '2026-07-14' author: 'Teo Deleanu' authorAvatar: '/team/teo.jpg' tags: ['Production AI', 'AI Engineering', 'Agents', 'MCP'] keywords: ['mcp vs function calling', 'model context protocol vs tools', 'when to use mcp'] options: ['MCP', 'Function calling'] verdict: 'Function calling is enough for one app talking to one model; MCP earns its complexity when tools must be shared across agents, clients, or teams.' featured: false tldr: 'Not rivals: function calling is how a model invokes a tool; MCP standardizes how tools are discovered and served so any client can use them.' keyTakeaways: - 'Function calling: per-app tool wiring, simplest path for a single integration' - 'MCP: one tool server, many agent clients — pays off at portfolio scale' - 'MCP servers still execute via function calling under the hood' faqs: - q: 'Does MCP replace function calling?' a: 'No — MCP sits above it. The model still emits tool calls; MCP standardizes how the tools are discovered, described, and served, so the same server works across different agent clients.' - q: 'When is plain function calling the right choice?' a: 'One application, one model provider, a handful of tools you control: define them inline and skip the server. The protocol overhead buys you nothing until a second consumer appears.' - q: 'What does an MCP server cost to run?' a: 'Operationally it is a small service: the real costs are auth design, tool-permission scoping, and versioning tool schemas across clients — the same API-governance work any shared service needs.' --- "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](/blog/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](/blog/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](/compare/langchain-vs-llamaindex) 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.