MCP vs Function Calling in Agents [2026]: When to Say No

MCP backlash is real. Here’s my decision framework for when to use MCP vs function calling in agents, and when a bespoke tool API beats both on ops, security, and debugging.

Part of theDev Tools & AI Workflow series
A computer screen displaying network configuration code in a terminal window
Listen to this article
--:--

MCP isn’t getting rejected because engineers hate standards. It’s getting rejected because standards come with operational ownership. And ownership means on-call, auth, upgrades, and the joy of debugging someone else’s “simple” integration at 2 a.m.

If you’re searching for when to use MCP vs function calling in agents, my take is blunt: start with function calling, graduate to MCP only when you can name the exact problem it solves for your team, and say “no MCP” the moment it creates a new deployable security boundary you’re not staffed to run.

Key takeaways

  • MCP wins when you need a real credential boundary and reusable tool integrations across multiple hosts. Not when you just need “tools.”
  • Function calling wins when you want the smallest production surface area: one service, one auth story, one telemetry pipeline.
  • Bespoke tool APIs beat both when you control both ends and want strict typed schemas without protocol churn.
  • The MCP backlash is mostly about auth, versioning, observability, and server sprawl. Those costs don’t show up in demos.
  • If your tools return text “for token efficiency,” you’ll pay it back in retries, brittleness, and eval failures.
MCP is only worth it when it replaces N integrations with 1 boundary you’re actually willing to own.

What is the Model Context Protocol (MCP)?

Model Context Protocol (MCP) is an open protocol for connecting AI applications (hosts) to external tools and context providers (servers) through a standardized client–server interface.

black and gray code padlock anchored on chain-link fence selective focus photo

The official docs describe an MCP host (the AI app) creating an MCP client per server, maintaining dedicated connections to those servers, with local servers commonly using STDIO and remote servers using Streamable HTTP (Model Context Protocol project). That architecture detail matters because it quietly picks your auth model, deployment model, and observability model for you.

Here’s the framing I’ve found most useful: MCP is not “tool calling.” It’s tool calling plus a transport, discovery, lifecycle, and security story.

And that “extra story” is exactly why people are yelling about it. Nobody serious is debating whether tools are useful. They’re debating whether the extra moving parts are worth introducing into a system that already fails in creative ways.

If you want the baseline definition straight from the project, start here: Model Context Protocol project.

When to use MCP vs function calling in agents?

Use MCP when you need cross-host portability, centralized secrets, and a plugin ecosystem. Use function calling when you’re building one agent inside one runtime and you want the smallest operational footprint. Use bespoke tool APIs when you control both ends and need stable, typed contracts more than ecosystem compatibility.

a close up of a computer screen with code on it

That’s the 10-second answer.

The real answer is whether MCP removes complexity or just relocates it to a place your team is bad at operating.

A decision matrix (the part most posts skip)

DimensionMCP (servers)Function calling (direct)Bespoke tool API (internal)
Primary benefitReusable integrations across hosts; credential boundaryMinimal moving parts; tight controlStable contracts; best fit for your domain
Deployment units**+1 deployable** per server (often many)Usually none beyond your agent serviceWhatever you already run (API/service)
Auth & secretsOAuth 2.1 on HTTP; env creds on STDIOYour service handles secrets or per-user tokensYour existing auth stack
VersioningProtocol negotiation per request; deprecation policyProvider-specific schema/tooling changesYour versioning rules
ObservabilityTrace across host → client → server → downstreamStraight shot: agent → tool endpointStraight shot: agent → internal API
PortabilityHigh across MCP hosts (in theory)Low across model providersMedium (internal portability)
Failure modesServer sprawl, confused deputy, text-y toolsPrompt/tool schema drift, key leakageContract drift, ownership ambiguity
Best for10+ tools, multiple clients/IDEs, mixed vendors1–5 tools, single app, early productMature orgs, strong API discipline

Now the “backlash” lens: for most teams, the decision isn’t about elegance. It’s about whether you can afford to run the boundary.

