Large language models are increasingly wired to tools: functions the runtime can call to fetch data or perform actions. Two popular patterns are Model Context Protocol (MCP) servers and plain command-line interfaces (CLIs). This article compares them for the common case of REST API access—listing resources, opening tickets, triggering deploys—and argues that a native, generated CLI is often the better integration surface.
If your API already publishes OpenAPI, you are choosing how that contract crosses the process boundary: as JSON-RPC messages to a long-lived adapter, or as argv + HTTP inside a binary you ship. That choice affects latency, memory, reliability, and how much context your model must carry on every turn.
The LLM Agent Tool-Use Problem
Agents need three things from a tool:
- Discovery — What can I invoke, with which arguments?
- Execution — How quickly and reliably does the call complete?
- Observability — Can I log, replay, and sandbox the call in standard ways?
REST APIs already have a machine-readable contract: OpenAPI. The design question is how that contract crosses the boundary into the agent runtime. MCP exposes tools over a protocol; a CLI exposes tools as subcommands and flags with structured stdout.
A separate but related pressure is context size. When the host injects large tool schemas into every completion request, you pay tokens whether or not the model invokes a tool. A thin SKILL.md plus on-demand --help lets the agent pull documentation when needed instead of pushing the entire surface area into the prompt up front. (Industry write-ups have measured steep overheads when many tools are registered; your mileage depends on host and model.)
How MCP Works
MCP typically runs a long-lived server process that speaks JSON-RPC over stdio or HTTP. The host (IDE, agent framework, or gateway):
- Starts or connects to the server.
- Negotiates capabilities and tool schemas (often large JSON blobs).
- For each model turn, may inject all tool definitions into the context.
- On a tool call, serializes arguments, waits for the server, and parses the response.
That indirection is powerful when you need sessions, streaming, or bidirectional communication. It is not free: process lifecycle, serialization, and context bloat all show up in latency and token usage.
How Native CLI Works
A generated CLI is a single binary. The agent (or a thin wrapper) runs:
./github repos get --owner octocat --repo Hello-World
The OS execs the program, connects to the API over HTTP, and prints JSON (or YAML, tables, etc.) to stdout. Discovery happens via --help, a SKILL.md file checked into the repo, or a small static manifest—not by re-sending full OpenAPI through the model on every request.
Performance Comparison
Numbers below are order-of-magnitude figures observed in typical desktop and CI environments; your stack will vary. They are useful for relative comparison, not benchmarking absolutes.
Cold start
- CLI: Spawning a small static binary is often on the order of ~5 ms of process overhead on a warm laptop kernel, plus network time.
- MCP: Starting a Node, Python, or Go MCP host plus interpreter startup, module loads, and JSON-RPC handshake often lands in ~200–500 ms before the first API byte—sometimes more if the server pulls dependencies at runtime.
For agents that issue dozens of short calls, cold start dominates wall time.
Memory
- CLI: The binary image is mapped read-only; resident set for a thin HTTP client is often ~5 MB scale.
- MCP: A language runtime plus framework holding schemas and connections commonly sits in ~50–100 MB per server, multiplied if you run several MCP services.
On developer laptops that is annoying; on shared agent workers it becomes scheduling and noisy-neighbor cost.
Latency per call
- CLI: After exec, cost is mostly HTTP RTT and server processing—~0 ms of extra protocol tax on top of the request you would have made anyway.
- MCP: Each tool invocation pays JSON-RPC encode/decode and IPC; expect ~10–50 ms of additional latency versus a direct HTTP client in the same process class, before counting any double serialization of large payloads.
Reliability: Fewer Moving Parts
A CLI failure mode is simple: nonzero exit, stderr message, truncated stdout. Operators already know how to retry, timeout, and capture logs for subprocesses.
MCP adds:
- Connection drops between host and server (some surveys report double-digit failure rates in real IDE integrations).
- Version skew between MCP client and server.
- Orphan processes when the parent editor crashes.
For stateless REST calls, that complexity buys little.
Security: No Long-Running Server
A CLI runs ephemerally with the environment variables and config files of that invocation. Secrets can be injected per call from a secret manager without keeping them in a server’s memory for hours.
An MCP server is long-lived. Compromise or misconfiguration exposes a persistent listener with whatever credentials the server holds. Hardening is absolutely possible—but it is more surface area than execve + HTTPS.
Developer Experience: Shells, Languages, and Pipes
CLIs are universal:
- Any language can
execthem. - Humans can run the same commands in bash, zsh, or PowerShell.
- Output is pipe-friendly (
| jq,| rg,| tee).
MCP shines inside integrated hosts that already speak the protocol. Outside that ecosystem, you write adapters.
When MCP Is the Better Fit
MCP is compelling when:
- You need stateful sessions (multi-step negotiation, open file handles, DB connections).
- The model benefits from streaming partial results back through the protocol.
- You want bidirectional notifications (server pushes updates to the host).
- A vendor ships only an MCP adapter and you do not control the API surface.
None of that is required to GET a JSON resource from a documented REST endpoint.
Conclusion: For REST API Access, CLI Wins
For OpenAPI-backed REST, a generated native CLI offers:
- Lower cold-start and per-call overhead
- Smaller memory footprint
- Simpler operational and mental models
- Native composability with the Unix-style ecosystem
MCP remains valuable for rich, stateful tool servers. But defaulting to MCP for every HTTP API inflates tokens, latency, and failure modes without improving the core request–response shape. OnlyCLI encodes the alternative: one spec, one binary, direct HTTP—plus a compact SKILL.md so agents learn the tool surface once, not on every turn.
Practical adoption checklist
When you evaluate an integration path for agents, ask:
- Is the tool stateless? If each call maps to an HTTP request/response, a CLI is a natural fit.
- Do you already distribute binaries or containers? If yes, adding one more small static binary is operationally familiar.
- Do you need streaming or server push? If yes, budget for MCP or another persistent channel.
- Can you tolerate another moving part in the IDE? If not, prefer subprocess-style tools with explicit argv and exit codes.
Learn how to generate your first CLI in minutes in our getting started guide.