Appearance
MicroSQL Authoring Guide
MicroSQL is a restricted SQL dialect for querying print domain reporting tables. Tenant isolation is enforced automatically — you never write WHERE tenant_id = '...'.
Quick Start
- Discover the schema — call
describe_sql_schemato see all tables, columns, and descriptions - Write a query — use standard SELECT syntax (see below)
- Validate — call
validate_sqlto check syntax and see inferred column types - Execute — call
execute_sqlto run the query and get tabular results - Save as Data Clip — use the
upsertDataClipGraphQL mutation to persist the query
MicroSQL Syntax
text
SELECT [DISTINCT] [columns | aggregates | CASE ... END]
FROM table
[[LEFT] JOIN table ON condition]
[WHERE conditions]
[GROUP BY columns]
[ORDER BY columns ASC|DESC]
[LIMIT n [OFFSET m]]Supported Features
- SELECT: Column references, which MUST be qualified as
table.column— a barecolumnis rejected with "Invalid SELECT column: <name>", even in a single-table query. Also aggregate functions and aliases (AS name). Qualification applies in WHERE, GROUP BY and ORDER BY too. - FROM: Single table or multiple tables via JOIN
- JOIN:
JOIN table ON left.col = right.col(INNER JOIN);LEFT JOINis accepted too - DISTINCT:
SELECT DISTINCT table.column - CASE:
CASE WHEN … THEN … ELSE … END AS namein the SELECT list - WHERE: Comparisons (
=,!=,<,>,<=,>=),AND,OR,IS NULL,IS NOT NULL,IN (...),BETWEEN … AND …,LIKE/NOT LIKE. AnORmust sit inside parentheses (WHERE (a = 1 OR a = 2)); outside them the query is rejected with "OR outside parentheses". There is no prefixNOT— use!=,NOT LIKEorIS NOT NULL. InLIKE,%and_are matched literally, not as wildcards, soLIKE 'COMP%'only matches the exact textCOMP%. - GROUP BY: Group results by one or more columns
- ORDER BY: Sort by columns; the direction
ASCorDESCis required - LIMIT: Cap the number of returned rows (server enforces max 1000);
LIMIT n OFFSET mpages through larger results
Aggregate Functions
COUNT(column)orCOUNT(*)— count rowsSUM(column)— sum numeric valuesAVG(column)— average numeric valuesMIN(column)— minimum valueMAX(column)— maximum value
Scalar Functions
- Date and time:
NOW(),DATE_TRUNC('day', t.col),DATE_PART('year', t.col) - Text:
UPPER,LOWER,LENGTH,TRIM,CONCAT(a, b) - Numbers:
ROUND,ABS,CEIL,FLOOR,GREATEST(a, b),LEAST(a, b) - Nulls:
COALESCE(a, b),NULLIF(a, b)
Not Supported
- Subqueries
- UNION / INTERSECT / EXCEPT
- Window functions (OVER, PARTITION BY)
- INSERT / UPDATE / DELETE (read-only)
HAVINGCAST- CTEs (WITH ... AS)
MCP Tools
execute_sql
Run a MicroSQL query and get tabular results (headers + rows).
execute_sql(query: "SELECT jobs.name, jobs.quantity FROM jobs WHERE jobs.status = 'COMPLETED' LIMIT 10")validate_sql
Parse and validate a query without executing. Returns inferred result columns with data types.
validate_sql(query: "SELECT jobs.name, COUNT(*) FROM jobs GROUP BY jobs.name")
→ Valid query.
Result columns:
jobs.name —
COUNT(*) integer (aggregate)describe_sql_schema
List all available tables, columns, types, and descriptions. No arguments needed.
describe_sql_schema()
→ Full DDL-style schema reference with column descriptionssearch_sql_tables
Semantic search over SQL tables. Use to orient — find which table has the data you need.
search_sql_tables(query: "production orders")
→ 1. jobs (score: 0.91)
jobs: Production jobs with scheduling, status tracking, and customer referencessearch_sql_columns
Semantic search over SQL columns. Use to find specific fields by meaning.
search_sql_columns(query: "delivery deadline")
→ 1. jobs.due_at (score: 0.87)
jobs.due_at: Delivery due date (timestamp)Both accept optional top_k parameter (default 5, max 20).
Data Clips
Data clips are saved MicroSQL queries with a persistent share token for public CSV/JSON export.
List Data Clips
graphql
query {
listDataClips(first: 20) {
edges {
node {
id
name
description
queryString
shareToken
createdAt
updatedAt
}
}
totalCount
}
}Get a Data Clip
graphql
query GetClip($id: DataClipID!) {
getDataClip(id: $id) {
id name description queryString shareToken
creatorId createdAt updatedAt
}
}Create or Update a Data Clip
graphql
mutation UpsertClip($input: UpsertDataClipInput!) {
upsertDataClip(input: $input) {
dataClip {
id name description queryString shareToken
}
errors { path message }
}
}Variables (create):
json
{
"input": {
"name": "Completed Jobs This Month",
"description": "All jobs with status COMPLETED, ordered by completion date",
"queryString": "SELECT jobs.name, jobs.quantity, jobs.actual_end_at FROM jobs WHERE jobs.status = 'COMPLETED' ORDER BY jobs.actual_end_at DESC LIMIT 500"
}
}Variables (update — include id):
json
{
"input": {
"id": "dcl_...",
"name": "Updated Name",
"queryString": "SELECT jobs.name, jobs.quantity FROM jobs LIMIT 100"
}
}Preview a Query (Without Saving)
graphql
query Preview($input: PreviewDataClipInput!) {
previewDataClip(input: $input) {
headers
rows
rowCount
error
}
}Variables:
json
{ "input": { "query": "SELECT jobs.name, jobs.status FROM jobs LIMIT 5" } }Delete a Data Clip
graphql
mutation DeleteClip($id: DataClipID!) {
deleteDataClip(id: $id) {
success
errors { path message }
}
}Roll (Regenerate) Share Token
Invalidates old share links and generates a new token:
graphql
mutation RollToken($input: RollShareTokenInput!) {
rollDataClipShareToken(input: $input) {
dataClip { id shareToken }
errors { path message }
}
}Variables:
json
{ "input": { "id": "dcl_..." } }Public Data Export (Share Token)
Data clips can be exported publicly via share token — no authentication required:
GET /api/v1/clips/{shareToken}.csv
GET /api/v1/clips/{shareToken}.json
GET /api/v1/clips/{shareToken}.ndjsonThe share token is generated when a data clip is created. Use rollDataClipShareToken to regenerate it and invalidate old links.
Lua Integration
In custom app server scripts (Lua), use ctx.sql.query():
lua
local ok, result = ctx.sql.query([[
SELECT jobs.name, jobs.quantity, jobs.status
FROM jobs
WHERE jobs.status = 'PRESS'
ORDER BY jobs.due_at ASC
LIMIT 50
]])
if not ok then error(result) end
-- result.headers = {"jobs.name", "jobs.quantity", "jobs.status"} (headers keep the table prefix)
-- result.rows = {{...}, {...}, ...}
-- result.rowCount = 50ML Integration
ML model configs accept MicroSQL via the dataQuerySQL field as an alternative to Lua data scripts. The SQL query provides the training data directly.
Worked Examples
Example 1: Simple Status Report
sql
SELECT jobs.status, COUNT(*) AS job_count
FROM jobs
GROUP BY jobs.status
ORDER BY job_count DESCExample 2: Jobs with Component Details
sql
SELECT jobs.name, jobs.status,
components.name AS component_name,
components.kind, components.quantity
FROM jobs
JOIN components ON components.job_id = jobs.id
WHERE jobs.status = 'PRESS'
ORDER BY jobs.due_at ASC
LIMIT 100Example 3: Overdue Jobs
sql
SELECT jobs.name, jobs.job_number, jobs.due_at, jobs.status
FROM jobs
WHERE jobs.status != 'COMPLETED'
AND jobs.status != 'CANCELLED'
AND jobs.due_at < '2026-02-22'
ORDER BY jobs.due_at ASCExample 4: Component Mix by Kind
sql
SELECT components.kind,
COUNT(*) AS count,
AVG(components.quantity) AS avg_quantity
FROM components
GROUP BY components.kind
ORDER BY count DESCExample 5: Full Data Clip Workflow
- Discover the schema — what tables and columns exist
- Draft the query and check it parses
- Preview the results before saving anything (
describe_sql_schema(),validate_sql(query:)andexecute_sql(query:)do these three.) - Save as clip with the
upsertDataClipmutationexecute_graphql_mutation(query: "mutation { upsertDataClip(input: { name: \"Monthly Report\", queryString: \"SELECT ...\" }) { dataClip { id shareToken } errors { path message } } }") - Share the public URL:
/api/v1/clips/{shareToken}.csv