When you give an agent access to Chrome DevTools through the Model Context Protocol, you load thousands of tokens of JSON schema into context before the agent does any work. A CLI wrapper around the same MCP server changes where discovery costs land, how errors propagate, and what the agent sees when something breaks.
Maxim Saplin ran the same 9-step browser smoke test through two paths: direct Chrome DevTools MCP and a custom CLI skill wrapping the MCP server via mcp2cli. The experiment used GitHub Copilot CLI with gpt-5.3-codex-medium, a private Python/Streamlit codebase, and stock MCP servers disabled. The goal was to measure token overhead and observe where the agent pays to discover the browser-control surface.
The Token Budget Problem
Direct MCP integration loads tool schemas upfront. Chrome DevTools MCP added roughly 5,000 tokens of context before the agent issued a single command. That context includes every available method, parameter type, and return shape for the Chrome DevTools Protocol.
A CLI wrapper defers discovery. The agent learns the tool surface the same way it learns any command-line utility: list commands, search help text, run a small probe, write down what worked. The mcp2cli README claims it saves 96-99% of the tokens wasted on tool schemas every turn. That number is high, but the structural difference is real.
The trade-off is not just token count. It is when and how the agent builds a mental model of the tool.
State Management Across the Boundary
Browser automation is stateful. You navigate to a page, wait for elements, click, extract data, then move to the next step. Each step depends on the previous one succeeding.
With direct MCP, the agent makes a series of tool calls. Each call is a JSON-RPC request over stdio. If a call fails, the agent sees a structured error response. If the browser state drifts (a modal appears, a redirect happens, a timeout fires), the error might not be actionable. The agent has to infer what went wrong from the protocol response.
With a CLI wrapper, the agent issues shell commands. Each command returns an exit code, stdout, and stderr. The wrapper can add context: “Element not found after 5 seconds, current URL is X, visible text includes Y.” The agent sees the failure in the same format it sees every other shell failure. That consistency matters when the agent is deciding whether to retry, adjust selectors, or bail.
The CLI layer also decides what state to persist. Does it keep a browser instance alive across commands? Does it snapshot the DOM after each step? Does it log screenshots to a temp directory? These decisions are invisible to the agent but change what recovery paths are possible.
Error Propagation and Debugging Surface
When a browser automation fails, you need to know what the page looked like, what the agent tried to do, and what the browser returned. The debugging surface is the set of artifacts and logs available after a failure.
Direct MCP gives you:
- The JSON-RPC request the agent sent
- The JSON-RPC response the server returned
- Any logs the MCP server wrote to stderr
A CLI wrapper gives you:
- The shell command the agent issued
- The exit code, stdout, and stderr
- Any intermediate files the wrapper created (screenshots, HTML snapshots, trace logs)
- The shell history, which shows the sequence of commands and their results
The CLI wrapper can also format output for agent consumption. Instead of returning raw JSON, it can return a summary table or a list of key-value pairs. That reduces the chance the agent hallucinates structure when parsing the response.
Architecture: CLI Wrapper Over MCP
Here is the flow when an agent uses a CLI wrapper around an MCP server:
- Agent issues a shell command:
mcp2cli chrome-devtools navigate --url https://example.com - CLI wrapper parses arguments, validates them, and constructs an MCP tool call
- Wrapper sends the tool call to the Chrome DevTools MCP server over stdio
- MCP server executes the Chrome DevTools Protocol command
- MCP server returns a JSON-RPC response
- Wrapper parses the response, formats it for shell output, and writes to stdout
- Agent sees the formatted output and decides the next step
The wrapper is a translation layer. It does not change what the browser does. It changes how the agent discovers capabilities, how errors surface, and what artifacts are available for debugging.
Implementation Considerations
If you build a CLI wrapper around an MCP server, you need to decide:
- Command structure: Do you expose every MCP tool as a subcommand, or do you group related tools under a single command with flags?
- Output format: Do you return JSON, plain text, or a custom format? Can the agent request a specific format with a flag?
- State persistence: Do you keep the MCP server process alive across commands, or do you start a new process for each command?
- Error handling: Do you pass through MCP errors verbatim, or do you add context and suggestions?
- Logging: Do you write logs to a file, to stderr, or to a structured logging system?
Here is a minimal example of a CLI wrapper that calls an MCP tool:
import subprocess
import json
import sys
def call_mcp_tool(server_path, tool_name, arguments):
"""Call an MCP tool and return the result."""
request = {
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": tool_name,
"arguments": arguments
}
}
process = subprocess.Popen(
[server_path],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
stdout, stderr = process.communicate(json.dumps(request))
if process.returncode != 0:
print(f"Error: {stderr}", file=sys.stderr)
sys.exit(1)
response = json.loads(stdout)
if "error" in response:
print(f"MCP error: {response['error']['message']}", file=sys.stderr)
sys.exit(1)
return response["result"]
if __name__ == "__main__":
server = sys.argv[1]
tool = sys.argv[2]
args = json.loads(sys.argv[3])
result = call_mcp_tool(server, tool, args)
print(json.dumps(result, indent=2))
This script takes a server path, tool name, and JSON arguments, calls the tool, and prints the result. A production wrapper would add argument parsing, output formatting, state management, and better error messages.
Trade-Offs: Direct MCP vs. CLI Wrapper
| Dimension | Direct MCP | CLI Wrapper |
|---|---|---|
| Token overhead | High upfront (5k+ tokens for schemas) | Low upfront, pay-as-you-go discovery |
| Error context | Structured JSON-RPC errors | Shell exit codes + formatted messages |
| State management | Agent manages state across tool calls | Wrapper can persist state between commands |
| Debugging artifacts | JSON-RPC logs | Shell history, stdout/stderr, intermediate files |
| Agent familiarity | Requires learning MCP tool surface | Leverages existing CLI knowledge |
| Latency | One process, low per-call overhead | Subprocess spawn per command (unless persistent) |
| Observability | MCP server logs | Shell logs + wrapper logs + MCP logs |
The CLI wrapper adds a layer of indirection. That indirection costs latency if you spawn a new process for each command. It also adds complexity: you now have two failure modes (CLI wrapper and MCP server) instead of one.
The benefit is a more familiar interface for the agent and more control over how errors and state are presented.
When CLI Wrappers Make Sense
A CLI wrapper around an MCP server is useful when:
- The agent already knows how to use command-line tools and you want to leverage that knowledge
- You need to add context to errors that the MCP server does not provide
- You want to persist state or artifacts between commands without teaching the agent how to do it
- You are working with a minimal agent that does not have MCP integration built in
- You need to format output in a way that reduces agent hallucination
A CLI wrapper is not useful when:
- The agent has native MCP support and you want to minimize latency
- The MCP server already provides rich error messages and state management
- You do not want to maintain an additional layer of tooling
- The token overhead of loading schemas upfront is acceptable
Technical Verdict
Use a CLI wrapper when you need more control over error presentation, state persistence, or output formatting than the raw MCP protocol provides. The wrapper is especially valuable if your agent is already fluent in shell commands and you want to avoid loading thousands of tokens of JSON schema upfront.
Avoid a CLI wrapper if you have native MCP support in your agent runtime and the token overhead is not a bottleneck. The extra layer adds latency and complexity. If the MCP server already gives you the error context and state management you need, the wrapper is unnecessary indirection.
For browser automation specifically, the CLI wrapper shines when failures are common and you need rich debugging artifacts. The ability to snapshot the DOM, capture screenshots, and format errors in plain text makes it easier for the agent to recover or report what went wrong.