What problem does MCP solve that function calling doesn’t?

MCP solves portability and boundary management.

A computer screen with the words back the web on it

Function calling is a capability of a model provider. MCP is an integration layer you can reuse across multiple hosts. When an MCP server exists for a system, you can plug it into different hosts (IDE agents, desktop agents, internal agent UIs) without rewriting every integration.

The MCP architecture calls this out explicitly: an AI application (the host) connects to one or more MCP servers by creating a client per server (Model Context Protocol project). That “one client per server” pattern is what enables the “plugin rack.” It’s also what creates the sprawl problem when people get excited and ship 14 servers with no ownership.

Broad ecosystem support (and why it matters)

This stopped being hypothetical. Vendors are shipping real MCP servers.

Sentry, for example, runs a hosted MCP endpoint and documents setup for Claude Code, Cursor, and VS Code, with OAuth for all connections and scoped endpoints like https://mcp.sentry.dev/mcp/{org}/{project} (Sentry). That’s not a toy demo. That’s a real vendor saying: “Here’s a supported integration boundary. Use it.”

This is how MCP becomes “inevitable.” Once your SaaS vendors ship MCP endpoints, “just use MCP” becomes the default advice in high-signal communities.

But the ecosystem argument cuts both ways:

  • If the vendor runs the server, you’re mostly consuming.
  • If you run the server, you’re operating.

If you mix those up, you will have a bad quarter.

When is function calling enough (and MCP is overkill)?

Function calling is enough when your “tool surface area” is small and the integration is owned by the same team shipping the agent.

These are the rules I use in practice:

  1. You have fewer than ~5 tools and they’re stable. The host/client/server model won’t amortize.
  2. Your agent already runs in a backend you control. If your agent is a service, calling your own APIs is boring. That’s a feature.
  3. You don’t need tool portability across hosts. If you’re not supporting Cursor plus VS Code plus a desktop app plus an internal UI, MCP’s “USB‑C for tools” story isn’t doing much work.
  4. You can tolerate secrets living in the agent runtime because the tool calls are low-risk or the environment is isolated.

That last point is exactly where the HN arguments get spicy. In the thread reacting to Earendil’s post, one commenter says they gave Claude an API key for Home Assistant and “no MCP needed,” and describes a workflow where the agent uses that key directly to reconfigure entities and automations (Hacker News community (thread)). That’s a totally valid setup.

It’s also a very specific threat model.

If you’re building anything enterprise-ish, “just hand the agent an API key” is the kind of advice that reads fine on HN and then gets you demolished in a security review.

For grounding on provider-native tools, OpenAI’s function calling docs are still the canonical reference for how provider tool schemas and structured outputs work (OpenAI).

The real costs driving the “no MCP” backlash

This is the part most pro-MCP content conveniently blurs out. MCP isn’t “free.” It externalizes complexity into places teams already struggle: auth, upgrades, debugging, and security boundaries.

Auth and secrets management: MCP moves the trust boundary

Auth is where MCP becomes either genius or a tax.

The official docs say authorization is optional, but strongly recommended when servers handle user data or administrative actions. For HTTP-based transports, MCP follows OAuth 2.1 conventions and can kick off flows via 401 Unauthorized plus WWW-Authenticate pointing at Protected Resource Metadata (Model Context Protocol project).

That’s “enterprise-ready.” It’s also real work.

The practical difference looks like this:

  • With function calling, your agent service often holds credentials (service tokens, API keys, or user tokens). Your biggest risk is leakage via logs, prompt injection, or an agent bug.
  • With MCP, you can move credentials into the MCP server. Now the agent doesn’t see the key. That’s a real improvement for certain threat models.

HN nails it in one sentence: an MCP server can hold keys so the agent can’t leak them (Hacker News community (thread)). The backlash is the immediate follow-up: “Cool. Now I’m running a security-sensitive broker service.”

Security: new attack classes you now own

MCP’s own security best practices explicitly warn about “confused deputy” vulnerabilities in MCP proxy servers under a set of conditions (static OAuth client IDs, dynamic client registration, consent cookies, and missing per-client consent checks) (Model Context Protocol project).

