cd /news/ai-agents/your-cli-tool-needs-an-mcp-server-sh… · home topics ai-agents article
[ARTICLE · art-126511] src=dev.to ↗ pub= topic=ai-agents verified=true sentiment=↑ positive

Your CLI Tool Needs an MCP Server: Shipping One in a Day, Protocol by Hand

A developer shipped `xbrowser mcp`, a stdio Model Context Protocol server that exposes seven browser-automation tools from the xbrowser CLI to MCP-capable clients like Claude Desktop and Cursor. The implementation hand-writes the JSON-RPC protocol layer instead of using the official SDK, reusing the CLI's existing executeChain/executeCommand functions so the MCP layer stays a pure translation concern. The build log documents two integration bugs — the CLI consuming stdin that doubles as the protocol channel, and a shutdown race that killed in-flight requests — plus a 50-line smoke script for testing MCP servers without a full client.

read4 min views2 publishedSep 11, 2026

Published on 2026-09-11 · xbrowser v1.23.1

Every CLI tool built before 2026 is asking itself the same question: how do AI agents discover me? The answer the ecosystem converged on is the Model Context Protocol — MCP. If Claude Desktop, Cursor, or any MCP-capable client can list your tool's capabilities and call them, your tool exists for agents. If not, it doesn't.

xbrowser is a browser-automation CLI (57 commands, 130+ site plugins, self-healing replay). This week we shipped xbrowser mcp — a stdio MCP server exposing 7 browser tools. This post is the build log: the design decisions, the two real bugs the first smoke run caught, and why we wrote the protocol layer by hand.

The official @modelcontextprotocol/sdk is fine software. We didn't use it, for three reasons that generalize:

initialize, tools/list, tools/call, plus an error path. That's a switch statement — about 60 lines including the newline-delimited framing. The SDK's value is in the long tail (resources, prompts, transports, typed helpers) that a 7-tool server doesn't touch. The whole protocol layer is small enough to hold in your head — which is exactly the property you want in the layer agents depend on.

The key decision: the MCP server rewrites no automation logic. Every tool call bottoms out in the same executeChain / executeCommand functions the CLI uses:

case 'browser_navigate': {
  const url = args.url as string;
  let chain = `goto ${url}`;
  if (args.chain) chain += ` && ${args.chain}`;   // follow-up commands as a chain
  const result = await executeChain(chain, { sessionName: session });
  return text({ ok: result.success, steps: ... });
}

This buys three things: one execution path to test (the CLI's 4000-test suite already covers it), one place to fix bugs, and automatic feature parity — when the replay engine gains a new healing strategy, MCP users get it the same day. The MCP layer is a translation concern (JSON-RPC ↔ CLI), and translation layers should be boring.

The 7 tools were chosen for agent ergonomics, not CLI symmetry: browser_navigate (with optional follow-up chain), browser_act (click/fill/press/…), browser_read (text/html/title), browser_snapshot (accessibility tree — the token-efficient page view), browser_screenshot, browser_network (request inspection), and browser_replay (the self-healing replay engine — our differentiator, exposed as a single tool call).

Both are the kind of thing you only find by running the real thing.

Bug 1: the CLI ate the protocol. Our entrypoint reads stdin when piped — that's how echo "goto x && title" | xbrowser works. To an MCP client, the server's stdin is the protocol channel. The first test sent initialize and the CLI helpfully tried to execute {"jsonrpc":"2.0"...} as a browser command. Fix: mcp short-circuits stdin collection; protocol lines and command lines never mix.

Bug 2: the shutdown race. Our smoke test piped requests from a file; EOF arrived while the browser was still launching for tools/call. The stdin end → process.exit(0) handler killed the in-flight request mid-flight. Real clients keep stdin open, so they'd never hit it — but a server that dies with pending work is wrong on principle. Fix: an inflight counter; exit only when stdin is closed and nothing is pending. Requests finish, then the process leaves.

Neither bug appears in the SDK's examples, because both live at the integration boundary between an existing CLI and the protocol — which is exactly where your bugs will live too.

You don't need Claude Desktop to test an MCP server. A 50-line smoke script spawns the server, writes four JSON-RPC lines (initialize, tools/list, unknown method, unknown tool), and asserts the responses: server info correct, exactly 7 tools, -32601 for bad methods, isError content for bad tools. That's now in the repo as scripts/mcp-smoke.mjs and takes 2 seconds to run. Protocol conformance is testable with plain processes — no client required.

For the real thing: claude mcp add xbrowser -- xbrowser mcp, then ask any MCP-capable agent to open a page and snapshot it.

If you maintain a CLI with an agent-relevant surface (browser, files, HTTP, search — most CLIs qualify), an MCP server is probably a day of work: enumerate 5–10 verbs agents actually need, wire them to your existing functions, hand-write the protocol loop, smoke-test with processes. The hard part isn't the protocol — it's the boundary decisions: which verbs, what granularity, how errors surface as tool results instead of crashes.

xbrowser is MIT-licensed: https://github.com/dyyz1993/xbrowser · npm: https://www.npmjs.com/package/@xbrowser/cli

This post was drafted through xbrowser's own content pipeline, and the browser_* tools above are the same ones any MCP agent gets.

── more in #ai-agents 4 stories · sorted by recency
── more on @xbrowser 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/your-cli-tool-needs-…] indexed:0 read:4min 2026-09-11 ·