← Back to Architecture Radar
Radar assessment

MCP vs Agent Skills vs function calling: an architecture fit assessment

Four ways to give an agent capability, and a decision most teams make on novelty rather than on where their security boundary actually sits. This is the long-form version of those calls on my Architecture Radar — and the framing that matters is that these are not competitors so much as answers to different questions.

Scope & vantage. Assessed mid-2026. Target use case: an agent in a production system with a security boundary and an audit requirement — internal tools, customer data, and a compliance team who will ask what the model was allowed to touch. Axes are integration-specific and not comparable with any other assessment here; two are inverted (Low Latency Overhead, Light Ops) so higher is always better.

1. Executive summary

The question is not "which protocol wins" — it is "is there a boundary to cross?" Get that right and the choice makes itself.

If the tool lives in your own process, written by your team, deployed in your repo: native function calling is correct. Not a compromise, not the legacy option — correct. It has the lowest latency, the lightest operations, and the most mature client support on this page, and wrapping it in a protocol adds a hop and a daemon to buy a boundary you do not need.

If the tool is genuinely external — another team's service, a vendor, something with its own auth and its own blast radius — that is what MCP is for. It scores highest here on Security Boundary and Governance for a reason: it makes the seam explicit, which is what lets you audit it.

Agent Skills are solving a third problem people keep conflating with the first two. They are not about calling code; they are about procedural knowledge — how we do releases, what our escalation policy is — loaded progressively so it does not sit in context permanently. That is why Skills score 4.7 on token efficiency and 3.2 on security boundary: they are documentation with a loading strategy, not a sandbox.

Verdict

MCP — Adopt where the tool boundary is genuinely external and needs a real security seam. Best-in-class on the security and governance axes, broadest client support, and a daemon plus a protocol hop to pay for it.

Native function calling — Adopt for in-process tools. Highest maturity and lowest overhead on this page. Choosing it is not settling.

Agent Skills — Trial for packaging procedural knowledge. Excellent token economics via progressive disclosure; a weak security story, because that was never the problem it solves.

Hand-rolled API layer — Narrow Use. Sometimes right for a hard compliance constraint, and you are rebuilding MCP badly with no ecosystem.

Main risk: adopting MCP for tools that never leave your process, and paying protocol overhead for a boundary that does not exist.

2. Positioning

These four are usually presented as a progression — hand-rolled, then function calling, then MCP, then Skills — which is misleading. They answer different questions.

DimensionMCPAgent SkillsNative function callingHand-rolled API
AnswersHow do I cross a boundary?How do I load knowledge?How do I call my own code?How do I satisfy an unusual constraint?
UnitA server exposing toolsA folder of instructionsA function signatureWhatever you built
BoundaryExplicit, process-levelNone — it is textNone — same processWhatever you enforced
Context costTool schemas, always loadedProgressive — loaded on demandSchemas, always loadedYours to manage
OpsA server to run and versionFiles in a repoNothing extraAll of it
EcosystemBroad and growingNarrower, client-specificUniversalNone

The progressive disclosure idea inside Agent Skills is the genuinely novel bit and worth understanding on its own terms. Instead of putting every procedure in the system prompt, a Skill exposes a short description; the agent pulls the full body only when the description matches what it is doing. That turns a linear context cost into a conditional one — which is why the token efficiency score is the highest on this page, and why Skills scale to knowledge volumes that would make a system prompt unusable.

The mirror-image trap is treating that as a security mechanism. A Skill is instructions the model reads. It has no sandbox, no capability model, and no enforcement — an agent that has been prompt-injected will read your Skill exactly as happily as your own instructions. If the requirement is containment, that is MCP's job, and I have written at length about why in prompt injection and guardrails.

3. Radar criteria — the reasoning

Interface clarity

MCP 4.4 — a typed, discoverable contract with a specification behind it. Agent Skills 4.2: markdown with frontmatter, which is clearer to a human and looser to a machine. Native function calling 4.0, entirely dependent on how good your docstrings are. Hand-rolled 3.4, as clear as the last engineer made it.

Security boundary

The axis that should decide most of this. MCP 4.6 — a separate process with its own auth, its own permissions, and a seam you can put a policy at. Hand-rolled 4.0: you can build a real boundary, and you will maintain it forever. Native function calling 3.4 and Agent Skills 3.2 — neither has a boundary at all, and it is important not to pretend otherwise. Skills in particular are text the model reads.

Discoverability

MCP 4.3 — servers advertise their tools, so a client can enumerate capability at runtime. Agent Skills 4.0, discoverable by description. Function calling 3.6, whatever you registered. Hand-rolled 2.6: discoverable by reading the source, which is to say not.

Versioning & evolution

