Skip to main content
Glama
README.md
# πŸš€ Cuba-Exec

**Advanced shell command execution for AI agents** β€” A Model Context Protocol (MCP) server with security policy engine, process lifecycle management, bounded output capture, POSIX signals, and token-efficient responses.

6 tools. Zero configuration. POSIX-native. Security by default.

---

## Why Cuba-Exec?

Existing command execution MCPs are thin wrappers over `subprocess.run`. Cuba-Exec solves the real problems:

| Problem | Existing MCPs | Cuba-Exec |
|---------|:---:|:---:|
| Output overflow (cat /dev/urandom) | ❌ OOM crash | βœ… 64KB bounded |
| Background processes | ❌ Sync only | βœ… Start/status/signal |
| Kill child processes (npm run dev) | ❌ Orphaned children | βœ… Process group kill (setsid) |
| Send stdin (REPLs, prompts) | ❌ Not supported | βœ… Full stdin pipe |
| POSIX signals (SIGTERM, SIGKILL) | ❌ Not supported | βœ… 5 signals + graceful shutdown |
| **Command allowlist/blocklist** | ⚠️ Some | βœ… Both + shell operator validation |
| **Directory restriction** | ⚠️ Rare | βœ… Path-resolved anti-traversal |
| **Audit logging** | ❌ None | βœ… Structured JSON to stderr |
| Token-efficient output | ❌ Verbose JSON | βœ… TOON compact format |
| Idle process cleanup | ❌ Resource leak | βœ… 1-hour TTL auto-cleanup |
| Fork bomb protection | ❌ None | βœ… Semaphore(20) |
| Process discovery | ❌ None | βœ… List all managed processes |

---

## Quick Start

### 1. Prerequisites

- **Python 3.14+**
- **Linux/macOS** (POSIX required for process groups)

### 2. Install

```bash
git clone https://github.com/LeandroPG19/cuba-exec.git
cd cuba-exec
uv venv && uv pip install -e .
```

### 3. Configure your AI editor

```json
{
  "mcpServers": {
    "cuba-exec": {
      "command": "/path/to/cuba-exec/.venv/bin/python",
      "args": ["-m", "cuba_exec"]
    }
  }
}
```

Zero environment variables needed. Zero configuration files. It just works β€” with 25 dangerous commands blocked by default.

---

## The 6 Tools

### `run` β€” Execute and wait

```
run(command="ls -la", cwd="/tmp", timeout_ms=5000)
```

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `command` | string | **required** | Shell command |
| `cwd` | string | None | Working directory |
| `env` | dict | None | Environment variables (merged with current) |
| `timeout_ms` | int | 30000 | Timeout in milliseconds |
| `max_output` | int | 65536 | Output buffer size (bytes) |
| `shell` | string | /bin/sh | Shell executable |

**Response:**
```
[exit:0 time:12ms trunc:no]
total 156
drwxrwxrwt 22 root root 4096 Mar  8 2026 .
...
```

### `start` β€” Background process

```
start(command="npm run dev", cwd="/app")
```

**Response:**
```
[pid:12345 state:running]
Background process started. Use status(12345) to check output.
```

### `status` β€” Check background process

```
status(pid=12345, tail_bytes=4096)
```

**Response:**
```
[pid:12345 state:running exit:- time:5432ms bytes:8192 trunc:no]
Server running on http://localhost:3000
```

### `send_signal` β€” POSIX signals

```
send_signal(pid=12345, sig="SIGTERM")
```

SIGTERM triggers **graceful shutdown**: SIGTERM β†’ wait 5s β†’ SIGKILL.

Valid signals: `SIGTERM`, `SIGKILL`, `SIGINT`, `SIGHUP`, `SIGQUIT`.

### `send_input` β€” stdin pipe

```
send_input(pid=12345, stdin="print('hello')\n")
```

For interactive processes (Python REPL, bash prompt, etc.).

### `list_processes` β€” Process discovery

```
list_processes()
```

**Response:**
```
[processes:2]
  pid:12345 state:running exit:- time:5432ms bytes:8192 cmd:npm run dev
  pid:12346 state:completed exit:0 time:1200ms bytes:256 cmd:echo done
```

---

## πŸ›‘οΈ Security Policy Engine

Cuba-Exec includes a multi-layer security engine β€” the most comprehensive of any MCP command server.

### Security Layers

