Repository navigation
SQLDesk 0.6.0 — MCP
A tool server over your warehouse, a catalog built from the queries people have already written, and an audit of everything it was asked.
MCP
- One endpoint, JSON-RPC over streamable HTTP, authenticated with the API key on your profile page.
- No service account, and no way to configure one. Every call runs as that user and sees only the data sources that user can read — a tool server with more access than its user is a way to launder permissions.
- Eight tools, in the order they are meant to be used.
find_queriesandfind_dashboardsto look for work that already exists;find_context,expand_tableandlist_data_sourcesto understand the data;check_sqlfor the shape andexplain_queryfor the cost; thenrun_query. run_queryreturns at most 1000 rows — the editor's own ceiling, reused rather than invented — and runs on a worker like any other query, so it appears in Admin under the name of whoever's key it was and can be cancelled there.- Every request leaves an audit row, including the refused ones. Never the question somebody put to their client, and never the SQL that came back.
MCP gives no more than the editor gives
run_queryandexplain_querycheck what the editor checks, paused sources included.- Reads only. One statement —
SELECT,WITH,SHOW,DESCRIBE— refusing a write anywhere in it, aDELETEinside aWITHandSELECT ... INTOincluded. That stops a model doing damage by accident; the database account's grants are still what stop it on purpose, so give each data source a read-only user. - Bounded. One request waits at most ten seconds less than gunicorn's timeout, across all its calls. Batches hold at most ten messages, SQL at most 100,000 characters.
A catalog, built from what you already have
You have no data hub and most installs never will. But the warehouse states its own structure, several engines carry COMMENT ON text nobody was reading, and every dashboard is built on SQL somebody wrote and saved.
- Harvesting reads all three: what exists, what it is called, which tables anyone uses, which columns anyone selects, which joins anyone writes.
- It learns only from queries that have run in the last week — measured by when a query last ran rather than when it was last edited, because a dashboard refreshed every morning and untouched for a year is the most important thing in the warehouse.
- On a schedule, and on demand. Admin → Catalog has a Harvest button per data source, and a Harvest new data sources that fills in what a source added this morning without re-reading every other one.
Measures are proposed, never assumed
SUM(amount) AS gross_revenuein a saved query is a person naming a metric, and the alias they chose becomes its name.- Admin → Catalog shows each proposal with how many distinct queries define it that way.
- Agree, or Deny. It reaches a model only once somebody agrees it — and a proposal nobody can reject comes back every night until the list stops being read.
- Aggregates across a join and arithmetic around an aggregate are refused, not guessed. A metric definition that is merely plausible is worse than none.
The semantic layer lives in git
manage ai exportwrites it as cube-shaped YAML, one file per table, into a directory you can commit from;manage ai importreads it back on deploy.- A Download button too, because the person writing the descriptions is often not the person with access to a container.
- SQLDesk never speaks to git itself. Importing never creates anything the warehouse has not stated, nor lets a file redefine what a measure computes.
Core and add-ons
- Core: server, worker, scheduler, Postgres, Redis.
- Add-ons: MCP & Catalog, and Rendering. Off until asked for — compose profiles
mcpandrendering, ormcp.enabledandrendering.enabledin the chart. - The published image is the same size either way. The renderer is its own image and runs as a non-root user.
- A Helm chart for Kubernetes, with NodePort for minikube.
Also in this release
- File uploads are confined to their own folder. The DuckDB behind uploads let SQL read any file the worker could — another organization's uploads,
/etc/passwd— write over the application, and install extensions. Each upload source's SQL now reads its own uploads and nothing else, with the setting locked so SQL cannot switch it back. - Dashboard export is a one-page report. Past twelve widgets or one page tall, Export says so straight away. What fits exports in under a second.
- A panel shows its chart's description, not its query's — as text under the title, rather than an "i" mark nobody on a phone or a wall screen could read.
- A security review of everything reachable from outside: five passes over authentication, the REST API, the query runners, MCP and the browser. Most of what it found needed a signed-in account or a foothold on the cluster network; none of it was reachable anonymously from the internet. Details under
0.6.0-rc.4in the changelog. - Twelve guide pages, with the in-app help links pointing into them.
Removed
- The model-provider layer. SQLDesk calls no model; MCP serves tools to the client's.
manage ai configure/status/test/disable/forgetare gone with it.
Images
ghcr.io/bot-netizen/sqldesk:0.6.0
ghcr.io/bot-netizen/sqldesk-screenshots:0.6.0
linux/amd64 and linux/arm64. The screenshots image is the optional renderer for pictures in alert emails.
Upgrading from 0.5
- Pull, then
manage db upgrade. - Nothing here changes an existing dashboard, query or data source.
- MCP and the catalog are off until you turn them on —
mcp.enabled=truein the chart, or themcpcompose profile. With them off,/mcpanswers 404 and the Catalog page is not there.
Verified before tagging
- CI green on every job, including the end-to-end suite.
- The published image smoke-tested through
compose.prod.yaml:/pingand/setupanswering, the scheduler registering its periodic jobs, andrefresh_queriesandcleanup_query_resultsboth running clean on the worker.