Skip to content

fix(mysql): wire is_available + extract_params into MySQL @tool decorators#704

Merged
Devesh36 merged 2 commits intoTracer-Cloud:mainfrom
ebrahim-sameh:fix/mysql-tool-injection
Apr 20, 2026
Merged

fix(mysql): wire is_available + extract_params into MySQL @tool decorators#704
Devesh36 merged 2 commits intoTracer-Cloud:mainfrom
ebrahim-sameh:fix/mysql-tool-injection

Conversation

@ebrahim-sameh
Copy link
Copy Markdown
Collaborator

References #702

Part 2 of 2. Continues the fix from #703 for the MySQL side of the same bug.

Summary

Adds the missing is_available and extract_params callbacks 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 at app/integrations/mariadb.py:160-176.

What changed

  • Added mysql_is_available and mysql_extract_params to app/integrations/mysql.py.
  • Wired both helpers into the @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), 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.

Scope: 6 files, +61 / -5 lines.

Evidence

No MySQL-specific synthetic scenario suite exists, but during the end-to-end audit (make test-rds-synthetic on origin/main with 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 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).

Test plan

  • make test-cov passes with no regressions
  • ruff check clean on all changed files
  • mypy clean on all changed files
  • MariaDB tools still work (not touched)
  • Live MySQL integration verification — not possible in my local env (no MySQL configured); happy to do a sanity-check if the maintainer has a live MySQL to point at

…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-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 20, 2026

Greptile Summary

This PR wires the missing is_available and extract_params callbacks into the @tool decorators for all 5 MySQL diagnostic tools, mirroring the working MariaDB pattern and closing the same TypeError bug fixed on the PostgreSQL side in #703. The int() cast on port (with or DEFAULT_MYSQL_PORT guard) correctly handles both None and string-typed port values from the store or env.

Confidence Score: 5/5

Safe 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 is_available and extract_params for whitespace-only host/database values — a real-but-improbable edge case that does not block the primary fix.

app/integrations/mysql.py — minor is_available / extract_params strip inconsistency

Important Files Changed

Filename Overview
app/integrations/mysql.py Adds mysql_is_available and mysql_extract_params helpers; is_available availability check is slightly inconsistent with extract_params's .strip() normalization for whitespace-only host/database values.
app/tools/MySQLCurrentProcessesTool/init.py Correctly wires mysql_is_available and mysql_extract_params into the @tool decorator; no issues.
app/tools/MySQLReplicationStatusTool/init.py Same wiring pattern applied; no issues.
app/tools/MySQLServerStatusTool/init.py Same wiring pattern applied; no issues.
app/tools/MySQLSlowQueriesTool/init.py Same wiring pattern applied; no issues.
app/tools/MySQLTableStatsTool/init.py Same wiring pattern applied; no issues.

Sequence Diagram

sequenceDiagram
    participant Agent
    participant ToolDecorator
    participant mysql_is_available
    participant mysql_extract_params
    participant MySQLTool
    participant resolve_mysql_config

    Agent->>ToolDecorator: check tool availability
    ToolDecorator->>mysql_is_available: sources dict
    mysql_is_available-->>ToolDecorator: bool (host && database present)
    alt is available
        ToolDecorator->>mysql_extract_params: sources dict
        mysql_extract_params-->>ToolDecorator: {host, database, port}
        ToolDecorator->>MySQLTool: invoke(host, database, port, ...)
        MySQLTool->>resolve_mysql_config: host, database, port
        resolve_mysql_config-->>MySQLTool: MySQLConfig (with credentials from store/env)
        MySQLTool-->>Agent: diagnostic result
    else not available
        ToolDecorator-->>Agent: skip tool (unavailable)
    end
Loading
Prompt To Fix All With AI
This is a comment left during a code review.
Path: app/integrations/mysql.py
Line: 202-205

Comment:
**Whitespace-only host passes `is_available` but strips to empty in `extract_params`**

`my.get("host")` is truthy for `"  "` (spaces), so `mysql_is_available` returns `True`, but `mysql_extract_params` then strips it to `""`. The tool is invoked with an empty host and fails to connect instead of being skipped as unavailable. Aligning the check with the strip fixes the mismatch:

```suggestion
def mysql_is_available(sources: dict[str, dict]) -> bool:
    """Check if MySQL integration identifying params are present."""
    my = sources.get("mysql", {})
    return bool(str(my.get("host", "")).strip() and str(my.get("database", "")).strip())
```

How can I resolve this? If you propose a fix, please make it concise.

Reviews (2): Last reviewed commit: "fix(mysql): harden port coercion against..." | Re-trigger Greptile

Comment thread app/integrations/mysql.py Outdated
ebrahim-sameh added a commit to ebrahim-sameh/opensre that referenced this pull request Apr 20, 2026
…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.
Devesh36 pushed a commit that referenced this pull request Apr 20, 2026
…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.
@Devesh36 Devesh36 merged commit 1fe937f into Tracer-Cloud:main Apr 20, 2026
8 checks passed
yashksaini-coder pushed a commit that referenced this pull request Apr 20, 2026
… 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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants