The Unreasonable Effectiveness of CLIs > MCP
Why CLI Tools + Skills often still beat MCP

When a Google engineer released the Google Workspace CLI, the internet went crazy — finally, a Google Workspace surface for the agent era.
But wait, why a CLI tool, and not an MCP server? MCP is all the rage nowadays — seems like everyone and their dog is shipping MCP servers.
MCP, The Silver Bullet?
MCP is pretty great. It enables AI agents to surface all kinds of relevant context and take meaningful actions to accomplish your goals.
But the MCP paradigm has real issues. The internet is replete with this refrain in the security department: The “S” in MCP Stands for Security, and The Linux Foundation's Horror Stories That Will Change How You Ship MCP.
But beyond security, MCP has some real fundamental limitations as a paradigm in how it fits into the agentic workflow.
The Agent Loop
To understand the limitations of MCP as a paradigm for the agent, we have to understand where it fits in within the agent loop.
When a model's CoT decides it wants to make a tool call (including MCP tools) , it emits tokens indicating the tool name and the arguments it cooked up for it, e.g., Bash(rm -rf /) or mcp__atlassian__getJiraIssue(…). Permission system notwithstanding, the harness then executes that tool call.
Although these both execute as tool calls, Bash script behavior is runtime-mediated, while MCP call behavior is ultimately model-mediated.
There are two fundamental differences between these two styles of tool calls, you might say at the control plane layer, and then at the data plane layer.
The Control Plane Limitation
Fundamentally, the intent to make the tool call updateConfluencePage(…) with its specific args is determined entirely by the reasoning step, and fixed on inference return. So one turn / one inference = one tool call.
With a shell available, if the agent needs to call an API a thousand times (e.g., run 1K trials of something with some delay between each), the model can do that easily, it just emits a Bash tool request for:
for i in {0..999}; do
…
done
And if it wants # of iterations determined dynamically by the outcome of some other command, and to manipulate each sub-step's outputs (e.g, parse with jq) and conditional logic, no prob — it's got a Turing-complete runtime environment right within that single tool call.
AI models are really good at this: they frequently emit ad-hoc, multi-line scripts to execute complicated workflows efficiently right within a single turn.
AI agents can't interact with MCP in the same way.
With Bash scripts, the outcome of a single turn of reasoning may be to run a complicated ad-hoc script that composes many primitives (many API calls)
With MCP, the outcome of a single turn of reasoning can only ever be to execute one MCP call representing one API call or primitive.
An agent using MCP must execute 1K separate inference turns for the same result, each turn having to stay on track and duplicate input + reasoning + output tokens — bloating latency, context window, and cost by 1K× 💳
The Data Plane Limitation
While the control plane dictates the intent to take an action (and it being limited to one per turn of inference), the data plane dictates action content, inputs + output.
Let's say you want to tail some logs, search for a term, or read a doc.
With Bash scripts, agents naturally compose tools like
head,tail,grepto manipulate the actual output before it lands in their context.- They know what they're looking for, don't need pull in 4KiB (or worse, potentially unbounded) of chonky content into their context window.
MCP servers are often known for blowing up context windows because they spit out huge amounts of content that land right into the model's context without the chance for model-mediated manipulation or filtering.
- While theoretically MCP servers can design their APIs to implement pagination, search and filtering, and targeted field hydration, in practice most don't.
Likewise, on the producer side, let's say you have this workflow:
You're working on a design doc with an agent.
You have it do its work and write it up locally as a
.mdfile for you to review, and for it to make targeted edits withsedor other tools based on feedback.So every time it wants to edit a section of the doc (e.g., "Fix that typo"), the model doesn't need to re-inference the whole 6KiB doc — very slow, very expensive to essentially tell a model, "Attend to that huge doc in the previous turn, and make this tiny edit" over and over.
The agent pushes it to Confluence / Google Docs.
Others review it, give feedback.
You instruct the agent to make edits and amend it → go to step (2).
With MCP, each time you push (step 3), the agent must:
Read the local copy (all 6KiB) into context
Send it to the model to get it to infer what is essentially a verbatim copy of it, but framed inside a JSON object as an arg to the
putDocMCP toolThis is all extremely wasteful and inefficient in context and spend. And slow, often prone to model API timeouts for very large docs.
But with a CLI, the agent can easily construct commands to refer to file paths on disk if the CLI supports it, or if not, craft it with variable / command substitution:
gws docs documents batchUpdate \
--documentId="…" \
--requests="[
{
…
\"body\": {
\"content\": \"$(cat mylocaldoc.md)\"
}
}
]"
It never has to first pull the contents into main conversation context and send that to the model just to have it infer a JSON copy of it for the MCP request. Zero tokens are spent on reading the file and inferencing up a JSON MCP request.
It's extremely efficient to iterate quickly while making small, targeted edits and uploading frequently.
The Google Workspace CLI gws is a model example of a CLI that plays well with agents:
It doesn't ship a static list of commands, but calls Google's Discovery API to build the entire command surface dynamically at runtime.
- When Google Workspace updates its APIs,
gwsreflects it instantly.
- When Google Workspace updates its APIs,
The CLI interface is self-describing and self-documenting, allowing agents to introspect the schema so they can discover available APIs for themselves along with metadata on how to use those commands.
It ships with skills that teach agents how to use it.
It's A Skill Issue
MCP has several advantages over plain CLI tools.
One is the MCP automatically surfaces context upfront to the model about available tools it ships, and when and how to use them.
As a result of this instruction preloading, often the agent automatically knows when to use the MCP server, and which tool to use and how to use it, whereas with CLI tools, the agent has to fiddle around with
whichcommands to see if a CLI exists, and then--helpcommands to figure out how to use it, or to search the web for documentation.The downside is wasted context: every tool and instruction a MCP server exposes is pulled into the context whether it's ever used or not.
Much of the discoverability gap of CLI tools can be remediated with the right skills, which instruct the agent when to use them, and only if the model determines they're needed is the whole skill doc pulled into context, where it can teach the model how to use the CLI.
MCP Advantages
Besides discoverability and instruction, the MCP paradigm has a number of other advantages.
For example, despite MCP's reputation for wildly inconsistent and sometimes insecurely implemented authn / authz paradigms across implementations, it does offer the ability to centralize control over what external systems agents can interact with and what they can do in them.
The MCP Gateway is one pattern more orgs are adopting, which centralizes control: which the MCP servers are allowed, and providing centralized authn + authz, and centralized logging, something a bunch of disparate CLI tools generally can't do.
Conclusion
MCP remains extremely beneficial, with a lot of active development going on, and as ecosystems standardize around it, AI agents will get more and more capable.
But it's not the right paradigm for every task. For many workflows, the humble CLI is underrated and much better suited.
When using MCP, every tool call is an inference boundary. If the agent needs to look at N files, filter out certain lines, and update a DB, it must loop N individual turns of text generation. This introduces significant latency, token costs, and a N× chances for hallucination mid-loop.
With a script, the agent acts like an engineer: it writes a Bash / Python script that handles the workflow and data manipulation and curating the model-facing output, and model doesn’t have to "think" about the raw data mid-loop.
That's why when I need to develop a new connector for my agents to interact with, I often reach for developing a CLI first over an MCP server.




