VibeSQL Documentation
Technical reference for VibeSQL Server and the SDK.
Quick Start
Prefer the command line? vsql is the open-source CLI for VibeSQL: query, manage schemas and seed data. See the install line on the home page.
VibeSQL Micro Micro
A single binary with embedded PostgreSQL 16, for local development and prototyping. Downloads are on the GitHub releases page.
VibeSQL Server Server
Build and run instructions are in the VibeSQL Server repository.
Vibe SDK SDK
TypeScript client for Node.js and browser applications.
import { createVibeClient } from '@payez/vibe-client'; const client = createVibeClient({ apiUrl: 'https://your-vibesql-server', }); const users = client.collection('users'); const result = await users.list({ limit: 10 });
Endpoint
All SQL queries are sent as JSON to a single HTTP endpoint:
POST /v1/query Content-Type: application/json
This is the VibeSQL Server API. Server requires authentication on every request; see Authentication below.
Request Format
{
"sql": "SELECT * FROM users WHERE id = 1"
}
| Field | Type | Required | Constraints |
|---|---|---|---|
sql |
string | Yes | Max 256 KB (512 KB for collection schema DDL). Must start with a valid SQL keyword. |
Response Format
Success (HTTP 200)
{
"success": true,
"rows": [
{"id": 1, "name": "Alice", "email": "alice@example.com"}
],
"rowCount": 1,
"executionTime": 0.42
}
| Field | Type | Description |
|---|---|---|
success | boolean | Always true for successful queries |
rows | array | null | Array of row objects (column name → value). null for non-SELECT statements without RETURNING. |
rowCount | integer | Number of rows returned or affected |
executionTime | float | Execution time in milliseconds |
Error (HTTP 4xx/5xx)
{
"success": false,
"error": {
"code": "INVALID_SQL",
"message": "Invalid SQL syntax",
"detail": "PostgreSQL error: relation \"nonexistent\" does not exist"
}
}
| Field | Type | Description |
|---|---|---|
success | boolean | Always false |
error.code | string | Machine-readable error code (see Error Codes) |
error.message | string | Human-readable error message |
error.detail | string | Additional context (optional) |
Value encoding notes:
- SQL
NULLvalues are returned as JSONnull - JSONB columns are returned as JSON objects/arrays (not as strings)
- TIMESTAMP values are returned as ISO 8601 strings
- NUMERIC values are returned as JSON numbers
- BOOLEAN values are returned as JSON
true/false
Supported SQL Statements
Queries must start with one of these 8 keywords (case-insensitive):
| Keyword | Description | WHERE Required? |
|---|---|---|
SELECT | Read rows from tables | No |
INSERT | Create new rows | No |
UPDATE | Modify existing rows | Yes |
DELETE | Remove rows | Yes |
CREATE | Create tables, indexes, etc. | No |
DROP | Remove tables | No |
ALTER | Modify table structure | No |
TRUNCATE | Remove all rows from a table | No |
WHERE 1=1. Omitting WHERE returns UNSAFE_QUERY (HTTP 400).
PostgreSQL Data Types
VibeSQL supports all standard PostgreSQL data types. Here is the complete reference with size restrictions:
| Type | Description | Size / Range |
|---|---|---|
TEXT | Variable-length string | Unlimited (up to 1 GB) |
VARCHAR(n) | Variable-length string with limit | Max n characters |
CHAR(n) | Fixed-length string | Exactly n characters, space-padded |
INTEGER / INT | Signed integer | -2,147,483,648 to 2,147,483,647 (4 bytes) |
BIGINT | Large signed integer | -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 (8 bytes) |
SMALLINT | Small signed integer | -32,768 to 32,767 (2 bytes) |
SERIAL | Auto-incrementing integer | 1 to 2,147,483,647 |
BIGSERIAL | Auto-incrementing large integer | 1 to 9,223,372,036,854,775,807 |
NUMERIC(p,s) | Exact decimal number | p total digits, s after decimal. Max precision: 131,072 digits before decimal, 16,383 after. |
REAL | Floating-point (single) | 6 decimal digits precision (4 bytes) |
DOUBLE PRECISION | Floating-point (double) | 15 decimal digits precision (8 bytes) |
BOOLEAN | True or false | TRUE, FALSE, NULL |
DATE | Calendar date | 4713 BC to 5874897 AD (4 bytes) |
TIME | Time of day (no timezone) | 00:00:00 to 24:00:00 (8 bytes) |
TIMESTAMP | Date and time (no timezone) | 4713 BC to 294276 AD (8 bytes) |
TIMESTAMPTZ | Date and time with timezone | Same range, timezone-aware (8 bytes) |
UUID | Universally unique identifier | 128-bit RFC 4122 format (16 bytes) |
JSONB | Binary JSON data | Up to 255 MB per value |
JSON | Text JSON data | Up to 255 MB per value (prefer JSONB) |
TEXT[] | Array of text | Multi-dimensional arrays supported |
INTEGER[] | Array of integers | Multi-dimensional arrays supported |
UUID[] | Array of UUIDs | Multi-dimensional arrays supported |
BYTEA | Binary data | Up to 1 GB |
CREATE TABLE
-- Basic table CREATE TABLE users ( id SERIAL PRIMARY KEY, name TEXT NOT NULL, email TEXT UNIQUE, age INTEGER, created_at TIMESTAMP DEFAULT NOW() ); -- Table with JSONB and UUID CREATE TABLE documents ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), data JSONB NOT NULL, tags TEXT[], price NUMERIC(10,2), is_active BOOLEAN DEFAULT true ); -- Table with foreign keys CREATE TABLE messages ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), conversation_id UUID REFERENCES conversations(id), agent_id UUID REFERENCES agents(id), content TEXT, metadata JSONB, created_at TIMESTAMP DEFAULT NOW() );
SELECT
-- All columns SELECT * FROM users; -- Specific columns SELECT name, email FROM users; -- With WHERE SELECT * FROM users WHERE age > 21 AND is_active = true; -- ORDER BY, LIMIT, OFFSET (pagination) SELECT * FROM users ORDER BY name ASC LIMIT 10 OFFSET 20; -- Aggregates SELECT role, COUNT(*) AS count FROM users GROUP BY role HAVING COUNT(*) > 5; -- JOINs SELECT u.name, m.content FROM users u JOIN messages m ON u.id = m.user_id WHERE m.created_at > '2024-01-01'; -- Subqueries SELECT * FROM users WHERE id IN (SELECT user_id FROM orders WHERE total > 100);
INSERT
-- Single row INSERT INTO users (name, email) VALUES ('Alice', 'alice@example.com'); -- With RETURNING (get inserted data back) INSERT INTO users (name, email) VALUES ('Bob', 'bob@example.com') RETURNING id, name, created_at; -- Multiple rows INSERT INTO users (name, email) VALUES ('Charlie', 'charlie@example.com'), ('Diana', 'diana@example.com'); -- With JSONB data INSERT INTO documents (data) VALUES ('{"name": "Invoice", "amount": 99.50, "tags": ["billing", "q4"]}'); -- With ON CONFLICT (upsert) INSERT INTO users (email, name) VALUES ('alice@example.com', 'Alice Updated') ON CONFLICT (email) DO UPDATE SET name = EXCLUDED.name;
UPDATE
UNSAFE_QUERY (HTTP 400). Use WHERE 1=1 to update all rows intentionally.
-- Update specific row UPDATE users SET email = 'newemail@example.com' WHERE id = 1; -- Update multiple columns UPDATE users SET name = 'Alice Smith', age = 31 WHERE id = 1; -- Update all rows (explicit) UPDATE users SET is_active = false WHERE 1=1; -- Update with RETURNING UPDATE users SET name = 'Updated' WHERE id = 1 RETURNING *; -- Update JSONB field UPDATE documents SET data = jsonb_set(data, '{status}', '"completed"') WHERE id = 1;
DELETE
UNSAFE_QUERY (HTTP 400). Use WHERE 1=1 to delete all rows intentionally.
-- Delete specific row DELETE FROM users WHERE id = 1; -- Delete with condition DELETE FROM messages WHERE created_at < '2024-01-01'; -- Delete all rows (explicit) DELETE FROM temp_data WHERE 1=1; -- Delete with RETURNING DELETE FROM users WHERE id = 1 RETURNING id, name;
ALTER TABLE
-- Add column ALTER TABLE users ADD COLUMN phone TEXT; -- Drop column ALTER TABLE users DROP COLUMN phone; -- Rename column ALTER TABLE users RENAME COLUMN name TO full_name; -- Change column type ALTER TABLE users ALTER COLUMN age TYPE BIGINT; -- Add constraint ALTER TABLE users ADD CONSTRAINT email_unique UNIQUE (email); -- Set default ALTER TABLE users ALTER COLUMN is_active SET DEFAULT true; -- Rename table ALTER TABLE users RENAME TO accounts;
DROP TABLE
DROP TABLE below is for real Postgres tables. Collection (doc-store) tables are different — agent_mail.*, vibe_agents.*, and vibe_app.* are rows in vibe.documents, not Postgres relations, so raw DROP TABLE returns relation "…" does not exist. To drop a collection table use the schema API.-- Drop table (relational tables only — see note above) DROP TABLE users; -- Drop if exists (no error if missing) DROP TABLE IF EXISTS users; -- Drop with cascading foreign keys DROP TABLE IF EXISTS users CASCADE;
TRUNCATE
-- Remove all rows (faster than DELETE WHERE 1=1) TRUNCATE TABLE users; -- Truncate with cascade TRUNCATE TABLE users CASCADE; -- Truncate and reset auto-increment TRUNCATE TABLE users RESTART IDENTITY;
JSONB Operators
VibeSQL supports all 9 PostgreSQL JSONB operators through standard SQL syntax.
-> Get Field as JSON
Returns a JSON element by key (object) or index (array). Result is JSON type.
SELECT data->'name' FROM documents; -- Returns: "Alice" (as JSON string, with quotes) SELECT data->'tags'->0 FROM documents; -- Returns: "staff" (first array element) SELECT data->'address' FROM documents; -- Returns: {"city": "Portland", "state": "OR"}
->> Get Field as Text
Returns a JSON element as plain text. The most common operator for extracting values.
SELECT data->>'name' FROM documents; -- Returns: Alice (as text, no quotes) SELECT data->>'age' FROM documents; -- Returns: 30 (as text) SELECT data->'address'->>'city' FROM documents; -- Returns: Portland
#> Get Path as JSON
Navigates a JSON path and returns JSON. Path is specified as a text array.
SELECT data #> '{address,city}' FROM documents; -- Returns: "Portland" (as JSON string) SELECT data #> '{tags,0}' FROM documents; -- Returns: "staff" (as JSON string)
#>> Get Path as Text
Navigates a JSON path and returns text.
SELECT data #>> '{address,city}' FROM documents; -- Returns: Portland (as text) SELECT data #>> '{address,state}' FROM documents; -- Returns: OR
@> Contains
Tests if the left JSONB value contains the right JSONB value. Ideal for filtering.
SELECT * FROM documents WHERE data @> '{"role": "admin"}'; -- Rows where data contains role=admin SELECT * FROM documents WHERE data @> '{"tags": ["manager"]}'; -- Rows where tags array contains "manager" SELECT * FROM documents WHERE data->'address' @> '{"state": "OR"}'; -- Rows where address.state = "OR"
<@ Contained By
Tests if the left JSONB value is contained by the right. Reverse of @>.
SELECT * FROM documents WHERE '{"role": "admin", "name": "Alice"}' <@ data; -- Rows where data contains both role=admin AND name=Alice
? Key Exists
Tests if a key exists in the top-level of a JSONB object.
SELECT * FROM documents WHERE data ? 'name'; -- Rows that have a "name" key SELECT * FROM documents WHERE data ? 'phone'; -- Rows that have a "phone" key
?| Any Key Exists
Tests if any of the given keys exist.
SELECT * FROM documents WHERE data ?| array['phone', 'email', 'name']; -- Rows with at least one of: phone, email, or name
?& All Keys Exist
Tests if all of the given keys exist.
SELECT * FROM documents WHERE data ?& array['name', 'age', 'role']; -- Rows that have ALL of: name, age, and role
Operator Summary
| Operator | Returns | Use When |
|---|---|---|
-> | JSONB | You need JSON for further operations or chaining |
->> | text | You need the value as a plain string |
#> | JSONB | You need a nested value as JSON via path |
#>> | text | You need a nested value as text via path |
@> | boolean | Filtering: left contains right |
<@ | boolean | Filtering: left is contained by right |
? | boolean | Check if a single key exists |
?| | boolean | Check if any key in array exists |
?& | boolean | Check if all keys in array exist |
Common JSONB Patterns
-- Filter by nested value SELECT data->>'name' AS name FROM documents WHERE data->'address'->>'city' = 'Portland'; -- Sort by JSONB field (cast to int for numeric sort) SELECT data->>'name' AS name, (data->>'age')::int AS age FROM documents ORDER BY (data->>'age')::int DESC; -- Count by JSONB field SELECT data->>'role' AS role, COUNT(*) AS count FROM documents GROUP BY data->>'role'; -- Check array contains a specific value SELECT * FROM documents WHERE data->'tags' @> '"manager"'; -- Extract multiple fields SELECT data->>'name' AS name, data->>'role' AS role, data #>> '{address,city}' AS city FROM documents; -- Multiple JSONB conditions SELECT data->>'name' AS name FROM documents WHERE data @> '{"role": "admin"}' AND data ? 'address' AND (data->>'age')::int > 25;
JSONB Type Casting
The ->> and #>> operators always return text. Cast to other types as needed:
(data->>'age')::int -- Cast to integer (data->>'price')::numeric -- Cast to decimal (data->>'active')::boolean -- Cast to boolean (data->>'created')::timestamp -- Cast to timestamp (data->>'score')::double precision -- Cast to float
Safety Rules
VibeSQL enforces safety rules to prevent accidental data loss:
| Rule | Applies To | Error Code |
|---|---|---|
| WHERE clause required | UPDATE, DELETE |
UNSAFE_QUERY (400) |
| Must start with valid SQL keyword | All queries | INVALID_SQL (400) |
| Query size ≤ 256 KB | All queries | QUERY_TOO_LARGE (413) |
WHERE 1=1. This confirms the operation is intentional.
Tier-Based Limits Server
VibeSQL Server supports configurable, tier-based limits. Timeouts and row limits are set per tenant tier.
Default Timeouts by Tier
| Tier | Timeout | Config Key |
|---|---|---|
free | 2 seconds | VibeQueryTimeouts:FreeSeconds |
starter | 5 seconds | VibeQueryTimeouts:StarterSeconds |
pro | 10 seconds | VibeQueryTimeouts:ProSeconds |
enterprise | 30 seconds | VibeQueryTimeouts:EnterpriseSeconds |
| (default / unspecified) | 5 seconds | VibeQueryTimeouts:DefaultSeconds |
Configurable Limits
| Setting | Config Key | Default |
|---|---|---|
| Max result rows | VibeQueryLimits:MaxResultRows | 1,000 |
| Max query size | Hardcoded | 256 KB (262,144 bytes); 512 KB for collection schema DDL |
Error Format
All errors return a JSON response with "success": false and an error object:
{
"success": false,
"error": {
"code": "ERROR_CODE",
"message": "Human-readable message",
"detail": "Additional context"
}
}
Error Codes
INVALID_SQL HTTP 400
SQL query has syntax errors, references undefined tables/columns, uses unsupported functions, or doesn't start with a valid SQL keyword.
Triggers: SQL syntax errors, undefined table (42P01), undefined column (42703), undefined function (42883), data type mismatch (42804), invalid keyword.
Resolution: Check SQL syntax. Verify table and column names exist. Ensure query starts with one of: SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, ALTER, TRUNCATE.
MISSING_REQUIRED_FIELD HTTP 400
The sql field is missing or empty in the request body.
// Wrong: using "query" instead of "sql" {"query": "SELECT 1"} // Correct {"sql": "SELECT 1"}
UNSAFE_QUERY HTTP 400
An UPDATE or DELETE statement is missing a WHERE clause.
// This fails: {"sql": "DELETE FROM users"} // This works (intentional delete-all): {"sql": "DELETE FROM users WHERE 1=1"}
QUERY_TIMEOUT HTTP 408
Query exceeded the maximum execution time. Limits are tier-based.
Resolution: Optimize the query (add indexes, reduce data scanned), add LIMIT, break into smaller operations.
QUERY_TOO_LARGE HTTP 413
SQL query exceeds the 256 KB (262,144 bytes) size limit.
Resolution: Reduce query size. Break large INSERT statements into multiple smaller requests.
RESULT_TOO_LARGE HTTP 413
Query result exceeds 1,000 rows.
Resolution: Add LIMIT 1000 (or smaller) to your query. Use OFFSET for pagination. Add WHERE clauses to filter results.
DOCUMENT_TOO_LARGE HTTP 413
A JSONB document or statement exceeds PostgreSQL's internal limits.
Triggers: PostgreSQL SQLSTATE 54000 (program_limit_exceeded), 54001 (statement_too_complex).
Resolution: Reduce JSONB document size. Simplify deeply nested JSON structures. Break large documents into smaller related records.
INTERNAL_ERROR HTTP 500
Unexpected server error not covered by other error codes.
Resolution: Check server logs for details. Retry the request. If persistent, restart the server.
SERVICE_UNAVAILABLE HTTP 503
The server is not ready to handle requests (still starting up).
Resolution: Wait for the server to finish starting. Check that the process is running.
DATABASE_UNAVAILABLE HTTP 503
The PostgreSQL instance is unreachable.
Triggers: PostgreSQL process crashed, connection failure (08xxx), insufficient resources (53xxx), too many connections (53300).
Resolution: Restart the server. Check disk space (PostgreSQL needs space for WAL files). Check if another process is using the PostgreSQL port.
HTTP Status Summary
| Status | Meaning |
|---|---|
200 | Query executed successfully |
400 | Invalid SQL, missing field, or unsafe query |
401 | Missing or invalid Authorization header (AUTH_REQUIRED) |
408 | Query timed out |
413 | Query or result too large |
500 | Internal server error |
503 | Database or service unavailable |
PostgreSQL SQLSTATE Mapping
VibeSQL maps PostgreSQL SQLSTATE codes to VibeSQL error codes:
| SQLSTATE | VibeSQL Code | PostgreSQL Description |
|---|---|---|
42601 | INVALID_SQL | syntax_error |
42703 | INVALID_SQL | undefined_column |
42P01 | INVALID_SQL | undefined_table |
42P02 | INVALID_SQL | undefined_parameter |
42883 | INVALID_SQL | undefined_function |
42804 | INVALID_SQL | datatype_mismatch |
57014 | QUERY_TIMEOUT | query_canceled |
53000 | DATABASE_UNAVAILABLE | insufficient_resources |
53100 | DATABASE_UNAVAILABLE | disk_full |
53200 | DATABASE_UNAVAILABLE | out_of_memory |
53300 | DATABASE_UNAVAILABLE | too_many_connections |
53400 | DATABASE_UNAVAILABLE | configuration_limit_exceeded |
08000 | DATABASE_UNAVAILABLE | connection_exception |
08003 | DATABASE_UNAVAILABLE | connection_does_not_exist |
08006 | DATABASE_UNAVAILABLE | connection_failure |
08001 | DATABASE_UNAVAILABLE | sqlclient_unable_to_establish |
08004 | DATABASE_UNAVAILABLE | sqlserver_rejected_establishment |
54000 | DOCUMENT_TOO_LARGE | program_limit_exceeded |
54001 | DOCUMENT_TOO_LARGE | statement_too_complex |
Server Configuration Server
appsettings.json Keys
| Key | Default | Description |
|---|---|---|
VibeQueryTimeouts:DefaultSeconds | 5 | Default query timeout (when tier is not specified) |
VibeQueryTimeouts:FreeSeconds | 2 | Timeout for free tier |
VibeQueryTimeouts:StarterSeconds | 5 | Timeout for starter tier |
VibeQueryTimeouts:ProSeconds | 10 | Timeout for pro tier |
VibeQueryTimeouts:EnterpriseSeconds | 30 | Timeout for enterprise tier |
VibeQueryLimits:MaxResultRows | 1000 | Maximum rows per query result |
VibeSQL:ContainerSecret | (none) | Required. The server refuses to start without it. Clients send it as Authorization: Secret <key>. |
Authentication Server
Every POST /v1/query request to VibeSQL Server carries one of two Authorization headers. Without either, the answer is 401 AUTH_REQUIRED.
| Header | Meaning |
|---|---|
Authorization: Secret <key> | The container secret configured as VibeSQL:ContainerSecret |
Authorization: Bearer <jwt> | A token issued by the identity provider the server is configured with (validated against its JWKS) |
Schema Versioning Server
VibeSQL Server supports JSON Schema versioning and evolution for collections. Schemas are scoped by client (tenant) and collection.
Schema Properties
| Property | Type | Default | Description |
|---|---|---|---|
CollectionSchemaId | int | — | Primary key |
ClientId | int | — | Tenant identifier (IDP client) |
Collection | string | "" | Collection name |
JsonSchema | string? | null | JSON Schema definition |
Version | int | 1 | Schema version (incremented on updates) |
IsActive | bool | true | Whether this is the active schema version |
IsSystem | bool | false | System collections are exempt from tier limits |
IsLocked | bool | false | Locked schemas prevent structural modifications |
CreatedAt | DateTimeOffset | — | Creation timestamp |
UpdatedAt | DateTimeOffset? | null | Last update timestamp |
IsLocked = true, structural changes (add, rename, delete fields) are blocked. Document CRUD (INSERT, UPDATE, DELETE) is still allowed.
Migration Transforms Server
Schema migrations define transforms applied to documents when migrating between schema versions. Transforms are defined in the JSON Schema under the x-vibe-migrations extension.
Transform Definition
{
"x-vibe-migrations": {
"1_to_2": [
{
"field": "price",
"transform": "multiply",
"args": 100,
"reason": "Convert dollars to cents"
}
]
}
}
Available Transforms
| Transform | Args | Description | Example |
|---|---|---|---|
multiply |
number | Multiplies a numeric field | {"field": "price", "transform": "multiply", "args": 100} |
divide |
number | Divides a numeric field (checks for zero divisor) | {"field": "price", "transform": "divide", "args": 100} |
map |
object (key→value) | Maps string values | {"field": "status", "transform": "map", "args": {"active": "enabled", "inactive": "disabled"}} |
default |
any | Sets a default value if null or missing | {"field": "role", "transform": "default", "args": "viewer"} |
cast |
string (target type) | Casts to a different type | {"field": "age", "transform": "cast", "args": "integer"} |
rename |
string (new name) | Renames the field | {"field": "name", "transform": "rename", "args": "full_name"} |
Cast Target Types
| Type String | Result |
|---|---|
"int" or "integer" | Integer |
"double" or "number" | Floating-point |
"string" | String |
"bool" or "boolean" | Boolean |
Compatibility Checks Server
Before applying schema changes, VibeSQL checks compatibility between the current and proposed schema. The levels include:
| Level | Description |
|---|---|
FullyCompatible | No breaking changes. Safe to apply immediately. |
ForwardCompatible | Changes exist but migration transforms are available. Documents will be migrated. |
Breaking | Fields removed or types changed without migration transforms. Requires manual review. |
Schema Change Types
Change types include:
| Change | Description |
|---|---|
Added | New field added to the schema |
Removed | Existing field removed from the schema |
TypeChanged | Field type changed (e.g., string → integer) |
SDK Installation SDK
npm install @payez/vibe-client
Client Setup SDK
import { createVibeClient } from '@payez/vibe-client'; const client = createVibeClient({ apiUrl: 'http://your-server:PORT', });
Collections SDK
const users = client.collection<User>('users');
list(options?)
const result = await users.list({ limit: 20, offset: 0, orderBy: 'created_at', orderDir: 'desc', // 'asc' | 'desc' filter: { role: 'admin' }, }); // result.data → User[] // result.pagination → { total, limit, offset, hasMore }
create(data)
await users.create({ name: 'Alice', email: 'alice@example.com', });
vsql CLI All Editions
vsql is a command-line client for VibeSQL. See the repository for details.
Installation
# Install from the release file
npm install -g https://github.com/PayEz-Net/vsql/releases/download/v1.3.5/vsql-1.3.5.tgz
vsql version
Help
Run vsql --help for the commands, options and environment variables.
AI Coding Skills
VibeSQL ships AI coding skills — lightweight plugins that let your AI coding assistant talk directly to your VibeSQL database. No servers to run, no packages to install. Just drop a skill file into your editor and go.
/vibe-sql show me all tables), your AI assistant translates it to PostgreSQL, executes it against VibeSQL's HTTP API, and presents the results — all in one step.
What Skills Can Do
- Explore — list tables, describe schemas, inspect columns and types
- Query — SELECT with filters, joins, aggregations, JSONB operators
- Create — CREATE TABLE, ALTER TABLE, add columns, set defaults
- CRUD — INSERT...RETURNING, UPDATE...WHERE, DELETE...WHERE
- JSONB — full PostgreSQL JSONB support with all 9 operators
Every skill embeds the full VibeSQL API reference, so your AI assistant knows the endpoint, response format, error codes, safety rules, and PostgreSQL syntax without any extra configuration.
Claude Code Available Now
The /vibe-sql skill for Claude Code lets you talk to your database in natural language directly from your terminal.
# Examples
/vibe-sql show me all tables
/vibe-sql create a users table with name, email, and age
/vibe-sql add a row: name=Widget, price=9.99
/vibe-sql what's the average price in stripe_sales
/vibe-sql add a phone column to the users table
The skill translates your request to PostgreSQL SQL, executes it via POST /v1/query, and presents formatted results. It enforces safety rules (UPDATE/DELETE require WHERE) and handles all error codes.
Supported Platforms
The /vibe-sql skill ships for three AI coding tools today:
| Tool | Skill Location | Status |
|---|---|---|
| Claude Code | ~/.claude/skills/vibe-sql/ | Available |
| OpenCode | ~/.opencode/skills/vibe-sql/ | Available |
| Codex CLI | ~/.agents/skills/vibe-sql/ | Available |
Same skill body, platform-specific frontmatter.
Installation
Skills are single files. Copy the skill into your project and your AI editor discovers it automatically.
Claude Code
git clone https://github.com/PayEz-Net/vibesql-skills.git /tmp/vibesql-skills cp -r /tmp/vibesql-skills/claude/vibe-sql ~/.claude/skills/vibe-sql
Open Claude Code and type /vibe-sql show me all tables.
OpenCode
git clone https://github.com/PayEz-Net/vibesql-skills.git /tmp/vibesql-skills cp -r /tmp/vibesql-skills/opencode/vibe-sql ~/.opencode/skills/vibe-sql
Codex CLI
git clone https://github.com/PayEz-Net/vibesql-skills.git /tmp/vibesql-skills cp -r /tmp/vibesql-skills/codex/vibe-sql ~/.agents/skills/vibe-sql
VIBESQL_URL environment variable to find your VibeSQL instance.
VibeSQL is open source under the Apache 2.0 license.
Server Edition · Micro Edition · AI Skills · Issues · Built by IdealVibe