DEV Community

Cover image for Why Raw Shell Access Is the Wrong Agent Integration—and What apexe Does Instead
tercel
tercel

Posted on

Why Raw Shell Access Is the Wrong Agent Integration—and What apexe Does Instead

Your organisation probably already owns the capabilities its agents need. They live in git, kubectl, jq, curl, operational scripts, and internal Unix command-line tools.

The usual choice is uncomfortable: give an agent shell access, or rebuild every useful command as an API or MCP server.

apexe is built for the middle path. It scans an existing CLI, turns each command into a schema-driven apcore module, and serves those modules through MCP or A2A with execution-time controls.

existing CLI → module contract + reviewed policy → governed Executor → many agent clients
What is supported today?
Precision matters in agent infrastructure. The current product boundary is:

Item apexe 0.8 status
Unix CLI tools on PATH Supported by apexe scan
macOS/Linux system utilities (ls, cat, cp, sort, …) Supported as CLI tools, including BSD-aware parsing
Built-in parsers (Man, BSD Usage, GNU, Click, Cobra, Clap) Supported scanner feature
ACL for scanned CLI modules Supported when supplied with --acl
MCP Supported by apexe serve (stdio, HTTP, Explorer)
A2A Supported by apexe a2a
OpenAPI input Not supported by the apexe CLI today
OpenAPI deserves a direct note. The apcore toolkit has OpenAPI-related functionality, but apexe does not call its OpenAPIScanner. apexe is currently a CLI-to-agent bridge, so we should not market OpenAPI support as shipped functionality.

Start with a real CLI
apexe scan git curl jq
apexe list
apexe serve --show-config claude-desktop --acl ~/.apexe/acl.yaml
The scan is deterministic: help text, man pages, and shell completions—not an LLM. It creates one binding per command, such as cli.git.log or cli.curl, and each binding contains JSON Schema for flags and operands.

An agent calls a JSON object rather than composing a command string:

{
"url": "https://httpbin.org/get",
"silent": true
}
apexe validates this object, builds argv, and launches the program directly. No shell parses the resulting arguments.

One capability configuration, many agent clients
The goal is not to create a separate tool definition and security policy for every agent product. Bindings and ACL are the source of truth:

~/.apexe/bindings/ + ~/.apexe/acl.yaml → apexe → Claude / Codex / Pi / another MCP client
This does not mean every client shares one configuration-file format. Each host still needs its own MCP connection entry. What stays shared is the command or HTTP endpoint, the scanned module bindings, and the reviewed ACL.

apexe can generate connection snippets for Claude Desktop and Cursor:

apexe serve --show-config claude-desktop --acl ~/.apexe/acl.yaml
apexe serve --show-config cursor --acl ~/.apexe/acl.yaml
Codex can register the same local stdio server:

codex mcp add apexe -- apexe serve --acl ~/.apexe/acl.yaml
Pi and other MCP-capable clients can connect through their own MCP configuration mechanism. apexe does not currently generate a Pi-specific file, so the client must support the transport you select. For a shared deployment, run one authenticated HTTP server and have compatible clients connect to that endpoint.

Why the module contract matters
The contract is what lets the same capability retain its meaning across delivery surfaces:

apexe serve exposes it over MCP, using stdio by default or HTTP with a browser Explorer.
apexe a2a exposes the same governed modules as agent skills.
--prefix and --tags reduce the registered surface before a client can discover or invoke it.
Policy does not need to be rewritten per protocol because MCP and A2A share the same Executor.

Governance is a product feature, not a checkbox
apexe scan writes a default-deny ACL. It is a starter policy, not an invisible decision: enforcement begins only when an operator serves with --acl.

apexe serve --acl ~/.apexe/acl.yaml
The ACL controls which capability may be called. An always-on path guard answers a different question: what filesystem location may this specific call touch? It checks path-typed inputs after resolving symlinks and traversal, protects system paths from writes, and protects credential locations from both reads and writes.

apexe policy
apexe policy --path /etc/nginx/conf.d --mode write
Every child process is shell-free, runs with a scrubbed environment, has a timeout and output cap, and can produce a private audit record. These are meaningful safeguards, but they are not a sandbox. Run exposed tools inside the container, VM, account, and network boundary your risk model requires.

Why 0.7 and 0.8 matter
The current releases make the boundary more useful in practice: path protection, argument-sensitive approvals, refusal of verified command-executing parameters, binary availability checks, an explicit default-deny tier for network-reaching tools, and externally maintained verified CLI overlays.

Bindings now live by default under ~/.apexe/bindings and record which apexe version produced them. Existing users should rescan after upgrading:

apexe scan git curl jq --no-cache
apexe list --available-only
What comes next: local execution, then native terminal workflows
The next proposed feature is F8 local execution. It is deliberately split into two stages rather than treating a shell wrapper as a shortcut.

Stage 1, apexe run, is a structured local entry point for one scanned module at a time:

apexe run cli.ls --input '{"long": true, "paths": ["/tmp"]}' --acl ~/.apexe/acl.yaml
It will reuse the same Executor that MCP and A2A use, produce captured output, and make preflight decisions inspectable from the terminal. It is not native shell syntax and does not provide streaming stdin or pipelines.

Stage 2, apexe env with PATH shims, is the intended end-user terminal experience. Eligible external commands retain their normal names and argv syntax, so an existing pipeline such as ls -l /tmp | grep ERROR | head -20 can remain unchanged. Each stage is independently resolved to a scanned module and receives schema validation, verified cli-permissions facts, ACL, path guard, timeout, output limit, and audit logging—without using sh -c.

This is not available yet. Its safety depends on contracts that are still being settled: passthrough I/O, an explicit human-reviewed allowlist for modules that may read streaming stdin, PATH isolation, and measurements of refusal rate and shim latency. It also cannot intercept shell builtins, aliases/functions, or absolute-path calls. Read the F8 design for the exact boundary.

Join the design conversation
The interesting question is not “can an agent run a command?” It can. The question is which existing capability sources should share a contract, governance layer, and protocol delivery model.

We would love your comments:

Which CLI or internal tool would you expose first?
Which agent hosts—Claude, Codex, Pi, or another MCP client—need the same governed tool surface?
Which existing scripts or pipelines should a governed PATH-shim mode support first?
Would OpenAPI ingestion be valuable in the same workflow?
What must an operator review before an agent can use a newly discovered capability?
Project: aiperceivable/apexe

Top comments (0)