Server Details
Kotlin compile-time SQL library. Docs, code validation, and SQLite execution tools.
- Status
- Healthy
- Last Tested
- Transport
- Streamable HTTP
- URL
Available Tools
5 toolsgetExoQueryDocsTry in Inspector
Access comprehensive ExoQuery documentation organized by topic and category.
ExoQuery is a Language Integrated Query library for Kotlin Multiplatform that translates Kotlin DSL expressions into SQL at compile time. This resource provides access to the complete documentation covering all aspects of the library.
AVAILABLE DOCUMENTATION CATEGORIES:
Getting Started
Introduction: What ExoQuery is and why it exists
Installation: Project setup and dependencies
Quick Start: First query in minutes
Core Concepts
SQL Blocks: The sql { } construct and query building
Parameters: Safe runtime data handling
Composing Queries: Functional query composition
Query Operations
Basic Operations: Map, filter, and transformations
Joins: Inner, left, and implicit joins
Grouping: GROUP BY and HAVING clauses
Sorting: ORDER BY operations
Subqueries: Correlated and nested queries
Window Functions: Advanced analytics
Actions
Insert: INSERT with returning and conflict handling
Update: UPDATE operations with setParams
Delete: DELETE with returning
Batch Operations: Bulk inserts and updates
Advanced Features
SQL Fragment Functions: Reusable SQL components with @SqlFragment
Dynamic Queries: Runtime query generation with @SqlDynamic
Free Blocks: Custom SQL and user-defined functions
Transactions: Transaction support patterns
Polymorphic Queries: Interfaces, sealed classes, higher-order functions
Local Variables: Variables within SQL blocks
Data Handling
Serialization: kotlinx.serialization integration
Custom Type Encoding: Custom encoders and decoders
JSON Columns: JSON and JSONB support (PostgreSQL)
Column Naming: @SerialName and @ExoEntity annotations
Nested Datatypes: Complex data structures
Kotlinx Integration: JSON and other serialization formats
Schema-First Development
Entity Generation: Compile-time code generation from database schema
AI-Enhanced Entities: Using LLMs to generate cleaner entity code
Reference
SQL Functions: Available string, math, and date functions
API Reference: Core types and function signatures
HOW TO USE THIS RESOURCE:
The resource URI follows the pattern: exoquery://docs/{file-path}
Where {file-path} is the relative path from the docs root, e.g.:
exoquery://docs/01-getting-started/01-introduction.md
exoquery://docs/03-query-operations/02-joins.md
exoquery://docs/05-advanced-features/01-sql-fragments.md
To discover available documents, use the MCP resources/list endpoint which will return all available documentation files with their titles, descriptions, and categories.
Each document includes:
Title and description
Category classification
Complete markdown content with code examples
Cross-references to related topics
WHEN TO USE:
User asks about ExoQuery syntax, features, or capabilities
User needs examples of specific query patterns
User encounters errors and needs to verify correct usage
User wants to understand advanced features or best practices
| Name | Required | Description | Default |
|---|---|---|---|
| filePath | Yes | The documentation file path to retrieve. Format: Relative path from docs root (e.g., "01-getting-started/01-introduction.md") The full URI is: exoquery://docs/{file-path} To find available file paths, use the MCP resources/list endpoint which returns metadata for all documentation files including their paths, titles, categories, and descriptions. Common paths: - Getting Started: 01-getting-started/01-introduction.md, 01-getting-started/02-installation.md, 01-getting-started/03-quick-start.md - Core Concepts: 02-core-concepts/01-sql-blocks.md, 02-core-concepts/02-parameters.md, 02-core-concepts/03-composing-queries.md - Query Operations: 03-query-operations/01-basic-operations.md, 03-query-operations/02-joins.md, 03-query-operations/03-grouping.md - Actions: 04-actions/01-insert.md, 04-actions/02-update.md, 04-actions/03-delete.md - Advanced: 05-advanced-features/01-sql-fragments.md, 05-advanced-features/02-dynamic-queries.md - Data Handling: 06-data-handling/03-json-columns.md, 06-data-handling/04-column-naming.md |
getExoQueryDocsMultiTry in Inspector
Access multiple ExoQuery documentation sections simultaneously.
This tool is similar to the single-document retrieval tool but allows fetching multiple documentation files in a single request. This is particularly useful when you need to gather information from several related topics at once.
ExoQuery is a Language Integrated Query library for Kotlin Multiplatform that translates Kotlin DSL expressions into SQL at compile time. This resource provides access to the complete documentation covering all aspects of the library.
HOW TO USE THIS RESOURCE:
Provide a list of file paths, where each path is the relative path from the docs root, e.g.:
01-getting-started/01-introduction.md
03-query-operations/02-joins.md
05-advanced-features/01-sql-fragments.md
To discover available documents, use the MCP resources/list endpoint which will return all available documentation files with their titles, descriptions, and categories.
Each returned document includes:
Title and description
Category classification
Complete markdown content with code examples
Cross-references to related topics
WHEN TO USE:
User asks about multiple ExoQuery topics that require information from different sections
User needs to compare or understand relationships between different features
User wants to get comprehensive information across multiple categories
More efficient than making multiple single-document requests
| Name | Required | Description | Default |
|---|---|---|---|
| filePaths | Yes | A list of documentation file paths to retrieve. Format: List of relative paths from docs root (e.g., ["01-getting-started/01-introduction.md", "03-query-operations/02-joins.md"]) Each path follows the pattern used in single-document retrieval: {category-folder}/{file-name}.md To find available file paths, use the MCP resources/list endpoint which returns metadata for all documentation files including their paths, titles, categories, and descriptions. Common paths: - Getting Started: 01-getting-started/01-introduction.md, 01-getting-started/02-installation.md, 01-getting-started/03-quick-start.md - Core Concepts: 02-core-concepts/01-sql-blocks.md, 02-core-concepts/02-parameters.md, 02-core-concepts/03-composing-queries.md - Query Operations: 03-query-operations/01-basic-operations.md, 03-query-operations/02-joins.md, 03-query-operations/03-grouping.md - Actions: 04-actions/01-insert.md, 04-actions/02-update.md, 04-actions/03-delete.md - Advanced: 05-advanced-features/01-sql-fragments.md, 05-advanced-features/02-dynamic-queries.md - Data Handling: 06-data-handling/03-json-columns.md, 06-data-handling/04-column-naming.md |
listExoQueryDocsTry in Inspector
Lists all available ExoQuery documentation resources with their metadata
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
validateAndRunExoqueryTry in Inspector
Compile ExoQuery Kotlin code and EXECUTE it against an Sqlite database with provided schema. ExoQuery is a compile-time SQL query builder that translates Kotlin DSL expressions into SQL.
WHEN TO USE: When you need to verify ExoQuery produces correct results against actual data.
INPUT REQUIREMENTS:
Complete Kotlin code (same requirements as validateExoquery)
SQL schema with CREATE TABLE and INSERT statements for test data
Data classes MUST exactly match the schema table structure
Column names in data classes must match schema (use @SerialName for snake_case columns)
Must include or or more .runSample() calls in main() to trigger SQL generation and execution (note that .runSample() is NOT or real production use, use .runOn(database) instead)
OUTPUT FORMAT:
Returns one or more JSON objects, each on its own line. Each object can be:
SQL with output (query executed successfully): {"sql": "SELECT u.name FROM "User" u", "output": "[(name=Alice), (name=Bob)]"}
Output only (e.g., print statements, intermediate results): {"output": "Before: [(id=1, title=Ion Blend Beans)]"}
Error output (runtime errors, exceptions): {"outputErr": "java.sql.SQLException: Table "USERS" not found"}
Multiple results appear when code has multiple queries or print statements:
{"sql": "SELECT * FROM "InventoryItem"", "output": "[(id=1, title=Ion Blend Beans, unit_price=32.00, in_stock=25)]"} {"output": "Before:"} {"sql": "INSERT INTO "InventoryItem" (title, unit_price, in_stock) VALUES (?, ?, ?)", "output": "Rows affected: 1"} {"output": "After:"} {"sql": "SELECT * FROM "InventoryItem"", "output": "[(id=1, title=Ion Blend Beans, unit_price=32.00, in_stock=25), (id=2, title=Luna Fuel Flask, unit_price=89.50, in_stock=6)]"}
Compilation errors return the same format as validateExoquery: { "errors": { "File.kt": [ { "interval": {"start": {"line": 12, "ch": 10}, "end": {"line": 12, "ch": 15}}, "message": "Type mismatch: inferred type is String but Int was expected", "severity": "ERROR", "className": "ERROR" } ] } }
Runtime Errors can have the following format: { "errors" : { "File.kt" : [ ] }, "exception" : { "message" : "[SQLITE_ERROR] SQL error or missing database (no such table: User)", "fullName" : "org.sqlite.SQLiteException", "stackTrace" : [ { "className" : "org.sqlite.core.DB", "methodName" : "newSQLException", "fileName" : "DB.java", "lineNumber" : 1179 }, ...] }, "text" : "\n{"sql": "SELECT x.id, x.name, x.age FROM User x"}\n\n" } If there was a SQL query generated before the error, it will appear in the "text" field output stream.
EXAMPLE INPUT CODE:
EXAMPLE INPUT SCHEMA:
EXAMPLE SUCCESS OUTPUT: {"sql": "SELECT u.name AS first, o.total AS second, u.age AS third FROM "User" u INNER JOIN "Order" o ON o.user_id = u.id", "output": "[(first=Alice, second=100, third=30), (first=Alice, second=200, third=30), (first=Bob, second=150, third=25)]"}
EXAMPLE WITH MULTIPLE OPERATIONS (insert with before/after check): {"output": "Before:"} {"sql": "SELECT * FROM "InventoryItem"", "output": "[(id=1, title=Ion Blend Beans)]"} {"sql": "INSERT INTO "InventoryItem" (title, unit_price, in_stock) VALUES (?, ?, ?)", "output": ""} {"output": "After:"} {"sql": "SELECT * FROM "InventoryItem"", "output": "[(id=1, title=Ion Blend Beans), (id=2, title=Luna Fuel Flask)]"}
EXAMPLE RUNTIME ERROR (if a user divided by zero): {"outputErr": "Exception in thread "main" java.lang.ArithmeticException: / by zero"}
KEY PATTERNS:
(See validateExoquery for complete pattern reference)
Summary of most common patterns:
Filter: sql { Table().filter { x -> x.field == value } }
Select: sql.select { val x = from(Table()); where { ... }; x }
Join: sql.select { val a = from(Table()); val b = join(Table()) { b -> b.aId == a.id }; Pair(a, b) }
Left join: joinLeft(Table()) { ... } returns nullable
Insert: sql { insert { setParams(obj).excluding(id) } }
Update: sql { update().set { it.field to value }.where { it.id == x } }
Delete: sql { delete().where { it.id == x } }
SCHEMA RULES:
Table names should match data class names (case-sensitive, use quotes for exact match)
Column names must match @SerialName values or property names
Include realistic test data to verify query logic
Sqlite database syntax (mostly compatible with standard SQL)
COMMON PATTERNS:
JSON columns: Use VARCHAR for storage, @SqlJsonValue on the nested data class
Auto-increment IDs: Use INTEGER PRIMARY KEY
Nullable columns: Use Type? in Kotlin, allow NULL in schema
| Name | Required | Description | Default |
|---|---|---|---|
| code | Yes | Complete ExoQuery Kotlin code to compile and execute. Must include: 1. Imports (minimum: io.exoquery.*, kotlinx.serialization.Serializable) 2. @Serializable data classes that EXACTLY match your schema tables 3. The query expression 4. A main() function ending with .buildFor.<Dialect>().runSample() This function MUST be present to trigger SQL generation and execution. Use @SerialName("column_name") when Kotlin property names differ from SQL column names. Use @Contextual for BigDecimal fields. Use @SqlJsonValue on data classes that represent JSON column values. Multiple queries in main() will produce multiple output JSON objects. | |
| schema | Yes | SQL schema to initialize the Sqlite test database. Must include: 1. CREATE TABLE statements for all tables referenced in the query 2. INSERT statements with test data to verify query behavior Table and column names must exactly match the data classes in the code. Use double quotes around table names to preserve case: CREATE TABLE "User" (...) Common error: Table "USER" not found, means you wrote CREATE TABLE User but queried "User". Always quote table names in schema to match ExoQuery's generated SQL. Example: CREATE TABLE "User" (id INT, name VARCHAR(100), age INT); INSERT INTO "User" VALUES (1, 'Alice', 30), (2, 'Bob', 25); |
validateExoqueryTry in Inspector
Compile ExoQuery Kotlin code and EXECUTE it against an Sqlite database with provided schema. ExoQuery is a compile-time SQL query builder that translates Kotlin DSL expressions into SQL.
WHEN TO USE: When you need to verify ExoQuery produces correct results against actual data.
INPUT REQUIREMENTS:
Complete Kotlin code (same requirements as validateExoquery)
SQL schema with CREATE TABLE and INSERT statements for test data
Data classes MUST exactly match the schema table structure
Column names in data classes must match schema (use @SerialName for snake_case columns)
Must include or or more .runSample() calls in main() to trigger SQL generation and execution (note that .runSample() is NOT or real production use, use .runOn(database) instead)
OUTPUT FORMAT:
Returns one or more JSON objects, each on its own line. Each object can be:
SQL with output (query executed successfully): {"sql": "SELECT u.name FROM "User" u", "output": "[(name=Alice), (name=Bob)]"}
Output only (e.g., print statements, intermediate results): {"output": "Before: [(id=1, title=Ion Blend Beans)]"}
Error output (runtime errors, exceptions): {"outputErr": "java.sql.SQLException: Table "USERS" not found"}
Multiple results appear when code has multiple queries or print statements:
{"sql": "SELECT * FROM "InventoryItem"", "output": "[(id=1, title=Ion Blend Beans, unit_price=32.00, in_stock=25)]"} {"output": "Before:"} {"sql": "INSERT INTO "InventoryItem" (title, unit_price, in_stock) VALUES (?, ?, ?)", "output": "Rows affected: 1"} {"output": "After:"} {"sql": "SELECT * FROM "InventoryItem"", "output": "[(id=1, title=Ion Blend Beans, unit_price=32.00, in_stock=25), (id=2, title=Luna Fuel Flask, unit_price=89.50, in_stock=6)]"}
Compilation errors return the same format as validateExoquery: { "errors": { "File.kt": [ { "interval": {"start": {"line": 12, "ch": 10}, "end": {"line": 12, "ch": 15}}, "message": "Type mismatch: inferred type is String but Int was expected", "severity": "ERROR", "className": "ERROR" } ] } }
Runtime Errors can have the following format: { "errors" : { "File.kt" : [ ] }, "exception" : { "message" : "[SQLITE_ERROR] SQL error or missing database (no such table: User)", "fullName" : "org.sqlite.SQLiteException", "stackTrace" : [ { "className" : "org.sqlite.core.DB", "methodName" : "newSQLException", "fileName" : "DB.java", "lineNumber" : 1179 }, ...] }, "text" : "\n{"sql": "SELECT x.id, x.name, x.age FROM User x"}\n\n" } If there was a SQL query generated before the error, it will appear in the "text" field output stream.
EXAMPLE INPUT CODE:
EXAMPLE INPUT SCHEMA:
EXAMPLE SUCCESS OUTPUT: {"sql": "SELECT u.name AS first, o.total AS second, u.age AS third FROM "User" u INNER JOIN "Order" o ON o.user_id = u.id", "output": "[(first=Alice, second=100, third=30), (first=Alice, second=200, third=30), (first=Bob, second=150, third=25)]"}
EXAMPLE WITH MULTIPLE OPERATIONS (insert with before/after check): {"output": "Before:"} {"sql": "SELECT * FROM "InventoryItem"", "output": "[(id=1, title=Ion Blend Beans)]"} {"sql": "INSERT INTO "InventoryItem" (title, unit_price, in_stock) VALUES (?, ?, ?)", "output": ""} {"output": "After:"} {"sql": "SELECT * FROM "InventoryItem"", "output": "[(id=1, title=Ion Blend Beans), (id=2, title=Luna Fuel Flask)]"}
EXAMPLE RUNTIME ERROR (if a user divided by zero): {"outputErr": "Exception in thread "main" java.lang.ArithmeticException: / by zero"}
KEY PATTERNS:
(See validateExoquery for complete pattern reference)
Summary of most common patterns:
Filter: sql { Table().filter { x -> x.field == value } }
Select: sql.select { val x = from(Table()); where { ... }; x }
Join: sql.select { val a = from(Table()); val b = join(Table()) { b -> b.aId == a.id }; Pair(a, b) }
Left join: joinLeft(Table()) { ... } returns nullable
Insert: sql { insert { setParams(obj).excluding(id) } }
Update: sql { update().set { it.field to value }.where { it.id == x } }
Delete: sql { delete().where { it.id == x } }
SCHEMA RULES:
Table names should match data class names (case-sensitive, use quotes for exact match)
Column names must match @SerialName values or property names
Include realistic test data to verify query logic
Sqlite database syntax (mostly compatible with standard SQL)
COMMON PATTERNS:
JSON columns: Use VARCHAR for storage, @SqlJsonValue on the nested data class
Auto-increment IDs: Use INTEGER PRIMARY KEY
Nullable columns: Use Type? in Kotlin, allow NULL in schema
| Name | Required | Description | Default |
|---|---|---|---|
| code | Yes | Complete ExoQuery Kotlin code to compile. Must include: 1. Imports (minimum: io.exoquery.*, kotlinx.serialization.Serializable) 2. @Serializable data classes matching your query entities 3. The query expression using sql { ... } or sql.select { ... } 4. A main() function ending with .buildFor.<Dialect>().runSample() or .buildPrettyFor.<Dialect>().runSample() This function MUST be present to trigger SQL generation. The runSample() function triggers SQL generation but does NOT execute the query for validateExoquery. (Note that this is NOT for production ExoQuery usage. For that you use `.runOn(database)`.) Dialect is part of the code (e.g., .buildFor.Postgres()), NOT a separate parameter. If compilation fails, check the error interval positions to locate the exact issue in your code. |
FAQ
How do I claim this server?
To claim this server, publish a /.well-known/glama.json file on your server's domain with the following structure:
The email address must match the email associated with your Glama account. Once verified, the server will appear as claimed by you.
What are the benefits of claiming a server?
- Control your server's listing on Glama, including description and metadata
- Receive usage reports showing how your server is being used
- Get monitoring and health status updates for your server