MCP vs Function Calling
Both MCP and function calling let AI models interact with the outside world — but they solve different problems at different layers. Here's exactly when to use each.
TL;DR
- Function calling is a model-level API feature: you define tools inline in your API request and the model decides when to call them. Logic lives in your app.
- MCP is a transport-level protocol: tools live in a separate server that any MCP-compatible client can connect to. Logic is decoupled from the AI app.
- MCP can use function calling internally — they're complementary, not competing.
What is Function Calling?
Function calling (also called "tool use" by Anthropic) is a native feature of AI APIs like OpenAI, Anthropic, and Google. You pass tool definitions alongside your message, and the model can respond with a request to call one of them.
Your application receives the tool call, executes it, returns the result, and continues the conversation. The entire loop happens inside your application code.
import anthropic
client = anthropic.Anthropic()
tools = [
{
"name": "get_weather",
"description": "Get current weather for a city",
"input_schema": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "City name"}
},
"required": ["city"]
}
}
]
response = client.messages.create(
model="claude-opus-4-5",
max_tokens=1024,
tools=tools,
messages=[{"role": "user", "content": "What's the weather in Prague?"}]
)
# If model wants to call a tool:
if response.stop_reason == "tool_use":
tool_call = response.content[0]
city = tool_call.input["city"]
# Your app executes the tool:
weather_result = fetch_weather_from_api(city)
# Continue with the result...Key characteristics: tools are defined per-request, the execution logic is tightly coupled to your application, and only this one AI client uses these tools.
What is MCP?
MCP is an open protocol that defines how AI clients discover and call tools from separate server processes. The tools are not defined in the API request — they live in a dedicated server that speaks the MCP protocol over stdio or HTTP.
MCP hosts (Claude Desktop, Cursor, Windsurf, etc.) connect to these servers at startup and make all discovered tools available to the AI. Internally, the host still uses the model's function calling API — but the tool definition is sourced from the MCP server, not hard-coded in the app.
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("weather-server")
@mcp.tool()
def get_weather(city: str) -> str:
"""Get current weather for a city."""
return fetch_weather_from_api(city)
if __name__ == "__main__":
mcp.run()
# This server can now be used by Claude Desktop, Cursor,
# or any other MCP-compatible client — no code changes needed.Side-by-Side Comparison
| Aspect | Function Calling | MCP |
|---|---|---|
| Where tools live | Inside your application code | Separate MCP server process |
| Reusability | Per-application | Any MCP-compatible client |
| Setup complexity | Low — just API calls | Medium — separate server process |
| Tool discovery | Manual — you define per request | Automatic — server advertises its tools |
| Transport | Inside API request/response | stdio or HTTP/SSE (separate connection) |
| Provider support | OpenAI, Anthropic, Google, etc. | Claude Desktop, Cursor, Windsurf, etc. |
| Stateful servers | Not natively — stateless per call | Yes — server keeps state between calls |
| Resources & prompts | No standard — custom per app | Built into the protocol |
| Best for | Custom AI apps with specific tools | Shared tools across AI clients |
When to Use Each
Building a custom AI product
You're building a chatbot, agent, or AI feature in your own application and don't need the tools to be reusable by other AI clients.
Provider-specific features
You need features specific to one model provider (e.g., Anthropic's computer use, OpenAI's Responses API).
Dynamic tools
Your set of available tools changes per user, session, or request context.
Tightest control loop
You need precise control over which tools are available and when — with minimal overhead.
Tools for power users / developers
Your users run Claude Desktop, Cursor, or similar tools and you want to give them extra capabilities without building a custom app.
Reusable internal tooling
You want your team's database, internal docs, or APIs accessible across all AI tools they use.
Stateful integrations
You need the server to maintain state between calls (e.g., an authenticated session, an open file handle, a running process).
Distributing tools to the ecosystem
You want to publish a tool that any MCP-compatible client can use, without each app needing to integrate it separately.
They Work Together
MCP and function calling are not in competition. Here's how they fit together in a real production architecture:
MCP Host (Claude Desktop)
Connects to your MCP servers at startup, discovers all available tools.
API Request with Function Calling
The host passes MCP-discovered tools to Claude via the standard function calling API.
Model calls a tool
Claude responds with a function call. The host intercepts it, routes to the correct MCP server.
Result back to model
The MCP server executes the tool and returns the result. The host feeds it back into the conversation.
So if you're building for Claude Desktop or Cursor, you write an MCP server. If you're building your own AI app from scratch, you use function calling directly. If you want both — you can build an MCP server for power-user integrations while also using function calling for app-specific tools.
Start building
Now that you know the difference, pick the right approach for your use case.