MCP vs. Tool Calling vs. Function Calling: What's the Difference?

Short answer: function calling is the model API feature that lets an LLM return a structured “call this function with these arguments” decision. Tool calling is the loop your code runs around that feature: execute the call, feed the result back, repeat. MCP (Model Context Protocol) is a standard for how tools are described and connected to an agent, so one tool server works with any MCP-compatible host. They aren’t alternatives. An MCP tool still gets invoked through function calling, inside a tool-calling loop.

If you’ve read three different agent tutorials, you’ve probably seen “tool-calling,” “function-calling,” and “MCP” used as if they’re synonyms. They’re not — they’re three layers of the same idea, and conflating them is where a lot of “why doesn’t my agent call the right tool” confusion actually comes from.

Function-calling: the model’s half of the contract

“Function-calling” (sometimes “tool use”) is a specific capability a model provider exposes: you describe available functions in a schema, the model decides whether to invoke one and with what arguments, and returns that decision as structured output instead of prose.

tools = [{
    "name": "get_weather",
    "description": "Get current weather for a location",
    "input_schema": {
        "type": "object",
        "properties": {"location": {"type": "string"}},
        "required": ["location"],
    },
}]

response = client.messages.create(
    model="claude-sonnet-5",
    tools=tools,
    messages=[{"role": "user", "content": "What's the weather in Edmonton?"}],
)
# response contains a tool_use block: {"name": "get_weather", "input": {"location": "Edmonton"}}

Note what the model does not do here: it doesn’t run anything. It emits a decision. Your code is responsible for executing get_weather, appending the result as a tool_result, and sending the conversation back for the model to continue. This request/response contract — schema in, structured decision out — is the entire scope of “function-calling.” It’s a model-API feature, not an architecture.

Tool-calling: the loop built around that contract

“Tool-calling” (or “tool use”) usually refers to the agent loop built on top of function-calling: the code that executes the requested function, feeds the result back, and repeats until the model stops requesting tools.

messages = [{"role": "user", "content": "What's the weather in Edmonton?"}]

while True:
    response = client.messages.create(model="claude-sonnet-5", tools=tools, messages=messages)
    messages.append({"role": "assistant", "content": response.content})

    tool_uses = [b for b in response.content if b.type == "tool_use"]
    if not tool_uses:
        break  # model produced a final answer, no more tools requested

    results = []
    for call in tool_uses:
        output = execute_tool(call.name, call.input)  # your code, your responsibility
        results.append({"type": "tool_result", "tool_use_id": call.id, "content": output})
    messages.append({"role": "user", "content": results})

This loop — not the schema, not the model — is where most agent bugs actually live: infinite tool-call loops, malformed results the model can’t parse, no limit on how many iterations run before you give up and return control to a human. “Tool-calling” is an architecture pattern you own end to end; function-calling is just the API primitive it’s built out of.

MCP: standardizing how tools get described and connected, not how they get called

The Model Context Protocol (MCP) doesn’t replace either of the above — it standardizes a different problem: how an agent discovers and connects to a server that exposes a set of tools, resources, and prompts, so the same tool integration works across different agent hosts without custom glue code for each one.

Without MCP, “give my agent access to Postgres” means writing custom tool-calling code specific to your framework. With MCP, it means running a Postgres MCP server once, and any MCP-compatible host — Claude Code, Claude Desktop, a custom agent — can connect to it and get the same tools, using the same JSON-RPC-based handshake, without you rewriting the integration per host:

# an MCP server exposes tools the same way regardless of which agent connects to it
@mcp.tool()
def query_database(sql: str) -> str:
    """Run a read-only SQL query against the reporting database."""
    return run_readonly_query(sql)

The tool still ultimately gets invoked through the model’s function-calling capability, and the host still runs a tool-calling loop around it — MCP just means the description and connection of that tool is portable instead of hand-wired into one specific agent’s codebase.

Putting it together

Layer What it actually is Who owns it
Function-calling The model API’s ability to emit a structured “call this function” decision instead of prose The model provider
Tool-calling (the loop) The code that executes what the model requested, feeds results back, and repeats You
MCP A standard for how tools/resources are described and connected, portable across hosts The ecosystem/spec

The confusion mostly comes from real overlap: an MCP tool call still travels through a function-calling response and still runs inside a tool-calling loop. MCP doesn’t sit next to the other two — it sits underneath them, standardizing how tools get wired up before either of the other two layers ever runs.

When you actually need MCP vs. when hand-rolled tool-calling is enough is its own question — see the companion piece on when you shouldn’t bother with MCP.

FAQ

Is MCP a replacement for function calling? No. MCP standardizes how tools are described and connected. The model still decides to use an MCP tool through its normal function-calling output, and the host still runs a tool-calling loop to execute it.

What’s the difference between an MCP server and tool calling? An MCP server is a separate process that exposes tools over a standard protocol. Tool calling is the loop inside your agent that uses tools. An agent can do tool calling with no MCP server at all (tools defined in code), or connect to MCP servers to get tools it didn’t have to write itself.

Are “tool calling” and “function calling” the same thing? Providers use the terms loosely (Anthropic says “tool use,” OpenAI moved from “functions” to “tools”). The useful distinction is the one above: the API capability versus the execution loop you build around it. Most agent bugs live in the loop, not the API.

Do I need MCP to build an agent? No. For one agent with a handful of tools, plain tool calling is simpler. MCP starts paying off when the same tools need to work across several hosts or teams. See when you shouldn’t bother with MCP.

Why does my agent call the wrong tool, or invent one? Usually a vague tool description, too many similar tools at once, or a result format the model can’t parse. The field guide to hallucinated tool calls walks through the causes in the order to check them.

← Back to all articles