That’s not fearmongering. That’s the protocol telling you: if you implement this wrong, you can mint authorization for the wrong party.

If you were previously doing function calling straight to your own backend, you did not have this particular class of risk. You had others, like prompt injection. But you weren’t building an OAuth broker.

Versioning: date-based versions plus negotiation sounds nice until you’re on-call

MCP uses date-based protocol versions like YYYY-MM-DD, where the value represents the last backwards-incompatible change date. Backwards-compatible updates do not bump the version (Model Context Protocol project).

The current protocol version in the docs is 2026-07-28. That freshness is part of why MCP feels like it’s maturing quickly.

But it also means you have to take negotiation seriously. MCP supports per-request protocol version declaration via _meta.io.modelcontextprotocol/protocolVersion, and on Streamable HTTP also carries it via the MCP-Protocol-Version header. Servers can reject with UnsupportedProtocolVersionError and list supported versions (Model Context Protocol project).

If you run MCP servers internally, this becomes governance whether you like it or not:

  • How many versions will you support concurrently? 2 is common. 3 is a smell.
  • How do you roll hosts and servers without breaking developers mid-sprint?
  • Who owns the deprecation timeline when features stick around 12+ months (or 90 days expedited)?

With function calling, you still have versioning. It’s just hidden inside “whatever the provider changed.” That’s not better. It’s just different ownership.

Observability: tracing across agent ↔ MCP client ↔ MCP server ↔ downstream

This is what bites teams hardest, because you don’t feel it until something goes sideways.

With direct function calling, your request path is typically:

  • agent run → tool invocation → HTTP call → response

With MCP you have at least one extra hop and often one extra runtime:

  • host (IDE/desktop) → MCP client → MCP server → downstream API(s)

If you don’t propagate correlation IDs and tool invocation metadata end-to-end, you get the worst kind of incident: “the agent did something weird” and nobody can prove what happened.

If you’re serious about shipping agents, invest in a real trace tree. The mental model I use is the same one I wrote about in execution trace trees for AI agents. Multi-hop tool use without trace trees is just vibes.

Also, MCP does have a devtools layer. The architecture docs point to an official Inspector for testing servers (modelcontextprotocol org). That’s a real advantage versus bespoke tool APIs, where you’re building your own “inspector” whether you planned to or not.

Earendil’s “no MCP” moment, and what changed

This debate is flaring now because the backlash isn’t theoretical. It’s coming from teams actually building.

Earendil’s Pi team publicly said they previously rejected MCP, then pulled it into the core. Their explanation is one of the healthiest takes I’ve seen: MCP improved, and their product needs converged with what MCP is good at.

But they also say the biggest ongoing issue is composability, and they point out an ugly reality: many MCP servers optimize token efficiency by returning text. Earendil argues MCP should be closer to OpenAPI with intelligent tool discovery, meaning tools should return structured data and be discoverable via documentation and description (Earendil Engineering (RFC mailbox)).

That single observation explains a lot of the backlash:

  • Teams want reliable automation.
  • Text is not a contract.
  • The moment your agent depends on “a paragraph that usually contains the ID,” you’ve built a flaky system with a shiny protocol wrapper.

What is Codemode?

In Earendil’s framing, Codemode is a sandbox to allow composing tool calls, and they note it still doesn’t fully solve composability because the ecosystem servers and harnesses vary wildly (Earendil Engineering (RFC mailbox)).

This matters beyond Pi. It’s pointing at the real missing piece: standardizing transport doesn’t standardize semantics.

If you care about multi-tool composition, you should also be reading about agent orchestration patterns. MCP can be part of that story, but it doesn’t replace it.

Build servers, build clients: the path that doesn’t rot

This is where I’m opinionated.

If you’re going to adopt MCP, treat it like you’re adopting Kubernetes, not like you’re adding a library.

Build servers (without rewriting your business logic)

