# MCP vs function calling: what each does and when to use which

Function calling, also called tool calling, is how a model asks your code to run a function - you send tool definitions with the request, the model answers with a call and its arguments, and your code runs it and sends back the result. MCP, the Model Context Protocol, is an open standard for putting tools in a server that any MCP app can connect to. They are not rivals - an app lists an MCP server's tools, gives them to the model through function calling, and passes each call the model makes back to the server.

- Page: https://tecyfy.com/ai-agents/mcp-vs-function-calling
- Part of: How AI agents work (https://tecyfy.com/ai-agents)

- **Function calling** (Part of each model maker's API, https://platform.claude.com/docs/en/agents-and-tools/tool-use/how-tool-use-works): A feature of a model's API. The request carries tool definitions, the reply can carry a call to one of them, and your code runs it.
- **MCP** (Open standard, https://modelcontextprotocol.io/docs/getting-started/intro): The Model Context Protocol, for putting tools, data and prompts in a server that any MCP app can connect to, such as Claude Code, ChatGPT or VS Code. Install: `uv add "mcp[cli]"`

Both are about one thing: letting a model do something outside its own text, such as look up an order. [Function calling](https://tecyfy.com/concepts/function-calling) is the mechanism inside a single conversation with the model. The [Model Context Protocol](https://tecyfy.com/concepts/model-context-protocol) is a standard for packaging tools so that any app can use them. Most confusion comes from treating them as 2 answers to the same question, when each answers a different one.

## Function calling: the model asks, your code runs

The model cannot run anything. With function calling you send, alongside the user's message, a list of tools: each one a name, a sentence on what it does and a JSON schema for its inputs. When the model decides a tool would help, it does not answer in text; it replies with the tool's name and the arguments it chose. Your code runs the function, and you send the result back in the next request, marked with the id of the call it answers. The model reads it and either answers or asks for another tool. That repeated round trip is the agent loop in its plainest form.

Every major model API works this way, under 2 names. Anthropic calls it [tool use](https://tecyfy.com/concepts/tool-use), OpenAI calls it function calling or tool calling, and the shapes are close: a name, a description and a schema.

## MCP: tools in a server any app can use

Function calling leaves one question open: where do the tools come from? If your agent, Claude Code and a colleague's chat app all need to look up orders, each one would need its own copy of the code. MCP answers that question. An MCP server holds the tools, and any app that speaks MCP can connect to it, ask for its list of tools with `tools/list` and run one with `tools/call`.

A server can run on your machine, started by the app and spoken to over standard input and output, or on another machine, reached over HTTP. Besides tools, a server can offer resources, which are data the app can read, and prompts, which are templates a person can pick.

## How they fit together

This is the part most comparisons miss. The model never talks to an MCP server. When an app such as Claude Code connects to one, it fetches the server's tools and hands them to the model as ordinary tool definitions, through function calling. When the model asks for one of those tools, the app catches the call, sends it to the server with `tools/call` and puts the result back into the conversation.

So MCP decides where tools live and how apps reach them; function calling is still how the model asks for one. Even the Claude Agent SDK's own custom tools are an in-process MCP server, offered to Claude through the same tool calls.

## Side by side

| | Function calling | MCP |
| --- | --- | --- |
| **What it is** | A feature of a model's API. The request carries tool definitions; the reply can carry a tool call. | A protocol between an AI app and a server, with JSON-RPC messages to list tools and call them. |
| **Who talks to whom** | Your code and the model. | The app's MCP client and the MCP server. The model never talks to the server. |
| **Where a tool is defined** | In your code, as a name, a description and a JSON schema for its inputs, sent with each request. | In the server. The app asks for the list with tools/list; a Python server writes each schema from the function's type hints. |
| **Who runs the tool** | Your code, in the same program. | The server, on your machine over stdio or on another machine over Streamable HTTP. |
| **Reuse** | Written for one app. Another app needs its own copy. | Written once. Any MCP app can connect to it. |
| **Beyond tools** | Tools only. | Tools, resources (data to read) and prompts (templates), and a way for the server to ask the user for input. |
| **When the tools change** | You send a different list with the next request. | The server can tell connected apps that its list changed, and they fetch it again. |
| **What you must trust** | Your own code. | The server's code and its tool descriptions, which reach the model. A local server runs with your permissions. |
| **Best for** | Tools only your app uses, kept next to its own code. | Tools that several apps or agents share, such as access to a company system. |

## The same tool both ways

The same tool, get_order, written both ways: look up order A-1042 and say what happened to it. The order data is made up so each file stands alone.

### Function calling: `ask_about_order.py`

```python
import anthropic

ORDERS = {"A-1042": "total 140.00, delivered 6 days late"}


def get_order(order_id: str) -> str:
    return ORDERS.get(order_id, "no such order")


tools = [
    {
        "name": "get_order",
        "description": "Look up an order: its total and how it was delivered.",
        "input_schema": {
            "type": "object",
            "properties": {"order_id": {"type": "string"}},
            "required": ["order_id"],
        },
    }
]

client = anthropic.Anthropic()
messages = [{"role": "user", "content": "What happened to order A-1042?"}]
while True:
    reply = client.messages.create(model="claude-sonnet-5-5", max_tokens=1024, tools=tools, messages=messages)
    messages.append({"role": "assistant", "content": reply.content})
    if reply.stop_reason != "tool_use":
        break
    # Your code runs each tool the model asked for and sends back what it returned.
    results = [
        {"type": "tool_result", "tool_use_id": block.id, "content": get_order(**block.input)}
        for block in reply.content
        if block.type == "tool_use"
    ]
    messages.append({"role": "user", "content": results})

print(next(block.text for block in reply.content if block.type == "text"))
```

You write the tool's schema by hand and run the loop yourself - while the model stops to ask for a tool, run it and send back a tool_result with the id of the call it answers. Set ANTHROPIC_API_KEY to run it.

### MCP: `shop_server.py`

```python
from mcp.server import MCPServer

ORDERS = {"A-1042": "total 140.00, delivered 6 days late"}

mcp = MCPServer("shop")


@mcp.tool()
def get_order(order_id: str) -> str:
    """Look up an order: its total and how it was delivered."""
    return ORDERS.get(order_id, "no such order")


if __name__ == "__main__":
    mcp.run(transport="stdio")
```

The server builds the same schema from the type hint and the docstring, and holds no model and no loop: the app that connects does that. Add it to Claude Code with claude mcp add shop -- python shop_server.py, or to an Agent SDK agent with mcp_servers={"shop": {"command": "python", "args": ["shop_server.py"]}}.

The function-calling file holds everything in 1 program: the schema, written by hand, the function and the loop. The MCP file holds only the tool. Its schema comes from the type hint and the docstring, and it has no model and no loop, because those belong to whichever app connects.

## Which to use

- **A tool only my app uses:** Function calling. Define it in your request and run it in your own code. There is no server to start, connect or secure.
- **The same tools in several apps:** MCP. Write the tools once as an MCP server, and Claude Code, ChatGPT, VS Code and your own agents can all connect to it.
- **Let customers' AI apps use my product:** MCP. A remote MCP server over Streamable HTTP lets any MCP app reach your product, with sign-in through OAuth.
- **A ready-made integration, such as GitHub:** MCP. Many systems already have an MCP server. Connecting one is a line of setup instead of writing the tools.
- **The fewest moving parts:** Function calling. 1 program, 1 API, and a short loop. Nothing else runs, so nothing else can fail or be swapped out.

## Things to watch with MCP

- **A server is code you run.** A local MCP server is a program on your machine with your permissions. Connect servers from sources you trust, as you would install any package.
- **Tool descriptions reach the model.** A server's descriptions and results go into the model's context, where text written to mislead it can act as [prompt injection](https://tecyfy.com/concepts/prompt-injection). Keep a person's approval on tools that change things.
- **Ask for the narrowest access.** A server that signs in to another system should hold only the scopes it needs, so a leaked token can do little.
- **Many tools cost context.** Every tool's definition takes room in the [context window](https://tecyfy.com/concepts/context-window) on every request, so connect the servers a job needs, not every one you have.

To see a tool call inside a whole agent, with the loop, memory and a guardrail, follow one request through [How AI agents work](/ai-agents). To choose a framework that runs the loop for you, read [LangGraph vs CrewAI vs Claude Agent SDK](/ai-agents/langgraph-vs-crewai-vs-claude-agent-sdk).

## Questions

### Is MCP the same as function calling?

No. Function calling is how a model asks for a tool to be run, inside one app's conversation with the model. MCP is a standard for where tools come from - a server that any MCP app can connect to. An MCP app takes the server's tools and offers them to the model through function calling, so the 2 work together.

### Does MCP replace function calling?

No, it sits on top of it. When the model decides to use an MCP tool, it still makes a tool call; the app catches that call, sends it to the MCP server with tools/call, and returns the result to the model as part of the conversation.

### What is tool calling?

Another name for function calling. OpenAI's documentation uses both names for the same thing - the model replies with the name of a tool and its arguments instead of text, your code runs the tool, and you send the result back.

### When should I build an MCP server instead of using function calling?

Build an MCP server when the same tools should work in more than 1 app, such as your own agent and Claude Code, or when people outside your team should connect their AI apps to your product. Keep plain function calling when the tool belongs to 1 app and sits next to its code.

### Can the Claude API use an MCP server directly?

Yes. The MCP connector in the Messages API, in beta, connects to a remote MCP server by its address with no MCP client of your own. It supports tool calls only, and only servers reachable over HTTP; a local stdio server needs an MCP client such as Claude Code or the Agent SDK.

### Is MCP safe to use?

As safe as the servers you connect. A local server is a program running with your permissions, and a server's tool descriptions and results reach the model, where text written to mislead it can act as prompt injection. Connect servers you trust, give each the narrowest access it needs, and keep a person's approval on tools that change things.

## Sources

- [How tool use works](https://platform.claude.com/docs/en/agents-and-tools/tool-use/how-tool-use-works)
- [Define tools](https://platform.claude.com/docs/en/agents-and-tools/tool-use/define-tools)
- [Handle tool calls](https://platform.claude.com/docs/en/agents-and-tools/tool-use/handle-tool-calls)
- [Function calling](https://developers.openai.com/api/docs/guides/function-calling)
- [What is the Model Context Protocol (MCP)?](https://modelcontextprotocol.io/docs/getting-started/intro)
- [Architecture overview](https://modelcontextprotocol.io/docs/learn/architecture)
- [Tools](https://modelcontextprotocol.io/specification/latest/server/tools)
- [Build an MCP server](https://modelcontextprotocol.io/docs/develop/build-server)
- [Security best practices](https://modelcontextprotocol.io/docs/tutorials/security/security_best_practices)
- [MCP connector](https://platform.claude.com/docs/en/agents-and-tools/mcp-connector)
- [Connect Claude Code to tools via MCP](https://code.claude.com/docs/en/mcp)
- [Connect to external tools with MCP](https://code.claude.com/docs/en/agent-sdk/mcp)
- [Give Claude custom tools](https://code.claude.com/docs/en/agent-sdk/custom-tools)
- [Tool use with Claude](https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview)
