About carto-develop-app
carto-develop-app is a CLI-path skill for generating CARTO and deck.gl mapping apps from a prompt. It guides choices such as app framework, authentication, and data shape, then builds on CARTO and deck.gl examples; app scaffolding requires a shell and development environment.
Key features
- 01Guides app type and framework selection
- 02Chooses authentication and data patterns
- 03Assembles map layers and widgets
- 04Supports filters, inputs, and legends
- 05Can add an embedded map agent
Use cases
- 01Scaffolding a CARTO mapping app
- 02Building a spatial dashboard with deck.gl
- 03Choosing an app authentication model
- 04Adding an agent to an embedded map
MCP support
An OAuth MCP session can mint tokens, manage OAuth/M2M clients, and discover map or workflow IDs. App scaffolding and running require a shell; no MCP tool generates the app.
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ cartodb/ agent-skills --skill carto-develop-appAfter installing, type/carto-develop-appin Claude Code to confirm it loaded.
Bundled files
References and scripts installed along with SKILL.md.
references/agentic-variant.mdauth-hosted.mdauth-m2m.mdauth-private-oauth.mdauth-private-sso.mdauth-public-token.mdbasemap-and-view.mddata-sources.mddesign-and-theming.mdfetchmap.mdfilters.mdinputs-and-parameters.mdlayers.mdlegend.mdscaffold-react.mdscaffold-vanilla.mdscaffold-vue-angular.mdstorymap.mdwidgets.mdworkflows-and-sql.mdreferences/recipes/chat-with-map.mdfetchmap-loader.mdparameterized-query.mdraster-temperature.mdreact-h3-aggregation.mdreact-private-app-oauth.mdreact-private-app-sso.mdvanilla-points-with-widgets.mdDetails
From the project READMEcarto-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).
Access path. This is a CLI-path skill: scaffolding and running an app needs a real shell and dev environment (npm, Vite), and no MCP tool generates an app — so this can't run on sandboxed chat hosts (Claude.ai, ChatGPT). Only the credential/discovery steps have MCP equivalents: over an OAuth MCP session, mint tokens with
manage_api_access_tokens, OAuth/M2M clients withmanage_oauth_clients, and discover map/workflow IDs withread_maps/read_workflows. Everything else needs the shell. Path detection:carto-basics/references/access-paths.md.
When to use
- "deck.gl app", "CARTO app", "map app", "spatial dashboard" — use this skill.
- Builder map ID supplied →
fetchmap.md. - Storymap / scrollytelling / narrative map / guided tour →
storymap.md(composes on top of the React scaffold below). - Chat-with-map / embedded AI agent →
agentic-variant.md. - Authoring maps in Builder →
carto-create-builder-maps. Migrating a Builder map across orgs / profiles →cross-profile-copy.md.
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 # → tenant_id (region) → apiBaseUrl
carto connections list --json # → connectionName (default: carto_dw)
# One token, ONE grant per source — repeat --connection alongside every --source.
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. Referers are wildcard-matched against the browser's full page URL, so end each pattern with * (a bare http://localhost:5173 won't match the http://localhost:5173/ browsers actually send). --apis and grants are independent: maps covers sources/layers, sql covers query(), and a *QuerySource needs its query in the grant — 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). Hosted apps need no credential at all: skip the token step and follow auth-hosted.md.
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.
- "public" / "share" / "embed" / no login → API access token (
auth-public-token.md) - "private" / "users log in" / "CARTO login" → OAuth SPA (
auth-private-oauth.md) - "SSO" / "Okta" / "Azure AD" / "corporate IdP" → OAuth + SSO (
auth-private-sso.md) - "backend" / "ETL" / "CI" / "scheduled" → M2M (
auth-m2m.md) - "hosted on CARTO" / "internal tool" / "for my team" / "no infrastructure" /
carto app deploy→ CARTO-hosted, viewer token at runtime (auth-hosted.md). Needs the Hosted Apps flag; not for anonymous access.
- "public" / "share" / "embed" / no login → API access token (
- 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 viabasemap-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
apiBaseUrlishttps://<tenant_id>.api.carto.com, withtenant_id(the region, e.g.gcp-us-east1) fromcarto auth status --json.tenant_domainis the organization's own domain (e.g.clausa.app.carto.com) and carries no region.apis.baseUrlinhttps://<tenant_domain>/config.yamlis authoritative (self-hosted included). Never hard-code.connectionNamedefaults tocarto_dw; confirm viacarto connections list --json.- Public tokens must always pass
--sourceand limit--apistosql,maps. Neverimports/ldsin 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 orCALLs 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. Seeworkflows-and-sql.md. - One
filtersobject is shared by source helpers and widget methods. Mutating it triggers re-fetch on both. - Layer z-order is the
layersarray, last = on top — not CSSz-index. Restack by changing array position and passing a fresh array (never mutate in place);z-indexorders DOM panels, never layers on the shared canvas. Seelayers.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 devand report the URL.