## Context
Shifts all remaining dashboard queries into pg-meta so that we
centralize all manually written queries in one place
Having them in packages/pg-meta also allows us to write tests for them
## To test
Just needs a smoke test on
- Role Impersonation
- Lints
- Data API
- Database
- Enumerated Types
- Integrations
- Foreign Data Wrappers
- Vault
## Summary
- MCP `getSecurityAdvisors` and `getPerformanceAdvisors` now pass
`exposedSchemas` to `getLints`, fixing empty advisor results in
local/self-hosted environments
- Extracts `DEFAULT_EXPOSED_SCHEMAS` constant shared between the MCP
handler and the `run-lints` API route (cc @joshenlim related
https://github.com/supabase/supabase/pull/40043)
- Adds unit tests for `enrichLintsQuery` and the MCP advisor operations
## The bug
The MCP advisor tools (`get_advisors`) return empty arrays (`[]`) for
**all** scenarios when running locally via `supabase start`. No security
or performance advisors are surfaced, even when the database has clear
issues (e.g., tables with no RLS).
### Root cause
In `lib/api/self-hosted/mcp.ts`, both `getSecurityAdvisors` and
`getPerformanceAdvisors` call `getLints({ headers })` **without passing
`exposedSchemas`**:
```typescript
// Before (mcp.ts:131)
const { data, error } = await getLints({ headers })
```
When `exposedSchemas` is `undefined`, `enrichLintsQuery` in `lints.ts`
skips the `SET LOCAL pgrst.db_schemas = '...'` SQL statement:
```typescript
// lints.ts:23
${!!exposedSchemas ? `set local pgrst.db_schemas = '${exposedSchemas}';` : ''}
```
Without this GUC being set, the splinter SQL queries filter results
using `current_setting('pgrst.db_schemas', 't')` — which returns an
empty string in local environments. Every schema-filtered lint matches
no schemas and returns zero rows.
### Why this only affects local/self-hosted environments
In **hosted Supabase**, PostgREST sets the `pgrst.db_schemas` GUC on its
own database connections based on the project's API configuration. The
Studio MCP server in production reads the same project configuration, so
the GUC is already available.
**Locally**, PostgREST runs in a separate Docker container and only sets
this GUC on _its own_ connections. Studio connects directly to
PostgreSQL (bypassing PostgREST), so
`current_setting('pgrst.db_schemas', 't')` returns `''`.
The HTTP API endpoint (`/api/platform/.../run-lints`) already worked
because `run-lints.ts` passes `exposedSchemas: 'public, storage'` — this
parameter was simply never added to the MCP code path.
## How we verified the fix
### 1. Tests written to fail against the previous code
We wrote two test files that target the exact bug:
**`tests/unit/lints/enrichLintsQuery.test.ts`** — validates the SQL
generation:
- Confirms `SET LOCAL pgrst.db_schemas` is included when
`exposedSchemas` is provided
- Confirms it's omitted when `undefined` or empty (documenting current
behavior)
**`tests/unit/lints/mcp-advisors.test.ts`** — validates the MCP
operations:
- Asserts `getSecurityAdvisors` passes `exposedSchemas` to `getLints`
- Asserts `getPerformanceAdvisors` passes `exposedSchemas` to `getLints`
- Asserts the value matches `DEFAULT_EXPOSED_SCHEMAS`
- Verifies SECURITY/PERFORMANCE category filtering still works
Before the fix, the two `exposedSchemas` assertions failed:
```
FAIL getSecurityAdvisors should pass exposedSchemas to getLints
→ expected { Object (headers) } to have property "exposedSchemas"
FAIL getPerformanceAdvisors should pass exposedSchemas to getLints
→ expected { Object (headers) } to have property "exposedSchemas"
```
### 2. Fix applied, all tests pass
After adding `exposedSchemas: DEFAULT_EXPOSED_SCHEMAS` to both MCP
operations, all 14 tests pass (9 new + 5 existing MCP tests).
## Test plan
run `supabase start`, create a table without RLS, call `get_advisors`
via MCP — should return `rls_disabled_in_public` lint
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>