Skip to content

When to use MCP instead of CLI for AI tools

Purpose

I’ve been watching the debate unfold on Reddit: “CLI is all you need? Do we really need MCPs?” The question comes from a good place - CLI tools work great for most tasks. But I think the debate misses the point. MCP isn’t trying to replace CLI. They solve different problems.

In this post, I’ll show you when MCP makes more sense than CLI, and why the stateful server architecture matters for certain use cases.

What I found

When I first started working with AI tools, I used CLI wrappers for everything. It made sense - every AI assistant can run shell commands, so why build anything more complex?

Then I hit problems. I tried to build an automated design workflow with Figma. The issue? Figma has no CLI. I tried using their REST API through a Python script, but every request had to re-authenticate, re-fetch the document structure, and re-parse everything. It worked, but it was slow and clunky.

I also tried browser automation with Playwright. Same problem - each test run started from scratch. I couldn’t maintain browser state between operations, which meant I couldn’t build multi-step workflows efficiently.

These experiences led me to MCP. The key difference I found is simple but important: CLI is stateless, MCP is stateful.

The CLI pattern

Let me show you how CLI tools work:

┌─────────┐ ┌─────────┐ ┌─────────┐
│ Command │───→│ Process │───→│ Output │
└─────────┘ └─────────┘ └─────────┘
Dies

Every CLI command starts fresh. When you run git status, it doesn’t know what git add just did. Each command is independent.

CLI Example
# Command 1: Add file
git add myfile.txt
# Command 2: Check status (no memory of Command 1)
git status

This is perfect for simple operations. But it has a problem when you need to maintain context. You have to pass all state in every command, which can lead to context window bloat when working with AI.

The MCP pattern

MCP works differently:

┌─────────┐ ┌─────────────┐
│ MCP │◄────────────►│ State Store │
│ Server │ └─────────────┘
└─────────┘ ▲
│ │
└────────────────────────┘
(Persistent)

The MCP server maintains state between requests. When you connect to a Figma MCP server, you open a design document once. All subsequent operations reference that same document by ID. You don’t need to re-send the entire document structure.

This is the key difference: reference-based responses vs full data transmission.

When MCP works better

I found four situations where MCP clearly beats CLI:

1. Visual design tools (Figma)

Figma has no CLI equivalent. When I tried to build automated design updates, I had two options:

  • Use the REST API with fresh authentication every request
  • Build a custom server that maintains the Figma connection

The REST API approach worked like this:

REST API approach
# Every request needs full context
def update_design(api_key, file_key, node_id, new_text):
# Authenticate
headers = {"X-Figma-Token": api_key}
# Get the whole file
file = requests.get(
f"https://api.figma.com/v1/files/{file_key}",
headers=headers
)
# Find the node
# ... parsing code ...
# Update the node
# ... update code ...
# Send the update
requests.patch(
f"https://api.figma.com/v1/files/{file_key}/nodes/{node_id}",
headers=headers,
json={"changes": changes}
)

The problem: every request re-authenticates and re-fetches data. With an MCP server:

MCP approach
# Connect once, reuse state
async with MCPClient("figma-server") as client:
# Open file once
file_id = await client.open_file("design.fig")
# Multiple operations share the connection
await client.update_text(file_id, "title", "New Title")
await client.update_color(file_id, "button", "#FF0000")
await client.export(file_id, "png")

The server maintains the Figma connection. Each operation references the file ID, not the full file data.

2. Browser automation (Playwright)

I hit this issue when building automated tests. With Playwright CLI:

CLI approach
# Each test run starts fresh
npx playwright test login.spec.ts
# This test knows nothing about the previous one
npx playwright test dashboard.spec.ts

The tests work, but they can’t share browser state. If you want to log in once and run multiple tests, you need custom setup code.

With a Playwright MCP server:

MCP approach
async with MCPClient("playwright-server") as client:
# Start browser once
browser = await client.start_browser()
# Login once
await client.goto(browser, "https://example.com/login")
await client.fill(browser, "#username", "[email protected]")
await client.fill(browser, "#password", "password123")
await client.click(browser, "#login-button")
# Now run multiple tests with same session
await client.check_element(browser, "#dashboard")
await client.check_element(browser, "#profile")
await client.screenshot(browser, "dashboard.png")

The browser stays open. All tests share the authenticated session.

3. WebSocket real-time communication

Some tools need real-time updates. CLI can’t handle this well - it’s request/response only. MCP supports WebSocket connections:

AI Assistant ◄───────────► MCP Server
│ │
│ (Persistent WebSocket)│
└─────────────────────────┘
← Real-time updates →

This matters for:

  • Live database query results
  • Streaming file processing
  • Real-time API monitoring
WebSocket MCP client
from mcp_client import WebSocketMCPClient
client = WebSocketMCPClient("wss://realtime.example.com/mcp")
# Subscribe to real-time updates
async for update in client.stream("database-changes"):
# Process updates as they arrive
if update.table == "users":
handle_user_update(update.data)

CLI tools would need polling or separate server components to achieve the same effect.

4. Accessibility and democratization

This one surprised me. I used to think MCP was just for technical optimization. Then I read this insight from the Reddit discussion:

“MCP can still be useful if you can package it in a way that makes it easier for non techies to access your stuff.”

Think about this scenario:

  • A designer needs to batch-update 50 Figma files
  • They don’t know Python or how to authenticate with APIs
  • An MCP server could provide a natural language interface

The designer just says: “Update the blue buttons to green in all marketing files.” The MCP server translates this to the right Figma API calls.

CLI tools require terminal access and command knowledge. MCP servers can have GUI frontends, chat interfaces, or other user-friendly wrappers.

When CLI still wins

MCP isn’t always the right choice. I use CLI for:

  • Simple, one-off operations: ls, git clone, npm install
  • Serverless environments where persistent servers aren’t available
  • Maximum portability across different systems
  • Tasks where state maintenance adds unnecessary complexity

If I just need to copy a file or run a test once, CLI is faster and simpler.

Performance comparison

I did some informal testing on a design automation task:

Performance comparison
Task: Update text in 50 Figma layers
CLI (REST API approach):
- 50 separate HTTP requests
- 50 authentication operations
- Full file data transferred 50 times
- Total time: ~45 seconds
MCP (stateful server):
- 1 authentication operation
- File opened once
- 50 lightweight reference-based updates
- Total time: ~12 seconds

The MCP approach was faster, but more importantly, it used less network bandwidth and fewer API calls.

The reason

I think the key distinction is:

  • CLI = stateless command execution
  • MCP = stateful service with persistent connections

Stateless tools are great when you want:

  • Simple, predictable behavior
  • No side effects between operations
  • Easy debugging and testing

Stateful tools are better when you need:

  • Context retention between operations
  • Efficient use of resources
  • Real-time communication
  • Complex multi-step workflows

They’re not competitors. They’re complementary tools for different problems.

Summary

In this post, I showed when MCP makes more sense than CLI for AI tools. The key point is that MCP’s stateful architecture provides benefits when you need to maintain context, enable real-time communication, or build complex multi-step workflows. CLI still wins for simple operations where state adds unnecessary complexity.

Use MCP when:

  • Working with tools that have no CLI (Figma, visual design tools)
  • You need to maintain browser or session state (Playwright)
  • Real-time WebSocket communication is required
  • You want to build accessible interfaces for non-technical users

Use CLI when:

  • Doing simple, one-off operations
  • Working in serverless environments
  • Maximum portability is required
  • State maintenance adds unnecessary complexity

The debate shouldn’t be “MCP vs CLI” - it should be “when should I use each?”

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments