ARCANADA
All Posts
Blog August 10, 2026

MCP vs CLI: Why I Think We Are Arguing at the Wrong Level of Abstraction

A comparison of CLI and MCP as different execution mechanisms for an AI agent.
CLI and MCP can be different ways to provide the same capability.
MP3
Audio
0:00 / 0:00

Yesterday I came across a Reddit post about MCP vs CLI.

The author raises a question that is coming up more and more often among AI-agent developers: do we actually need MCP if an ordinary CLI turns out to be cheaper, faster, and more reliable for many tasks?

The topic directly overlaps with what I am working on in Arcanada: the architecture of autonomous agents, the technology of managing meaning, and the idea of a Knowledge Contract.

The longer I looked at the argument in the comments, the more I came to the conclusion that MCP vs CLI may be the wrong question altogether. Both camps are right in important ways; the discussion is simply happening one abstraction level below where the problem should ultimately be solved.

Let us start with the argument itself.

Where “CLI is better than MCP” came from

The post author points to a Scalekit benchmark: 75 test runs comparing an AI agent working through MCP and CLI.

The results were fairly harsh for MCP.

Depending on the task, CLI required several times fewer tokens, and sometimes several dozen times fewer.

The simplest example is telling: determining the language of a repository required 1,365 tokens through CLI and 44,026 through MCP.

In a separate Scalekit reliability check with 25 runs per approach, CLI completed 25 out of 25 runs (100%), while MCP completed 18 out of 25 (72%).

And that leads to a perfectly reasonable question:

why are we building thousands of MCP servers at all?

Are we repeating the familiar pattern in which an entire industry rushes to adopt a new standard simply because it is fashionable?

One comment in the discussion states it clearly:

MCP is liked by people who build tools. CLI is liked by people who use them.

That is fairly accurate.

Why CLI fits agents so well

When taking apart the arguments on both sides, it is important to remember that CLI is not just another interface for calling a function.

With a direct tool call, the agent receives the tool's response immediately. CLI inserts a shell and programmable data processing between the model and the result.

Diagram of two agent data paths: a direct tool call returns the full response, while CLI sends data through a shell and filtering before producing a selected result. The full description follows in the text.
CLI adds programmable processing between the agent and its context.

The upper path shows a direct tool call and a full response. In the lower path, the agent starts a shell, CLI obtains the data, intermediate programs filter it, and only the selected result enters the context.

A programmable computing environment appears between the model and the tool.

This lets the system reduce the response before it reaches the model's context.

Suppose an agent requests several megabytes of logs.

If an ordinary tool returns all of that to the context, the model has to somehow read and process a huge response.

Through CLI, the agent can instead do this:

deployment list --json \
| jq '.[] | select(.status == "failed") | {id, reason}' \
> /tmp/failed.json

and read only the result.

It can use:

  • grep
  • jq
  • awk
  • sed
  • sort
  • head
  • SQL
  • Python
  • pipes
  • files

So the AI is not merely calling a tool.

It is programming the intermediate processing of information.

That is one reason, in my view, coding agents feel so comfortable in a terminal.

There is a good comment under the post:

CLI makes the agent more capable.

At first the sentence sounds a little grand.

But if you think about it, it makes sense.

CLI does not give the agent a catalog of functions.

It gives the agent a small computer.

But MCP has a very strong argument too

Now imagine a different situation.

Your AI agent is not running on your server and is not working with your Git.

It is acting on behalf of thousands of customers.

It connects to:

  • GitHub
  • Gmail
  • Slack
  • Salesforce
  • corporate databases
  • internal SaaS systems

That creates a completely different set of problems.

Which user is the agent acting as right now?

What permissions does that user have?

Which organization do they belong to?

Which credentials should be used?

Is the agent allowed only to read information, or can it change it too?

How do you revoke access?

How do you later establish who performed an action and why?

When an agent acts on behalf of other users, simply giving it a shell conflicts with authentication, authorization, tenant isolation, and audit requirements.

One comment in the discussion described the boundary well:

CLI works beautifully for personal tools and internal automation.

But when an agent acts on behalf of other users, authentication, authorization, tenant isolation, and audit appear.

And MCP really does solve a different problem there.

So are both camps right?

To a large extent, yes.

CLI is good as an execution mechanism.

MCP is good as an integration mechanism.

That is why I liked another comment:

We use both. The core product is a full CLI; remote access and A2A sessions use MCP.

That is much closer to what I am designing in Arcanada now.

Even the choice “CLI or MCP?” is too low-level for the agent itself.

Why should the agent think about this at all?

This connects to the subject I have been working on for the last few months.

One problem with modern AI agents is that we keep placing almost all of the system's complexity onto the model.

The model receives a task, a prompt, RAG, 150 tools, 20 MCP servers, permissions, documentation, and additional rules, and then has to work out the infrastructure for itself.

Then we are surprised when the context grows to hundreds of thousands of tokens and the agent starts getting confused.

I am increasingly convinced of the opposite model:

the more a system can determine before the agent starts, the less of that infrastructure complexity the agent itself should have to see.

That is where one of the ideas I am working on in Arcanada came from: the Knowledge Contract.

What is a Knowledge Contract?

I will not reveal the entire structure here yet; it will be the subject of a separate publication.

The basic structure is this.

The agent should not receive only:

“Here is a task. Figure it out.”

Before execution, the task passes through a separate Resolver Pipeline.

The system tries to determine which meanings the agent needs in order to do this particular piece of work.

The result is a structured Knowledge Contract.

In the current model it includes:

Task, Role, Skills, Blueprints, Constraints, and Success Criteria.

For example, suppose the task is:

Determine why a production deployment failed and prepare a fix.

The system might determine:

Role: Software Engineer

  • Skills: debugging, deployment analysis, testing
  • Blueprints: production incident investigation; safe code modification; maker-checker validation
  • Constraints: production read-only; deployment only after approval
  • Success Criteria: root cause found; fix prepared; tests pass

So the agent receives more than a prompt.

It receives a contract for the meaning and execution of the task.

This connects to my broader task of managing meaning.

We are not simply trying to put more information into the context.

We are trying to determine:

which information, role, skill, behavior pattern, and constraint are needed to solve this particular task.

Reddit suggested another element

While taking apart the MCP vs CLI argument, I realized that the Knowledge Contract probably needs one more fundamental dimension.

Capabilities.

In other words, the abilities the agent needs in order to complete the task.

For our example, this might look like:

Capabilities: repository.read, repository.diff, logs.query, deployment.status, tests.run

The Knowledge Contract lists capabilities, but not whether they will be provided through MCP or CLI. The Capability Resolver decides that.

A capability is “what”; the execution mechanism is “how”

Suppose the agent needs the capability repository.diff. The agent should not have to choose the protocol itself: the Capability Resolver creates a binding between the capability, its executor, and its access mechanism.

Table mapping four capabilities to executors and execution mechanisms: repository.diff to Git adapter and CLI, customer.crm.read to CRM service and MCP with OAuth, deployment.status to Deployment service and an internal API, and research.deep to Research Agent through A2A delegation. The full description follows in the text.
The Capability Resolver maps each required capability to an executor and an execution mechanism.

Each row is an independent mapping, not a sequence of calls. OAuth defines access to the MCP service, while the Research Agent remains the executor and A2A handles delegation between agents.

At the reasoning level, the agent works with the required capabilities:

  • repository.diff
  • customer.crm.read
  • deployment.status
  • research.deep

It does not need to know whether the system provides them through CLI, MCP, an API, or another agent.

That is the abstraction boundary I consider correct.

Agents should think in actions, not protocols

Imagine a person.

When a programmer thinks:

I need to inspect the changes in a repository,

they do not reason at the level of Linux kernel system calls.

When we open a file, we do not think about SSD sectors.

When we run an SQL query, we do not think about B-tree pages.

Good architecture keeps creating boundaries like these.

Why did we suddenly decide that an AI agent should understand the entire infrastructure?

I think an agent should think something like this:

I need to obtain the deployment logs.

Not:

Now I need to find MCP server number 17, choose tool number 43, study its JSON Schema, and form the correct payload.

And not:

Now I need to remember the exact CLI command for a particular vendor.

That is infrastructure.

It is not reasoning.

Capability Resolver

The Arcanada architecture uses the Capability Resolver to connect a task and its Knowledge Contract to a concrete execution plan.

Capability Resolver route map: a Task becomes a Knowledge Contract and execution plan, then four independent routes use CLI, MCP, API, or A2A and return observations to Validation and Result. The full description follows in the text.
A capability may run locally, through a remote service, through an internal API, or via another agent.

The Knowledge Contract records what the agent needs to know and do. The Resolver decides who will execute each capability, by which mechanism, and with which permissions. These routes are alternative bindings and converge only after observations are produced.

If a task needs three capabilities, the agent does not need to know that the system has three hundred other tools.

It receives only repository.diff, logs.query, and tests.run.

It is no longer only about saving tokens

Context-window cost is only one aspect of the problem.

