fix: select query_logs dialect via logsDialect - #357
Conversation
Add an optional `logsDialect` ('clickhouse' | 'bigquery') to
`DebuggingOperations` so each platform declares which SQL its logs
endpoint speaks. `query_logs` picks the whole dialect-appropriate
description and `sql` param hint from a single `queryLogsByDialect`
table instead of hardcoding ClickHouse copy — no environment-conditional
prose the model has to reason about.
Defaults to 'clickhouse' when unset, so existing platform implementers
are unaffected. Hosted (api-platform) declares 'clickhouse'; the tool
gating (`hidden: Boolean(queryLogs)`) is unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Assemble the per-dialect description and `sql` hint from shared wording, supplying only the two parts that differ per dialect: the dialect `name` and the `schemaHint`. The ClickHouse description and `sql` hint remain byte-identical to before. Correct the BigQuery hint to match the real self-hosted Logflare implementation (apps/studio/lib/api/self-hosted/logs.ts): per-service source tables with fields nested under `metadata` (read via `cross join unnest(metadata)`), dropping `function_logs` / `workflow_run_logs`, which self-hosted does not serve. Verified end-to-end against a local self-hosted stack: query_logs passes BigQuery-dialect SQL through to Logflare and returns real logs. Also assert in tests that the BigQuery hint omits the unsupported sources. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Coverage Report for CI Build 31418601124Warning No base build found for commit Coverage: 96.512%Details
Uncovered ChangesNo uncovered changes found. Coverage RegressionsRequires a base build to compare against. How to fix this → Coverage Stats
💛 - Coveralls |
commit: |
| clickhouse: buildQueryLogsCopy({ | ||
| name: 'ClickHouse', | ||
| schemaHint: | ||
| "Logs are exposed through a `logs` table; filter by `source` (e.g. 'edge_logs', 'postgres_logs', 'function_edge_logs', 'function_logs', 'auth_logs', 'storage_logs', 'realtime_logs', 'workflow_run_logs') and read nested fields via `log_attributes['<key>']`.", |
There was a problem hiding this comment.
Unrelated, but in my testing I noticed a postgrest_logs table available in staging not part of this list.
I gather this is just an example not meant to be exhaustive, but I wonder:
- Will agents reliably discover the list of available sources before querying? Is there risk of hallucination if our description doesn't match the real full schema?
- Will they understand what each log source represents? For example, will they know what
function_edge_logsmeans compared tofunction_logs?
Not something we need to solve in this PR but maybe worth revisiting w/ evals.
There was a problem hiding this comment.
Thanks @mattrossman. I dug in and made a change plus stood up evals to keep us honest about it. Result below.
The log set is genuinely incomplete. I checked a live self-hosted Logflare catalog (_analytics.sources) and it exposes postgREST.logs.prod and pgbouncer.logs.prod, neither of which we listed. So I trimmed it to a few representative examples marked non-exhaustive, and added a discovery step, for both dialects:
ClickHouse — from:
A read-only ClickHouse SQL query to run against the project's unified logs stream. Logs are exposed through a
logstable; filter bysource(e.g. 'edge_logs', 'postgres_logs', 'function_edge_logs', 'function_logs', 'auth_logs', 'storage_logs', 'realtime_logs', 'workflow_run_logs') and read nested fields vialog_attributes['<key>'].
to:
A read-only ClickHouse SQL query to run against the project's unified logs stream. Logs are exposed through a
logstable; filter bysource(common values include 'edge_logs', 'postgres_logs', and 'function_edge_logs', but this list is not exhaustive — runselect distinct source from logsto discover the sources available for this project) and read nested fields vialog_attributes['<key>'].
BigQuery — from:
A read-only BigQuery SQL query to run against the project's unified logs stream. Each service has its own source table (e.g. 'edge_logs', 'postgres_logs', 'function_edge_logs', 'auth_logs', 'storage_logs', 'realtime_logs'); nested fields live under
metadata, read viacross join unnest(metadata) as mand thenm.<field>(unnest further for nested structs such asm.requestorm.parsed).
to:
A read-only BigQuery SQL query to run against the project's logs. Each service has its own source table (common ones include 'edge_logs', 'postgres_logs', and 'function_edge_logs', but the set is not exhaustive — other per-service tables, e.g. Data API and pooler logs, also exist); read nested fields by cross joining
unnest(metadata) as mand thenm.<field>(unnest further for nested structs such asm.requestorm.parsed).
(BigQuery has no unified stream to select distinct source from, so it names the real-but-unlisted families instead. I also fixed "unified logs stream" → "logs" there, since it's per-table.)
Created 2 evals to answer both questions
Two "ephemeral" regression evals, pinned to this PR's preview build: supabase/evals#185. CI run across gpt-5.4-mini + claude-code sonnet 5 — all 4 pairs pass:
| Eval | gpt-5.4-mini (codex) | claude-code sonnet 5 |
|---|---|---|
| logs-002 (undescribed source) | ✅ 3/3 | ✅ 3/3 |
| logs-003 (function streams) | ✅ 2/3 | ✅ 3/3 |
There was a problem hiding this comment.
Answering directly to your questions:
do agents discover sources / is there hallucination risk?
investigate-logs-002-undescribed-source-discovery: the root cause (permission denied for table orders, 403) lives only in postgrest_logs, which the description never lists.
Result: both models solved it and neither hallucinated
- both first enumerated the available sources (
select … group by source/distinct source) - then queried
postgrest_logsby name.
So they don't blindly trust the listed set; given the unified stream they discover what's actually there.
do they understand
function_edge_logsvsfunction_logs?
investigate-logs-003-function-stream-disambiguation: the bug (charging dollars as cents) is only in the runtime console (function_logs), while the request stream (function_edge_logs) shows healthy 200s.
Result: both models surfaced the runtime bug and weren't fooled by the 200s.
- claude-code sonnet 5 specifically queried the
function_logsstream (understood the distinction, 3/3); - gpt-5.4-mini reached it by content-filtering the unified stream rather than naming the source (2/3).
mattrossman
left a comment
There was a problem hiding this comment.
Tested the preview build working against staging. Agreed this logsDialect approach adds flexibility for easier migration to query_logs across environments.
Based on eval findings (supabase/evals#185): capable models query the unified `logs` stream and filter by content/`log_attributes` rather than relying on the enumerated source list, and reach sources the description never lists. The exhaustive-looking list added little and risked reading as complete (false authority → missed sources). Shrink the ClickHouse `sql` hint's source list to three representative examples marked non-exhaustive, and point the model at `select distinct source from logs` to discover the rest — so correctness no longer depends on a frozen list staying current. Apply the same non-exhaustive framing to BigQuery (no unified stream to enumerate, so it names the real-but-unlisted Data API / pooler tables instead), and fix the BigQuery copy to say "logs" rather than "unified logs stream" via a per-dialect `logsNoun`. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
🤖 I have created a release *beep* *boop* --- <details><summary>mcp-utils: 0.6.0</summary> ## [0.6.0](mcp-utils-v0.5.1...mcp-utils-v0.6.0) (2026-08-10) ### Features * hide tools from tools/list ([#334](#334)) ([d80471a](d80471a)) </details> <details><summary>mcp-server-supabase: 0.10.0</summary> ## [0.10.0](mcp-server-supabase-v0.9.0...mcp-server-supabase-v0.10.0) (2026-08-10) ### ⚠ BREAKING CHANGES * add query_logs tool for custom log queries ([#333](#333)) ### Features * add --content-api-url flag and SUPABASE_CONTENT_API_URL env var ([#343](#343)) ([6fcaaa3](6fcaaa3)) * add query_logs tool for custom log queries ([#333](#333)) ([798806b](798806b)) * hide tools from tools/list ([#334](#334)) ([d80471a](d80471a)) ### Bug Fixes * hide read-only mode ([#349](#349)) ([5cda067](5cda067)) * **pg-meta:** pair composite FK columns positionally to avoid cartesi… ([#317](#317)) ([10af00b](10af00b)) * select query_logs dialect via logsDialect ([#357](#357)) ([80ff453](80ff453)) </details> --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: supabase-releaser[bot] <223506987+supabase-releaser[bot]@users.noreply.github.com>
Adds an optional
logsDialect('clickhouse' | 'bigquery') toDebuggingOperationsso each platform declares which SQL its logs endpoint speaks.query_logsthen picks its whole dialect-appropriate description andsqlparam hint from a single lookup table, instead of hardcoding ClickHouse copy — no environment-conditional prose the model has to reason about. Defaults to'clickhouse'when unset, so existing platform implementers are unaffected.Implements Matt's
logsDialectsuggestion raised in the #333 review.Why: it lets
query_logsroll out across every environment without env-specific branching. Hosted stays ClickHouse; self-hosted/CLI can keep its BigQuery-backed logs endpoint and declarelogsDialect: 'bigquery'. This unblocks shipping the MCP server to self-hosted (studio) regardless of whether it's on BigQuery or ClickHouse, rather than holding the studio image back until self-hosted moves to ClickHouse.Note
The comment sketched "reuse the
get_logsdescription forbigquery" — I instead givequery_logsa proper BigQuery-dialect description, sinceget_logsdescribes aserviceparam whilequery_logstakessql. The BigQuerysqlhint mirrors the canonical Logflare self-hosted schema (cross join unnest(metadata)); worth confirming when studio wires up the platform.Refs AI-1046