install
source · Clone the upstream repo
git clone https://github.com/corv89/shannot
Claude Code · Install into ~/.claude/skills/
git clone --depth=1 https://github.com/corv89/shannot ~/.claude/skills/corv89-shannot-shannot
manifest:
SKILL.mdsource content
Shannot - Sandboxed System Diagnostics
Run diagnostic scripts in a sandbox with human approval.
Usage
# Run a script locally shannot run script.py # Run against a remote host shannot run script.py --target user@host # Dry-run mode (queue all operations for review) shannot run script.py --dry-run
After running with
--dry-run, instruct the user to open shannot approve to review and execute queued operations.
MCP Integration (v0.5.0+)
Shannot provides MCP (Model Context Protocol) integration for LLM agents like Claude.
Available Tools
sandbox_run - Execute Python 3.6 scripts with profile-based approval:
- Fast path: Auto-approved operations execute immediately
- Review path: Unapproved operations create sessions for user approval
- Blocked path: Denied operations rejected immediately
session_result - Poll status of pending sessions
Profiles
- minimal: ls, cat, grep, find
- readonly: minimal + head, tail, file, stat, wc, du
- diagnostics: readonly + df, free, ps, uptime, hostname, uname, env, id
Example MCP Usage
# Check disk space (diagnostics profile auto-approves df) sandbox_run({ "script": "import subprocess\nsubprocess.call(['df', '-h'])", "profile": "diagnostics" }) # Returns immediately with disk usage # Search files (may require approval depending on profile) sandbox_run({ "script": "import subprocess\nsubprocess.call(['find', '/home', '-name', '*.log'])", "profile": "minimal" }) # Returns session ID for user approval # User reviews: shannot approve show <session_id> # User approves: shannot approve --execute <session_id> # Poll results: session_result({"session_id": "<session_id>"})
Remote Execution
# Run diagnostics on a remote server sandbox_run({ "script": "import subprocess\nsubprocess.call(['df', '-h'])", "profile": "diagnostics", "target": "prod" # Uses configured remote target })
Installation
# Install for Claude Desktop/Code shannot mcp install --client claude-code
See MCP Documentation for complete guide.
Diagnostic Script Examples
System Health Check
# health_check.py - Comprehensive system status import subprocess print("=== System Information ===") subprocess.call(["uname", "-a"]) print("\n=== Uptime ===") subprocess.call(["uptime"]) print("\n=== Disk Usage ===") subprocess.call(["df", "-h"]) print("\n=== Memory Usage ===") subprocess.call(["free", "-h"]) print("\n=== Top Processes ===") subprocess.call(["ps", "aux", "--sort=-pcpu"])
Log Analysis
# analyze_logs.py - Search for errors in logs import subprocess print("=== Recent Errors ===") subprocess.call(["grep", "-i", "error", "/var/log/syslog"]) print("\n=== Last 50 Lines ===") subprocess.call(["tail", "-n", "50", "/var/log/syslog"])
Network Diagnostics
# network_check.py - Network status import subprocess print("=== Network Interfaces ===") subprocess.call(["ip", "addr"]) print("\n=== Listening Ports ===") subprocess.call(["ss", "-tlnp"]) print("\n=== Routing Table ===") subprocess.call(["ip", "route"])
Service Status
# service_check.py - Check systemd services import subprocess services = ["nginx", "postgresql", "redis"] for service in services: print(f"\n=== {service} ===") subprocess.call(["systemctl", "status", service, "--no-pager"])
File System Inspection
# fs_check.py - Filesystem analysis import subprocess import os print("=== Large Files ===") subprocess.call(["find", "/var", "-size", "+100M", "-type", "f"]) print("\n=== Disk I/O Stats ===") subprocess.call(["iostat", "-x", "1", "3"]) print("\n=== Mount Points ===") subprocess.call(["mount"])
Configuration Audit
# config_audit.py - Check configuration files import os configs = [ "/etc/nginx/nginx.conf", "/etc/postgresql/14/main/postgresql.conf", "/etc/redis/redis.conf" ] for config in configs: print(f"\n=== {config} ===") if os.path.exists(config): with open(config) as f: # Print first 20 lines for i, line in enumerate(f): if i >= 20: print("... (truncated)") break print(line.rstrip()) else: print("File not found")
Writing Scripts
Scripts run in a virtualized environment with Python 3.6 syntax.
Running Commands
import subprocess # Commands are intercepted and queued for approval result = subprocess.run(["ls", "-la", "/etc"], capture_output=True, text=True) print(result.stdout)
Reading Files
# File reads are allowed within the virtual filesystem with open("/etc/hostname") as f: print(f.read())
Writing Files
# Writes are queued for approval, not executed immediately with open("/tmp/output.txt", "w") as f: f.write("diagnostic results")
Python 3.6 Compatibility
Sandboxed scripts must use Python 3.6 syntax:
# Use namedtuple instead of dataclasses from collections import namedtuple Point = namedtuple('Point', ['x', 'y']) # Use Union from typing instead of pipe syntax from typing import Union def process(x): # type: (Union[int, str]) -> None pass # Use if/elif instead of match statements if value == 1: print("one") elif value == 2: print("two")
Security Model
- Reads: Allowed within virtual filesystem boundaries
- Commands: Queued for human approval (or auto-approved via profile)
- Writes: Queued for human approval
- Network: Disabled (socket calls return errors)
Tips
- Keep scripts focused on diagnostics and information gathering
- Use
to preview what operations a script will request--dry-run - Tell the user to run
to review queued operationsshannot approve - Use the appropriate profile for your use case (diagnostics for system info, minimal for restricted access)
- For remote targets, ensure SSH keys are configured