The consequences go beyond context-window cost.

The smaller the capability surface an agent receives, the smaller the:

  • token cost;
  • chance of choosing the wrong tool;
  • number of possible errors;
  • attack surface;
  • permission set;
  • reasoning complexity.

So the Capability Resolver becomes, at the same time:

a context optimizer, an execution router, and part of the security architecture.

The Resolver therefore reduces the capability surface, routes execution, and participates in the security architecture.

There is a third path: Code Mode

The Reddit comments contain a strong argument in favor of CLI:

CLI lets you send large output to a file, filter it, process it with other programs, and show it to the model only afterward.

Traditional tool calls do this much less effectively.

There is another approach: Code Mode. Instead of several sequential exchanges between the model and tools, the agent performs intermediate processing in one program:

const deployments = await deployment.list();
const failed = deployments
  .filter(x => x.status === "failed")
  .map(x => ({
    id: x.id,
    reason: x.failureReason,
  }));
return failed;

It runs inside a sandbox.

The model receives only the result.

Code Mode keeps the main advantage of CLI — programmable intermediate computation — and separates it from a particular shell interface. That is why I consider Code Execution Runtime part of this layer in Arcanada.

Sometimes a capability is not a tool at all

A capability may also mean delegating work to another agent.

Suppose the agent understands:

I need deep research on this hypothesis.

Why must it call a tool?

For example, the capability research.deep may mean delegating work to a Research Agent through A2A. A2A is not the executor; it transfers the work to another autonomous executor.

CLI, MCP, API, and A2A do not have to compete. They support different ways of executing capabilities.

What level should the agent operate at?

For some time now I have been trying to move one level above the traditional conversation:

Model + Prompt + Tools

I am not satisfied with an approach where agent design starts with choosing a harness or listing tools.

First, we need to define the meaning of the task and the conditions required to execute it.

After that, the system chooses the agent model, skills, knowledge, tools, and execution mechanism. That is why I am working on the Knowledge Contract: MCP vs CLI is a clear example of why this layer is needed.

The system first identifies the required capability and then chooses how to provide it. That distinction matters to me.

What is being designed in Arcanada now

In Arcanada and the projects connected to it, I am currently designing and gradually implementing something like this:

Arcanada architecture diagram with five layers: Task Intake, a Knowledge Contract containing Role, Skills, Blueprints, Constraints, Success Criteria, and Required Capabilities, Orchestration with Capability Resolver and Planning, Execution with CLI, MCP, API, Code, and A2A adapters, and Validation with observations and Result. The full description follows in the text.
In Arcanada, protocols are interchangeable adapters inside the execution layer, not separate reasoning stages.

The diagram separates five layers. Task Intake formulates the task and performs Semantic Analysis. The Knowledge Contract stores parallel fields: role, skills, blueprints, constraints, success criteria, and required capabilities. Orchestration then connects the Capability Resolver with Planning.

At the Execution layer, the runtime can use CLI, MCP, API, Code, or A2A adapters. After execution, the system sends observations to Validation and produces a Result.

Different parts of this architecture are distributed across several Arcanada components.

  • Tasks, their state, and their lifecycle are managed separately.
  • Knowledge and blueprints are stored separately.
  • Context selection is a separate layer.
  • Execution and result control are separated.

I do not want to build one giant “smart agent.”

I am trying to move as much complexity as possible out of the agent's head and into the architecture around it.

So, MCP or CLI after all?

After reading the Reddit discussion, my answer is:

CLI is an excellent execution mechanism.

MCP is an excellent candidate for a standardized integration layer.

APIs and SDKs are not going away.

Code Mode may become one of the best ways to compose operations.

A2A is needed for interaction between autonomous executors.

There is no need to choose one winner.

In Arcanada I am moving toward a hybrid model:

The Knowledge Contract records capabilities, and the Capability Resolver chooses the best available Execution Mechanism.

Today a capability may run through CLI.

Tomorrow the same contract may use MCP.

In a year, a better protocol may appear — and we will change the adapter.

The agent will not care.

MCP vs CLI is an implementation argument

The question above implementation is: what should remain stable when the protocol changes?

My current answer is the meaning of the task, the Knowledge Contract, and the required capabilities. CLI, MCP, API, Code Mode, and A2A are ways to turn that meaning into action.

Perhaps in a few years we will stop discussing which protocol an agent uses to access a particular tool.

Just as an application developer rarely thinks today about how an SSD controller writes a particular data block.

If the architectural boundary is chosen correctly, this complexity should disappear from the upper level.

That is the boundary I am trying to build in Arcanada.