Community-maintained open-source project. Not affiliated with, endorsed by, or sponsored by Oracle Corporation or the MariaDB Foundation. "MySQL" and "MariaDB" trademarks belong to their respective owners. Source is publicly auditable under the MIT license.
Report privately via a GitHub Security Advisory on github.com/AIops-tools/MySQL-AIops or email zhouwei008@gmail.com. Please do not open public issues for security reports.
- Per-target MySQL account passwords live encrypted in
~/.mysql-aiops/secrets.enc(Fernet/AES-128 + scrypt-derived key; chmod 600), never inconfig.yamland never in source. The master password is never stored — only a per-store random salt and the ciphertext are on disk. - A legacy plaintext env var
MYSQL_<TARGET_NAME_UPPER>_PASSWORDis still honoured as a fallback with a deprecation warning (migrate withmysql-aiops secret migrate). - The password is passed to
pymysql.connectat connect time and held only in memory. It is never logged or echoed;config.yamlholds only host, port, database, user,ssl_mode(and optionallyssl_ca). The redacted DSN (dsn_redacted) masks it.
- All values are bound query parameters (session ids, thresholds, limits, variable values) — never string-formatted into SQL.
- The few identifiers that cannot be parameterised (schema/table/index/column
names, global variable names,
ORDER BYcolumns) are validated against a strict identifier charset / allow-lists (mysql_aiops.ops._util) and backtick-quoted before the single interpolation site; anything that is not a plain identifier is rejected, not interpolated. EXPLAINrejects multi-statement input (an embedded;is refused).- The
drop_indexundo replay path (create_index(definition=...)) is shape-gated: single statement, must beCREATE [UNIQUE] INDEX ... ON ....
Every MCP tool runs through the bundled @governed_tool harness
(mysql_aiops.governance):
- Audit — every call logged to a local SQLite DB under
~/.mysql-aiops/(relocatable viaMYSQL_AIOPS_HOME), agent-attributed, secret-redacted. - Token/runaway budget — hard ceilings (
MYSQL_MAX_TOOL_CALLS/MYSQL_MAX_TOOL_SECONDS— the env-var names the bundled harness reads) plus an on-by-default guard that trips a tight poll/retry loop. - Graduated risk tiers — secure by default: with no
~/.mysql-aiops/rules.yaml, high/critical writes are denied unlessMYSQL_AUDIT_APPROVED_BYnames an approver (MYSQL_AUDIT_RATIONALEfor why);mysql-aiops initseeds an explicit starter rules.yaml. - Undo-token recording — reversible writes fetch the real before-state
first and record a faithful inverse (
create_index↔drop_index, where drop rebuilds the definition fromSHOW CREATE TABLE;set_global_variablerestores the prior value).
Every write supports --dry-run (CLI) / dry_run=True (MCP) and requires double
confirmation at the CLI layer. Destructive/irreversible ops (kill_session,
kill_query, drop_index) are risk_level=high; mutating maintenance ops
(optimize_table, analyze_table, create_index, set_global_variable,
reset_query_stats) are medium. Irreversible ops capture prior state for the
audit record but record no undo token. SET GLOBAL is runtime-only and reports
(but does not auto-perform) the my.cnf / SET PERSIST persistence step.
ssl_mode follows MySQL client semantics (default preferred); set
verify_ca / verify_identity (with ssl_ca) on untrusted networks;
disabled is for isolated labs only.
All catalog- and query-returned text (query text, object names, error strings)
is passed through a sanitize() truncate + control-character strip before
reaching the agent. This bounds length and strips control/format characters;
semantic resistance to adversarial text must come from the consuming agent.
No webhooks, no telemetry, no outbound calls beyond the configured MySQL connection. No post-install scripts or background services.
uvx bandit -r mysql_aiops/ mcp_server/
uv run ruff check .The latest released version receives security fixes. This is a preview (0.x); pin a version in production.