fix(mysql): wire is_available + extract_params into MySQL @tool decorators#704
Conversation
…ators All 5 MySQL diagnostic tools previously raised `TypeError: missing 2 required positional arguments: 'host' and 'database'` whenever the agent tried to invoke them. Same bug pattern as the PostgreSQL side; same fix approach. Mirror the working MariaDB pattern from app/integrations/mariadb.py: add `mysql_is_available` and `mysql_extract_params` to app/integrations/mysql.py, then wire both into all 5 MySQL tools' decorators (current processes, replication status, server status, slow queries, table stats). MySQL integration resolves credentials internally via `resolve_mysql_config`, so `extract_params` only surfaces the three identifying params: host, database, port. Credentials stay out of tool signatures and are never seen by the LLM. During the end-to-end audit on Gemini 2.5-flash the agent opportunistically invoked `get_mysql_replication_status` on RDS PostgreSQL scenarios, which failed with the same TypeError pattern (6 failures). After this fix those drop to 0.
Greptile SummaryThis PR wires the missing Confidence Score: 5/5Safe to merge; the one remaining finding is a minor edge case (whitespace-only host) unlikely to occur in practice. All five tool files are straightforward and correct. The only finding is a P2 inconsistency between app/integrations/mysql.py — minor Important Files Changed
|
…ed integration
`dict.get("port", DEFAULT)` only substitutes the default when the key is
absent. If the resolved integration stores `{"port": None}` explicitly,
`pg.get("port", DEFAULT_POSTGRESQL_PORT)` returns None and `int(None)`
raises TypeError inside postgresql_extract_params.
Use `or` so both missing and None collapse to the default. Applied
preemptively — same finding Greptile raised on the MySQL side in Tracer-Cloud#704.
…tegration Address Greptile review on Tracer-Cloud#704: `dict.get("port", DEFAULT)` only substitutes the default when the key is absent. If the resolved integration stores `{"port": None}` explicitly, `my.get("port", DEFAULT_MYSQL_PORT)` returns None and `int(None)` raises TypeError inside mysql_extract_params. Use `or` so both missing and None collapse to the default.
…tool decorators (#703) * fix(postgresql): wire is_available + extract_params into PostgreSQL @tool decorators All 5 PostgreSQL diagnostic tools previously raised `TypeError: missing 2 required positional arguments: 'host' and 'database'` whenever the agent tried to invoke them. The `@tool(...)` decorator on each was missing the `is_available` and `extract_params` callbacks, so the tool function got called with no kwargs even though `host` and `database` are required. Mirror the working MariaDB pattern from app/integrations/mariadb.py: add `postgresql_is_available` and `postgresql_extract_params` to app/integrations/postgresql.py, then wire both into all 5 PG tools' decorators (current queries, replication status, server status, slow queries, table stats). PostgreSQL integration resolves credentials internally via `resolve_postgresql_config`, so `extract_params` only surfaces the three identifying params: host, database, port. Credentials stay out of tool signatures and are never seen by the LLM. Verified against the full RDS PostgreSQL synthetic suite (make test-rds-synthetic) on Gemini 2.5-flash: PostgreSQL TypeError failures drop from 184 to 0. * fix(postgresql): harden port coercion against explicit None in resolved integration `dict.get("port", DEFAULT)` only substitutes the default when the key is absent. If the resolved integration stores `{"port": None}` explicitly, `pg.get("port", DEFAULT_POSTGRESQL_PORT)` returns None and `int(None)` raises TypeError inside postgresql_extract_params. Use `or` so both missing and None collapse to the default. Applied preemptively — same finding Greptile raised on the MySQL side in #704.
… decorators (#707) * fix(azure-sql): wire is_available + extract_params into Azure SQL @tool decorators Same @tool decorator wiring bug the PostgreSQL and MySQL sides fixed in #703 and #704, now for Azure SQL. All 5 Azure SQL diagnostic tools were silently failing with TypeError: missing 2 required positional arguments ('server' and 'database') on every agent invocation, because the decorator was missing the is_available and extract_params callbacks that inject identifying params from resolved integrations. Mirror the MariaDB/PostgreSQL/MySQL pattern: add azure_sql_is_available and azure_sql_extract_params to app/integrations/azure_sql.py, then wire both into all 5 Azure SQL tools (current queries, resource stats, server status, slow queries, wait stats). Azure SQL integration resolves credentials internally via resolve_azure_sql_config, so extract_params only surfaces the three identifying params (server, database, port). Credentials stay out of tool signatures and are never seen by the LLM. Applied Greptile's port-None hardening preemptively: int(az.get("port") or DEFAULT_AZURE_SQL_PORT). Covers both missing and explicit None port values in the resolved integration dict. Added 14 unit tests covering azure_sql_is_available (6 cases) and azure_sql_extract_params (8 cases including port-None, port-0, port-as-string, and credential isolation). Verified on make test-rds-synthetic (Gemini 2.5-flash): Azure SQL TypeError failures drop from 92 to 0 across the 15 RDS scenarios. * fix(azure-sql): harden server/database coercion against explicit None Address Greptile review on #707: `str(None).strip()` produces the literal string "None", not an empty string. If a stored integration has `{"server": null}` or `{"database": null}`, az.get("server", "") returns None (the default only applies when the key is absent), and str(None).strip() yields "None" — a misleading non-empty string. In practice is_available guards against this, but the extract_params function can be called directly in tests or future code paths and silently produce garbage. Mirror the `or` pattern already applied to port, and match the AzureSQLConfig._normalize_server / _normalize_database validators which use str(value or "").strip(). Added two unit tests covering the None case for both server and database.
References #702
Part 2 of 2. Continues the fix from #703 for the MySQL side of the same bug.
Summary
Adds the missing
is_availableandextract_paramscallbacks to the@tool(...)decorators on all 5 MySQL diagnostic tools. Same bug pattern and same fix approach as #703 for PostgreSQL. Mirrors the working MariaDB pattern atapp/integrations/mariadb.py:160-176.What changed
mysql_is_availableandmysql_extract_paramstoapp/integrations/mysql.py.@tool(...)decorator of all 5 MySQL tools (current processes, replication status, server status, slow queries, table stats).MySQL integration resolves credentials internally via
resolve_mysql_config(reading from the store/env), soextract_paramsonly surfaces the three identifying params:host,database,port. Credentials stay out of tool signatures and are never seen by the LLM.Scope: 6 files, +61 / -5 lines.
Evidence
No MySQL-specific synthetic scenario suite exists, but during the end-to-end audit (
make test-rds-syntheticon origin/main with Gemini 2.5-flash) the agent opportunistically invokedget_mysql_replication_statuson RDS PostgreSQL scenarios, which failed with the sameTypeErrorpattern — 6 failures across the suite. After this fix, those failures drop to 0 (the tool now correctly reports unavailable when no MySQL source is configured, so the agent no longer tries it on PG scenarios).make test-cov→ 2725 passed, 1 skipped, 10 warnings (no regressions)ruff checkon all changed files → cleanmypyon all changed files → cleanTest plan
make test-covpasses with no regressionsruff checkclean on all changed filesmypyclean on all changed files