All posts
AI Engineering 21 July 2026 2 views

MCP vs CLI

The same GitHub tasks cost up to 32x more tokens through an MCP server than through the gh CLI — and failed more often. When a tool server earns its context budget, and when it doesn't.

Introduction

Last year every team I know raced to build an MCP server. Thousands of them shipped. Then people started measuring, and the numbers are not flattering.

This lesson is about a habit worth breaking: reaching for a tool server by default, when the model already knows a perfectly good command-line tool.

What you will learn

By the end of this lesson you will be able to:

  • Explain where an MCP server's token cost actually comes from
  • Decide between a tool server and a CLI for a given integration
  • Know the cases where MCP genuinely earns its place

The measurement

Scalekit ran the same GitHub tasks two ways: once through the official GitHub MCP server, once through gh, GitHub's ordinary command-line tool. Same model, same tasks, 75 trials.

Ask "what language and licence is this repo?" and you get an identical answer either way. The cost is not identical:

Task via gh CLI via MCP server
Repo language + licence 1,365 tokens 44,026 tokens
Fetch pull request details 1,648 tokens 32,279 tokens

Across every task they tried, MCP cost 4× to 32× more tokens for the same result.

Why the gap is so wide

An MCP server has to describe itself before it can be useful. Every tool it exposes ships a name, a description and a JSON schema, and all of that enters the context window before the agent does any work:

{
  "name": "github_get_pull_request",
  "description": "Get details of a specific pull request in a GitHub repository",
  "inputSchema": {
    "type": "object",
    "properties": {
      "owner":      { "type": "string", "description": "Repository owner" },
      "repo":       { "type": "string", "description": "Repository name" },
      "pullNumber": { "type": "number", "description": "Pull request number" }
    },
    "required": ["owner", "repo", "pullNumber"]
  }
}

Now multiply that by every tool on the server. A GitHub server exposing seventy operations spends a large share of the window explaining itself before answering anything.

The CLI pays none of that. Models have read millions of shell scripts and man pages, so the interface is already in the weights:

gh pr view 4821 --json title,state,reviews,files

No schema, no registration, no preamble. The model knows what gh is the same way it knows what git is.

Anthropic published the workaround for their own protocol

This is the part worth sitting with. Anthropic — who created MCP — published an engineering piece showing a task that consumed 150,000 tokens through direct tool calls, and brought it down to 2,000 by having the agent write code instead. A 98.7% reduction.

The shift is from many round trips:

list_pull_requests  →  get_pull_request  →  get_reviews  →  get_files  →  …

to one piece of code that does the whole job and returns only the answer:

// One execution. Only the result crosses back into context.
const prs = await gh('pr list --json number,title,updatedAt --limit 50');

const stale = prs
  .filter((pr) => daysSince(pr.updatedAt) > 14)
  .map((pr) => `#${pr.number} ${pr.title}`);

return stale;

Fifty pull requests were fetched. Perhaps three lines come back. The intermediate data never enters the window at all.

What I actually do

I run a lot of automation through agents — Supabase, Vercel, Stripe, Gmail, Google Calendar. Every one of those integrations goes through a CLI or a direct REST call. The MCP connectors are deliberately switched off.

That was not ideological. It came from watching context fill up with tool definitions for services I was touching once a session. Now each integration is a documented skill: a short markdown file explaining which endpoint to call and which environment variable holds the token. The agent reads the file when it needs it and ignores it otherwise.

The difference in cost and latency is not subtle.


When MCP is still right

This is not an argument that MCP is dead. It earns its place when:

  • No CLI exists. An internal service with only an HTTP API is a fine reason to build a server.
  • The consumer is not your terminal. MCP is a protocol, and a desktop client that cannot shell out needs one.
  • Access must be brokered. A server is a real boundary — it can hold credentials, enforce scopes and audit calls in a way that handing an agent a shell does not.
  • The tool surface is small and used constantly. Three tools called on every turn amortise their schema cost immediately. Seventy tools called once do not.

The failure mode is not MCP. It is reaching for MCP by default.


Lesson summary

MCP server CLI
Schemas load before any work happens Interface already known to the model
Cost scales with the tools exposed Cost scales with the command you run
4×–32× more tokens in the benchmark Baseline
18 of 25 runs completed 25 of 25
Right when no CLI exists, or access must be brokered Right when a documented CLI exists

The most important point:

Give an agent a shell and a few lines of documentation before you give it a protocol. The model has already read the manual.

Try this before the next lesson

Take one MCP server you have connected and count the tools it exposes. Then ask how many you actually invoked this week.

If the answer is one or two, you are paying a full schema tax for a rounding error of value.

Built with by Hatem's AI Agents