Hand-rolled 4.2 wins here almost by accident — you own the whole surface, so you can version it however you like. Agent Skills 3.9 (files in git, which is a decent versioning story). MCP 3.8: the protocol is stabilising and tool-schema evolution across clients is still awkward. Function calling 3.4.

Token efficiency

Agent Skills 4.7 and this is their entire argument — progressive disclosure means a large body of procedural knowledge costs almost nothing until it is relevant. Function calling 4.2. Hand-rolled 3.4. MCP 3.6: every connected server's tool schemas sit in context, and with several servers connected that is a real and often unnoticed tax.

Low latency overhead (inverted — higher means less)

Native function calling 4.6 — an in-process call. Agent Skills 4.4, a file read. Hand-rolled 4.2. MCP 3.6: a real IPC or network hop per call, which is invisible in a chat interface and very visible in a voice agent's latency budget.

Client & ecosystem support

Native function calling 4.4 — every model provider supports it, and has for years. MCP 4.5 has overtaken it on breadth of tooling: clients, servers, registries, inspectors. Agent Skills 3.4, more client-specific. Hand-rolled 2.4, by definition none.

Light ops (inverted — higher means less to run)

Native function calling 4.6 — nothing to deploy. Agent Skills 4.3, files in a repo. MCP 3.6: a server per integration, each needing deployment, monitoring and a version. Hand-rolled 3.0.

Governance & audit

MCP 4.2 — a process boundary is where you can actually log, rate-limit and authorise, which is what auditors want to see. Hand-rolled 4.0. Function calling 3.4 and Skills 3.4: both auditable in principle, with nothing structural helping you.

Maturity

Native function calling 4.6 — years of production use across every provider. Hand-rolled 4.4, mature in the sense that it is just software. MCP 4.0: young, moving fast, and already the de facto standard for external tools. Agent Skills 3.4, the newest idea here and the least settled.

4. Radar scorecard

How these scores are arrived at — the rings, the 0–5 scale, and the evidence rules — is written up in the radar methodology. This table and the chart below are generated from one file, scorecards.json: the source of truth, machine-readable, and kept separate so two copies of the same numbers cannot drift apart between editions. Last reviewed 2026-Q3.

CriterionMCPEvidenceRisk → mitigation
Interface Clarity4.4Typed, discoverable contract with a published specSchema evolution across clients is still awkward
Security Boundary4.6Separate process, own auth, a real policy seam
Discoverability4.3Servers advertise tools; runtime capability enumeration
Versioning & Evolution3.8Protocol stabilising; tool-schema evolution in fluxBreaking changes → pin server versions
Token Efficiency3.6Every connected server's schemas sit in contextSeveral servers → measure the always-on token cost
Low Latency Overhead (inverted)3.6A real IPC/network hop per callLatency-critical paths → native calls, not MCP
Client & Ecosystem Support4.5Broad client, server, registry and inspector tooling
Light Ops (inverted)3.6A server per integration to deploy, monitor and versionServer sprawl → consolidate; one server, many tools
Governance & Audit4.2Process boundary is where logging and authz can live
Maturity4.0Young, fast-moving, already the de facto external standard

Comparator scores (same axis order) — Agent Skills: 4.2 / 3.2 / 4 / 3.9 / 4.7 / 4.4 / 3.4 / 4.3 / 3.4 / 3.4. Native function calling: 4 / 3.4 / 3.6 / 3.4 / 4.2 / 4.6 / 4.4 / 4.6 / 3.4 / 4.6. Hand-rolled API layer: 3.4 / 4 / 2.6 / 4.2 / 3.4 / 4.2 / 2.4 / 3 / 4 / 4.4.

5. The radar

Ten integration axes, two inverted so higher is always better. The shapes are the argument: MCP peaks on Security and Governance and dips on latency and ops; native function calling is its mirror image; and Agent Skills spike alone on Token Efficiency while sitting lowest on Security — because they were never a containment mechanism. Click any name in the legend to toggle it on or off — comparing two shapes at a time is far more legible than comparing four.

6. Workload-specific analysis

SituationBest fitNotes
Your own functions, your own processFunction callingNo boundary to cross. Adding a protocol adds a hop and buys nothing.
Another team's service, or a vendor'sMCPA real seam, with auth and audit at it. This is the case it was designed for.
"How we do X here" — procedures, policiesAgent SkillsProgressive disclosure keeps it out of context until it matters.
Large body of knowledge, rarely all relevantAgent Skills4.7 on tokens. This is the whole point.
Tool the agent must not be able to misuseMCPContainment needs a process boundary, not instructions.
Latency-critical path (voice, trading)Function callingThe MCP hop is invisible in chat and expensive in a voice latency budget.
Air-gapped or unusual compliance regimeHand-rolledLegitimate — and you are maintaining a protocol nobody else tests.
Because it is the newest thingStopThe most common reason MCP gets adopted, and the worst one.

The trap: treating Agent Skills as a security control. A Skill is markdown the model reads. It has no sandbox, no capability model, and no enforcement — so "the Skill says not to touch production" is a suggestion to a system that can be argued with. An agent that has been prompt-injected will read an attacker's instruction with exactly the same weight as yours. Skills are excellent at their actual job, which is loading procedural knowledge cheaply. If the requirement is that something cannot happen, that needs a process boundary with authorisation at it, which is MCP or a hand-rolled equivalent. The second trap is the mirror: paying MCP's hop and daemon for functions that live in your own codebase.

7. Reference architecture

The pattern I would build: native calls inside the trust boundary, MCP across it, Skills for knowledge, and one gateway so the model provider stays swappable.

graph LR
  U["User / caller"] --> AG["Agent runtime"]
  AG --> GW["LLM gateway
routing + fallback"] GW --> M["Model"] AG -->|"in-process, no hop"| FN["Native functions
your own code"] FN --> DB[("Your database")] AG -->|"protocol hop"| MCP["MCP servers"] MCP -->|"own auth"| EXT[("External systems
CRM, vendor APIs")] SK["Agent Skills
procedural knowledge"] -.->|"loaded on demand"| AG MCP -.->|"authz + audit log"| AUD[("Audit store")] POL["Policy"] -.->|"enforced at the seam"| MCP

The trust boundary is the vertical the diagram is built around. Everything reached by a solid line from the agent is inside it and called directly; everything past MCP is outside it and pays a hop to gain authorisation and an audit trail. Skills enter dotted because they are context, not capability.

The distinction in code — the same capability, on either side of a boundary:

# INSIDE the boundary: your code, your process. A direct call. No protocol,
# no daemon, no hop. Bind parameters — tool args are model-controlled input.
@tool
def get_order(order_id: str) -> dict:
    # Order from our own database.
    return db.query("select * from orders where id = ?", [order_id])

# ACROSS the boundary: someone else's system, with its own credentials and
# its own blast radius. The MCP server is where authz and audit live, so the
# agent never holds the vendor credential at all.
#
#   mcp.json
#   {
#     "servers": {
#       "billing": {
#         "command": "billing-mcp",
#         "env": { "BILLING_TOKEN": "${SECRET:billing_token}" }
#       }
#     }
#   }
#
# The seam is the point: the agent asks the server, the server decides.
# Scope the token to the smallest set of operations the agent needs, and
# log every call — a boundary you cannot audit is decoration.

8. POC plan (4 weeks)

  • Week 1 — draw the trust boundary before choosing anything. List every tool and mark it inside or outside. That single exercise decides most of this, and teams that skip it end up wrapping their own functions in a protocol.
  • Week 2 — measure the context tax. Connect the MCP servers you expect to need and count the tokens their schemas consume before a single user message. Several servers add up faster than anyone expects.
  • Week 3 — attack it. Prompt-inject the agent and try to reach a tool it should not. If a Skill is the only thing standing between the model and a destructive action, you have found your answer about what Skills are for.
  • Week 4 — decide per tool, not per stack. The right outcome is usually a mix. Re-score this table and write the ADR listing which tools sit on which mechanism and why.

9. Final recommendation

Adopt MCP for external tools, native function calling for internal ones, and Agent Skills for knowledge. This is not a competition, and the right answer is a mix.

The decision rule I would give a team is one question: does this call cross a trust boundary? If the code is yours, in your process, in your repo — call it directly. Native function calling scores highest on maturity and lowest on overhead precisely because there is nothing between the model and the function, and adding MCP there buys a security seam where there is nothing to secure while costing a hop and a daemon to operate.

If the call reaches someone else's system — another team, a vendor, anything with its own credentials — MCP is the right answer and its 4.6 on Security Boundary is why. A separate process is where authorisation, rate limiting and audit logging can actually live, and "the agent asked the server and the server decided" is a sentence an auditor accepts. Watch the context tax as servers accumulate, and consolidate: one server exposing many tools beats many servers exposing few.

Agent Skills deserve a trial on their own merits and should not be measured against the other two. Progressive disclosure is a genuinely good idea — a large corpus of procedural knowledge that costs nothing until relevant is a real advance over stuffing a system prompt. Use them for how-we-do-things. Do not use them for must-not-happen.

I would re-assess when MCP's authorisation story matures further — today the protocol gives you the seam and largely leaves the policy to you, and a standard for capability scoping is the change that would push it from a strong Adopt to an obvious one.

References

MCP is young enough that the specification is the primary source and most commentary is speculation. I have leaned on the spec.

Primary sources

Background

Deeper reading (blog)

The internals behind the scores:

Scores are my own architect-level judgment for the stated use case, calibrated to mid-2026 — not a vendor ranking. Agent-integration axes, two inverted. NOT comparable with any other assessment here. Re-score against your own POC numbers before you commit.