AI Agent Tool Use in 2026: MCP, CLI, Skills, and Code Execution

Part 3 of the Engineering the Agentic Stack series

Part 1 covered reasoning loops, and Part 2 covered memory. This article adds the action layer: how an agent exposes, selects, and runs tools.

The tooling story changed in 2025–2026. MCP gave vendors a shared protocol for external services, while code-executing agents showed that a model can sometimes compose a small program more efficiently than it can issue a long sequence of JSON calls. Anthropic reported a 98.7% token reduction for one expense-analysis workflow, and the CodeAct paper reported gains of up to 20% across its benchmark setup. Those results describe their tasks and harnesses, not a universal advantage for code execution.

I compare JSON tool calling, MCP, Skills, CLI tools, and code execution in that order. The final section applies Agent-Computer Interface (ACI) design principles to the Market Analyst Agent.

TL;DR: Five useful interface patterns cover most agent tool use. Skills carry instructions, CLI tools fit local development, MCP connects shared services, and code execution composes multi-step work inside a sandbox. JSON tool calling remains the simplest choice for small atomic actions. Whatever the protocol, the Agent-Computer Interface (ACI) must make actions clear, feedback compact, and errors recoverable.


Five ways AI agents use tools

In Part 1, the reasoning loop chose the next step. Part 2 stored the state needed to resume it. The tool boundary turns that decision into an action and returns an observation to the loop. These five patterns make different trade-offs in token cost, flexibility, and enforcement.

Five AI agent tool modalities and their trade-offs

1. JSON tool calling: the baseline

The original pattern: you define tool schemas as JSON, the LLM emits structured function calls, your code executes them. It is well-understood and works fine for small toolsets.

# Traditional tool definition — each tool consumes ~550-1,400 tokens (Apideck benchmark)
tools = [
    {
        "name": "get_stock_price",
        "description": "Get the current stock price for a ticker symbol",
        "input_schema": {
            "type": "object",
            "properties": {
                "ticker": {"type": "string", "description": "Stock ticker (e.g., NVDA)"}
            },
            "required": ["ticker"]
        }
    }
]

With 5-10 tools, this is fine. The problem is scale: each tool definition costs 550-1,400 tokens. At 20 tools you are spending 15-25K tokens before the agent even starts reasoning.

2. MCP for shared integrations

The Model Context Protocol is the standard most vendors converged on. Anthropic donated it to the Linux Foundation under the Agentic AI Foundation (AAIF) in December 2025, co-founded with OpenAI and Block, and backed by Google, Microsoft, and AWS as platinum members. OpenAI added MCP support in its Responses API. There are now 10,000+ active MCP servers and 97M+ monthly SDK downloads.

MCP fits cross-vendor SaaS integration (Figma, Notion, Salesforce), services without CLI equivalents, and environments that need OAuth orchestration. Its value is a shared discovery and transport layer. Governance still depends on the server’s authentication, authorization, logging, and deployment controls.

The production story is messier than the headline numbers suggest.

The security surface is the first problem. The Vulnerable MCP Project tracks 50 vulnerabilities across MCP servers, 13 rated Critical, contributed by 32 security researchers. Attack classes span prompt injection, input validation failures, authentication gaps, and network security holes. The first real-world malicious MCP server appeared in September 2025: a package called postmark-mcp that BCC’d every outgoing email to an attacker’s address, affecting an estimated 300–500 organizations before discovery.

Tool poisoning is the attack class I worry about most. Invariant Labs demonstrated that poisoned MCP tools can exfiltrate data even when they are never invoked. The model just reading the tool’s metadata is enough to trigger the attack. MCPTox benchmarks testing 20 LLM agents against 45 real-world MCP servers found attack success rates as high as 72.8%.

Token overhead is the operational problem. One team running MCP servers for GitHub, Slack, and Sentry (~40 tools total) found 55,000 tokens of schema definitions injected before a user asks anything. Another reported 143,000 of 200,000 available tokens (72%) consumed by tool definitions alone.

Token Overhead Comparison

Anthropic reported an 85% schema reduction from its Tool Search Tool and a 96% reduction when loading three to five relevant tools for the evaluated tasks. Selective loading keeps the protocol surface while removing schemas the current request does not need.

3. Skills package expertise, not execution

Late 2025 brought the standardization of agent skills as an open format (launched October 2025, published as an open standard in December 2025). The distinction matters: tools provide capabilities (what agents can do), and skills provide expertise (what agents know about how to accomplish complex tasks).

The SKILL.md standard defines a skill as a markdown file with YAML frontmatter:

---
name: deploy
description: Deploy the application to production
argument-hint: "[environment]"
user-invocable: true
---
Deploy the application to the $0 environment (default: staging).
Steps:
1. Run the test suite
2. Build the production bundle
3. Deploy using the deploy script
4. Verify the deployment health check

Skills use progressive disclosure. About 100 tokens of metadata load at startup; full instructions load only when the skill is active. By contrast, around 40 MCP tools can consume roughly 55,000 tokens before reasoning begins. By March 2026, Claude Code, OpenAI Codex CLI, Cursor, GitHub Copilot, Gemini CLI, Goose, Windsurf, and Roo Code had adopted the format. Victor Dibia describes this shift toward code-driven agent actions.

Use skills for domain knowledge, multi-step procedures, and recurring work such as database migrations or payment integrations. They fit tasks where the agent needs instructions for how to use an existing capability.

4. CLI and shell tools

CLI interfaces can be much cheaper in context when the model already knows the command. Scalekit reported a 4-32x token difference between its CLI and MCP paths across 75 runs. That case study measures its tools and tasks; it does not replace a comparison on your own manifests and command output.

Widely documented commands such as git, docker, kubectl, gh, curl, and jq often need little introductory schema text. Less common or internal CLIs still need discoverable help, examples, and stable machine-readable output.

Ugo Enyioha’s guide “Writing CLI Tools That AI Agents Actually Want to Use” codified eight design rules:

  1. Structured output is mandatory — support --json
  2. Exit codes are control flow — use distinct codes for different error types
  3. Commands should be idempotent
  4. Self-documenting --help with realistic examples
  5. Design for composability--quiet for bare values, stdin support
  6. Provide --dry-run and --yes flags
  7. Support version introspection
  8. Handle auth via environment variables

The trade-offs are real. CLI lacks MCP’s type safety, built-in OAuth orchestration, tool discovery, and audit trail. The pattern I see most teams converge on: CLI as default for development and local operations, MCP for external service integration and enterprise governance.

5. Code execution for multi-step work

This is the change in agent tooling I find most consequential. Instead of the LLM emitting structured JSON to invoke predefined functions one at a time, the agent writes a complete Python or bash script that calls multiple tools, processes results with loops and conditionals, and returns only final summaries to the model context.

Anthropic formalized this with Programmatic Tool Calling (PTC), now GA in the Claude API. The academic foundation is the CodeAct paper (Wang et al., ICML 2024), which tested across 17 LLMs and found code actions achieved up to 20% higher task success rates and 30% fewer steps than JSON alternatives.

Code Execution Flow

Three first-party case studies show where the pattern can help. Treat them as vendor evidence and re-run the comparison on your own tasks.

Here is the pattern from Anthropic’s PTC documentation. Traditional tool calling for expense analysis requires 20+ separate inference passes, one per team member, with all intermediate data flowing through context. A workflow consuming ~150,000 tokens with direct tool calling was reimplemented for ~2,000 tokens, a 98.7% reduction. With code execution, the agent writes a single script:

# Agent generates this code, executes in sandbox
import json
members = get_team_members("engineering")
over_budget = []
for m in members:
    expenses = get_expenses(m["id"], "Q3")
    total = sum(e["amount"] for e in expenses)
    if total > 5000:
        custom = get_custom_budget(m["id"])
        limit = custom["limit"] if custom else 5000
        if total > limit:
            over_budget.append({"name": m["name"], "spent": total, "limit": limit})
# Only this final summary returns to the LLM context
print(json.dumps(over_budget))

The LLM sees only the final JSON summary, not the thousands of expense line items processed in the sandbox. Token efficiency, native composability (loops and conditionals come for free), real error handling (try/except instead of natural-language reasoning about errors), and privacy (sensitive data stays in the sandbox) all improve at once.

When JSON tool calling still makes sense: single atomic operations, environments without sandboxing infrastructure, smaller models with weak code generation, or audit requirements that need every individual tool invocation logged.


AI agent tool-calling comparison table

DimensionJSON Tool CallingMCPSkills (SKILL.md)CLI/BashCode Execution (PTC)
Best forSimple, single actionsCross-vendor SaaSDomain expertiseDev workflows, local opsMulti-step orchestration
Token overheadMedium (schemas per request)Very high (550-1,400/tool)Very low (~100 tokens)Near zeroLow (2 meta-tools)
Task evidenceBaseline in cited studiesDepends on server and taskN/A (expertise layer)Measure on CLI-native tasksCodeAct reports up to +20%
ComposabilityLow (sequential)Low (sequential)High (procedural knowledge)High (pipes, chaining)Very high (native code)
Security surfaceModerateHigh (50+ CVEs)Low (prompt-based)High (shell access)High (needs sandboxing)
Setup complexityLowMedium (server deployment)Very low (markdown)Very low (existing CLIs)Medium (sandbox infra)
Latency per action1 inference/call1 inference + transport0 (context injection)1 inference/call1 pass for N calls
DebuggingGood (structured I/O)Moderate (transport layer)Good (readable markdown)Excellent (visible)Good (readable code)

Composability here means how easily multiple operations chain into a larger workflow. Low means each tool call usually requires another model round trip; high means the interface can pass intermediate results through pipes, variables, or procedural steps. Token and task-success cells summarize the cited examples, not one controlled benchmark across all five columns.


The Agent-Computer Interface (ACI) for AI agent tools

The term “Agent-Computer Interface” (ACI) was coined by John Yang, Carlos E. Jimenez, and colleagues at Princeton in their SWE-agent paper (NeurIPS 2024). The idea is straightforward: just as humans benefit from well-designed interfaces (HCI), LM agents are “a new category of end users with their own needs and abilities, and would benefit from specially-built interfaces.”

The ablation results backed this up. SWE-agent with its full ACI achieved 18.0% on SWE-bench Lite versus 7.3% with only a standard Linux shell, a 10.7 percentage point improvement from interface design alone. The linting guardrail by itself accounted for 3 percentage points: 51.7% of agent edits had at least one error caught by the linter before it could propagate.

ACI Design Principles

Anthropic adopted ACI as a foundational concept in their “Building Effective Agents” guide, listing it as one of three core principles: “Carefully craft your agent-computer interface through thorough tool documentation and testing.” Their practical guidance: “One rule of thumb is to think about how much effort goes into human-computer interfaces, and plan to invest just as much effort in creating good agent-computer interfaces.”

Four ACI principles in practice

1. Actions should be simple and easy to understand. The most common mistake is wrapping API endpoints one-to-one. Instead of list_users, list_events, create_event, implement schedule_event that finds availability and schedules in one call. Instead of read_logs, implement search_logs that returns only the relevant lines with context.

2. Actions should be compact and efficient. Consolidate important operations into as few actions as possible. In the Market Analyst Agent, I combine price fetching with basic metrics into a single get_stock_snapshot tool rather than requiring separate calls for price, volume, market cap, and PE ratio.

3. Environment feedback should be informative but concise. Avoid returning raw HTML or full API payloads. Resolve cryptic IDs to semantic names. Anthropic’s testing showed that adding a response_format enum letting agents request concise (~72 tokens) or detailed (~206 tokens) responses (a 3x token cost difference) noticeably improved real-world performance.

4. Guardrails should mitigate error propagation. Automatic error detection helps agents recognize and correct mistakes quickly. In SWE-agent, a custom file editor with integrated linting automatically rejects syntax errors, catching 51.7% of agent edit errors before they could compound. I apply the same principle in the Market Analyst Agent by validating tool arguments with Pydantic schemas before execution:

from pydantic import BaseModel, Field, field_validator

class StockQuery(BaseModel):
    """Validated input for stock queries.

    Pydantic catches malformed tickers before the API call,
    preventing error propagation through the reasoning loop.
    """
    ticker: str = Field(description="Stock ticker symbol (e.g., NVDA)")
    period: str = Field(default="1mo", description="Time period: 1d, 5d, 1mo, 3mo, 1y")

    @field_validator("ticker")
    @classmethod
    def validate_ticker(cls, v: str) -> str:
        v = v.upper().strip()
        if not v.isalpha() or len(v) > 5:
            raise ValueError(f"Invalid ticker format: {v}")
        return v

    @field_validator("period")
    @classmethod
    def validate_period(cls, v: str) -> str:
        valid = {"1d", "5d", "1mo", "3mo", "6mo", "1y", "5y"}
        if v not in valid:
            raise ValueError(f"Invalid period: {v}. Must be one of {valid}")
        return v

