The verdict first: MCP vs function calling is the wrong fight, because MCP runs on top of function calling. Function calling is how a model asks for a tool. MCP is how tools get packaged so that many apps can find and run them. If you're building one agent whose tools are your own product's logic, plain function calling is the right default in October 2026: fewer moving parts, and your code keeps control of identity, tenancy and side effects. Add an MCP server when the same tools need to live in several hosts, when a vendor already ships an MCP server for the system you need, or when another team owns the tools.
The disclosure comparison posts usually skip: every agent I ship to production uses plain tool use through @anthropic-ai/sdk, with the tool definitions sitting in my own code. I use MCP every day, but as a consumer: coding agents and my personal assistants reach Supabase, Gmail, Google Calendar and Notion through MCP servers. I have not shipped an MCP server to production. So the function-calling column below comes from code I can show you, and the MCP column comes from the spec, the vendor docs, and the receiving end.
MCP vs function calling at a glance (as of October 2026)
| Function calling (tool use) | MCP (Model Context Protocol) | |
|---|---|---|
| What it is | A feature of the model API | An open client-server protocol (JSON-RPC 2.0) |
| Where tool definitions live | In your app's code, sent with every request | Published by a server, fetched by the client with tools/list |
| Who runs the tool | Your code, in your process | The MCP server: a local subprocess (stdio) or a remote service (Streamable HTTP) |
| Format | Provider-specific: Claude tool_use / tool_result blocks, OpenAI function_call / function_call_output items | One format for every MCP-capable host |
| Identity and auth | Whatever your app already knows (session, tenant, API keys) | The server's own: env vars for local servers, OAuth 2.1 for remote ones |
| Beyond tools | Tools only | Tools, resources and prompts, plus opt-in extensions |
| Reuse across apps | Copy the code | Point another host at the same server |
| Extra infrastructure | None | A server to run, or a hosted one to trust |
| Token cost | The tools you define | Every tool the server exposes, unless you filter them |
| Best fit | Tools that are part of one product | Tools that many hosts or agents share |
The short version of the table: function calling is a call inside your app; MCP is an integration boundary. Boundaries are useful when there's actually something on the other side.
They're layers, not alternatives
Here's a tool from Claient's booking flow, defined for Claude's API. Field names are in Spanish because the product is:
{
name: "consultar_disponibilidad",
description: "Returns the free slots for a given date (YYYY-MM-DD)...",
input_schema: {
type: "object",
properties: { fecha: { type: "string", description: "Day to check, YYYY-MM-DD." } },
required: ["fecha"],
},
}
The same tool published by an MCP server shows up in tools/list looking almost the same:
{
"name": "consultar_disponibilidad",
"description": "Returns the free slots for a given date (YYYY-MM-DD)...",
"inputSchema": {
"type": "object",
"properties": { "fecha": { "type": "string" } },
"required": ["fecha"]
}
}
That's no accident. An MCP host (Claude Code, Cursor, ChatGPT, your own client) takes those definitions, passes them to the model as ordinary tools, and when the model returns a tool call, forwards it to the server as tools/call. The model never "speaks MCP". So the real question isn't which one to use. It's where should this tool's definition and execution live: inside my app, or behind a protocol boundary that other apps can also cross?
What changed in MCP this year
If the last time you read about MCP was 2025, the protocol has moved. The current spec revision is 2026-07-28, and the changes matter for anyone deploying a server:
- It's stateless now. The
initializehandshake and theMcp-Session-Idheader are gone. Every request carries its protocol version and client capabilities in_meta, and servers must implementserver/discoverto advertise what they support. That makes remote MCP servers much friendlier to serverless hosting and load balancers. - Roots, Sampling and Logging are deprecated (eligible for removal from the first revision on or after 2027-07-28). The suggested migration for Sampling is literally "integrate directly with LLM provider APIs".
- Tasks moved to an official extension, alongside others like MCP Apps for interactive UI.
- Servers should return
tools/listin a deterministic order so clients get better prompt-cache hit rates, and list results now carryttlMsandcacheScopehints. - The old HTTP+SSE transport is deprecated in favour of Streamable HTTP.
The prompt-cache point is a nice tell. Tool definitions sit at the front of every request, so if a server reshuffles them, everything after them misses the cache. If you've read how I keep Claude's token bill under control, you'll know why that's worth a line in the spec.
Using MCP straight from the model APIs
You don't need to write an MCP client to use a remote MCP server any more. Both big providers will act as the client for you, with caveats worth reading before you ship.
Anthropic's MCP connector (beta header mcp-client-2025-11-20 as of October 2026) takes an mcp_servers array in the Messages API and an mcp_toolset entry in tools. You can allowlist or denylist tools per server and set defer_loading so definitions are only loaded through the tool search tool. The limits: tool calls only (no resources or prompts), the server must be reachable over public HTTP (no local stdio servers), and the feature is not eligible for zero data retention. A newer beta, mcp-client-2026-09-15, lets you pin a server's tool list so the API doesn't fetch it on every request.
OpenAI's MCP tool in the Responses API is a tools entry with type: "mcp", a server_url, an optional allowed_tools filter and require_approval. By default OpenAI asks for your approval before sharing data with a remote MCP server. There's no per-call fee; you pay for the tokens of the imported tool definitions and the calls. For private servers there's Secure MCP Tunnel.
Both docs say the same thing in different words: an MCP server with dozens of tools gets expensive and slow if you expose all of them, so filter. And both are explicit that you should only connect servers you trust.
Why my production agent uses plain function calling
Claient answers and qualifies WhatsApp leads for small businesses, many of them on one deployment (the build story). It has three booking tools (check availability, book, reschedule) and one forced extraction tool. None of that is MCP, for reasons that have nothing to do with fashion.
Tenant scoping happens outside the model. The model never sees or chooses which business it's booking for. The webhook resolves the tenant from the WhatsApp number that received the message, and the executor gets that context from my code:
// The model only sends { fecha } or { inicio_iso }.
// Who the booking is for comes from the webhook, never from the model.
export interface ToolContext {
accountId: string;
leadId: string;
}
ejecutarTool: (name, input) =>
ejecutarTool(name, input, { accountId: tenant.accountId, leadId: lead.id }, efectos),
That's the most important line in the agent. If the account id were a tool argument, a cleverly worded message could try to steer it. With MCP you can get the same guarantee, but the server now needs a per-tenant identity, which means OAuth or minted tokens, which means another service to secure. For tools that only exist inside one product, that's cost without benefit.
Side effects flow back into the app. When the model books a slot, the executor records it in efectos, and after the turn the webhook marks the lead as booked and sends a calendar invite. Keeping that in one process is a function call. Across an MCP boundary it's a result you have to parse and trust.
The time budget is mine. The tool loop runs inside a serverless function with a hard cap of five iterations and a deadline, because a function killed mid-turn means Meta retries the webhook and the lead gets a duplicate message. Every network hop to a tool server eats that budget.
Three tools don't need discovery. Discovery solves "which tools exist?". In Claient I wrote all of them, they ship with the code, and they're versioned with git.
None of this is a knock on MCP. It's what happens when the tool and the agent are the same product.
Where MCP earns its place
The other side is just as concrete. The assistants I use every day are connected to Gmail, Google Calendar, Notion and Supabase through MCP servers. Nobody at those companies wrote a tool for my assistant, and I didn't write one for theirs. That's the problem MCP solves, and it solves it well: one server, many hosts.
It also showed me the failure mode. In one personal project, the Supabase MCP server my coding agent had was authenticated against a different organisation than the one that owned the project, so every call came back permission denied. The fix wasn't clever: a note in the repo's CLAUDE.md telling the agent not to use those MCP tools there, and a small script that goes through Supabase's Management API with an access token for the right account. The lesson generalises: an MCP server brings its own identity. Whatever it authenticated as is what your agent can touch, which is fine in a dev tool with a human watching and something to design deliberately in a product.
Treat MCP servers the way the spec tells hosts to: tools are arbitrary code execution, and tool descriptions from a server you don't trust are untrusted input. Approvals, allowlists and scoped credentials aren't optional extras. For the credential side, the same rules as securing your API keys apply.
When to use which
Use plain function calling when:
- The tools are part of your product and only your agent calls them.
- Identity, tenancy or permissions come from your app's session, and the model shouldn't be able to choose them.
- Tool results trigger side effects your app needs to act on in the same request.
- You run on serverless with a tight time budget.
- You have a handful of tools you'd version with your code anyway.
Use MCP when:
- You want the same tools available in several hosts: Claude, ChatGPT, Cursor, Claude Code, your own agent.
- A vendor already publishes an MCP server for the system you need, and building your own integration would just duplicate it.
- Another team owns the tools and needs to ship changes without redeploying every agent.
- You're exposing your product to other people's agents. That's a real distribution channel now, and an MCP server is how you show up in it.
Either way:
- Expose as few tools as the job needs. Filter MCP servers with allowlists; don't hand the model forty tools to use three.
- Keep anything that decides who or which account out of the model's arguments.
- Cap the loop and the spend from day one. Neither the protocol nor the provider will do it for you.
Most production agents I'd design today end up with mostly function calling plus, at most, a couple of MCP servers for third-party systems. If you need an agent that touches real systems, and you want someone who's shipped them to draw that line with you, that's what I do. If budget is the next question, read what AI agent development actually costs; if you're still choosing the model, Claude vs GPT for AI agents.
FAQ
What is the difference between MCP and function calling?
Function calling (tool use) is a feature of a model's API: you send tool definitions with the request, the model replies with a tool name and JSON arguments, and your code runs the tool and sends the result back. MCP (Model Context Protocol) is an open client-server protocol that lets an application discover tools published by a separate server (tools/list) and invoke them (tools/call). An MCP client still hands those tools to the model through function calling, so MCP sits one layer above it rather than replacing it.
Should I use MCP or function calling for my AI agent?
If the tools belong to your product and only your agent calls them, use plain function calling: no extra server, and your code keeps control of identity, tenancy and side effects. Use MCP when the same tools must be reused across several agents or hosts (Claude, ChatGPT, Cursor, Claude Code), when a third party already publishes an MCP server for the system you need, or when another team owns the tools. Most production agents end up with mostly function calling and, at most, a couple of MCP servers.
Does MCP replace function calling?
No. MCP standardises how tools are packaged, discovered, authorised and executed; the model still selects them through its provider's function-calling mechanism. Anthropic's MCP connector turns MCP tools into mcp_tool_use blocks in the Messages API, and OpenAI's mcp tool type imports them into the Responses API, but in both cases the model is doing ordinary tool use underneath.
Can I call an MCP server directly from the Claude or OpenAI API?
Yes, for remote servers. As of October 2026, Anthropic's MCP connector (beta header mcp-client-2025-11-20) connects the Messages API to MCP servers exposed over HTTP; it supports tool calls only, not local stdio servers, and is not eligible for zero data retention. OpenAI's Responses API has an mcp tool type with server_url, allowed_tools and require_approval, plus Secure MCP Tunnel for private servers, and charges no per-call fee beyond the tokens of the imported tool definitions and calls. On both, every tool you import takes up context, so filter.
What changed in the MCP spec in 2026?
The current revision, 2026-07-28, made MCP stateless: the initialize handshake and the Mcp-Session-Id header are gone, every request carries its protocol version and client capabilities in _meta, and servers must implement server/discover. It also deprecated Roots, Sampling and Logging, moved tasks into an official extension, and asks servers to return tools/list in a deterministic order so clients get better prompt-cache hit rates.
Does Joan Comadran use MCP in production agents?
No. The agents he ships to clients and his own products, such as Claient, the WhatsApp lead agent, use plain tool use through the Anthropic TypeScript SDK, with tool definitions in code and tenant scoping enforced by the executor. He uses MCP as a consumer: coding agents and personal assistants connected to services like Supabase, Gmail, Google Calendar and Notion. He has not shipped an MCP server to production.