The permission scope map previously flattened every `x-fga-permissions`
annotation into a single conjunctive scope list, turning OR-alternatives
(e.g. development vs production branching on `GET /v1/.../branches`)
into impossible AND requirements. EndpointMap and McpMap now store
ScopeGroupAlternatives (string[][]) verbatim, and an item is enabled
when ALL scopes of at least ONE group are granted — matching how the
mgmt-api FGA guard actually evaluates the annotation. Edge cases follow
plain DNF semantics: `[[]]` (one empty group) is ungated and callable by
any token, `[]` (no groups) is satisfied by nobody, and endpoints with
unusable annotations are dropped rather than recorded as ungated.
The MCP tool map is rewritten as a direct audit of the Management API
endpoint each tool's handler in @supabase/mcp-server-supabase actually
calls, with the endpoint named above every entry for re-auditing. The
old two-hop derivation (tool -> legacy OAuth scope bundle -> FGA
permissions) expanded coarse bundles into conjunctions of permissions
the tool never uses, hiding tools from tokens that could genuinely call
them. The map now covers the full tool registry, including ungated
tools (search_docs, get_project_url, get_cost, confirm_cost), and a new
test diffs its keys against the installed package's tool schemas so a
dependency bump that adds or drops a tool fails CI by name.
buildAPIPermissionScopeMap now fetches the v1/v2 specs in parallel and
indexes them in one pass (their path prefixes can't collide), replacing
the lodash mergeWith custom-merger, and deep-clones the module-level
tool mapping so callers can't mutate state that outlives the request.