| name | carto-develop-app |
| description | Generate a working geospatial app powered by CARTO and deck.gl — basemap, layers (vector / H3 / quadbin / raster), widgets, filters, legend, inputs, optional chat-with-map agent, and the right auth strategy (public token, OAuth, SSO, or M2M). |
| license | MIT |
carto-develop-app
Generate a working CARTO + deck.gl app from a prompt. Four decisions — app type, framework, auth model, data shape — then assemble layers, widgets, filters, inputs, a legend, and (optionally) an embedded map agent.
Apps follow the CartoDB/deck.gl-examples blueprint. Layers from @deck.gl/carto; data sources, widgets, filters, fetchMap, query() from @carto/api-client (sources left @deck.gl/carto in v0.4.0 — don't import them from there).
When to use
Operating mode — autonomous by default
Generate the env via the CARTO CLI. Don't interview the user. Pass --json and parse.
Run, then write .env:
carto auth status --json
carto connections list --json
carto credentials create token --json \
--connection <connectionName> --source <fully.qualified.table.A> \
--connection <connectionName> --source <fully.qualified.table.B> \
--apis sql,maps \
--referers http://localhost:5173,<production-origin>
One token, many grants — not one token per table. --connection and --source pair positionally, so repeat --connection for every --source. Use --referers (plural CSV); --referer (singular) overwrites if repeated. See auth-public-token.md.
Private apps swap the token command for carto credentials create spa --json (OAuth) or carto credentials create m2m --json (M2M).
If carto isn't on PATH or auth status fails: say so once and stop — print the missing command. Don't fall back to interviewing. Assumes a working CLI (carto-basics).
Only ask what the CLI can't answer: table name (if not obvious) and ambiguous app-shape choices below.
Decision flow — only ask when truly ambiguous
- Builder map ID supplied? Yes →
fetchmap.md. Done.
1b. Storymap shape? Cues: "story map", "scrollytelling", "guided tour", "scenes/chapters/slides", "fly through". → storymap.md. Still pick the React scaffold + sources/layers below; storymap is a state pattern layered on top, not a separate scaffold.
- Demo or production? Default to vanilla TS + Vite + MapLibre (
scaffold-vanilla.md) unless the prompt says "production", "auth", "deploy", "team", "multi-screen" → React (scaffold-react.md). Vue / Angular only if explicitly named (scaffold-vue-angular.md).
- Auth model? Pick from prompt cues; ask only if cues conflict.
- Data shape? Source/layer pair from
data-sources.md + layers.md: points/lines/polygons → vector; H3 → H3; quadbin → quadbin; surfaces → raster. Wire the basemap and view-state sync via basemap-and-view.md.
Then layer in only what was asked for: widgets, filters, inputs, legend, SQL/workflows, agentic chat. Recipes in recipes/.
- Visual style? No style cues → Meridian-inspired default (clean, professional, CARTO-native). Any style cue ("futuristic", "corporate", a named design system) → custom theme matching the user's description. Either way, apply the UX layout principles. See
design-and-theming.md.
Always-on guidance
apiBaseUrl comes from carto auth status --json (tenant.domain region). Never hard-code.
connectionName defaults to carto_dw; confirm via carto connections list --json.
- Public tokens must always pass
--source and limit --apis to sql,maps. Never imports / lds in a public bundle.
- Complex analysis is a workflow, not app SQL. Predicting revenue, composite/suitability scores, segmentation, multi-step joins — build it with
carto-create-workflow; the app then reads its output table or CALLs its procedure via the Workflows API (or, if an embedded agent drives it, invokes it as a published MCP tool). Heavy logic stays server-side; the app passes parameters. See workflows-and-sql.md.
- One
filters object is shared by source helpers and widget methods. Mutating it triggers re-fetch on both.
- Layer z-order is the
layers array, last = on top — not CSS z-index. Restack by changing array position and passing a fresh array (never mutate in place); z-index orders DOM panels, never layers on the shared canvas. See layers.md. (Opaque fills hiding basemap labels is a separate, basemap-side question — basemap-and-view.md.)
- Debounce viewport spatial filters ~300 ms on
onViewStateChange.
- Design — apply the Meridian-inspired default theme unless the user specifies a different aesthetic. UX layout principles (map as hero, panel discipline, progressive disclosure) always apply. See
design-and-theming.md.
- Never invent logos. Only render a logo (CARTO, customer, partner, third-party brand) when the user supplied a verified PNG / JPG / SVG asset or pointed to an official source. No verified asset → ask the user, or use a text wordmark / omit the logo entirely. Do not generate SVG marks, fetch from unverified URLs, or substitute a similar-looking brand. Applies even to well-known brands.
- End by running the app. Run
npm install && npm run dev and report the URL.