mirror of https://github.com/sysown/proxysql
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
172 lines
5.3 KiB
172 lines
5.3 KiB
#!/usr/bin/env bash
|
|
#
|
|
# static_harvest.sh - Wrapper for Phase 1 static discovery
|
|
#
|
|
# Triggers ProxySQL's deterministic metadata harvest via the MCP endpoint.
|
|
# No Claude Code required.
|
|
#
|
|
# Usage:
|
|
# ./static_harvest.sh --target-id TARGET_ID [--schema SCHEMA] [--notes NOTES] [--endpoint URL]
|
|
#
|
|
# Examples:
|
|
# ./static_harvest.sh --target-id tap_mysql_default
|
|
# ./static_harvest.sh --target-id tap_mysql_default --schema sales
|
|
# ./static_harvest.sh --target-id tap_pgsql_default --schema public --notes "Prod DB discovery"
|
|
# ./static_harvest.sh --endpoint https://192.168.1.100:6071/mcp/query
|
|
|
|
set -e
|
|
|
|
# Default values
|
|
ENDPOINT="${PROXYSQL_MCP_ENDPOINT:-https://127.0.0.1:6071/mcp/query}"
|
|
SCHEMA_FILTER=""
|
|
NOTES=""
|
|
TARGET_ID="${MCP_TARGET_ID:-}"
|
|
|
|
# Parse arguments
|
|
while [[ $# -gt 0 ]]; do
|
|
case $1 in
|
|
--schema)
|
|
SCHEMA_FILTER="$2"
|
|
shift 2
|
|
;;
|
|
--target-id)
|
|
TARGET_ID="$2"
|
|
shift 2
|
|
;;
|
|
--notes)
|
|
NOTES="$2"
|
|
shift 2
|
|
;;
|
|
--endpoint)
|
|
ENDPOINT="$2"
|
|
shift 2
|
|
;;
|
|
-h|--help)
|
|
echo "Usage: $0 --target-id TARGET_ID [--schema SCHEMA] [--notes NOTES] [--endpoint URL]"
|
|
echo ""
|
|
echo "Options:"
|
|
echo " --target-id ID Logical MCP target_id (required)"
|
|
echo " --schema SCHEMA Restrict harvest to one MySQL schema (optional)"
|
|
echo " --notes NOTES Optional notes for this discovery run"
|
|
echo " --endpoint URL ProxySQL MCP endpoint (default: PROXYSQL_MCP_ENDPOINT env var or https://127.0.0.1:6071/mcp/query)"
|
|
echo " -h, --help Show this help message"
|
|
echo ""
|
|
echo "Environment Variables:"
|
|
echo " PROXYSQL_MCP_ENDPOINT Default MCP endpoint URL"
|
|
echo ""
|
|
echo "Examples:"
|
|
echo " $0 --target-id tap_mysql_default"
|
|
echo " $0 --target-id tap_mysql_default --schema sales"
|
|
echo " $0 --target-id tap_pgsql_default --schema public --notes 'Prod DB discovery'"
|
|
exit 0
|
|
;;
|
|
*)
|
|
echo "Error: Unknown option: $1"
|
|
echo "Use --help for usage information"
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
if [[ -z "$TARGET_ID" ]]; then
|
|
echo "Error: --target-id is required"
|
|
echo "Use --help for usage information"
|
|
exit 1
|
|
fi
|
|
|
|
# Build JSON arguments
|
|
JSON_ARGS="{}"
|
|
JSON_ARGS=$(echo "$JSON_ARGS" | jq --arg target_id "$TARGET_ID" '. + {target_id: $target_id}')
|
|
|
|
if [[ -n "$SCHEMA_FILTER" ]]; then
|
|
JSON_ARGS=$(echo "$JSON_ARGS" | jq --arg schema "$SCHEMA_FILTER" '. + {schema_filter: $schema}')
|
|
fi
|
|
|
|
if [[ -n "$NOTES" ]]; then
|
|
JSON_ARGS=$(echo "$JSON_ARGS" | jq --arg notes "$NOTES" '. + {notes: $notes}')
|
|
fi
|
|
|
|
# Build the full JSON-RPC request
|
|
JSON_REQUEST=$(jq -n \
|
|
--argjson args "$JSON_ARGS" \
|
|
'{
|
|
jsonrpc: "2.0",
|
|
id: 1,
|
|
method: "tools/call",
|
|
params: {
|
|
name: "discovery.run_static",
|
|
arguments: $args
|
|
}
|
|
}')
|
|
|
|
# Display what we're doing
|
|
echo "=== Phase 1: Static Harvest ==="
|
|
echo "Endpoint: $ENDPOINT"
|
|
echo "Target ID: $TARGET_ID"
|
|
if [[ -n "$SCHEMA_FILTER" ]]; then
|
|
echo "Schema: $SCHEMA_FILTER"
|
|
else
|
|
echo "Schema: all schemas"
|
|
fi
|
|
if [[ -n "$NOTES" ]]; then
|
|
echo "Notes: $NOTES"
|
|
fi
|
|
echo ""
|
|
|
|
# Execute the curl command
|
|
# Disable SSL verification (-k) for self-signed certificates
|
|
curl_result=$(curl -k -s -X POST "$ENDPOINT" \
|
|
-H "Content-Type: application/json" \
|
|
-d "$JSON_REQUEST")
|
|
|
|
# Check for curl errors
|
|
if [[ $? -ne 0 ]]; then
|
|
echo "Error: Failed to connect to ProxySQL MCP endpoint at $ENDPOINT"
|
|
echo "Make sure ProxySQL is running with MCP enabled."
|
|
exit 1
|
|
fi
|
|
|
|
# Check for database directory errors
|
|
if echo "$curl_result" | grep -q "no such table: fts_objects"; then
|
|
echo ""
|
|
echo "Error: FTS table missing. This usually means the discovery catalog directory doesn't exist."
|
|
echo "Please create it:"
|
|
echo " sudo mkdir -p /var/lib/proxysql"
|
|
echo " sudo chown \$USER:\$USER /var/lib/proxysql"
|
|
echo "Then restart ProxySQL."
|
|
exit 1
|
|
fi
|
|
|
|
# Pretty-print the result
|
|
echo "$curl_result" | jq .
|
|
|
|
# Check for JSON-RPC errors
|
|
if echo "$curl_result" | jq -e '.error' > /dev/null 2>&1; then
|
|
echo ""
|
|
echo "Error: Server returned an error:"
|
|
echo "$curl_result" | jq -r '.error.message'
|
|
exit 1
|
|
fi
|
|
|
|
# Display summary - extract from nested content[0].text JSON string
|
|
echo ""
|
|
if echo "$curl_result" | jq -e '.result.content[0].text' > /dev/null 2>&1; then
|
|
# Extract the JSON string from content[0].text and parse it
|
|
INNER_JSON=$(echo "$curl_result" | jq -r '.result.content[0].text' 2>/dev/null)
|
|
|
|
if [[ -n "$INNER_JSON" ]]; then
|
|
RUN_ID=$(echo "$INNER_JSON" | jq -r '.run_id // empty')
|
|
OBJECTS_COUNT=$(echo "$INNER_JSON" | jq -r '.objects.table // 0')
|
|
COLUMNS_COUNT=$(echo "$INNER_JSON" | jq -r '.columns // 0')
|
|
INDEXES_COUNT=$(echo "$INNER_JSON" | jq -r '.indexes // 0')
|
|
FKS_COUNT=$(echo "$INNER_JSON" | jq -r '.foreign_keys // 0')
|
|
|
|
echo "=== Harvest Summary ==="
|
|
echo "Run ID: $RUN_ID"
|
|
echo "Objects discovered: $OBJECTS_COUNT"
|
|
echo "Columns discovered: $COLUMNS_COUNT"
|
|
echo "Indexes discovered: $INDEXES_COUNT"
|
|
echo "Foreign keys discovered: $FKS_COUNT"
|
|
fi
|
|
fi
|