MCP Is Not a Better Function Calling. It's a Different Layer Entirely.
Ten months after MCP went multi-vendor, most teams are still treating it as a nicer function-calling wrapper. That's the wrong mental model — and it's quietly producing architectures that don't scale.
A team I know migrated a production agent from custom function-calling wrappers to MCP last quarter. The result they led with in the post-mortem was: "deployment time for new tool integrations dropped from three days to eleven minutes." Three days to eleven minutes sounds like a performance win. It isn't. It's an architectural win. The eleven minutes happened because the tool was no longer part of the application — it was infrastructure. The three days had nothing to do with slow engineers.
That distinction is what most MCP explainers miss.
Since Anthropic published the Model Context Protocol in late 2024 and OpenAI, Google, Microsoft, and Cloudflare adopted it through 2025, the internet has produced roughly 10,000 tutorials that explain how MCP works. Almost none of them explain what layer it belongs to — and that's the question that determines whether your adoption goes well.
What function calling actually is
Function calling is the mechanism by which an LLM tells you it wants to invoke a tool. The model generates structured output — a tool name and a set of arguments — and your code acts on it. That's the full scope of what function calling does.
The critical detail: tool definitions live in your application code, in the payload you send to the model's API.
const response = await openai.chat.completions.create({
model: "gpt-5",
messages: conversationHistory,
tools: [
{
type: "function",
function: {
name: "search_database",
description: "Search the product catalog",
parameters: {
type: "object",
properties: { query: { type: "string" } },
required: ["query"]
}
}
}
]
});
// Your app handles the execution
if (choice.finish_reason === "tool_calls") {
const result = await searchDatabase(args);
// push result back into conversation, continue...
}
The tool schema is part of your API call. The tool handler is in your application process. The deployment unit is your application.
This is clean and simple when you have one model and three tools. The friction shows up when:
- You want to add a second LLM provider (OpenAI's schema format, Anthropic's schema format, and Google's
FunctionDeclarationare all slightly different) - A second team needs the same tool (now you have two copies of the schema drifting apart)
- You add a new tool and have to redeploy the entire application
- You want to give your tool its own authentication, rate limiting, or versioning
None of these are show-stoppers at small scale. At medium scale they become the background hum of technical debt that no one can point to but everyone feels.
What MCP actually is
MCP is not a better tool schema format. It is a protocol for a separate process — an MCP server — that exposes tools over a standard interface. Your application talks to the server via JSON-RPC 2.0, either over stdio (local subprocess) or HTTP/SSE (remote service). The model discovers what tools are available by asking the MCP client, which proxies the question to the server.
// Your application — no tool schemas in your code
const client = new Client({ name: "my-app", version: "1.0" });
await client.connect(new StdioClientTransport({
command: "node",
args: ["./db-mcp-server.js"]
}));
// Any model discovers tools automatically from the MCP server
const { tools } = await client.listTools();
const response = await anthropic.messages.create({
model: "claude-opus-4-7",
tools, // ← served by the MCP server, not defined here
messages: [...]
});
The MCP server is its own deployable unit. It has its own process, its own secrets, its own release cycle. When you add a new tool to the server, you don't touch the application. When you want to use the same tool from a different LLM provider, you point a new MCP client at the same server. The tool logic exists once.
As of March 2026, there are over 10,000 public MCP servers and 97 million monthly downloads across the Python and TypeScript SDKs. Block (Square) runs MCP for internal developer tooling. Sourcegraph wired it into Cody. Cloudflare ships an MCP server for Workers AI. This isn't experimental — it's the default assumption for new AI integrations at a meaningful number of companies.
The mental model that makes this click
Think about how you'd describe a database. You wouldn't say "PostgreSQL is a better way to store structs in your application code." PostgreSQL is infrastructure. Your application connects to it. The database has its own deployment lifecycle, its own backup strategy, its own access control.
MCP servers are the same thing, one layer up. They're tool infrastructure. Your application connects to them. They have their own deployment lifecycle, their own authentication, their own versioning.
Function calling is closer to embedding SQL strings directly in your application — totally fine for simple use cases, starts to hurt when the query logic needs to be shared, versioned independently, or used from multiple services.
The shift isn't from "bad function calling" to "good function calling." It's from tools as application code to tools as infrastructure.
Transport: stdio vs HTTP/SSE
MCP has two transport options and the choice matters operationally.
stdio runs the MCP server as a subprocess of your application. The client spawns it, communicates via stdin/stdout, and kills it when done. This is the right choice for developer tooling (Claude Desktop, IDE plugins) and single-application deployments where the tool and app share a lifecycle. No network stack, no auth surface, minimum latency.
HTTP/SSE runs the MCP server as a network service. Your client connects via HTTP; server-sent events push responses back. This is the right choice when multiple applications need the same tools, when the tool needs to scale horizontally, or when you're building something that other teams will consume. You get a real service: auth headers, TLS, rate limiting, monitoring. You also get a real operational surface.
The 2026 MCP roadmap is explicitly focused on making HTTP/SSE servers stateless and horizontally scalable — removing the current limitation where a session must stay pinned to a server instance. Watch for that if you're building at scale.
Who should own the MCP server in your organization
This is the question most architecture discussions skip, and it's the one that determines whether MCP actually delivers on the "write once, use anywhere" promise.
If your data team writes the PostgreSQL MCP server and your product team ships it as part of their application, you've recreated the coupling problem in a different location. The ownership question is: who has the deploy key?
The pattern that works is: the team that owns the underlying resource owns the MCP server. The data platform team owns the database MCP server. The security team owns the secrets MCP server. The devtools team owns the GitHub MCP server. Product teams are consumers, not owners.
This maps cleanly onto how your org probably already handles API ownership. An MCP server is just an internal API with a standardized interface that LLMs happen to understand.
When not to reach for MCP
I've seen two failure modes with MCP adoption.
The first is under-adoption: teams building multi-provider agent systems who are still copy-pasting tool schemas between integrations because they haven't made the architectural commitment. They're doing the work of MCP without the benefits.
The second is over-engineering: teams standing up a dedicated MCP server for three tools in a prototype that talks to one model. They've added operational complexity (subprocess management, stdio debugging, server health) to a system that didn't need it. Function calling would have been fine.
The signal that you're ready for MCP:
- You're integrating with a second LLM provider
- A second team wants to use one of your tools
- You're releasing tool updates independently from your application
- You're building an internal tool registry that multiple agents will consume
If none of those apply, function calling is the honest choice.
The honest tradeoff
MCP isn't free. Every stdio transport adds process management. Every HTTP transport adds a network call, a new service to monitor, a new auth surface. The protocol overhead is real.
What you get back is an architectural boundary that scales. Your tools are no longer coupled to your application's release cycle. Any model that speaks MCP can use them without schema translation. The "three days to eleven minutes" improvement that team measured was the sound of an organizational bottleneck dissolving — not because engineers got faster, but because a tool deployment no longer required coordination across teams.
That's the real trade. Not "better DX." A different way of drawing boundaries.
MCP specification and SDK: modelcontextprotocol.io. The transport comparison is drawn from the MCP 2026 roadmap. Production case studies cited from public engineering posts by Block, Sourcegraph, and Replit.
Work with me
I consult with engineering teams on AI adoption, cloud architecture, and engineering effectiveness. If this post surfaced a challenge you're facing, let's talk.
Get in touch →Related posts
Explore more on these topics: