MCP vs Agent Skills: What Each One Adds to an Agent, Measured on Bedrock with Strands
Quick summary: On 23 October 2026 we ran one refund-triage job 120 times on Bedrock with Strands 1.62. MCP alone passed 0 of 30 and approved an out-of-window $129 refund 10 times. A Skill alone passed 0 of 30. Both together passed 30 of 30.
Key Takeaways
- On 23 October 2026 we ran one refund-triage job 120 times on Bedrock with Strands 1.62
- MCP alone passed 0 of 30 and approved an out-of-window $129 refund 10 times
- A Skill alone passed 0 of 30
- Both together passed 30 of 30
- On 23 October 2026 we gave one Strands agent one job: decide a refund

Table of Contents
On 23 October 2026 we gave one Strands agent one job: decide a refund. We ran it on Amazon Bedrock (claude-haiku-4-5, us-east-1, Strands Agents 1.62.0, MCP Python SDK 2.1.1) in four setups, 10 runs per task, 120 runs in all. With the order data over MCP and the refund policy in a Skill, it got 30 of 30 right. With MCP alone it got 0 of 30, and it approved a $129 refund outside the return window in all 10 tries. With the Skill alone it also got 0 of 30, but it never guessed: every time, it asked for the order fields it needed.
That is the whole argument in numbers. MCP gives agents reach. Skills give them know-how. The rest of this post explains what each one is, how each one enters the model’s context, what each costs in tokens, and how to build your own on Bedrock with Strands. The benchmark code and raw results are public.
What MCP is
The Model Context Protocol is the connection layer. An MCP server is a separate process that exposes tools (and optionally resources and prompts). The agent’s MCP client lists those tools at startup and passes their names, descriptions, and JSON input schemas to the model. When the model decides to call one, the client forwards the call, the server talks to the real system, and the result comes back into the conversation.
What this buys you is one interface for many systems. A database query, a SaaS API, an AWS account, and an internal order service all look the same to the agent: a named tool with a schema. You write the integration once, as a server, and any MCP-capable agent can use it, whether that is Strands, Claude Code, Kiro, or AgentCore Gateway.
What it costs: every listed tool’s schema rides along on every model request. In our run, three small tools added 835 input tokens to a request that otherwise costs 34.
What an Agent Skill is
A Skill is a reusable playbook. It is a folder with a SKILL.md file: YAML frontmatter with a name and a description, then a Markdown body with the procedure, rules, and conventions. It can also carry scripts/, references/, and assets/. A Skill adds no service and no network hop. It runs inside the agent’s existing environment.
Skills use progressive disclosure. The agent sees only each Skill’s name and description up front. When a task matches the description, the model calls a tool to load the full body, and from then on it follows that procedure. In Strands that tool is called skills, and it is registered by the AgentSkills plugin, which follows the agentskills.io layout.
In our run, the full refund-triage body (about 170 words of rules) cost nothing until the model asked for it. The plugin’s fixed overhead did not: 719 input tokens per request, mostly the skills tool schema plus the metadata block. One Skill is not much cheaper to carry than three MCP tools. The saving shows up when you have 20 Skills and the agent needs one.
Why both exist
They answer different questions.
- MCP answers “can the agent see and touch the system?” Without it, our agent did not know the headphones cost $249 or were delivered on 8 October. A prompt cannot fix that unless a person pastes the data in.
- A Skill answers “does the agent know how we do this here?” Without it, our agent read the right order and then applied a generic idea of a return policy. Your 15-day window for opened electronics, your 15% restocking fee, and your $200 approval threshold live nowhere in the model’s training data.
Teams usually reach for whichever one they already know. Platform engineers build an MCP server and wonder why the agent makes bad calls with good data. Prompt-minded teams write a long policy file and wonder why the agent hallucinates order dates. The benchmark below shows both failures side by side.
How each works at runtime

Per request, the model sees:
- Your system prompt.
- Skills: an
<available_skills>block with each Skill’s name, description, and location, appended by the plugin before every invocation. Plus theskillstool schema. - MCP: the name, description, and input schema of every tool the client listed.
- The conversation so far, including any Skill body already loaded and any tool results.
So a Skill is cheap to have and costs real tokens only when used. An MCP tool costs its schema on every turn whether it is used or not. That asymmetry matters once you list 40 tools from five servers. It matters much less with three.
MCP vs Skills, side by side
| MCP server | Agent Skill | |
|---|---|---|
| Adds | Access to a system: read, write, act | Procedure: rules, steps, output format |
| Lives in | A separate process (stdio or streamable HTTP) | A folder of files the agent can read |
| Fixed context cost (our run) | 835 tokens for 3 tools, every request | 719 tokens for 1 Skill + plugin, every request |
| On-demand cost | Each tool result | The SKILL.md body, once per conversation |
| Credentials | Held by the server | None; it reuses the agent’s tools |
| Versioning | Deploy the server | Edit and review a Markdown file |
| Failure when missing (our run) | Agent asks for data, or invents a placeholder | Agent applies a generic policy to real data |
| Security surface | Tool permissions, auth, network (MCP security) | Prompt-injection risk in the file; scripts run with the agent’s permissions |
Same prompt, four setups
Every run got the same user message, a one-line request plus a required JSON format:
Order FM-1063: the customer wants a refund for the blender.
Reply with a JSON block: {"order_id": str, "decision": "approve" | "deny",
"refund_amount_usd": number, "needs_human_approval": bool}The system prompt was one line: a support assistant for an online store, today is 2026-10-20. The blender was delivered on 12 September, 38 days earlier. The Skill’s 30-day window says deny.
| Setup | What the model had | What it answered (10 of 10 runs identical) | Correct? |
|---|---|---|---|
| No MCP, no Skill | The prompt | "decision": "deny", "needs_human_approval": true, after saying it had no order data | No: approval flag wrong, decision was a guess |
| MCP only | Order data via get_order | "decision": "approve", "refund_amount_usd": 129.0. Its reasoning: “not marked as final sale, so a refund is eligible” | No: a $129 refund the policy forbids |
| Skill only | Refund policy via skills | No decision. Asked for final-sale flag, delivery date, price, category, and opened status | No by the grader, and the safe failure |
| MCP + Skill | Both | "decision": "deny", 0, "needs_human_approval": false, citing the 30-day rule | Yes |
The “without” alternative is the prompt you would have to write by hand: paste the order record, then paste the whole policy, on every request. That works for one ticket. It does not survive a policy change across 40 prompt templates, and it puts the person, not a tool, in charge of fetching the right order.
| You write in the prompt | The agent fetches or loads | |
|---|---|---|
| Without MCP or a Skill | Order id, item, price, delivery date, opened flag, final-sale flag, every policy rule | Nothing |
| MCP only | Every policy rule | Order record |
| Skill only | Order id, item, price, delivery date, opened flag, final-sale flag | Policy |
| MCP + Skill | Order FM-1063: the customer wants a refund for the blender. | Order record and policy |
Benchmark: what we measured
First-party benchmark — Run on 23 October 2026, 08:23 UTC. Strands Agents 1.62.0,
mcp2.1.1, Python 3.14,us.anthropic.claude-haiku-4-5-20251001-v1:0inus-east-1, temperature 0. Three tasks against a synthetic order fixture (not a real store), four setups, N=10 per cell. A run passes only if decision, amount (±$0.01), and approval flag all match the expected answer and the agent did not callissue_refund. Raw rows:results.json. Summary:summary.md.
| Task | Expected | No MCP, no Skill | MCP only | Skill only | MCP + Skill |
|---|---|---|---|---|---|
| Opened headphones, $249, day 12 | Approve $211.65, needs approval | 0/10 (“pending”) | 0/10 (approved $249.00) | 0/10 (asked for data) | 10/10 |
| Clearance jacket, final sale | Deny, no approval | 0/10 (approval flag) | 0/10 (approval flag) | 0/10 (asked for data) | 10/10 |
| Blender, day 38 | Deny, no approval | 0/10 (approval flag) | 0/10 (approved $129.00) | 0/10 (asked for data) | 10/10 |
| Median input tokens | 88 | 2,021 | 2,044 | 2,908 | |
| Median seconds | 2.8 | 4.2 | 3.6 | 4.5 | |
| Fixed overhead (“reply OK”) | 34 | 869 | 753 | 1,091 |
Three things stand out.
Correctness came only from the pair. The jump from 0 to 30 out of 30 cost 887 extra input tokens per decision over MCP alone, about 44% more, and 0.4 seconds of median latency.
MCP alone failed in the expensive direction. It overpaid the headphones by $37.35 (no restocking fee) and approved the out-of-window blender in every run. A person reviewing a queue of these would see confident, well-formatted JSON built on real data. That is harder to catch than an agent that admits it does not know.
The Skill alone failed in the safe direction. The SKILL.md says “If you cannot see the order data, say which fields you need. Do not guess dates or prices.” The model followed that line in 30 of 30 runs. Our grader counts it as a fail because there is no decision. In a live support queue it is the behavior you want.
What broke — Building the artifact on 23 October 2026, the first MCP run died with
MCPClientInitializationError: the client initialization failed. The server subprocess had crashed on import:mcp2.x renamedFastMCPtoMCPServer, sofrom mcp.server.fastmcp import FastMCPraisesModuleNotFoundError. Strands only reports the closed connection, not the server’s traceback. We found it by running the server file directly, switched tofrom mcp.server.mcpserver import MCPServer, and pinnedmcp==2.1.1. Second failure, same day: the default model id we planned to use returnedAccessDeniedException ... is not available for this account, so the published run uses Haiku 4.5. A bigger model may close some of the MCP-only gap. It will not know your restocking fee.
One more observation, without a failure attached: the MCP-only agent said the blender was delivered “39 days ago” in all 10 runs. It was 38. The policy outcome is the same here. Date math you care about belongs in a script the Skill calls, not in the model’s head.
Build your own on Bedrock with Strands
The full project is in examples/architecture-blog-2026/mcp-vs-agent-skills/. The three pieces:
1. An MCP server. Python 3.12+, mcp==2.1.1, stdio transport. Tool docstrings become the descriptions the model reads, so write them for the model.
from mcp.server.mcpserver import MCPServer
mcp = MCPServer("order-tools")
@mcp.tool()
def get_order(order_id: str) -> dict:
"""Look up one order by id (for example FM-1042). Returns item, category, price, delivery date, opened and final_sale flags."""
return next((o for o in ORDERS if o["order_id"] == order_id), {"error": f"order {order_id} not found"})
if __name__ == "__main__":
mcp.run()2. A Skill. Plain Markdown in skills/refund-triage/SKILL.md. The folder name must match name, or Strands logs a warning (and raises in strict=True mode). The description is the trigger, so name the situations it covers.
---
name: refund-triage
description: Decide whether a return or refund request is eligible and how much to refund. Use for any refund, return, or exchange question about an order.
---
# Refund triage
1. **Final sale.** If the order is `final_sale: true`, deny. Refund 0.
2. **Return window.** More than 30 days since `delivered_on`: deny.
3. **Opened electronics.** 15-day window; inside it, refund price × 0.85.
4. **Everything else inside the window:** approve the full price.
Any approved refund above $200.00 sets `needs_human_approval: true`.
Never call a refund or payment tool. You propose; a person executes.
If you cannot see the order data, say which fields you need. Do not guess.3. The agent. Strands Agents 1.62.0 on Bedrock. MCPClient starts the server as a subprocess and lists its tools. AgentSkills injects the metadata and registers the skills tool.
from mcp import StdioServerParameters, stdio_client
from strands import Agent
from strands.models import BedrockModel
from strands.tools.mcp import MCPClient
from strands.vended_plugins.skills import AgentSkills
server = StdioServerParameters(command="python", args=["mcp_server/order_tools.py"])
orders = MCPClient(lambda: stdio_client(server))
with orders:
agent = Agent(
model=BedrockModel(model_id="us.anthropic.claude-haiku-4-5-20251001-v1:0", region_name="us-east-1", temperature=0),
system_prompt="You are a support assistant for an online store. Today is 2026-10-20.",
tools=orders.list_tools_sync(),
plugins=[AgentSkills(skills=["./skills/refund-triage"])],
)
print(agent("Order FM-1042: the customer wants a refund for the headphones."))result.metrics.accumulated_usage gives you input and output tokens per call, and result.metrics.tool_metrics gives tool call counts. That is all bench.py reads.
Reproduce this — Download
examples/architecture-blog-2026/mcp-vs-agent-skills/.uv sync, thenAWS_PROFILE=your-profile uv run bench.py --n 10. It makes 124 agent runs and writesresults/summary.mdwith the pass counts and token medians above. UseMODEL_ID=...to rerun on a different model.
Advantages, and the trade-off
MCP’s advantages: one integration serves every MCP-capable agent; credentials stay in the server, not the prompt; the same server can run locally over stdio or remotely behind AgentCore Gateway or the managed AWS MCP Server.
Skills’ advantages: a reviewer can read the whole procedure in a pull request; changing a rule is a text edit, not a deploy; dozens can be installed while only the matching one enters context; and they travel between agents that follow the same layout, which is why AWS shipped 43 of them in the Agent Toolkit for AWS in May 2026.
Our recommendation: when an agent gives wrong answers, check which half is missing before you build anything. If it says “I don’t have access to,” add an MCP tool. If it has the data and still decides wrong, write a Skill. Start with read-only tools and one Skill per decision type. The trade-off is context: in our run, the pair cost 2,908 input tokens per decision against 2,021 for MCP alone, and every extra MCP server adds its schemas to every turn. Past roughly a dozen tools, filter the tool list per agent instead of loading every server.
Neither layer solves the next problem. A refund agent also needs the right customer history, the right policy version, and retrieval over documents too large for a Skill. That is a data and context question: S3 Vectors behind AgentCore is where we would go next.
If You Only Do One Thing
Take the agent you already have and run its last 10 wrong answers through one question: did it lack access or procedure? Fix that half first. Then take write tools such as issue_refund off the agent’s tool list until a named person approves the action. A Skill line saying “never call it” held in all 120 runs here, and it is still only an instruction.
What This Post Doesn’t Cover
- MCP security. Auth, tool poisoning, and network exposure are in MCP security for enterprise AI.
- Remote MCP and AgentCore. We ran stdio on one machine. We have not measured Gateway-hosted tools or Harness Skills with this workload.
- Bigger models. The run used Haiku 4.5 because it was the model this account could call. We have not rerun on Sonnet or Opus.
- Many Skills. With one Skill, progressive disclosure saves little. We did not measure 20 Skills competing for one trigger.
- Real data. The orders are a four-row synthetic fixture. Real tickets are messier, and the pass rate will be lower.
Frequently asked questions
What is the difference between MCP and Agent Skills?
Do Agent Skills cost fewer tokens than MCP?
When should I not write a Skill?
When should I not add an MCP server?
Is a Skill rule like 'never call the refund tool' a security control?
Can I use MCP and Skills with Amazon Bedrock AgentCore instead of a local Strands agent?

AWS Cloud Architect & AI Expert
AWS-certified cloud architect and AI expert with deep expertise in cloud migrations, cost optimization, and generative AI on AWS.