The pattern that ages best is boring: keep your existing internal APIs, then wrap them behind an MCP server that does three jobs:

  • auth/token brokerage (especially per-tenant)
  • schema normalization (force structured outputs)
  • policy enforcement (rate limits, allowlists)

Don’t move business logic into the MCP server unless you’re actively looking for a re-platforming project.

If you’re already running APIs in production, borrow the same reliability discipline you use for webhooks: retries, idempotency, ordering. I wrote a full playbook on how to design webhook retries, ordering, idempotency. MCP tool calls have the same failure modes. They just look cooler in a demo.

Build clients (or choose hosts that already did)

Most teams don’t want to build hosts or clients. They want their developers to use existing hosts.

That’s where MCP’s architecture is genuinely compelling. If VS Code or Claude Code acts as the host, your job is “just ship servers.” Still work, but bounded work.

Also, if you’re building internal AI agents, plan for the day your agent is not the only host. The moment you have a desktop assistant and an IDE assistant, “portability” stops being a slide deck argument.

Connecting and setup instructions (what this looks like when it’s real)

Sentry’s doc is a good example of what production MCP setup looks like:

  • Base endpoint: https://mcp.sentry.dev/mcp
  • Optional org/project scoping in the URL (a strong default)
  • OAuth for all connections
  • CLI setup for Claude Code: claude mcp add --transport http ...

All of that is operationally sane. It also highlights the hidden requirement: your MCP servers need to be dependable enough that developers will trust them inside their daily workflow (Sentry).

Here’s the official walkthrough video that explains MCP mechanics at a higher level:

My decision framework (and the “graduate to MCP” triggers)

Here’s the framework I use with teams. It’s blunt because the cost of being vague here is months of churn.

Start with function calling if you can answer “yes” to 3 of these

  • Can you ship with 1 deployable (your agent service) and keep it that way?
  • Are your tools mostly internal endpoints you already own?
  • Is your tool catalog under 5–7 functions and unlikely to explode next quarter?
  • Do you already have solid guardrails (budget caps, tool allowlists, audit logs)?

If you’re missing guardrails, fix that first. AI in production doesn’t forgive “we’ll add safety later.”

Graduate to MCP when you hit these triggers

  • Tool count is growing and integrations are getting duplicated across hosts.
  • You need a credential boundary because you can’t put keys in an agent runtime.
  • You want vendors to ship integrations and you want to consume them with minimal glue.
  • You can invest in operating auth, versioning, and observability.

If you’re already doing heavy retrieval work, sanity-check whether your bigger problem is actually RAG quality and tool selection, not the protocol.

Say “no MCP” when any of these are true

  • You cannot name an owner for “MCP server lifecycle.” Not a team. A person.
  • You don’t have the stomach for OAuth 2.1 flows and token brokerage.
  • You’re going to allow arbitrary third-party servers without a security program.
  • Your tools return text blobs and everyone is pretending that’s fine.

On that last one: text outputs are how you end up writing brittle prompt parsers. Then you’ll spend 6 weeks on evals trying to stabilize a system that never had a contract.

If you want to go deeper on tool safety, pair this post with my work on prompt injection and LLM security. MCP doesn’t remove those problems. It just changes where you enforce controls.

Observability patterns that keep MCP from becoming a black box

If you adopt MCP, treat tool calls like a distributed system. Because that’s what you just built.

Patterns that work:

  • Single trace ID created at the host, propagated through MCP client to MCP server and downstream.
  • Span per tool call, with attributes: tool name, arguments hash, policy decision, latency, retries.
  • Redaction at the edge so the agent transcript isn’t your secrets store.

If you want a concrete logging schema, I already laid out what I use in AI agent observability logging schema and how to instrument end-to-end with OpenTelemetry instrumentation for AI agents.

This is also where the backlash becomes fair. Function calling gives you one place to instrument. MCP gives you three. If you don’t budget for that work, don’t ship MCP.

Data anchor: what I track that influences this recommendation

Running this site’s pricing tracker taught me that “one more hop” is rarely free.

