Home / Blog / MCP

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.

How MCP and function calling fit together MCP Server AMCP delivers it MCP Server BMCP delivers it Tool registryname, schema Modelcalls the tool fn calling picks scalably.io
Not competitors. MCP delivers the tools; function calling decides when to use them.

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:

  1. An MCP client connects to your MCP server and calls tools/list.
  2. It merges those tools into the model's tool list, alongside any tools defined inline.
  3. The model reads the user's request and emits a tool_use call for one of them.
  4. The client matches that call to the server it came from and issues tools/call.
  5. 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 isA model capabilityA client-server protocol
Answers the questionWhen to call a tool, with what argsHow a tool is delivered to the model
Who owns itThe model and the applicationThe standard (modelcontextprotocol.io)
Lives whereIn a single API requestBetween a client and one or more servers
ExposesTools you define inlineTools, resources, and prompts a server advertises
Reuse across clientsNone - per-app glue each timeOne server, every MCP client, no glue
DiscoveryYou hardcode the tool listtools/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.

Rule of thumb: if the tool will only ever be called from inside one codebase, inline function calling is fine. If you'd ever say the words "I wish I could use this tool from somewhere else," that's the signal to make it an MCP 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.

P

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.