| Layer | Description | Config |
|-------|-------------|--------|
| **Command Allowlist** | Only listed commands can execute | `CUBA_EXEC_ALLOWED_COMMANDS` |
| **Command Blocklist** | Dangerous commands always rejected | `CUBA_EXEC_BLOCKED_COMMANDS` |
| **Shell Operator Validation** | Validates each sub-command after `;`, `&&`, `\|\|`, `\|` | Automatic |
| **Directory Restriction** | Restrict `cwd` to allowed paths (anti-traversal) | `CUBA_EXEC_ALLOWED_DIRS` |
| **Audit Logging** | Structured JSON log of every execution | `CUBA_EXEC_AUDIT` |

### Default Behavior (Zero Config)

Out of the box, Cuba-Exec blocks 25 dangerous commands:

```
rm, dd, mkfs, shutdown, reboot, halt, poweroff, init, systemctl,
passwd, chown, chmod, chgrp, mount, umount, fdisk, parted,
iptables, nft, ip6tables, crontab, at, useradd, userdel,
groupadd, groupdel, visudo
```

### Production Hardening

```bash
export CUBA_EXEC_ALLOWED_COMMANDS="ls,cat,echo,grep,find,head,tail,wc,git,python3,node,npm"
export CUBA_EXEC_BLOCKED_COMMANDS="rm,dd,mkfs,shutdown"
export CUBA_EXEC_ALLOWED_DIRS="/home/user/project,/tmp"
export CUBA_EXEC_AUDIT=1
```

### Shell Operator Bypass Prevention

`ls && rm -rf /` β€” the `rm` after `&&` is validated against blocklist/allowlist too.

Operators parsed: `;`, `&&`, `||`, `|` β€” each sub-command checked independently.

### Audit Log (stderr)

```json
{"ts":"2026-03-08T15:00:00-0600","event":"exec","command":"ls -la","pid":12345,"exit":0,"ms":12,"ok":true}
```

---

## Output Format (TOON)

All responses use Token-Oriented Object Notation β€” compact headers that save ~200 tokens per tool call vs verbose JSON.

```
[exit:0 time:1543ms trunc:no]
...output...
```

### Error Codes

| Error | Exit Code | Field | Example |
|-------|:---------:|-------|---------:|
| Command not found | 127 | `ENOENT` | `nonexistent_binary` |
| Permission denied | 126 | `EACCES` | `cat /etc/shadow` |
| Timeout | -1 | `TIMEOUT` | `sleep 60` with 1s timeout |
| Signal killed | -9 | `SIGKILL` | Process killed by signal |
| **Blocked by policy** | β€” | `BLOCKED` | `rm -rf /` |

---

## Head+Tail Output Buffer β€” Shannon (1948)

Command output has **high entropy at the extremes** (preamble + results/errors) and low entropy in the middle (progress bars, repetitive logs).

```
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Head (25%) β”‚        Truncated middle       β”‚                Tail (75%)                β”‚
β”‚  ~16KB      β”‚   [... N bytes truncated ...] β”‚                ~48KB (ring buffer)       β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
```

- **Head**: First 25% of buffer β€” captures headers, version info
- **Tail**: Last 75% via ring buffer β€” captures results, errors (highest entropy)
- **Ring buffer**: O(1) write, O(C) memory (Cormen et al., CLRS 4th ed.)
- **Default**: 64KB per process. Max memory: 20 Γ— 64KB = 1.28MB

---

## POSIX Process Groups β€” IEEE Std 1003.1

`npm run dev` spawns child processes. Sending SIGTERM to the parent doesn't kill children.

Cuba-Exec creates process groups via `setsid`:

```python
asyncio.create_subprocess_exec(..., start_new_session=True)
os.killpg(os.getpgid(pid), signal.SIGTERM)  # Kills entire tree
```

### Graceful Shutdown

```
SIGTERM β†’ wait 5s β†’ SIGKILL (if still alive)
```

Two-phase shutdown (Stevens & Rago, 2013): SIGTERM allows cleanup, SIGKILL is uncatchable.

---

## Configuration

All defaults work out of the box. Override via environment variables:

| Setting | Default | Env Var |
|---------|:-------:|---------|
| Max concurrent processes | 20 | `CUBA_EXEC_MAX_PROCS` |
| Output buffer size | 64KB | `CUBA_EXEC_BUFFER_SIZE` |
| Idle process TTL | 1 hour | `CUBA_EXEC_TTL` |
| Shutdown timeout | 5s | `CUBA_EXEC_SHUTDOWN_TIMEOUT` |
| Allowed commands | β€” (all) | `CUBA_EXEC_ALLOWED_COMMANDS` |
| Blocked commands | 25 defaults | `CUBA_EXEC_BLOCKED_COMMANDS` |
| Allowed directories | β€” (all) | `CUBA_EXEC_ALLOWED_DIRS` |
| Audit logging | off | `CUBA_EXEC_AUDIT` |