AI agent tool design patterns that work

Anthropic’s “Writing effective tools for agents” guide frames tools as “a new kind of software reflecting a contract between deterministic systems and non-deterministic agents.” Here are the patterns that have crystallized from production experience.

Treat tool descriptions as prompt engineering

Descriptions should run 3-4+ sentences minimum, covering when to use the tool, required versus optional parameters, output format, and edge cases. Namespacing with prefixes (asana_search, jira_search) has “non-trivial effects” on tool selection accuracy. In Anthropic’s experiments, Claude-optimized tool descriptions outperformed expert human-written ones on evaluation benchmarks.

# Bad: vague, no context for when to use
tools = [{
    "name": "search",
    "description": "Search for items",
}]

# Good: specific, with input examples and edge cases
tools = [{
    "name": "stock_search_news",
    "description": (
        "Search for recent news articles about a specific stock or company. "
        "Use this tool when the user asks about recent events, earnings, "
        "announcements, or market-moving news for a specific ticker. "
        "Returns up to 10 articles sorted by relevance. "
        "For broad market news (not ticker-specific), use market_overview instead."
    ),
    "input_schema": {
        "type": "object",
        "properties": {
            "query": {
                "type": "string",
                "description": "Search query. Examples: 'NVDA earnings Q3 2025', 'Tesla delivery numbers'"
            },
            "max_results": {
                "type": "integer",
                "description": "Max articles to return (1-10, default 5)",
                "default": 5
            }
        },
        "required": ["query"]
    }
}]

Internal testing showed that input examples (input_examples field) substantially improved accuracy on complex parameter handling.

Return high-signal, machine-readable output

Avoid low-level identifiers (uuid, mime_type). Resolve cryptic IDs to semantic names. Structure the response so the agent can reason about it without parsing boilerplate:

# Bad: raw API response dumped to agent
def get_stock_price(ticker: str) -> dict:
    response = api.get(f"/v1/quotes/{ticker}")
    return response.json()  # 500+ tokens of nested JSON

# Good: high-signal summary the agent can immediately reason about
def get_stock_price(ticker: str) -> dict:
    data = api.get(f"/v1/quotes/{ticker}").json()
    return {
        "ticker": ticker,
        "price": data["regularMarketPrice"],
        "change_pct": round(data["regularMarketChangePercent"], 2),
        "volume": data["regularMarketVolume"],
        "market_cap_b": round(data["marketCap"] / 1e9, 1),
        "pe_ratio": data.get("trailingPE"),
        "summary": f"{ticker} at ${data['regularMarketPrice']:.2f} "
                   f"({'up' if data['regularMarketChangePercent'] > 0 else 'down'} "
                   f"{abs(data['regularMarketChangePercent']):.1f}%)"
    }

Return errors the loop can act on

Use four separate mechanisms because they handle different failure classes:

  1. Retry with exponential backoff for transient errors
  2. Model fallback chains for provider outages
  3. Error classification routing — transient errors retry, LLM-recoverable errors return to the agent with context, human-required errors escalate
  4. Checkpoint recovery for crash survival

The cited Anthropic guide supports clear tool errors and evaluation-driven tool design. It does not establish one universal failure-rate reduction, so measure recovery rate, retries, and escalation on your own task suite.

from tenacity import retry, stop_after_attempt, wait_exponential

@retry(
    stop=stop_after_attempt(3),
    wait=wait_exponential(multiplier=1, min=2, max=10),
)
def call_stock_api(ticker: str) -> dict:
    """Fetch stock data with automatic retry on transient failures.

    Layer 1: Exponential backoff handles rate limits and network blips.
    If all retries fail, the error propagates to the agent with
    enough context to decide whether to try a different approach.
    """
    response = httpx.get(
        f"https://api.example.com/v1/quotes/{ticker}",
        timeout=10.0,
    )
    response.raise_for_status()
    return response.json()

Applying the patterns to the Market Analyst Agent

The Market Analyst Agent from Part 1 makes the effect of the interface visible.

Tool consolidation