Based on the live API pricing data I maintain at kunalganglani.com/llm-prices, teams routinely pick cheaper per-token models and then lose the savings to higher tool-call retry rates and longer agent loops. If MCP adoption increases your average tool-call latency by even 200–400ms across 10 tool calls, you’ve created seconds of user-visible delay. That’s before you pay the human cost of debugging.

Don’t optimize for protocol elegance. Optimize for end-to-end task cost and end-to-end task latency.

My prediction: MCP survives, but the winning version looks more like OpenAPI

The backlash week is healthy. It’s the ecosystem hitting the painful part of adoption, then arguing about who should carry the weight.

My bet is that MCP survives, but only if tool authors stop returning prose and start returning contracts. Earendil is right to push MCP toward “OpenAPI with intelligent tool discovery” and structured returns (Earendil Engineering (RFC mailbox)).

If you’re building agents today, make one choice this week: pick one integration approach, write down the ownership model, and instrument it like production. If you can’t do that, you don’t have a protocol problem. You have a team problem.

Photo by Cong Long Vu on Unsplash.

Continue reading

A wooden block that says token sitting on a table

MCP OAuth Security: Tool Impersonation, aud Mismatch, Token Replay [2026]

A threat model for MCP tool servers using OAuth: how tool impersonation and audience mismatch happen, how tokens get replayed, and what to validate and log in 2026.

a clipboard with a checklist on it next to a cup of coffee and

MCP Server Security Best Practices: Checklist + CI Linter [2026]

A practical MCP server security test plan: authn/authz models, tool allowlists, prompt-injection regression tests, rate limits, audit logs, and a CI-friendly permissions linter.

black hp laptop computer turned on displaying desktop

7 AI Agent Swarm Coordination Patterns [2026]: Failure Modes Included

AI agent swarm coordination patterns decide whether your multi-agent system converges or spirals. Here are the topologies, stop conditions, budget caps, and failure modes most teams don’t test in 2026.

an open laptop computer sitting on top of a table

How to Implement OWASP Agentic Top 10 Controls [2026]

A control-by-control guide to OWASP agentic top 10 controls: where to gate tool calls (MCP/client/server), what to log, what to block, and what belongs in CI vs runtime.

Cite this article
Kunal Ganglani (2026, October 1). MCP vs Function Calling in Agents [2026]: When to Say No. Kunal Ganglani. Retrieved October 1, 2026, from https://www.kunalganglani.com/blog/mcp-vs-function-calling-agents

Frequently Asked Questions

When should I use MCP instead of function calling for an agent?

Use MCP when you need the same tool integrations to work across multiple hosts (IDE, desktop app, internal UI) and you want a hard boundary where credentials live outside the agent runtime. It’s also a good fit when vendors already provide MCP servers you can consume. If you’re only building one agent in one backend, MCP is often extra complexity.

What problem does MCP solve that function calling doesn’t?

MCP standardizes how tools are exposed and connected so different AI apps can reuse the same integrations. Function calling is usually tied to one model provider’s schema and runtime. MCP can also centralize secrets in the MCP server, so the agent doesn’t need direct access to API keys.

How do auth and secrets management differ between MCP and function calling?

With function calling, secrets often live in your agent service or in the environment it runs in, which increases leakage risk if logs or prompts are mishandled. With MCP, a server can hold credentials and authenticate clients, often using OAuth-style flows for HTTP transports. That improves isolation, but you now have to operate an auth-capable service.

How does MCP versioning and negotiation work in production?

MCP uses date-based protocol versions like YYYY-MM-DD, and clients declare the version per request. Servers can accept the version, reject it, or return a list of supported versions so the client can retry. In production, that means you need an upgrade plan and often have to support more than one version at a time during rollouts.

How can I avoid MCP server sprawl on a growing team?

Treat MCP servers like products: define owners, enforce output schemas, and standardize deployment and observability. Prefer fewer “gateway” servers that wrap existing internal APIs over many one-off servers. If you can’t name who will maintain each server for the next year, you’re already on the sprawl path.