Run any command with retry, timeout, and JSON extraction for AI agents
Project description
For LLMs: Executes a shell command with exponential backoff, extracts a valid JSON from mixed output, validates it against an optional schema, and returns a structured diagnostic report – all in a single deterministic CLI tool.
Installation
pip install exec-json
Or install from source:
git clone https://github.com/your-org/exec-json.git
cd exec-json
pip install -e .
Usage
CLI reference
| Argument | Type | Default | Description |
|---|---|---|---|
--cmd |
str |
required | Command to execute (wrap in quotes). |
--retries |
int |
3 |
Maximum number of retry attempts. |
--timeout |
float |
30.0 |
Per-attempt timeout in seconds. |
--backoff |
float |
2.0 |
Exponential backoff factor: wait = backoff^attempt. |
--schema |
str |
None |
Path to a .json schema file or inline JSON string for validation. |
--shell |
flag |
False |
Use shell=True in subprocess (enables pipes, redirects, etc.). |
--no-extract |
flag |
False |
Skip JSON extraction; return raw stdout only. |
--version |
— | — | Print version and exit. |
Input → Output examples
| Input | Output (compressed) |
|---|---|
--cmd "echo '{\"status\":\"ok\"}'" |
{"success": true, "exit_code": 0, "parsed_json": {"status": "ok"}, ...} |
--cmd "invalid-command" |
{"success": false, "error": "Command not found: invalid-command", ...} |
--cmd "" |
{"success": false, "error": "No command provided", ...} |
--cmd "sleep 10" --timeout 0.5 |
{"success": false, "error": "Command timed out after 0.5s", ...} |
Examples
Before/After — Recovering from a flaky command
Before: A command that fails 50% of the time requires custom retry logic.
# Manual retry loop – fragile, no structured output
for i in 1 2 3; do
output=$(flaky-command 2>/dev/null) && break
sleep $((2**i))
done
echo "$output"
After: exec-json handles retry, backoff, and structured output automatically.
exec-json --cmd "flaky-command" --retries 3 --backoff 2.0
{
"success": true,
"attempts": 2,
"duration_ms": 1502.3,
"parsed_json": { "result": "data" }
}
Before/After — Extracting JSON from verbose log output
Before: Grep + sed + jq – brittle and verbose.
output=$(some-tool --verbose 2>&1)
json=$(echo "$output" | grep -oP '\{.*\}' | head -1)
echo "$json" | jq .
After: A single exec-json call with automatic extraction.
exec-json --cmd "some-tool --verbose"
{
"success": true,
"parsed_json": { "metric": 42, "unit": "ms" },
"stdout_raw": "[2024-01-01] Starting...\n{\"metric\": 42, \"unit\": \"ms\"}\nDone.\n"
}
Before/After — Schema validation
Before: Manual validation with ad‑hoc checks.
output=$(produce-json)
echo "$output" | python -c "
import json, sys
data = json.load(sys.stdin)
assert 'id' in data, 'missing id'
assert isinstance(data['id'], int), 'id not int'
"
After: Declarative schema validation built in.
exec-json --cmd "produce-json" --schema '{"type":"object","required":["id"],"properties":{"id":{"type":"integer"}}}'
{
"success": true,
"parsed_json": { "id": 1, "name": "test" },
"schema_valid": true,
"schema_errors": []
}
Error handling
| Scenario | error |
suggestion |
|---|---|---|
| Empty command | No command provided |
Pass a command with --cmd |
| Timeout | Command timed out after Xs |
Increase --timeout or simplify the command |
| Command not found | Command not found: <cmd> |
Verify the command is installed and in PATH |
| Schema file missing | Schema file not found |
Verify path |
| Invalid schema JSON | Invalid JSON schema: ... |
Check schema syntax |
| Non-zero exit code | Command exited with code X |
Review the command output for errors |
Note:
exec-jsonalways exits with code0. The agent reads the JSON to decide how to proceed, avoiding cascade failures in chained tool calls.
Output schema
Every invocation returns a JSON object with the following structure:
| Field | Type | Description |
|---|---|---|
success |
bool |
true if the last attempt had exit code 0. |
exit_code |
int |
Last exit code (or -1 for timeout). |
attempts |
int |
Total attempts made. |
duration_ms |
float |
Wall-clock duration in milliseconds. |
stdout_raw |
str |
Full stdout from the final/last attempt. |
stderr_raw |
str |
Full stderr from the final/last attempt. |
parsed_json |
any |
Extracted JSON value, or null. |
schema_valid |
bool or null |
true/false if schema provided, null otherwise. |
schema_errors |
list[str] |
Validation error messages (empty on success). |
error |
str |
(only if success == false) Error description. |
suggestion |
str |
(only if success == false) Actionable suggestion. |
MCP Integration
exec-json ships with a built-in MCP server that exposes the CLI as a native MCP tool named run_command, ready for use with Claude Desktop, Cline, Cursor, and any MCP-compatible agent.
The server communicates exclusively via stdio (standard input/output) — no HTTP or cloud services are involved.
Quick start
After installing the package, the MCP server is available as the exec-json-mcp command:
exec-json-mcp
Or directly with Python:
python -m server
Exposed tool: run_command
| Parameter | Type | Default | Description |
|---|---|---|---|
cmd |
str |
required | Command to execute. |
retries |
int |
3 |
Maximum retry attempts. |
timeout |
float |
30.0 |
Per-attempt timeout in seconds. |
backoff |
float |
2.0 |
Exponential backoff factor. |
schema |
str |
None |
JSON schema file path or inline string. |
shell |
bool |
False |
Use shell=True. |
no_extract |
bool |
False |
Skip JSON extraction. |
The tool returns the same structured JSON output as the CLI (see Output schema).
Claude Desktop configuration
Add the following entry to your claude_desktop_config.json:
Windows: %APPDATA%\Claude\claude_desktop_config.json
macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
Linux: ~/.config/Claude/claude_desktop_config.json
{
"mcpServers": {
"exec-json": {
"command": "exec-json-mcp"
}
}
}
If the command is not in your PATH, use the full Python invocation:
{
"mcpServers": {
"exec-json": {
"command": "python",
"args": ["-m", "server"]
}
}
}
Verifying the server
To test that the server starts correctly, run:
exec-json-mcp
The server will listen on stdio and wait for MCP requests. No output means it started successfully. You can also run the provided integration test:
python -m unittest tests.test_mcp_server -v
Why AI Agents Choose This
- 🔬 Deterministic — Same input always produces the same structured JSON. No ambiguity, no parsing guesswork.
- 📦 Predictable output — Rigid JSON schema means agents can programmatically extract
parsed_json,schema_valid, anderrorwithout custom parsing. - 🔄 Automatic retry — Exponential backoff handles transient failures (network hiccups, rate limits, service restarts) without custom loop logic.
- 🛠 Robust JSON extraction — Finds valid JSON even when embedded in log noise, error messages, or multi-line text — no more brittle
greporsedpipelines.
License
MIT — see LICENSE for details.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file exec_json-1.0.0.tar.gz.
File metadata
- Download URL: exec_json-1.0.0.tar.gz
- Upload date:
- Size: 21.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
77fe1dc15b18b71c4246908fd98f3989e5b950af9178dd85d6eca247cafba80c
|
|
| MD5 |
0b30f5b42a7bcceb3a0023b3216f86fc
|
|
| BLAKE2b-256 |
4f87d998592fa4dcc660dc6fc28e83aba3dcc1aa7ff2cb3a886a0d87bcd18dea
|
File details
Details for the file exec_json-1.0.0-py3-none-any.whl.
File metadata
- Download URL: exec_json-1.0.0-py3-none-any.whl
- Upload date:
- Size: 15.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bbd6483ee908b8e025fdd527c8d68cc147c838994b07bc688c45570b5e09b714
|
|
| MD5 |
5f7c2909acc903fbb269f5fe2b0dbdd3
|
|
| BLAKE2b-256 |
850fbec7758952b3c00b4bd59ebee26445c7404dbfcb00e2eab31e91a496ff68
|