Short answer
Function calling is a feature of a model's API: you describe tools in your request, the model replies with the name and arguments of the one it wants, and your own code runs it. MCP is a protocol one level up: it packages tools into separate servers that any compatible app can discover and call, so a tool written once works in many assistants.
Follow-up question When do you need both?
Both ideas let a language model use tools, and both describe those tools the same way, with a name, a description and a list of inputs written in JSON Schema (a standard format for describing what data should look like). That is why they are easy to confuse. The difference is who owns the tool and where it lives.
With function calling, which OpenAI added to its API in June 2023 and other providers now offer too, the tool lives inside your application. You list it in every request, the model answers "please call get_weather with these arguments," and your code does the work. With the Model Context Protocol (MCP), released by Anthropic on November 25, the tool lives in a separate program called a server. An app that speaks MCP, such as Claude Desktop, asks the server what tools it has, and calls them on the model's behalf.
The two paths in words
Picture a user asking, "Do I need an umbrella in Montreal today?"
Function calling:
- Your app sends the question plus the tool's description to the model's API.
- The model replies with a tool call:
get_weather,{"city": "Montreal"}. - Your app runs its own
get_weathercode, which calls a weather service. - Your app sends the result back; the model writes the answer.
MCP:
- When the host app starts, it connects to a weather MCP server and sends
tools/list. The server answers withget_weatherand its input schema. - The host passes that tool description to its model along with the question.
- The model asks for
get_weather; the host sendstools/callto the server. - The server runs the code and returns the result; the host gives it to the model, which writes the answer.
Inside the host, step 3 of the MCP path still uses the model's own tool-use feature. MCP doesn't replace function calling; it standardizes how tools are found and run from outside the app.
A weather tool, built both ways
First, function calling with OpenAI's Python library. The tool is described in the tools list; setting strict to true, an option added in August, makes the model's arguments match the schema exactly.
from openai import OpenAI
client = OpenAI()
tools = [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Current weather for a city",
"strict": True,
"parameters": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"],
"additionalProperties": False,
},
},
}]
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Do I need an umbrella in Montreal today?"}],
tools=tools,
)
call = response.choices[0].message.tool_calls[0]
print(call.function.name, call.function.arguments) # get_weather {"city":"Montreal"}
Your code would now run its weather lookup and send the result back in a message with the role tool. The tool exists only in this program.
Now the same tool as an MCP server, using the pattern from the MCP documentation's Python example. The weather data here is a placeholder for illustration.
from mcp.server import Server
import mcp.types as types
app = Server("weather")
@app.list_tools()
async def list_tools() -> list[types.Tool]:
return [types.Tool(
name="get_weather",
description="Current weather for a city",
inputSchema={
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"],
},
)]
@app.call_tool()
async def call_tool(name: str, arguments: dict):
if name == "get_weather":
city = arguments["city"]
report = f"{city}: 4°C, light rain" # illustrative; call a real weather API here
return [types.TextContent(type="text", text=report)]
raise ValueError(f"Unknown tool: {name}")
A complete server also needs a few lines to start it over standard input and output, which the SDK provides. Notice what's missing: no model, no API key, no chat loop. The server only knows how to describe and run its tool. Any MCP host can connect to it and offer it to whatever model the host uses.
| Function calling | MCP | |
|---|---|---|
| Where the tool runs | In your application's code | In a separate server process |
| How the model learns about it | You list it in each API request | The host asks the server with tools/list |
| Who runs it | Your code | The server, when the host sends tools/call |
| Reuse | One app, one provider's format | Any MCP-compatible host |
| Best for | A single app you control end to end | Tools you want to share across apps |
Try it: Take a tool you already built with function calling and write down three things: its JSON Schema, the code that runs it, and the code that talks to the model. Only the first two would move into an MCP server. Seeing which lines go where makes the split clear.
When do you need both?
Whenever you build your own app that should use shared tools. Suppose your company writes one MCP server for its order database, so staff can use it from Claude Desktop. Later you build a customer-service chatbot on a model's API. Your chatbot can act as an MCP host: it connects to the same server, reads the tool list, converts each tool into the function-calling format its model expects, and forwards the model's tool calls to the server. Function calling handles the conversation with the model; MCP handles finding and running the tool. Nobody writes the database code twice.
If you are building one small app with two or three tools that nobody else will use, plain function calling is simpler: there is no extra process to run. MCP pays off as tools multiply, or when several apps, or several teams, need the same ones.
In both cases the safety rules are the same. The MCP documentation asks server builders to validate every input against the schema and to keep a person in the loop to approve actions. OpenAI's own guidance on function calling says to confirm real-world actions, such as sending an email or making a purchase, with the user before running them.
Sources
- Tools (Model Context Protocol documentation) — Model Context Protocol / Anthropic (GitHub), December 2, 2024
- Introducing Structured Outputs in the API — OpenAI, August 6, 2024
- Function calling and other API updates — OpenAI, June 13, 2023