secubox-openwrt/package/secubox/secubox-mcp-server/files/usr/lib/secubox-mcp/protocol.sh
CyberMind-FR 0e0749ed08 feat: Add threat-analyst, dns-guard, mcp-server and DNS provider DynDNS
New packages:
- secubox-threat-analyst: AI-powered threat analysis with CrowdSec integration
- luci-app-threat-analyst: LuCI dashboard for threat intelligence
- secubox-dns-guard: DNS security monitoring and blocking
- secubox-mcp-server: Model Context Protocol server for AI assistant integration

Enhancements:
- dns-provider: Add DynDNS support (dyndns, get, update, domains commands)
- gandi.sh: Full DynDNS with WAN IP detection and record updates
- luci-app-dnsguard: Upgrade to v1.1.0 with improved dashboard

Infrastructure:
- BIND9 DNS setup for secubox.in with CAA records
- Wildcard SSL certificates via DNS-01 challenge
- HAProxy config fixes for secubox.in subdomains
- Mail server setup with Roundcube webmail

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 08:30:28 +01:00

220 lines
8.1 KiB
Bash

# SecuBox MCP Protocol Handler
# JSON-RPC 2.0 implementation for Model Context Protocol
# Sourced by /usr/bin/secubox-mcp
# =============================================================================
# JSON HELPERS
# =============================================================================
# Parse a JSON field using jsonfilter
json_get() {
local json="$1"
local path="$2"
echo "$json" | jsonfilter -e "$path" 2>/dev/null
}
# Escape a string for JSON output
json_escape() {
printf '%s' "$1" | sed 's/\\/\\\\/g; s/"/\\"/g; s/ /\\t/g' | tr '\n' ' '
}
# =============================================================================
# RESPONSE BUILDERS
# =============================================================================
send_response() {
local id="$1"
local result="$2"
printf '{"jsonrpc":"2.0","id":%s,"result":%s}\n' "$id" "$result"
}
send_error() {
local id="$1"
local code="$2"
local message="$3"
if [ "$id" = "null" ] || [ -z "$id" ]; then
printf '{"jsonrpc":"2.0","id":null,"error":{"code":%d,"message":"%s"}}\n' "$code" "$message"
else
printf '{"jsonrpc":"2.0","id":%s,"error":{"code":%d,"message":"%s"}}\n' "$id" "$code" "$message"
fi
}
# =============================================================================
# MCP PROTOCOL HANDLERS
# =============================================================================
# Handle initialize request
send_initialize_response() {
local id="$1"
cat <<EOF
{"jsonrpc":"2.0","id":$id,"result":{"protocolVersion":"$MCP_VERSION","capabilities":{"tools":{}},"serverInfo":{"name":"$SERVER_NAME","version":"$SERVER_VERSION"}}}
EOF
}
# Handle initialized notification (no response needed)
handle_initialized() {
logger -t secubox-mcp "Client initialized"
}
# Handle tools/list request
send_tools_list() {
local id="$1"
# Build tools array based on allowed tools in config
local tools_json='['
local first=1
# Get allowed tools from UCI
local allowed_tools=$(uci -q get ${CONFIG}.main.allowed_tool 2>/dev/null || echo "")
# Tool definitions
for tool in $allowed_tools; do
[ $first -eq 0 ] && tools_json="${tools_json},"
first=0
case "$tool" in
crowdsec.alerts)
tools_json="${tools_json}"'{"name":"crowdsec.alerts","description":"Get active CrowdSec security alerts","inputSchema":{"type":"object","properties":{"limit":{"type":"number","description":"Maximum alerts to return (default: 50)"}},"required":[]}}'
;;
crowdsec.decisions)
tools_json="${tools_json}"'{"name":"crowdsec.decisions","description":"Get active CrowdSec blocking decisions","inputSchema":{"type":"object","properties":{"limit":{"type":"number","description":"Maximum decisions to return (default: 50)"}},"required":[]}}'
;;
waf.logs)
tools_json="${tools_json}"'{"name":"waf.logs","description":"Get WAF threat events from mitmproxy","inputSchema":{"type":"object","properties":{"limit":{"type":"number","description":"Maximum events to return (default: 100)"}},"required":[]}}'
;;
dns.queries)
tools_json="${tools_json}"'{"name":"dns.queries","description":"Get DNS query statistics from AdGuard Home","inputSchema":{"type":"object","properties":{},"required":[]}}'
;;
network.flows)
tools_json="${tools_json}"'{"name":"network.flows","description":"Get network traffic summary","inputSchema":{"type":"object","properties":{"interface":{"type":"string","description":"Network interface (optional)"}},"required":[]}}'
;;
system.metrics)
tools_json="${tools_json}"'{"name":"system.metrics","description":"Get system metrics (CPU, memory, disk, temperature)","inputSchema":{"type":"object","properties":{},"required":[]}}'
;;
wireguard.status)
tools_json="${tools_json}"'{"name":"wireguard.status","description":"Get WireGuard VPN tunnel status","inputSchema":{"type":"object","properties":{"interface":{"type":"string","description":"WireGuard interface (optional, shows all if omitted)"}},"required":[]}}'
;;
uci.get)
tools_json="${tools_json}"'{"name":"uci.get","description":"Read OpenWrt UCI configuration value","inputSchema":{"type":"object","properties":{"key":{"type":"string","description":"UCI key (e.g., network.lan.ipaddr)"}},"required":["key"]}}'
;;
uci.set)
tools_json="${tools_json}"'{"name":"uci.set","description":"Write OpenWrt UCI configuration value","inputSchema":{"type":"object","properties":{"key":{"type":"string","description":"UCI key"},"value":{"type":"string","description":"Value to set"}},"required":["key","value"]}}'
;;
ai.analyze_threats)
tools_json="${tools_json}"'{"name":"ai.analyze_threats","description":"AI-powered analysis of CrowdSec alerts with recommendations (requires LocalAI)","inputSchema":{"type":"object","properties":{},"required":[]}}'
;;
ai.cve_lookup)
tools_json="${tools_json}"'{"name":"ai.cve_lookup","description":"AI-powered CVE vulnerability analysis and mitigation advice","inputSchema":{"type":"object","properties":{"cve":{"type":"string","description":"CVE ID (e.g., CVE-2024-1234)"}},"required":["cve"]}}'
;;
ai.suggest_waf_rules)
tools_json="${tools_json}"'{"name":"ai.suggest_waf_rules","description":"AI-powered WAF/mitmproxy filter rule suggestions based on threat patterns","inputSchema":{"type":"object","properties":{},"required":[]}}'
;;
ai.explain_ban)
tools_json="${tools_json}"'{"name":"ai.explain_ban","description":"AI explanation of why an IP was banned by CrowdSec","inputSchema":{"type":"object","properties":{"ip":{"type":"string","description":"IP address to explain"}},"required":["ip"]}}'
;;
ai.security_posture)
tools_json="${tools_json}"'{"name":"ai.security_posture","description":"AI-powered security posture assessment of the SecuBox appliance","inputSchema":{"type":"object","properties":{},"required":[]}}'
;;
esac
done
tools_json="${tools_json}]"
printf '{"jsonrpc":"2.0","id":%s,"result":{"tools":%s}}\n' "$id" "$tools_json"
}
# Check if a tool is allowed
is_tool_allowed() {
local tool="$1"
local allowed_tools=$(uci -q get ${CONFIG}.main.allowed_tool 2>/dev/null || echo "")
for allowed in $allowed_tools; do
[ "$allowed" = "$tool" ] && return 0
done
return 1
}
# Execute a tool
execute_tool() {
local id="$1"
local tool_name="$2"
local arguments="$3"
# Check if tool is allowed
if ! is_tool_allowed "$tool_name"; then
send_error "$id" -32601 "Tool not allowed: $tool_name"
return
fi
# Source tool module and execute
local module_name="${tool_name%%.*}"
local func_name="tool_$(echo "$tool_name" | tr '.' '_')"
local tool_file="${TOOLS_DIR}/${module_name}.sh"
if [ ! -f "$tool_file" ]; then
send_error "$id" -32601 "Tool module not found: $module_name"
return
fi
# Source the tool module
. "$tool_file"
# Check if function exists
if ! type "$func_name" >/dev/null 2>&1; then
send_error "$id" -32601 "Tool function not found: $func_name"
return
fi
# Execute tool and capture result
local result
result=$("$func_name" "$arguments" 2>/dev/null)
if [ $? -eq 0 ] && [ -n "$result" ]; then
# Wrap result in content array for MCP
printf '{"jsonrpc":"2.0","id":%s,"result":{"content":[{"type":"text","text":%s}]}}\n' "$id" "$result"
else
send_error "$id" -32000 "Tool execution failed"
fi
}
# =============================================================================
# MAIN REQUEST HANDLER
# =============================================================================
handle_request() {
local request="$1"
# Parse JSON-RPC fields
local method=$(json_get "$request" '@.method')
local id=$(json_get "$request" '@.id')
local params=$(json_get "$request" '@.params')
# Handle missing id (notification)
[ -z "$id" ] && id="null"
# Route by method
case "$method" in
initialize)
send_initialize_response "$id"
;;
initialized)
handle_initialized
# No response for notifications
;;
tools/list)
send_tools_list "$id"
;;
tools/call)
local tool_name=$(json_get "$request" '@.params.name')
local arguments=$(json_get "$request" '@.params.arguments')
execute_tool "$id" "$tool_name" "$arguments"
;;
ping)
send_response "$id" '{}'
;;
*)
send_error "$id" -32601 "Method not found: $method"
;;
esac
}