The Part 1 article shows the simplified 5-tool surface after this refactor. Before that cleanup, the original design had 10+ tools: get_stock_price, get_company_metrics, get_market_cap, get_pe_ratio, get_volume, and so on. Each was a thin wrapper around one API endpoint. The agent had to reason about which combination to call for every request.

I consolidated these into 5 high-level tools, following the ACI principle of compact, efficient actions:

Before (10+ tools)After (5 tools)Why
get_stock_price + get_company_metrics + get_pe_ratioget_stock_snapshotSingle call returns everything needed for basic analysis
get_price_history + get_volume_historyget_price_historyCombined with configurable period and indicators
search_news + search_press_releasessearch_newsUnified search with source filtering
search_competitors + get_sector_datasearch_competitorsReturns competitors with relative metrics
get_financials + get_balance_sheet + get_cash_flowget_financialsUnified with statement_type parameter

This cut the tool schema overhead substantially and made the agent’s tool selection more reliable, because there are fewer ambiguous choices to make.

Structured outputs for tool results

Every tool in the Market Analyst Agent returns a Pydantic-validated response. This applies the ACI guardrails principle at the tool boundary:

class StockSnapshot(BaseModel):
    """Structured tool response — the agent never sees raw API noise."""
    ticker: str
    price: float
    change_pct: float
    volume: int
    market_cap_b: float
    pe_ratio: float | None
    summary: str  # Human-readable one-liner for direct use in reports

class NewsResult(BaseModel):
    """Each news item is pre-processed for agent consumption."""
    headline: str
    source: str
    date: str
    relevance_score: float  # Pre-ranked so the agent doesn't waste tokens sorting
    key_points: list[str]  # Extracted by the tool, not the agent

The summary field is the one that matters most. It gives the agent a ready-to-use string that can go directly into a report without additional processing. The key_points in NewsResult are extracted server-side, saving the agent from spending inference tokens parsing article bodies.


Trade-offs and considerations

Beyond the modality-specific caveats above, a few cross-cutting concerns shape the choice:


Tool selection at larger scale

Three directions are worth tracking.

The first is tool RAG for scaling. As toolsets grow to hundreds or thousands, naive tool selection accuracy degrades to 13.62%. The RAG-MCP paper showed that applying retrieval-augmented generation to tool selection (indexing tool descriptions in a vector database and retrieving only relevant tools per query) reaches 43.13% accuracy, a 3.2x improvement, while cutting prompt tokens by ~50%.

The second is agents creating their own tools. The LATM framework (“LLMs As Tool Makers”) established a two-phase paradigm where a powerful LLM creates reusable Python functions and a lightweight LLM uses them. ToolMaker (ACL 2025) autonomously transforms GitHub repositories into LLM-compatible tools with an 80% success rate. The frontier is moving from tool use to tool creation, and then to tool library management.

The third is the A2A + MCP dual-protocol stack. Google’s Agent2Agent Protocol (A2A) addresses what MCP cannot: agent-to-agent communication. MCP handles agent-to-tool integration; A2A handles agent-to-agent discovery, negotiation, and task delegation. The combined formula: build with your framework, equip with MCP, communicate with A2A.


Key takeaways

  1. Choose the interface from the action: JSON calling for small typed operations, MCP for shared services, Skills for procedures, CLI for established commands, and sandboxed code for local composition.
  2. Keep benchmark conditions attached to the result. CodeAct, Anthropic, Vercel, Cloudflare, Apideck, and Scalekit measured different models, tasks, tools, and harnesses.
  3. ACI quality survives protocol changes. Clear actions, compact feedback, validation, and useful errors help every modality.
  4. Consolidate overlapping tools only when evaluations show that the smaller surface improves selection or task success.
  5. Security moves with execution power. Shell and code interfaces need sandboxing; MCP needs scoped identity and server policy; Skills remain instructions rather than enforcement.

The next layer is policy

Part 4, AI Agent Security in 2026, puts a policy check between a proposed tool call and execution. Part 5 then places the tool and its sandbox inside a recoverable runtime. Part 6 will return to the interface from the harness side: how traces, evaluators, retry rules, and acceptance checks turn tool feedback into a measured improvement loop.


References

Papers

Anthropic engineering

Industry case studies

Security

CLI design

Demo project


The complete Market Analyst Agent code, including the tool designs described in this post, is on GitHub.

Series: Engineering the Agentic Stack