Skip to content

Instantly share code, notes, and snippets.

@R44VC0RP
Created February 7, 2026 18:47
Show Gist options
  • Select an option

  • Save R44VC0RP/fa297e8db729ddf0699aec43c40d6e68 to your computer and use it in GitHub Desktop.

Select an option

Save R44VC0RP/fa297e8db729ddf0699aec43c40d6e68 to your computer and use it in GitHub Desktop.
#!/usr/bin/env bash
# solo-mcp: Stateless CLI for Solo process manager via MCP protocol
# Usage:
# solo-mcp list-projects
# solo-mcp list [project_id]
# solo-mcp status <project_id> <process_name>
# solo-mcp restart <project_id> <process_name>
# solo-mcp start <project_id> <process_name>
# solo-mcp stop <project_id> <process_name>
# solo-mcp restart-all <project_id>
# solo-mcp start-all <project_id>
# solo-mcp stop-all <project_id>
# solo-mcp output <project_id> <process_name> [lines]
# solo-mcp ports <project_id> <process_name>
# solo-mcp stats <project_id>
set -euo pipefail
SOLO_MCP_PORT="${SOLO_MCP_PORT:-45678}"
SOLO_MCP_URL="http://localhost:${SOLO_MCP_PORT}/"
# --- helpers ---
mcp_call() {
local id="$1" method="$2" params="$3"
curl -sf -X POST "$SOLO_MCP_URL" \
-H "Content-Type: application/json" \
-d "{\"jsonrpc\":\"2.0\",\"id\":${id},\"method\":\"${method}\",\"params\":${params}}" \
2>/dev/null | sed 's/^data: //'
}
mcp_tool() {
local tool_name="$1" args="$2"
mcp_call 1 "tools/call" "{\"name\":\"${tool_name}:${SUFFIX}\",\"arguments\":${args}}"
}
get_suffix() {
local tools_resp
tools_resp=$(mcp_call 1 "tools/list" "{}")
SUFFIX=$(echo "$tools_resp" | python3 -c "
import sys, json
data = json.loads(sys.stdin.read())
tools = data.get('result', {}).get('tools', [])
for t in tools:
if ':' in t['name']:
print(t['name'].split(':')[-1])
break
" 2>/dev/null)
if [ -z "$SUFFIX" ]; then
echo "Error: Could not connect to Solo MCP server on port ${SOLO_MCP_PORT}" >&2
exit 1
fi
}
select_project() {
local project_id="$1"
local resp
resp=$(mcp_tool "select_project" "{\"project_id\":${project_id}}")
local is_error
is_error=$(echo "$resp" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('result',{}).get('isError',False))" 2>/dev/null)
if [ "$is_error" = "True" ]; then
echo "$resp" | python3 -c "import sys,json; r=json.loads(sys.stdin.read()); [print(c['text']) for c in r['result']['content']]" 2>/dev/null
exit 1
fi
}
extract_text() {
python3 -c "
import sys, json
data = json.loads(sys.stdin.read())
result = data.get('result', {})
if result.get('isError'):
for c in result.get('content', []):
print('Error:', c.get('text', ''))
sys.exit(1)
for c in result.get('content', []):
print(c.get('text', ''))
" 2>/dev/null
}
# --- commands ---
cmd_list_projects() {
get_suffix
mcp_tool "list_projects" "{}" | extract_text
}
cmd_list() {
local project_id="${1:-}"
get_suffix
if [ -n "$project_id" ]; then
select_project "$project_id"
mcp_tool "list_processes" "{}" | extract_text
else
mcp_tool "list_projects" "{}" | extract_text
fi
}
cmd_status() {
local project_id="$1" process_name="$2"
get_suffix
select_project "$project_id"
mcp_tool "get_process_status" "{\"process_name\":\"${process_name}\"}" | extract_text
}
cmd_process_action() {
local action="$1" project_id="$2" process_name="$3"
get_suffix
select_project "$project_id"
mcp_tool "${action}_process" "{\"process_name\":\"${process_name}\"}" | extract_text
}
cmd_project_action() {
local action="$1" project_id="$2"
get_suffix
select_project "$project_id"
mcp_tool "${action}_all_processes" "{}" | extract_text
}
cmd_output() {
local project_id="$1" process_name="$2" lines="${3:-50}"
get_suffix
select_project "$project_id"
mcp_tool "get_process_output" "{\"process_name\":\"${process_name}\",\"lines\":${lines}}" | extract_text
}
cmd_ports() {
local project_id="$1" process_name="$2"
get_suffix
select_project "$project_id"
mcp_tool "get_process_ports" "{\"process_name\":\"${process_name}\"}" | extract_text
}
cmd_stats() {
local project_id="$1"
get_suffix
select_project "$project_id"
mcp_tool "get_project_stats" "{}" | extract_text
}
cmd_project_status() {
local project_id="$1"
get_suffix
select_project "$project_id"
mcp_tool "get_project_status" "{}" | extract_text
}
# --- main ---
usage() {
cat <<'EOF'
solo-mcp: Stateless CLI for Solo process manager
Usage:
solo-mcp list-projects List all projects
solo-mcp list [project_id] List processes (optionally for a project)
solo-mcp project-status <project_id> Get project status summary
solo-mcp status <project_id> <process_name> Get process status
solo-mcp restart <project_id> <process_name> Restart a process
solo-mcp start <project_id> <process_name> Start a process
solo-mcp stop <project_id> <process_name> Stop a process
solo-mcp restart-all <project_id> Restart all processes
solo-mcp start-all <project_id> Start all processes
solo-mcp stop-all <project_id> Stop all processes
solo-mcp output <project_id> <name> [lines] Get process output
solo-mcp ports <project_id> <process_name> Get process ports
solo-mcp stats <project_id> Get project resource stats
Environment:
SOLO_MCP_PORT MCP server port (default: 45678)
EOF
}
case "${1:-help}" in
list-projects) cmd_list_projects ;;
list) cmd_list "${2:-}" ;;
project-status) cmd_project_status "$2" ;;
status) cmd_status "$2" "$3" ;;
restart) cmd_process_action "restart" "$2" "$3" ;;
start) cmd_process_action "start" "$2" "$3" ;;
stop) cmd_process_action "stop" "$2" "$3" ;;
restart-all) cmd_project_action "restart" "$2" ;;
start-all) cmd_project_action "start" "$2" ;;
stop-all) cmd_project_action "stop" "$2" ;;
output) cmd_output "$2" "$3" "${4:-50}" ;;
ports) cmd_ports "$2" "$3" ;;
stats) cmd_stats "$2" ;;
help|--help|-h) usage ;;
*) echo "Unknown command: $1" >&2; usage >&2; exit 1 ;;
esac
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment