8 min read

Turn any CLI coding agent into an MCP server

Why command line coding agents are ideal Model Context Protocol servers, how stdio transport works, and the three mistakes that break a JSON-RPC stream.

  • MCP
  • Integration
  • Protocol

Why a CLI agent is a good MCP server

MCP is a JSON-RPC protocol over stdio or HTTP that lets a host application, Claude Desktop, Cursor, an IDE, another agent, call tools your program exposes. Most MCP servers are small scripts written for this purpose.

A coding agent is a different animal. It is a long-lived process that already owns everything an MCP server needs:

  • A working directory and an idea of what “the project” means, so relative paths in a tool call resolve the way a human expects.
  • A model client with provider fallbacks, cost accounting and context compaction already solved.
  • A permission model: modes, tool allowlists, path sandboxing. Which is exactly the control plane an MCP host wants to inherit rather than reimplement.
  • Sessions on disk, so two hosts can share a conversation rather than each starting from zero.

Sentinel is built this way: sentinel mcp starts the same in-process agent the TUI uses and publishes three tools over stdio. See the MCP server documentation for the client config.

The contract: three tools, not thirty

The instinct is to publish every capability the agent has. Resist it. Each tool costs the calling model a decision, and thin wrappers around primitives are the worst possible trade: the model now spends turns on protocol mechanics instead of on your code.

  • sentinel_health

    Is Sentinel installed, configured, and reachable? Returns structured status, model, providers, working directory, sandbox root.

  • sentinel_ask

    Run one goal through the full agent loop and return the answer plus its cost. Model is optional and falls back to the configured default.

  • sentinel_review_diff

    Read-only review of the current git diff, returning findings with file:line evidence. Cannot write in any mode.

sentinel_health is the one people forget. It returns a cheap, structured answer to “is this thing installed, configured and working?”. Which is the first thing a host model will try, and the first thing that fails confusingly if it is missing.

Transport: why stdio is the right default

With stdio, the client spawns your server as a child process and writes newline-delimited JSON-RPC frames to its stdin, reading replies from stdout. The practical consequences:

  • No port to choose, no port to collide, nothing to expose on a network.
  • No authentication problem, because there is no remote caller to authenticate.
  • No lifecycle to manage. If the host exits, the server is reaped with it.
mcp.json
{
  "mcpServers": {
    "sentinel": {
      "command": "npx",
      "args": ["-y", "sentinel-cli", "mcp"]
    }
  }
}

The stdout trap

The single most common way to break an MCP server is to print to stdout. Not the protocol output, anything else. A console.log, a Node deprecation warning, a shell script that echoes its own commands, a progress bar: all of it lands between two JSON-RPC frames, and the client desynchronises and reports an unhelpful parse error.

the fix
// stdout belongs to the protocol. Everything else goes to stderr.
console.error("model:", model, "elapsed:", ms);

// and in a shell wrapper, silence the child unless debugging
sentinel mcp 1>&2

Rule of thumb

If a line you did not intend as protocol output can reach stdout, it eventually will. Redirect unconditionally while you debug, then remove the redirect.

Tool descriptions are the real API

The calling model never sees your implementation. It sees a name, a description, and a JSON schema. That makes the description the API surface, and it is where most integrations are won or lost.

Three rules, learned the hard way:

  1. Write the description for the caller's decision, not for your function. “Runs a read-only review over the current git diff and returns findings with file:line evidence” beats “Reviews code” every time, because the first one tells the model when to reach for it.
  2. State the failure mode. If a tool can be refused because of a permission mode, say so in the description. A model that does not know why a call failed will retry it.
  3. Make optional parameters earn their place. Every optional argument is a branch the model has to reason about on every call.

Expose capabilities, not primitives

The strongest pattern we have found: expose one entry point that takes a goal, and let the agent’s own loop decide which internal tools to use. The host model spends one turn instead of eight, and it gets the agent's permission model and cost accounting for free.

bash
# one call, whole loop
sentinel ask "why does auth fail behind the CDN?"

# or, when the host wants a contract first
sentinel outcome "the sync is flaky" --plan

The tradeoff is real and worth stating: composability drops, because the host can no longer choose the exact tools. For review and health-style calls, expose fine-grained tools. For “fix this”, expose the loop.

Frequently asked questions

Can a terminal coding agent be used as an MCP server?

Yes, and it is one of the better fits for the protocol. A CLI agent is already a long-lived process that owns a project directory, a model client and a tool allowlist. Exposing it over MCP adds a transport and a tool schema. It does not require restructuring anything. The three things you must get right are the transport, the tool descriptions, and keeping stdout free of logs.

What transport should an MCP server use?

stdio for anything local. The client spawns the server as a child process and speaks newline-delimited JSON-RPC over its stdin and stdout. It needs no port, no auth, no lifecycle management, and it dies when the client exits. HTTP transports exist for remote or shared servers, but they add an authentication and deployment problem you should not take on for a tool that runs on the same machine as its caller.

Why does my MCP server return invalid JSON?

Almost always because something wrote to stdout. A stray console.log, a Node warning, a progress bar, or a library that writes progress to stdout will land in the middle of a JSON-RPC frame and desynchronise the stream. Send all diagnostics to stderr instead. This is the single most common MCP integration bug and it presents as a confusing parse error rather than as a logging problem.

How many tools should an MCP server expose?

Fewer than you think. Every tool competes for the model's attention and for the human's context budget. Expose capabilities, not primitives: one ask-style tool that takes a prompt beats ten thin wrappers around read, grep and patch, because the model can then compose them without spending turns on protocol mechanics.

Written by Kunj Shah

Sentinel is an open source AI coding agent for the terminal, MIT licensed, no servers, no telemetry. Read the source or install it.