create_project
ChatGPTCreate a new Hatchable project. This generates a URL slug, creates a dedicated PostgreSQL database, and returns the project ID and URLs. Call this FIRST, then keep going — creating a project does NOT make anything live. The returned URL is an empty shell that returns 404 until you (1) write your files with write_files and (2) call deploy. Do not stop, report success, or hand the user the URL until you have deployed. Project structure `` public/ static files, served at their file path api/ backend functions — each file is one endpoint hello.js → /api/hello users/list.js → /api/users/list users/[id].js → /api/users/:id (req.params.id — one segment) docs/[...path].js → /api/docs/*path (req.params.path — string[], catches multi-segment) pages/ server-rendered HTML at clean URLs — each file renders one page index.js → / about.js → /about blog/[id].js → /blog/:id (req.params.id; res.send(html), SSR'd in the isolate) lib/ shared code pool, not routed — import from any file as lib/<name>.js migrations/*.sql SQL files, run in filename order on every deploy seed.sql optional — runs on first deploy / fork, once per project hatchable.toml optional overrides (cron, auth, project name) package.json dependencies (no build scripts yet — build locally, commit public/) ` Routing precedence Most-specific wins. For a request to /api/users/42: 1. api/users/42.js (static) — beats 2. api/users/[id].js (single-param, params.id = "42") — beats 3. api/users/[...rest].js (catch-all, params.rest = ["42"]) Catch-all params arrive as string[], never slash-joined. Use req.params.path as an array: const [first, ...rest] = req.params.path; Page & static resolution (the clean-URL order) A request to a non-/api/ URL like /about resolves in this order: 1. **Exact static file** — public/about, public/about.html, public/about/index.html 2. **pages/ handler** — pages/about.js (server-rendered in the isolate; see skill pages/server-render-a-page) 3. **Ancestor index.html fallback** — walks up: public/foo/index.html → public/index.html (the SPA shell) A committed static file always wins over a page handler at the same URL, and a page handler always wins over the SPA shell — so pick public/ *or* pages/ for a given route, not both. Step 3 means each folder with an index.html acts as its own mini-site: ship an /admin/` SPA beside a static marketing `/` and unmatched paths under `/admin/` fall back to `public/admin/index.html`. Use `pages/` when the HTML must be filled in server-side (share/OG/SEO pages, signed-in first paint); use `public/` + Alpine for everything else. Handler contract Every file under api/ exports a default async function: ```js // api/notes/list.js import { db } from "hatchable"; export const access = "member"; // require a signed-in collaborator (edge-gated) export default async function (req, res) { // The edge already gated the route; req.member is the signed-in caller. const member = req.member; // { id, handle, ... } const { rows } = await db.query( "SELECT id, title FROM notes WHERE author_id = $1", [member.id] ); res.json(rows); } // Optional: restrict methods export const methods = ["GET"]; // Optional: register this endpoint as a recurring scheduled task. // Minimum interval is hourly. See also: scheduler.at() in the SDK // for imperative / one-shot / per-firing-payload scheduling. // export const schedule = "0 /6 *"; `` req (Express-shaped) - method, url, path, headers, cookies, params, query - body — parsed by Content-Type: JSON → object, urlencoded → object, multipart/form-data → object of non-file fields - files — present for multipart uploads: [{ field, filename, contentType, buffer }] res (Express-shaped) - res.json(data), res.status(code) (chainable), res.send(text|buffer) - res.redirect(url), res.cookie(name, value, opts), res.setHeader(name, value) SDK — import from "hatchable" Everything you need lives under one import. Do not reach for npm packages that duplicate these — the deploy linter rejects puppeteer-core, @anthropic-ai/sdk, pg…
create_project
ChatGPTCreate a new Hatchable project. This generates a URL slug, creates a dedicated PostgreSQL database, and returns the project ID and URLs. Call this FIRST, then keep going — creating a project does NOT make anything live. The returned URL is an empty shell that returns 404 until you (1) write your files with write_files and (2) call deploy. Do not stop, report success, or hand the user the URL until you have deployed. Project structure `` public/ static files, served at their file path api/ backend functions — each file is one endpoint hello.js → /api/hello users/list.js → /api/users/list users/[id].js → /api/users/:id (req.params.id — one segment) docs/[...path].js → /api/docs/*path (req.params.path — string[], catches multi-segment) pages/ server-rendered HTML at clean URLs — each file renders one page index.js → / about.js → /about blog/[id].js → /blog/:id (req.params.id; res.send(html), SSR'd in the isolate) lib/ shared code pool, not routed — import from any file as lib/<name>.js migrations/*.sql SQL files, run in filename order on every deploy seed.sql optional — runs on first deploy / fork, once per project hatchable.toml optional overrides (cron, auth, project name) package.json dependencies (no build scripts yet — build locally, commit public/) ` Routing precedence Most-specific wins. For a request to /api/users/42: 1. api/users/42.js (static) — beats 2. api/users/[id].js (single-param, params.id = "42") — beats 3. api/users/[...rest].js (catch-all, params.rest = ["42"]) Catch-all params arrive as string[], never slash-joined. Use req.params.path as an array: const [first, ...rest] = req.params.path; Page & static resolution (the clean-URL order) A request to a non-/api/ URL like /about resolves in this order: 1. **Exact static file** — public/about, public/about.html, public/about/index.html 2. **pages/ handler** — pages/about.js (server-rendered in the isolate; see skill pages/server-render-a-page) 3. **Ancestor index.html fallback** — walks up: public/foo/index.html → public/index.html (the SPA shell) A committed static file always wins over a page handler at the same URL, and a page handler always wins over the SPA shell — so pick public/ *or* pages/ for a given route, not both. Step 3 means each folder with an index.html acts as its own mini-site: ship an /admin/` SPA beside a static marketing `/` and unmatched paths under `/admin/` fall back to `public/admin/index.html`. Use `pages/` when the HTML must be filled in server-side (share/OG/SEO pages, signed-in first paint); use `public/` + Alpine for everything else. Handler contract Every file under api/ exports a default async function: ```js // api/notes/list.js import { db } from "hatchable"; export const access = "member"; // require a signed-in collaborator (edge-gated) export default async function (req, res) { // The edge already gated the route; req.member is the signed-in caller. const member = req.member; // { id, handle, ... } const { rows } = await db.query( "SELECT id, title FROM notes WHERE author_id = $1", [member.id] ); res.json(rows); } // Optional: restrict methods export const methods = ["GET"]; // Optional: register this endpoint as a recurring scheduled task. // Minimum interval is hourly. See also: scheduler.at() in the SDK // for imperative / one-shot / per-firing-payload scheduling. // export const schedule = "0 /6 *"; `` req (Express-shaped) - method, url, path, headers, cookies, params, query - body — parsed by Content-Type: JSON → object, urlencoded → object, multipart/form-data → object of non-file fields - files — present for multipart uploads: [{ field, filename, contentType, buffer }] res (Express-shaped) - res.json(data), res.status(code) (chainable), res.send(text|buffer) - res.redirect(url), res.cookie(name, value, opts), res.setHeader(name, value) SDK — import from "hatchable" Everything you need lives under one import. Do not reach for npm packages that duplicate these — the deploy linter rejects puppeteer-core, @anthropic-ai/sdk, pg…
create_project
ChatGPTCreate a new Hatchable project. This generates a URL slug, creates a dedicated PostgreSQL database, and returns the project ID and URLs. Call this FIRST, then keep going — creating a project does NOT make anything live. The returned URL is an empty shell that returns 404 until you (1) write your files with write_files and (2) call deploy. Do not stop, report success, or hand the user the URL until you have deployed. Project structure `` public/ static files, served at their file path api/ backend functions — each file is one endpoint hello.js → /api/hello users/list.js → /api/users/list users/[id].js → /api/users/:id (req.params.id — one segment) docs/[...path].js → /api/docs/*path (req.params.path — string[], catches multi-segment) pages/ server-rendered HTML at clean URLs — each file renders one page index.js → / about.js → /about blog/[id].js → /blog/:id (req.params.id; res.send(html), SSR'd in the isolate) lib/ shared code pool, not routed — import from any file as lib/<name>.js migrations/*.sql SQL files, run in filename order on every deploy seed.sql optional — runs on first deploy / fork, once per project hatchable.toml optional overrides (cron, auth, project name) package.json dependencies (no build scripts yet — build locally, commit public/) ` Routing precedence Most-specific wins. For a request to /api/users/42: 1. api/users/42.js (static) — beats 2. api/users/[id].js (single-param, params.id = "42") — beats 3. api/users/[...rest].js (catch-all, params.rest = ["42"]) Catch-all params arrive as string[], never slash-joined. Use req.params.path as an array: const [first, ...rest] = req.params.path; Page & static resolution (the clean-URL order) A request to a non-/api/ URL like /about resolves in this order: 1. **Exact static file** — public/about, public/about.html, public/about/index.html 2. **pages/ handler** — pages/about.js (server-rendered in the isolate; see skill pages/server-render-a-page) 3. **Ancestor index.html fallback** — walks up: public/foo/index.html → public/index.html (the SPA shell) A committed static file always wins over a page handler at the same URL, and a page handler always wins over the SPA shell — so pick public/ *or* pages/ for a given route, not both. Step 3 means each folder with an index.html acts as its own mini-site: ship an /admin/` SPA beside a static marketing `/` and unmatched paths under `/admin/` fall back to `public/admin/index.html`. Use `pages/` when the HTML must be filled in server-side (share/OG/SEO pages, signed-in first paint); use `public/` + Alpine for everything else. Handler contract Every file under api/ exports a default async function: ```js // api/notes/list.js import { db } from "hatchable"; export const access = "member"; // require a signed-in collaborator (edge-gated) export default async function (req, res) { // The edge already gated the route; req.member is the signed-in caller. const member = req.member; // { id, handle, ... } const { rows } = await db.query( "SELECT id, title FROM notes WHERE author_id = $1", [member.id] ); res.json(rows); } // Optional: restrict methods export const methods = ["GET"]; // Optional: register this endpoint as a recurring scheduled task. // Minimum interval is hourly. See also: scheduler.at() in the SDK // for imperative / one-shot / per-firing-payload scheduling. // export const schedule = "0 /6 *"; `` req (Express-shaped) - method, url, path, headers, cookies, params, query - body — parsed by Content-Type: JSON → object, urlencoded → object, multipart/form-data → object of non-file fields - files — present for multipart uploads: [{ field, filename, contentType, buffer }] res (Express-shaped) - res.json(data), res.status(code) (chainable), res.send(text|buffer) - res.redirect(url), res.cookie(name, value, opts), res.setHeader(name, value) SDK — import from "hatchable" Everything you need lives under one import. Do not reach for npm packages that duplicate these — the deploy linter rejects puppeteer-core, @anthropic-ai/sdk, pg…
delete_file
ChatGPTDelete a project file. Takes effect after the next deploy. Optional reason: surfaces in the History view so the user understands why the file was removed.
delete_file
ChatGPTDelete a project file. Takes effect after the next deploy. Optional reason: surfaces in the History view so the user understands why the file was removed.
delete_file
ChatGPTDelete a project file. Takes effect after the next deploy. Optional reason: surfaces in the History view so the user understands why the file was removed.
deploy
ChatGPTDeploy the project. Runs migrations/*.sql (tracked so each runs once), runs seed.sql on first deploy, copies public/ files to the CDN, and registers api/ files as live endpoints. Increments the project version. Always populate intent and summary so the user sees a readable changelog in the console: - intent is what the USER asked for, in their own words. Quote or lightly paraphrase their last instruction. e.g. "Add a split-the-bill section", "Make the buttons rounder". - summary is what YOU did, in plain language they can read. 1–3 sentences. e.g. "Added a SplitBill component with a member counter and per-person breakdown. Updated the main page nav to switch between solo and split modes." These become the commit message AND the History row title in the console. A user will likely scroll their History a week from now to remember what they built — write the summary so a future-you-with-no-context understands what shipped. If there is no clear user prompt (autonomous maintenance), leave intent blank but still pass a summary describing what changed and why. Call this after writing all your files. To verify your functions work after deploying, use run_function — it calls the function directly through your authenticated session and works for all project visibilities. The url field is the public URL for end users — personal projects require visitors to sign up before they can view the site.
deploy
ChatGPTDeploy the project. Runs migrations/*.sql (tracked so each runs once), runs seed.sql on first deploy, copies public/ files to the CDN, and registers api/ files as live endpoints. Increments the project version. Always populate intent and summary so the user sees a readable changelog in the console: - intent is what the USER asked for, in their own words. Quote or lightly paraphrase their last instruction. e.g. "Add a split-the-bill section", "Make the buttons rounder". - summary is what YOU did, in plain language they can read. 1–3 sentences. e.g. "Added a SplitBill component with a member counter and per-person breakdown. Updated the main page nav to switch between solo and split modes." These become the commit message AND the History row title in the console. A user will likely scroll their History a week from now to remember what they built — write the summary so a future-you-with-no-context understands what shipped. If there is no clear user prompt (autonomous maintenance), leave intent blank but still pass a summary describing what changed and why. Call this after writing all your files. To verify your functions work after deploying, use run_function — it calls the function directly through your authenticated session and works for all project visibilities. The url field is the public URL for end users — personal projects require visitors to sign up before they can view the site.
deploy
ChatGPTDeploy the project. Runs migrations/*.sql (tracked so each runs once), runs seed.sql on first deploy, copies public/ files to the CDN, and registers api/ files as live endpoints. Increments the project version. Always populate intent and summary so the user sees a readable changelog in the console: - intent is what the USER asked for, in their own words. Quote or lightly paraphrase their last instruction. e.g. "Add a split-the-bill section", "Make the buttons rounder". - summary is what YOU did, in plain language they can read. 1–3 sentences. e.g. "Added a SplitBill component with a member counter and per-person breakdown. Updated the main page nav to switch between solo and split modes." These become the commit message AND the History row title in the console. A user will likely scroll their History a week from now to remember what they built — write the summary so a future-you-with-no-context understands what shipped. If there is no clear user prompt (autonomous maintenance), leave intent blank but still pass a summary describing what changed and why. Call this after writing all your files. To verify your functions work after deploying, use run_function — it calls the function directly through your authenticated session and works for all project visibilities. The url field is the public URL for end users — personal projects require visitors to sign up before they can view the site.
dry_run_deploy
ChatGPTRun every deploy-time validator against the project's current files without actually deploying. Returns errors (hard gates) and warnings (soft lints), plus a would_deploy summary of what would ship. Errors catch: package.json build scripts, reserved table names in migrations, auth route collisions, usage cap breaches. Warnings catch known runtime footguns that type-check but silently misbehave — most notably db.query() / ai.generateText() / config.get() calls without await (returning a Promise is truthy, so if (!result) guards pass and downstream property reads are undefined). Safer than calling deploy blindly and finding out mid-flight.
dry_run_deploy
ChatGPTRun every deploy-time validator against the project's current files without actually deploying. Returns errors (hard gates) and warnings (soft lints), plus a would_deploy summary of what would ship. Errors catch: package.json build scripts, reserved table names in migrations, auth route collisions, usage cap breaches. Warnings catch known runtime footguns that type-check but silently misbehave — most notably db.query() / ai.generateText() / config.get() calls without await (returning a Promise is truthy, so if (!result) guards pass and downstream property reads are undefined). Safer than calling deploy blindly and finding out mid-flight.
dry_run_deploy
ChatGPTRun every deploy-time validator against the project's current files without actually deploying. Returns errors (hard gates) and warnings (soft lints), plus a would_deploy summary of what would ship. Errors catch: package.json build scripts, reserved table names in migrations, auth route collisions, usage cap breaches. Warnings catch known runtime footguns that type-check but silently misbehave — most notably db.query() / ai.generateText() / config.get() calls without await (returning a Promise is truthy, so if (!result) guards pass and downstream property reads are undefined). Safer than calling deploy blindly and finding out mid-flight.
execute_sql
ChatGPTRun SQL against the project's dedicated PostgreSQL database. Supports: CREATE TABLE, ALTER TABLE, DROP TABLE, INSERT, SELECT, UPDATE, DELETE. Use parameterized queries for safety: pass values in the params array with $1, $2, etc. placeholders. Guarded gateway — these avoid "This SQL operation is not allowed": - One statement per call — no ;-separated multi-statements. - No $<digit> inside string literals ($29 parses as a bind param) — write "29 USD", or pass it via params. - No standalone GRANT/REVOKE tokens, even inside string data. - Avoid large multi-row VALUES — use one INSERT ... SELECT ... WHERE NOT EXISTS per row. - Use gen_random_uuid() for UUID defaults. Return format: - SELECT: { rows: [...], count: N } — DECIMAL columns return as strings (e.g. "45.00") - INSERT/UPDATE/DELETE: { changes: N } - DDL: { changes: 0 }
execute_sql
ChatGPTRun SQL against the project's dedicated PostgreSQL database. Supports: CREATE TABLE, ALTER TABLE, DROP TABLE, INSERT, SELECT, UPDATE, DELETE. Use parameterized queries for safety: pass values in the params array with $1, $2, etc. placeholders. Guarded gateway — these avoid "This SQL operation is not allowed": - One statement per call — no ;-separated multi-statements. - No $<digit> inside string literals ($29 parses as a bind param) — write "29 USD", or pass it via params. - No standalone GRANT/REVOKE tokens, even inside string data. - Avoid large multi-row VALUES — use one INSERT ... SELECT ... WHERE NOT EXISTS per row. - Use gen_random_uuid() for UUID defaults. Return format: - SELECT: { rows: [...], count: N } — DECIMAL columns return as strings (e.g. "45.00") - INSERT/UPDATE/DELETE: { changes: N } - DDL: { changes: 0 }
execute_sql
ChatGPTRun SQL against the project's dedicated PostgreSQL database. Supports: CREATE TABLE, ALTER TABLE, DROP TABLE, INSERT, SELECT, UPDATE, DELETE. Use parameterized queries for safety: pass values in the params array with $1, $2, etc. placeholders. Guarded gateway — these avoid "This SQL operation is not allowed": - One statement per call — no ;-separated multi-statements. - No $<digit> inside string literals ($29 parses as a bind param) — write "29 USD", or pass it via params. - No standalone GRANT/REVOKE tokens, even inside string data. - Avoid large multi-row VALUES — use one INSERT ... SELECT ... WHERE NOT EXISTS per row. - Use gen_random_uuid() for UUID defaults. Return format: - SELECT: { rows: [...], count: N } — DECIMAL columns return as strings (e.g. "45.00") - INSERT/UPDATE/DELETE: { changes: N } - DDL: { changes: 0 }
fork_project
ChatGPTFork a public project into your account. Copies all code and database schema (no data). The fork starts as a personal project you can modify freely. This is the recommended way to start from an existing app: fork it, then modify the code.
fork_project
ChatGPTFork a public project into your account. Copies all code and database schema (no data). The fork starts as a personal project you can modify freely. This is the recommended way to start from an existing app: fork it, then modify the code.
fork_project
ChatGPTFork a public project into your account. Copies all code and database schema (no data). The fork starts as a personal project you can modify freely. This is the recommended way to start from an existing app: fork it, then modify the code.
get_deployment
ChatGPTDetail view of one deployment by version number — returns the full file manifest (paths, hashes, sizes) and function list captured when that version shipped. Use it with list_deployments to audit or compare what changed between versions.
get_deployment
ChatGPTDetail view of one deployment by version number — returns the full file manifest (paths, hashes, sizes) and function list captured when that version shipped. Use it with list_deployments to audit or compare what changed between versions.
get_deployment
ChatGPTDetail view of one deployment by version number — returns the full file manifest (paths, hashes, sizes) and function list captured when that version shipped. Use it with list_deployments to audit or compare what changed between versions.
get_project
ChatGPTGet project details including slug, status, deployed functions, and the database schema (tables, columns, types).
get_project
ChatGPTGet project details including slug, status, deployed functions, and the database schema (tables, columns, types).
get_project
ChatGPTGet project details including slug, status, deployed functions, and the database schema (tables, columns, types).
get_schema
ChatGPTReturn the database schema for the project's PostgreSQL database: tables, columns (with types), and indexes.
get_schema
ChatGPTReturn the database schema for the project's PostgreSQL database: tables, columns (with types), and indexes.
get_schema
ChatGPTReturn the database schema for the project's PostgreSQL database: tables, columns (with types), and indexes.
grep
ChatGPTRegex content search across a project's files. Postgres-backed, scoped to one project, with glob filtering. Three output modes: - files_with_matches (default) — list paths containing a match - content — matching lines with optional context and line numbers - count — per-file match counts + total Default head_limit is 250 to prevent context blowups on broad patterns. Use glob to narrow by path (e.g. 'api/*/.js', 'public/*/.html'). Regex uses Postgres syntax (~ / ~*). Invalid or catastrophic patterns error out via a 2s statement timeout — simplify the pattern if that happens.
grep
ChatGPTRegex content search across a project's files. Postgres-backed, scoped to one project, with glob filtering. Three output modes: - files_with_matches (default) — list paths containing a match - content — matching lines with optional context and line numbers - count — per-file match counts + total Default head_limit is 250 to prevent context blowups on broad patterns. Use glob to narrow by path (e.g. 'api/*/.js', 'public/*/.html'). Regex uses Postgres syntax (~ / ~*). Invalid or catastrophic patterns error out via a 2s statement timeout — simplify the pattern if that happens.
grep
ChatGPTRegex content search across a project's files. Postgres-backed, scoped to one project, with glob filtering. Three output modes: - files_with_matches (default) — list paths containing a match - content — matching lines with optional context and line numbers - count — per-file match counts + total Default head_limit is 250 to prevent context blowups on broad patterns. Use glob to narrow by path (e.g. 'api/*/.js', 'public/*/.html'). Regex uses Postgres syntax (~ / ~*). Invalid or catastrophic patterns error out via a 2s statement timeout — simplify the pattern if that happens.
import_file_from_url
ChatGPTFetch a remote URL and save the response body as a project file — server-side, so the bytes never pass through your context window. Useful for seed data, vendor libs, and asset migration. Capped at 10 MB and 10s timeout. Private/loopback addresses are rejected. Path must live under public/, api/, or migrations/, or be one of seed.sql / hatchable.toml / package.json.
import_file_from_url
ChatGPTFetch a remote URL and save the response body as a project file — server-side, so the bytes never pass through your context window. Useful for seed data, vendor libs, and asset migration. Capped at 10 MB and 10s timeout. Private/loopback addresses are rejected. Path must live under public/, api/, or migrations/, or be one of seed.sql / hatchable.toml / package.json.
import_file_from_url
ChatGPTFetch a remote URL and save the response body as a project file — server-side, so the bytes never pass through your context window. Useful for seed data, vendor libs, and asset migration. Capped at 10 MB and 10s timeout. Private/loopback addresses are rejected. Path must live under public/, api/, or migrations/, or be one of seed.sql / hatchable.toml / package.json.
list_cron_jobs
ChatGPTList every scheduled task for a project. A task points at a function and carries a cron expression (recurring) or a one-shot fire_at, plus an optional payload delivered as the request body. Tasks are either 'declared' (written into source via export const schedule or hatchable.toml, reconciled on deploy) or 'armed' (inserted by the SDK scheduler.at() call, preserved across deploys). Response includes next_fire_at, last_fired_at, attempts, last_error, and 7-day run/error counts from FunctionLog. Diagnostic: if next_fire_at keeps moving forward but last_fired_at never advances, the scheduler isn't running.
list_cron_jobs
ChatGPTList every scheduled task for a project. A task points at a function and carries a cron expression (recurring) or a one-shot fire_at, plus an optional payload delivered as the request body. Tasks are either 'declared' (written into source via export const schedule or hatchable.toml, reconciled on deploy) or 'armed' (inserted by the SDK scheduler.at() call, preserved across deploys). Response includes next_fire_at, last_fired_at, attempts, last_error, and 7-day run/error counts from FunctionLog. Diagnostic: if next_fire_at keeps moving forward but last_fired_at never advances, the scheduler isn't running.
list_cron_jobs
ChatGPTList every scheduled task for a project. A task points at a function and carries a cron expression (recurring) or a one-shot fire_at, plus an optional payload delivered as the request body. Tasks are either 'declared' (written into source via export const schedule or hatchable.toml, reconciled on deploy) or 'armed' (inserted by the SDK scheduler.at() call, preserved across deploys). Response includes next_fire_at, last_fired_at, attempts, last_error, and 7-day run/error counts from FunctionLog. Diagnostic: if next_fire_at keeps moving forward but last_fired_at never advances, the scheduler isn't running.
list_deployments
ChatGPTList deployments for a project in reverse-chronological order. Each entry includes version, status, deployed_at, description, and summary counts (files, functions). Use this to understand recent deploy history, identify a known-good version for rollback, or debug a regression by comparing two versions.
list_deployments
ChatGPTList deployments for a project in reverse-chronological order. Each entry includes version, status, deployed_at, description, and summary counts (files, functions). Use this to understand recent deploy history, identify a known-good version for rollback, or debug a regression by comparing two versions.
list_deployments
ChatGPTList deployments for a project in reverse-chronological order. Each entry includes version, status, deployed_at, description, and summary counts (files, functions). Use this to understand recent deploy history, identify a known-good version for rollback, or debug a regression by comparing two versions.
list_files
ChatGPTList all files in a project with their paths, sizes, and hashes.
list_files
ChatGPTList all files in a project with their paths, sizes, and hashes.
list_files
ChatGPTList all files in a project with their paths, sizes, and hashes.
list_functions
ChatGPTList every deployed API function for a project: route, method, runtime tier, type ('scheduled' if the function has at least one active scheduled task, else 'api'), and 24-hour invocation and error counts. This is the 'what routes did I ship' introspection tool. Call it after a fork, after picking up an unfamiliar project, or to verify a deploy registered the endpoints you expected. Much cheaper than reading every api/ file with read_file. For scheduling details (cron, fire_at, payload, run history) use list_cron_jobs.
list_functions
ChatGPTList every deployed API function for a project: route, method, runtime tier, type ('scheduled' if the function has at least one active scheduled task, else 'api'), and 24-hour invocation and error counts. This is the 'what routes did I ship' introspection tool. Call it after a fork, after picking up an unfamiliar project, or to verify a deploy registered the endpoints you expected. Much cheaper than reading every api/ file with read_file. For scheduling details (cron, fire_at, payload, run history) use list_cron_jobs.
list_functions
ChatGPTList every deployed API function for a project: route, method, runtime tier, type ('scheduled' if the function has at least one active scheduled task, else 'api'), and 24-hour invocation and error counts. This is the 'what routes did I ship' introspection tool. Call it after a fork, after picking up an unfamiliar project, or to verify a deploy registered the endpoints you expected. Much cheaper than reading every api/ file with read_file. For scheduling details (cron, fire_at, payload, run history) use list_cron_jobs.
list_pending_uploads
ChatGPTShow multipart uploads currently staged for this project that haven't yet been committed. Use this to recover from a disconnect — find the upload_id and resume from the next chunk_index. Uploads expire 10 minutes after the last chunk was added.
list_pending_uploads
ChatGPTShow multipart uploads currently staged for this project that haven't yet been committed. Use this to recover from a disconnect — find the upload_id and resume from the next chunk_index. Uploads expire 10 minutes after the last chunk was added.
list_pending_uploads
ChatGPTShow multipart uploads currently staged for this project that haven't yet been committed. Use this to recover from a disconnect — find the upload_id and resume from the next chunk_index. Uploads expire 10 minutes after the last chunk was added.
list_projects
ChatGPTList all projects you own or collaborate on, with their tier, role, and current version.
list_projects
ChatGPTList all projects you own or collaborate on, with their tier, role, and current version.
list_projects
ChatGPTList all projects you own or collaborate on, with their tier, role, and current version.
list_skills
ChatGPTList the registry of platform skills — discrete how-to guides for one specific task each (e.g. 'gate-an-endpoint', 'add-a-cron-job', 'add-rag-search'). Each entry is a name, one-line purpose, and category. Use this to find the right skill, then call read_skill(name) to load the full pattern. When in doubt about how a Hatchable feature works, list_skills first. The skills are the canonical, agent-tested patterns. They beat guessing or reading the verbose docs. Filter by query (matches name + purpose) or tag (auth, data, ai, ops, etc.). Without filters, returns the full registry (~35 entries).
list_skills
ChatGPTList the registry of platform skills — discrete how-to guides for one specific task each (e.g. 'gate-an-endpoint', 'add-a-cron-job', 'add-rag-search'). Each entry is a name, one-line purpose, and category. Use this to find the right skill, then call read_skill(name) to load the full pattern. When in doubt about how a Hatchable feature works, list_skills first. The skills are the canonical, agent-tested patterns. They beat guessing or reading the verbose docs. Filter by query (matches name + purpose) or tag (auth, data, ai, ops, etc.). Without filters, returns the full registry (~35 entries).
list_skills
ChatGPTList the registry of platform skills — discrete how-to guides for one specific task each (e.g. 'gate-an-endpoint', 'add-a-cron-job', 'add-rag-search'). Each entry is a name, one-line purpose, and category. Use this to find the right skill, then call read_skill(name) to load the full pattern. When in doubt about how a Hatchable feature works, list_skills first. The skills are the canonical, agent-tested patterns. They beat guessing or reading the verbose docs. Filter by query (matches name + purpose) or tag (auth, data, ai, ops, etc.). Without filters, returns the full registry (~35 entries).
patch_file
ChatGPTApply a targeted edit to an existing project file without rewriting the entire file. Finds the first occurrence of old_string and replaces it with new_string. Use this instead of write_file when modifying large files (e.g. HTML) — you only send the changed portion, not the whole file. The old_string must match exactly (including whitespace). If it's not found, the tool returns an error. To insert at a specific position, use a nearby string as old_string and include it in new_string with your addition. Optional reason: short note about why this patch — surfaces in the console's History view next to the file's diff. Include when the patch's purpose diverges from the deploy's overall intent.
patch_file
ChatGPTApply a targeted edit to an existing project file without rewriting the entire file. Finds the first occurrence of old_string and replaces it with new_string. Use this instead of write_file when modifying large files (e.g. HTML) — you only send the changed portion, not the whole file. The old_string must match exactly (including whitespace). If it's not found, the tool returns an error. To insert at a specific position, use a nearby string as old_string and include it in new_string with your addition. Optional reason: short note about why this patch — surfaces in the console's History view next to the file's diff. Include when the patch's purpose diverges from the deploy's overall intent.
patch_file
ChatGPTApply a targeted edit to an existing project file without rewriting the entire file. Finds the first occurrence of old_string and replaces it with new_string. Use this instead of write_file when modifying large files (e.g. HTML) — you only send the changed portion, not the whole file. The old_string must match exactly (including whitespace). If it's not found, the tool returns an error. To insert at a specific position, use a nearby string as old_string and include it in new_string with your addition. Optional reason: short note about why this patch — surfaces in the console's History view next to the file's diff. Include when the patch's purpose diverges from the deploy's overall intent.
read_file
ChatGPTRead the content of a project file. Pass offset/limit to read a range of lines — useful for large files where the whole file would blow the context window. When either is set, the response includes cat -n style line-numbered content so subsequent patch_file calls can reference exact line numbers.
read_file
ChatGPTRead the content of a project file. Pass offset/limit to read a range of lines — useful for large files where the whole file would blow the context window. When either is set, the response includes cat -n style line-numbered content so subsequent patch_file calls can reference exact line numbers.
read_file
ChatGPTRead the content of a project file. Pass offset/limit to read a range of lines — useful for large files where the whole file would blow the context window. When either is set, the response includes cat -n style line-numbered content so subsequent patch_file calls can reference exact line numbers.
read_skill
ChatGPTLoad the full markdown body of one skill: when to use it, the canonical code shape, common pitfalls, and how to verify it works. Skills are the platform's primary agent-facing reference — every pattern an agent might need is one of these. Pass either the bare name (e.g. 'gate-an-endpoint') or the category-qualified path (e.g. 'auth/gate-an-endpoint'). Use list_skills first to discover names.
read_skill
ChatGPTLoad the full markdown body of one skill: when to use it, the canonical code shape, common pitfalls, and how to verify it works. Skills are the platform's primary agent-facing reference — every pattern an agent might need is one of these. Pass either the bare name (e.g. 'gate-an-endpoint') or the category-qualified path (e.g. 'auth/gate-an-endpoint'). Use list_skills first to discover names.
read_skill
ChatGPTLoad the full markdown body of one skill: when to use it, the canonical code shape, common pitfalls, and how to verify it works. Skills are the platform's primary agent-facing reference — every pattern an agent might need is one of these. Pass either the bare name (e.g. 'gate-an-endpoint') or the category-qualified path (e.g. 'auth/gate-an-endpoint'). Use list_skills first to discover names.
run_code
ChatGPTExecute arbitrary JS in the project's isolate runtime. The SDK is pre-imported into local scope — db, auth, email, storage, ai, agent, cache, knowledge, memory, tasks, scheduler, browser, run, approval are ready to use without import. process.env and global fetch also work. return to produce the result field. Top-level import and dynamic import('hatchable') are NOT supported in this REPL — the bindings above are how you reach the SDK. Use this as a REPL: probe the database, verify a computation, test an API shape before committing it to a file. Nothing is persisted — the snippet runs once and disappears. Caps: 5s default timeout (max 30s), 256 KB max source length. Example: run_code({ project_id, code: const { rows } = await db.query("SELECT count(*) FROM users"); return rows[0]; })
run_code
ChatGPTExecute arbitrary JS in the project's isolate runtime. The SDK is pre-imported into local scope — db, auth, email, storage, ai, agent, cache, knowledge, memory, tasks, scheduler, browser, run, approval are ready to use without import. process.env and global fetch also work. return to produce the result field. Top-level import and dynamic import('hatchable') are NOT supported in this REPL — the bindings above are how you reach the SDK. Use this as a REPL: probe the database, verify a computation, test an API shape before committing it to a file. Nothing is persisted — the snippet runs once and disappears. Caps: 5s default timeout (max 30s), 256 KB max source length. Example: run_code({ project_id, code: const { rows } = await db.query("SELECT count(*) FROM users"); return rows[0]; })
run_code
ChatGPTExecute arbitrary JS in the project's isolate runtime. The SDK is pre-imported into local scope — db, auth, email, storage, ai, agent, cache, knowledge, memory, tasks, scheduler, browser, run, approval are ready to use without import. process.env and global fetch also work. return to produce the result field. Top-level import and dynamic import('hatchable') are NOT supported in this REPL — the bindings above are how you reach the SDK. Use this as a REPL: probe the database, verify a computation, test an API shape before committing it to a file. Nothing is persisted — the snippet runs once and disappears. Caps: 5s default timeout (max 30s), 256 KB max source length. Example: run_code({ project_id, code: const { rows } = await db.query("SELECT count(*) FROM users"); return rows[0]; })
run_function
ChatGPTExecute a deployed function and return the real response. Use this to test your API endpoints. Returns: { status, headers, body, logs, error, duration_ms } Example: run_function({ project_id: 1, path: "/api/users", method: "GET" }) Example: run_function({ project_id: 1, path: "/api/users", method: "POST", body: { name: "Alice" } }) VERIFY EACH ACCESS TIER with as: run the route as it would behave for a 'public' (anonymous), 'member', or 'admin' caller. The route's declared access is enforced — so as:'public' on a member-only route returns the real 401, as:'member' on an admin route returns 403, and an allowed tier runs the handler with req.member synthesized for that role. Use this to confirm both 'the page works for a member' AND 'the gate blocks the public'. Without as, runs as you (the owner, full access). IMPORTANT: Always run_function on your API endpoints after writing them. Inspect the response body field names and types. Then write your frontend to match those exact names.
run_function
ChatGPTExecute a deployed function and return the real response. Use this to test your API endpoints. Returns: { status, headers, body, logs, error, duration_ms } Example: run_function({ project_id: 1, path: "/api/users", method: "GET" }) Example: run_function({ project_id: 1, path: "/api/users", method: "POST", body: { name: "Alice" } }) VERIFY EACH ACCESS TIER with as: run the route as it would behave for a 'public' (anonymous), 'member', or 'admin' caller. The route's declared access is enforced — so as:'public' on a member-only route returns the real 401, as:'member' on an admin route returns 403, and an allowed tier runs the handler with req.member synthesized for that role. Use this to confirm both 'the page works for a member' AND 'the gate blocks the public'. Without as, runs as you (the owner, full access). IMPORTANT: Always run_function on your API endpoints after writing them. Inspect the response body field names and types. Then write your frontend to match those exact names.
run_function
ChatGPTExecute a deployed function and return the real response. Use this to test your API endpoints. Returns: { status, headers, body, logs, error, duration_ms } Example: run_function({ project_id: 1, path: "/api/users", method: "GET" }) Example: run_function({ project_id: 1, path: "/api/users", method: "POST", body: { name: "Alice" } }) VERIFY EACH ACCESS TIER with as: run the route as it would behave for a 'public' (anonymous), 'member', or 'admin' caller. The route's declared access is enforced — so as:'public' on a member-only route returns the real 401, as:'member' on an admin route returns 403, and an allowed tier runs the handler with req.member synthesized for that role. Use this to confirm both 'the page works for a member' AND 'the gate blocks the public'. Without as, runs as you (the owner, full access). IMPORTANT: Always run_function on your API endpoints after writing them. Inspect the response body field names and types. Then write your frontend to match those exact names.
search_documentation
ChatGPTSearch Hatchable's own documentation for platform behavior — routing, the SDK surface, deploy semantics, auth config, runtime limits. Call this instead of guessing when you're unsure how a Hatchable feature works. Ranks results by term frequency across headed sections. Returns source file, section heading, and a snippet around the hit.
search_documentation
ChatGPTSearch Hatchable's own documentation for platform behavior — routing, the SDK surface, deploy semantics, auth config, runtime limits. Call this instead of guessing when you're unsure how a Hatchable feature works. Ranks results by term frequency across headed sections. Returns source file, section heading, and a snippet around the hit.
search_documentation
ChatGPTSearch Hatchable's own documentation for platform behavior — routing, the SDK surface, deploy semantics, auth config, runtime limits. Call this instead of guessing when you're unsure how a Hatchable feature works. Ranks results by term frequency across headed sections. Returns source file, section heading, and a snippet around the hit.
search_projects
ChatGPTSearch the public Hatchable project directory — other people's projects that you can view or fork. Use this to find existing apps to fork-and-modify as a starting point. Note: this searches the public marketplace. To search inside your own project's files, use the grep tool instead.
search_projects
ChatGPTSearch the public Hatchable project directory — other people's projects that you can view or fork. Use this to find existing apps to fork-and-modify as a starting point. Note: this searches the public marketplace. To search inside your own project's files, use the grep tool instead.
search_projects
ChatGPTSearch the public Hatchable project directory — other people's projects that you can view or fork. Use this to find existing apps to fork-and-modify as a starting point. Note: this searches the public marketplace. To search inside your own project's files, use the grep tool instead.
submit_platform_feedback
ChatGPTTell the Hatchable team about a platform footgun, friction point, or surprising behavior you hit during this build. Reports go straight into the platform's triage queue and turn into bug fixes, doc updates, or explicit decisions. When to call: any time a platform constraint, undocumented limit, misleading error, missing helper, or stale doc cost you more than ~5 minutes to figure out — OR any time you successfully reach for a non-obvious workaround that future builds shouldn't have to rediscover. Calling mid-build (right after the workaround) is more useful than at the end of the build, because the painful details are still fresh. Report quality matters: the title should be one sentence ("TextDecoder caps decoded strings at 32 KB per decode() call"). The body should describe what you tried, the error you got, and the workaround. Reports become Github issues / docs PRs verbatim — write for the engineer who'll fix it, not for yourself. Don't use this for: generic praise, app-level bugs in the user's own code, anything that's already documented (search skills first via list_skills).
submit_platform_feedback
ChatGPTTell the Hatchable team about a platform footgun, friction point, or surprising behavior you hit during this build. Reports go straight into the platform's triage queue and turn into bug fixes, doc updates, or explicit decisions. When to call: any time a platform constraint, undocumented limit, misleading error, missing helper, or stale doc cost you more than ~5 minutes to figure out — OR any time you successfully reach for a non-obvious workaround that future builds shouldn't have to rediscover. Calling mid-build (right after the workaround) is more useful than at the end of the build, because the painful details are still fresh. Report quality matters: the title should be one sentence ("TextDecoder caps decoded strings at 32 KB per decode() call"). The body should describe what you tried, the error you got, and the workaround. Reports become Github issues / docs PRs verbatim — write for the engineer who'll fix it, not for yourself. Don't use this for: generic praise, app-level bugs in the user's own code, anything that's already documented (search skills first via list_skills).
submit_platform_feedback
ChatGPTTell the Hatchable team about a platform footgun, friction point, or surprising behavior you hit during this build. Reports go straight into the platform's triage queue and turn into bug fixes, doc updates, or explicit decisions. When to call: any time a platform constraint, undocumented limit, misleading error, missing helper, or stale doc cost you more than ~5 minutes to figure out — OR any time you successfully reach for a non-obvious workaround that future builds shouldn't have to rediscover. Calling mid-build (right after the workaround) is more useful than at the end of the build, because the painful details are still fresh. Report quality matters: the title should be one sentence ("TextDecoder caps decoded strings at 32 KB per decode() call"). The body should describe what you tried, the error you got, and the workaround. Reports become Github issues / docs PRs verbatim — write for the engineer who'll fix it, not for yourself. Don't use this for: generic praise, app-level bugs in the user's own code, anything that's already documented (search skills first via list_skills).
update_project
ChatGPTUpdate project metadata: name, tagline, description, category, is_template. Only the fields you pass are touched. Slug and tier are immutable from MCP. Setting is_template: true lists the project in the Templates gallery so other users can fork it.
update_project
ChatGPTUpdate project metadata: name, tagline, description, category, is_template. Only the fields you pass are touched. Slug and tier are immutable from MCP. Setting is_template: true lists the project in the Templates gallery so other users can fork it.
update_project
ChatGPTUpdate project metadata: name, tagline, description, category, is_template. Only the fields you pass are touched. Slug and tier are immutable from MCP. Setting is_template: true lists the project in the Templates gallery so other users can fork it.
upload_file
ChatGPTMultipart file upload for content that exceeds a single model response's output token cap (big SPA bundles, large seed data, inline vendor libs). Flow: first call with chunk_index=0 and NO upload_id — response returns an upload_id. Subsequent calls pass that upload_id with chunk_index=1, 2, 3…. Last call sets final=true to atomically concatenate and commit as one ProjectFile. Chunks are staged in Redis with a 10-minute TTL. chunk_index overwrites (safe to retry). Max chunk size: 64 KB. Max assembled file: 20 MB.
upload_file
ChatGPTMultipart file upload for content that exceeds a single model response's output token cap (big SPA bundles, large seed data, inline vendor libs). Flow: first call with chunk_index=0 and NO upload_id — response returns an upload_id. Subsequent calls pass that upload_id with chunk_index=1, 2, 3…. Last call sets final=true to atomically concatenate and commit as one ProjectFile. Chunks are staged in Redis with a 10-minute TTL. chunk_index overwrites (safe to retry). Max chunk size: 64 KB. Max assembled file: 20 MB.
upload_file
ChatGPTMultipart file upload for content that exceeds a single model response's output token cap (big SPA bundles, large seed data, inline vendor libs). Flow: first call with chunk_index=0 and NO upload_id — response returns an upload_id. Subsequent calls pass that upload_id with chunk_index=1, 2, 3…. Last call sets final=true to atomically concatenate and commit as one ProjectFile. Chunks are staged in Redis with a 10-minute TTL. chunk_index overwrites (safe to retry). Max chunk size: 64 KB. Max assembled file: 20 MB.
view_logs
ChatGPTView function execution logs with rich filtering. Each entry includes status_code, duration_ms, log_output (captured console.log), error (if any), and a derived level field (error/warning/info). Filter by any combination of function_name, route, method, status_code (exact or 4xx/5xx wildcards), level, time range (since/until — ISO or relative like '1h'/'30m'/'7d'), full-text query across log_output and error, or specific request_id. Use this to debug production issues: e.g. level='error' + since='1h' finds everything that blew up in the last hour.
view_logs
ChatGPTView function execution logs with rich filtering. Each entry includes status_code, duration_ms, log_output (captured console.log), error (if any), and a derived level field (error/warning/info). Filter by any combination of function_name, route, method, status_code (exact or 4xx/5xx wildcards), level, time range (since/until — ISO or relative like '1h'/'30m'/'7d'), full-text query across log_output and error, or specific request_id. Use this to debug production issues: e.g. level='error' + since='1h' finds everything that blew up in the last hour.
view_logs
ChatGPTView function execution logs with rich filtering. Each entry includes status_code, duration_ms, log_output (captured console.log), error (if any), and a derived level field (error/warning/info). Filter by any combination of function_name, route, method, status_code (exact or 4xx/5xx wildcards), level, time range (since/until — ISO or relative like '1h'/'30m'/'7d'), full-text query across log_output and error, or specific request_id. Use this to debug production issues: e.g. level='error' + since='1h' finds everything that blew up in the last hour.
write_file
ChatGPTWrite or overwrite a project file. Paths are relative to the project root. Valid locations: public/ static files (HTML, CSS, JS, images, etc.) api/.js backend functions (each file is one endpoint) pages/.js server-rendered HTML at clean URLs (pages/about.js → /about) lib/ shared code pool, not routed — import anywhere as lib/<name>.js migrations/*.sql database migrations, run in filename order seed.sql optional seed data, runs once on fresh installs hatchable.toml optional config overrides package.json dependencies (no build script yet) Files are stored but not live until you call deploy. Editing an existing file? Don't re-send the whole thing — call read_file to fetch current contents, then patch_file to change just the lines you need (or write_files to update several files at once). write_file overwrites the entire file, so reserve it for new files or full rewrites. Optional reason: a short note about why THIS specific file edit is happening. Skip it when the reason is obvious from the deploy's overall intent (most edits). Include it when the per-file purpose meaningfully diverges — e.g. "bumped vue 3.4 → 3.5 to fix the reactivity bug" on a package.json edit during an unrelated feature deploy. The reason shows up in the console next to the file's diff in the History view.
write_file
ChatGPTWrite or overwrite a project file. Paths are relative to the project root. Valid locations: public/ static files (HTML, CSS, JS, images, etc.) api/.js backend functions (each file is one endpoint) pages/.js server-rendered HTML at clean URLs (pages/about.js → /about) lib/ shared code pool, not routed — import anywhere as lib/<name>.js migrations/*.sql database migrations, run in filename order seed.sql optional seed data, runs once on fresh installs hatchable.toml optional config overrides package.json dependencies (no build script yet) Files are stored but not live until you call deploy. Editing an existing file? Don't re-send the whole thing — call read_file to fetch current contents, then patch_file to change just the lines you need (or write_files to update several files at once). write_file overwrites the entire file, so reserve it for new files or full rewrites. Optional reason: a short note about why THIS specific file edit is happening. Skip it when the reason is obvious from the deploy's overall intent (most edits). Include it when the per-file purpose meaningfully diverges — e.g. "bumped vue 3.4 → 3.5 to fix the reactivity bug" on a package.json edit during an unrelated feature deploy. The reason shows up in the console next to the file's diff in the History view.
write_file
ChatGPTWrite or overwrite a project file. Paths are relative to the project root. Valid locations: public/ static files (HTML, CSS, JS, images, etc.) api/.js backend functions (each file is one endpoint) pages/.js server-rendered HTML at clean URLs (pages/about.js → /about) lib/ shared code pool, not routed — import anywhere as lib/<name>.js migrations/*.sql database migrations, run in filename order seed.sql optional seed data, runs once on fresh installs hatchable.toml optional config overrides package.json dependencies (no build script yet) Files are stored but not live until you call deploy. Editing an existing file? Don't re-send the whole thing — call read_file to fetch current contents, then patch_file to change just the lines you need (or write_files to update several files at once). write_file overwrites the entire file, so reserve it for new files or full rewrites. Optional reason: a short note about why THIS specific file edit is happening. Skip it when the reason is obvious from the deploy's overall intent (most edits). Include it when the per-file purpose meaningfully diverges — e.g. "bumped vue 3.4 → 3.5 to fix the reactivity bug" on a package.json edit during an unrelated feature deploy. The reason shows up in the console next to the file's diff in the History view.
write_files
ChatGPTWrite multiple project files in a single call. Same rules as write_file but batched — faster for scaffolding a new project or updating several files at once. Each entry in the files array has a path and content. All files are written atomically — if any path is invalid, none are written. Optional top-level reason applies to the whole batch (typical: one logical change touching many files). Per-entry reason overrides the batch reason for that specific file when their purposes diverge.
write_files
ChatGPTWrite multiple project files in a single call. Same rules as write_file but batched — faster for scaffolding a new project or updating several files at once. Each entry in the files array has a path and content. All files are written atomically — if any path is invalid, none are written. Optional top-level reason applies to the whole batch (typical: one logical change touching many files). Per-entry reason overrides the batch reason for that specific file when their purposes diverge.
write_files
ChatGPTWrite multiple project files in a single call. Same rules as write_file but batched — faster for scaffolding a new project or updating several files at once. Each entry in the files array has a path and content. All files are written atomically — if any path is invalid, none are written. Optional top-level reason applies to the whole batch (typical: one logical change touching many files). Per-entry reason overrides the batch reason for that specific file when their purposes diverge.