We have started testing our tools the way they will be used, inside enterprise environments.
We took ethos, our agent harness, and z-spec, our specification toolkit, to a locked-down work laptop. We tried those two first because our workflow depends on them: agent delegation with evaluation and an audit trail, and formal methods. Neither would install as a Claude Code plugin, because the environment forbids it. That is ordinary in the enterprise, where software is reviewed before it runs and a plugin that registers an MCP [1] server rarely clears review [2].
So we cloned the repositories and installed a local build of each. The command-line tools ran; the plugins and their MCP tools did not. Left with the command line alone, we found real gaps. Work that ran through the plugin was missing or broken at the terminal.
This was not bad news. It is what testing is for: running on a locked-down machine shows what we had assumed about the environment. We had called our four interfaces equal, meaning you could do the same work through a Python import, a command line, an MCP server, or a REST API. The laptop proved otherwise. The command line ran and the plugin did not, and the command line on its own could not do what the plugin did. The four were never equal. We had built for Claude Code and forgotten that many of our users cannot install a plugin at all. Much software is written inside enterprises, on managed machines, behind review, and that is increasingly where AI-assisted work happens. There, a plugin-free install and a complete command line decide whether the tool is usable. We changed three things: how complete the command line is, how the tools install, and how we test both.
The interface with a helper
Take vox, one of our tools. It speaks text aloud, and it can also play background music, which it generates with ElevenLabs [3]. Starting the music means writing the prompts first: a base description and twelve genre-accurate variations, one for each track in the pool. In Claude Code, a slash command is run by a model. So when we run vox music as a slash command, the model composes the pool and calls the tool with it. The command-line verb, vox music on, got only what a person typed. Nobody writes twelve genre-accurate variations by hand at a prompt. Same capability on both interfaces, very different input. An agent restricted to a shell, or a person at a terminal, has no model to compose that input.
An intention is not a design pattern
We did not choose to let the command line fall behind. We intended the opposite. But an intention is not a mechanism. Each time we added a capability, we built it into the slash command and the MCP tool and left the command-line verb alone. Nothing broke, so no one noticed, until the bare shell, where the missing verbs were the whole problem. Parity is not something you declare once. It is something you have to build for and test, and we had done neither.
The model authors, the tool executes
The fix puts one boundary in the same place everywhere. The model writes a structured document; the tool reads it and runs, with no model of its own.
This is an old idea. Unix expected the output of one program to become the input of another, and favoured text over binary so that anything could read it [4]. The only new part is the author: a model now, rather than another program.
vox music on reads a twelve-variation pool as JSON from standard input. The slash command, run by the model, writes that pool; cat pool.json | vox music on pipes it in. Nothing piped falls back to a built-in prompt, and a malformed pool is a clean error rather than a stack trace. z-spec works the same way: cat analysis.json | z-spec partition spec.tex takes an authored test-partition analysis, validates it, and stores it. We used the same split for visual output in Building L1 Tools for L4 Agents, where the model declared a table and a display server rendered it.
The input needs a published contract
An authored input is useless if the author does not know what fields it must contain. An MCP tool gets this for free, because the protocol publishes each tool’s schema before the model calls. The command line publishes nothing, so we are giving each subcommand a schema verb that prints the fields it reads and their types. vox music on schema describes a music pool as JSON; ethos teams schema describes a team definition as YAML. The serialization differs by tool; the pattern does not. An agent reads the schema, writes to it, and pipes it in. The schema comes from the same definition the MCP tool advertises, so the two interfaces describe the input identically.
$ vox music on schema
{
"type": "object",
"required": ["base_prompt", "variations"],
"properties": {
"base_prompt": {"type": "string"},
"variations": {
"type": "array", "items": {"type": "string"},
"minItems": 12, "maxItems": 12
}
}
}
$ cat pool.json
{
"base_prompt": "Synthwave, warm analog synths, gated-reverb drums, instrumental, loopable",
"variations": [
"outrun driver at 118 BPM in A minor, punchy gated snare, saw-wave lead",
"dreamwave at 100 BPM in C major, lush pad chords, soft FM bells, wistful",
"... ten more, one per pool slot"
]
}
$ cat pool.json | vox music on
Enforcing parity
A command line that consumes authored input can carry every capability the MCP server does. In z-spec we made that a test. A registry lists each capability, and the build fails if one lacks either its command-line verb or its MCP tool. A missing verb is now a failing build, not a discovery months later.
Installing without the plugin
That leaves the install. If the command line is a true equal, you do not need the plugin to use the tool. Each tool now installs without one, and a committed per-repository marker turns the tool on without touching the editor. The capability is one shell command away, and the input it needs is a text file, JSON or YAML, that an agent, a script, or a slash command elsewhere can produce.
We taught our tests the same case. A Docker image with none of our conveniences installs each tool from scratch and drives its command line, the way a reviewed enterprise machine would. If a tool needs the plugin to install, or a capability is missing from the command line, the build fails. The clean-machine case now runs on every change.
What this does not do
This adds no intelligence to the command line. It moves the author. Something upstream still writes the document: a slash command, an agent, or a person willing to write it themselves. A human at a terminal with no model nearby still faces an empty pool, and filling twelve slots by hand is real work. The pattern helps agents and model-backed workflows, and nothing else.
We are early. A handful of tools follow the pattern, the parity test lives in one, and the Docker suite is new. But the direction is clear. Our users need agent tools that run on the machines they actually use, often a managed machine that will not install a plugin. That means a plugin-free install, a command line that is a genuine equal, an input contract an agent can read, and a test that proves it on a clean machine. The plugin stays for the environments that allow it. It is now optional.
References
- Anthropic. “Model Context Protocol.” 2024–present. modelcontextprotocol.io
- Microsoft. “Manage extensions in enterprise environments.” Visual Studio Code Documentation, 2025. code.visualstudio.com
- ElevenLabs. “Music.” 2024–present. elevenlabs.io
- McIlroy, M. D., Pinson, E. N., Tague, B. A. “UNIX Time-Sharing System: Foreword.” Bell System Technical Journal 57(6), 1978. archive.org