You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
component_mcp_guard: schema rules don't see reads inside stored functions and triggers #247
mcp.allowed_schemas and mcp.deny_system_schemas are enforced on the server's table access events. Statements inside a stored function or trigger run on tables the calling statement has already pre-locked, so the server raises no table access event for them. A RETURN (SELECT …) subquery doesn't even raise a nested statement event.
Example, with mcp.allowed_schemas = 'd1' and d1.f_read_d2() defined as RETURN (SELECT MAX(i) FROM d2.t): SELECT d1.f_read_d2() from an MCP session returns d2's data. This holds for SQL SECURITY DEFINER functions, and for INVOKER functions when the account has grants on d2 (which is the case allowed_schemas exists for).
Not affected:
Writes inside functions and triggers are refused by mcp.read_only (nested statement check).
Stored procedures: CALL is limited to mysql_mcp under read-only, and a procedure's statements open their own tables and raise events.
Views: their base tables are checked, nested views included.
Why
In sql/sql_base.cc (open_tables), mysql_event_tracking_table_access_notify() runs before the routines' tables are added to the pre-locking list. The comment there assumes those tables get events "during the queries of these stored procedures". That holds for procedures, not for functions or triggers. No component service tells a component that a statement runs inside a function (mysql_thd_attributes has no in_sub_stmt), and EVENT_TRACKING_STORED_PROGRAM_EXECUTE fires only for CALL.
Status
Documented in docs/MCP_COMPONENTS.md (Limits), with grants as the mitigation. scripts/test-mcp-guard.sh has a check named "KNOWN LIMITATION" that shows the behavior, so it fails if the server changes.
Options
Upstream: ask MySQL to raise table access events for the pre-locked tables of routines, or to expose in_sub_stmt / the routine being executed to components.
A guard setting that refuses statements calling stored functions outside mysql_mcp. This needs a reliable way to detect a function call. No event provides one today, and parsing the statement text for function calls isn't reliable.
Keep it as a documented limitation and rely on grants.
Found while adding the RFC 0001 §5.2 item 7 / Q3 tests (askdba/mysql-mcp-server RFC 0001).
Reviewing the guard code around this issue turned up a related class the guard can catch, now in #251. Statements on the read-only allowlist that read tables without table-access events got past both schema rules: subqueries in SET/DO/CALL arguments/SHOW … WHERE, and CHECK/CHECKSUM TABLE. For example, SET @x = (SELECT … FROM mysql.user) returned the data. #251 refuses those while a schema rule is in effect. The stored-function and trigger case in this issue still stands: a component gets no signal for those reads.
Activity
askdba commentedon Nov 1, 2026
Reviewing the guard code around this issue turned up a related class the guard can catch, now in #251. Statements on the read-only allowlist that read tables without table-access events got past both schema rules: subqueries in
SET/DO/CALLarguments/SHOW … WHERE, andCHECK/CHECKSUM TABLE. For example,SET @x = (SELECT … FROM mysql.user)returned the data. #251 refuses those while a schema rule is in effect. The stored-function and trigger case in this issue still stands: a component gets no signal for those reads.