MCP vs function calling: what's the difference?
People ask which one to use, MCP or function calling, as if they're rival options. They're not. Function calling is the model deciding when to call a tool. MCP is the standard for how that tool gets delivered to the model in the first place. I run MCP servers in production across a multi-tenant platform, and the distinction stops being academic the moment you want one tool to work in more than one place.
The short answer
MCP and function calling aren't competitors, they're different layers of the same stack. Function calling (also called tool use) is a model capability: given a list of tools, the model emits a structured call when it decides one is needed. MCP, the Model Context Protocol, is a standard for packaging and delivering those tools so any MCP-aware client can discover and run them. MCP tools become function-call options for the model. You don't pick one over the other, you use function calling, and MCP is one way to supply the functions.
If you only remember one line: function calling is the model deciding to call a tool; MCP is how the tool gets to the model. The confusion comes from comparing them at all, because they sit at different heights. It's a bit like asking "REST vs JSON" - one is the thing being moved, the other is the way it travels.
What function calling actually is
Function calling is the model capability where you describe a set of functions, and the model
responds with a structured request to call one of them when the user's question needs it. With Claude,
you pass tools with a name, description, and JSON input_schema; the model returns a
tool_use block and stops; your code runs the function and sends back a
tool_result. The model never executes anything itself, it just decides and asks.
Here's the shape of it with the Anthropic API. You define a tool, the model sees it, and it can choose to call it:
import anthropic
client = anthropic.Anthropic()
tools = [
{
"name": "get_order_status",
"description": "Look up the status of an order by its ID.",
"input_schema": {
"type": "object",
"properties": {"order_id": {"type": "string"}},
"required": ["order_id"],
},
}
]
resp = client.messages.create(
model="claude-opus-4-8",
max_tokens=1024,
tools=tools,
messages=[{"role": "user", "content": "Where is order A1003?"}],
)
print(resp.stop_reason) # "tool_use"
When stop_reason comes back as tool_use, the response carries a block naming
the tool and the arguments the model chose, for example get_order_status with
{"order_id": "A1003"}. The model decided, on its own, that answering needed that
function. Per Anthropic's tool-use docs, with the default tool_choice of auto, "Claude
decides on each turn whether to call a tool or respond directly." That decision is the whole of what
function calling is. The protocol that carried the tool definition into the request, and carried the
call back out, is a separate concern, and that's where MCP comes in.
What MCP actually is
MCP is a client-server protocol for delivering tools (and data and prompt templates) to AI
applications in a standard way. An MCP server advertises its tools; an MCP client connects, calls
tools/list to discover them, and calls tools/call to run one. The
modelcontextprotocol.io spec defines three things a server can expose: tools (executable functions),
resources (data the model can read), and prompts (reusable templates). It says nothing about how the
model decides to call a tool, because that's not its job.
The MCP architecture docs are explicit that the protocol "focuses solely on the protocol for context exchange - it does not dictate how AI applications use LLMs or manage the provided context." That sentence is the cleanest line I've found on why MCP and function calling aren't the same thing. MCP standardizes the wire format and the handshake. What the model does with the tools once they arrive is up to the application and the model.
Concretely, an MCP client connects to a server and asks what it offers:
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/list"
}
The server answers with an array of tools, each with a name, a human-readable
title, a description, and an inputSchema. If that field list
looks familiar, it should: it's the same information a function-calling API needs to define a tool.
That's not a coincidence. MCP was designed so its tool definitions drop straight into the model's
tool list.
How they fit together
The MCP client takes the tools it discovered from a server and hands them to the model as function-calling options. From the model's side, an MCP tool is indistinguishable from a tool you defined inline, it's just a name, a description, and a schema. The model decides to call it (function calling); the client routes that call to the right MCP server, runs it, and feeds the result back.
Anthropic's own architecture description spells out the join. The AI application "fetches available tools from all connected MCP servers and combines them into a unified tool registry that the language model can access," and when the model decides to use one, the application "intercepts the tool call, routes it to the appropriate MCP server, executes it, and returns the results back to the LLM." Read that twice. The deciding is function calling. The fetching, routing, and executing is MCP. Same loop, two layers.
So the flow end to end looks like this:
- An MCP client connects to your MCP server and calls
tools/list. - It merges those tools into the model's tool list, alongside any tools defined inline.
- The model reads the user's request and emits a
tool_usecall for one of them. - The client matches that call to the server it came from and issues
tools/call. - The server runs the logic and returns content; the client passes it back as the tool result.
Step 3 is function calling. Steps 1, 2, 4, and 5 are MCP. You can run step 3 with no MCP at all, by defining tools inline. What you can't do is skip the model's decision, that's the part MCP never touches.
MCP vs function calling, side by side
The cleanest way to hold the difference: function calling is a capability of the model, MCP is a protocol between programs. One answers "should I call a tool, and with what arguments." The other answers "how does a tool get described, discovered, and invoked across different clients."
| Function calling (tool use) | MCP | |
|---|---|---|
| What it is | A model capability | A client-server protocol |
| Answers the question | When to call a tool, with what args | How a tool is delivered to the model |
| Who owns it | The model and the application | The standard (modelcontextprotocol.io) |
| Lives where | In a single API request | Between a client and one or more servers |
| Exposes | Tools you define inline | Tools, resources, and prompts a server advertises |
| Reuse across clients | None - per-app glue each time | One server, every MCP client, no glue |
| Discovery | You hardcode the tool list | tools/list at runtime |
The bottom two rows are the ones that matter in practice. Inline function calling means you wire the same tool into each application by hand. MCP means you write the tool once and any client discovers it.
When to use raw function calling vs an MCP server
Use raw inline function calling when the tool lives inside one application and nothing else will ever call it. Wrap it in an MCP server the moment a second client needs the same tool, or you want it to run as a standalone process you can test, deploy, and reuse independently of any one app.
For a single script with two functions the model calls, an MCP server is overhead you don't need. Just define the tools inline and run the loop. I do this all the time for one-off automations. The schema lives in the same file as the call site, and there's no second process to manage.
The calculus flips fast, though. The first time you think "I'd like Claude Desktop and my own agent and a teammate's Cursor setup to all hit this same Shopify data," inline function calling means writing the same tool three times against three different harnesses. An MCP server means writing it once. That's the line for me: one consumer, inline; two or more, server.
The real advantage of MCP: reuse
The practical payoff of MCP over hand-rolled function calling is reuse. One MCP server works across every MCP client - Claude Desktop, Claude Code, Cursor, your own agent - with no per-client glue. You write the tool, its schema, and its safety checks once, and every client that speaks MCP can discover and call it unchanged.
The alternative makes it concrete. Before MCP, every time an agent needed to read a client's Shopify store, I rewrote the same tool against whatever tool-calling format that particular harness used. Different schema shape, different result handling, different place to put the read-only safety check. Five clients, five copies of the same logic, five places for the safety boundary to drift out of sync.
With one MCP server, that read-only Shopify connector is a single process. The schema is defined once.
The check that rejects every write before it reaches the store lives in one function. Claude Desktop
sees the same six tools my own agent does, because they're both just MCP clients calling
tools/list against the same server. When I fix a bug or add a tool, every client gets it
on reconnect. That's the win that's hard to see from the spec and obvious the day you stop maintaining
five copies of one tool.
If you want the concrete build, I've written up how to build an MCP server in Python around that exact read-only Shopify connector, and a separate piece on what an MCP server is if you want the ground floor first. The short version of this whole article: don't choose between MCP and function calling. Use function calling, because that's how the model calls tools at all, and reach for MCP when you want those tools to be reusable instead of rewritten.
Pavle Lazic is the founder of Scalably, where he builds and runs multi-tenant Claude agent platforms in production for real businesses. He writes about the Claude Agent SDK, MCP servers, and what it actually takes to put AI agents to work. See the platform.