---

## Architecture

```
cuba-exec/
β”œβ”€β”€ pyproject.toml            # 1 dependency: fastmcp
└── src/
    └── cuba_exec/
        β”œβ”€β”€ __init__.py
        β”œβ”€β”€ __main__.py        # Entry point
        β”œβ”€β”€ server.py          # FastMCP 6 tool definitions (~105 LOC)
        β”œβ”€β”€ security.py        # SecurityPolicy engine (~135 LOC)
        β”œβ”€β”€ process_manager.py # Lifecycle FSM + signals + TTL (~520 LOC)
        └── output_buffer.py   # Head+Tail ring buffer (~110 LOC)
```

**Total: ~880 LOC.** FastMCP SDK handles protocol boilerplate.

### Dependencies (1 total)

| Package | Purpose |
|---------|---------|
| `fastmcp` | MCP protocol server β€” auto tool schemas from type hints, Pydantic validation |

Everything else is Python stdlib: `asyncio`, `os`, `signal`, `time`, `json`, `re`, `pathlib`.

---

## Part of the Cuba Ecosystem

| Project | Purpose |
|---------|---------|
| [Cuba-Memorys](https://github.com/LeandroPG19/cuba-memorys) | Persistent memory β€” knowledge graph, Hebbian learning |
| [Cuba-Thinking](https://github.com/LeandroPG19/cuba-thinking) | Sequential reasoning β€” cognitive engine, NLI, MCTS |
| [Cuba-Search](https://github.com/LeandroPG19/cuba-search) | Web search β€” research, scraping, validation, documentation lookup |
| **Cuba-Exec** | Shell execution β€” process lifecycle, security, bounded output, POSIX signals |

Together: **memory + reasoning + search + execution** β€” the four pillars of capable AI agents.

---

## Academic References

| # | Citation | Used For |
|---|----------|----------|
| 1 | Yang et al. (2024). "SWE-agent: Agent-Computer Interfaces." NeurIPS | ACI design, output truncation, guardrails |
| 2 | Shannon (1948). "A Mathematical Theory of Communication" | Information-theoretic output strategy |
| 3 | IEEE Std 1003.1-2024. "POSIX.1: System Interfaces" | Process groups, setsid, signals |
| 4 | Cormen et al. (2022). "Introduction to Algorithms." 4th ed. | Ring buffer O(1) analysis |
| 5 | Dijkstra (1965). "Cooperating Sequential Processes" | Semaphore concurrency limiting |
| 6 | Stevens & Rago (2013). "APUE" 3rd ed. | Process lifecycle, graceful shutdown |
| 7 | TOON (2025). "Token-Oriented Object Notation" | 95-97% token reduction |
| 8 | OWASP (2025). "Top 10 for Agentic Applications" | Security policy design, allowlist/blocklist |

---

## License

[CC BY-NC 4.0](https://creativecommons.org/licenses/by-nc/4.0/) β€” Free to use and modify, **not for commercial use**.

---

## Author

**Leandro PΓ©rez G.**

- GitHub: [@LeandroPG19](https://github.com/LeandroPG19)
- Email: [leandropatodo@gmail.com](mailto:leandropatodo@gmail.com)

TDQS

A4.1/5.0

Scored across 6 tools

Disambiguation5/5

Each tool targets a distinct operation: foreground execution, background start, status query, signal sending, stdin input, and process listing. No overlap in purpose, and the descriptions make the boundaries clear.

Naming Consistency3/5

There is a mix of conventions: 'status' is a noun, 'run' and 'start' are bare verbs, while 'send_signal', 'send_input', and 'list_processes' follow verb_noun. The send_* and list_* group is consistent, but the others break the pattern.

Tool Count5/5

Six tools cover the core process execution and management workflow without unnecessary bloat. The count feels well-scoped for this domain.

Completeness5/5

The set provides complete lifecycle coverage: synchronous execution, background starts, status checks, signal handling, stdin interaction, and process enumeration. No obvious gaps for a process execution server.

Maintenance

ActivityInactive
ResponsivenessUnresponsive