dataapp-development
Use when building, modifying, deploying, or debugging Keboola Apps (Streamlit or Python/JS). Covers the full lifecycle — choosing app type, the Python/JS prod-app-and-drafts model (never create a second app), configuring keboola-config/, storage access (RO workspace, RW Query Service, input mapping), authentication, DuckDB caching for performance, default Keboola styling, dashboard patterns, optional Kai chat integration, and the three client paths (MCP-only, Claude Code with filesystem, kbagent CLI).
How do I install this agent skill?
npx skills add https://github.com/keboola/ai-kit --skill dataapp-developmentIs this agent skill safe to install?
- Gen Agent Trust Hubpass
The skill provides a comprehensive environment for developing Keboola Data Apps using Streamlit or Python/JS stacks. It includes boilerplate templates, configuration for Nginx and Supervisord, and extensive documentation on authentication, storage access, and performance caching. Security is a primary focus, with detailed guidance on preventing SQL injection and managing sensitive environment variables and tokens.
- Socketpass
No alerts
- Snykpass
Risk: LOW · No issues
What does this agent skill do?
Keboola App Development
This skill covers the full lifecycle of Keboola Apps (formerly "Data Apps"): choosing the right app type, building locally, deploying, and debugging. It supports both Streamlit apps and Python/JS apps, and three client paths (MCP-only, Claude Code with filesystem access, and kbagent CLI).
The skill is a router — it does not contain all the guidance itself. The references/ files load on demand based on the task at hand. Read the decision tree below, then load only the references you need.
Decision tree
Answer these questions in order. Each answer routes to the right reference.
1. What is the task?
| Task | Where to look first |
|---|---|
| Build a new app from scratch | references/choosing-app-type.md → type-specific reference → references/deployment-paths.md. Python/JS: read references/python-js-prod-and-drafts.md first — a prod app usually exists already |
Anything that will call modify_python_js_data_app | references/python-js-prod-and-drafts.md, before the call |
| Modify an existing app (add feature, fix bug) | references/dev-workflow.md for the change loop. Python/JS: continue the app's draft — references/python-js-prod-and-drafts.md |
| See what an app running in dev mode actually shows | references/dev-workflow.md §Verify an app running in dev mode — a preview link opened in your own browser |
| Read, describe, or push from a clone you did not just make | references/existing-clone.md |
| Deploy or redeploy | references/deployment-paths.md |
| Debug a deployment or runtime issue | references/troubleshooting.md |
| Migrate an existing app between types | references/choosing-app-type.md + references/streamlit-apps.md + references/python-js-apps.md |
2. Which app type?
If unsure → references/choosing-app-type.md. Short version:
- Streamlit — fastest when the team is Python-only and the UI is mostly sidebar + main pane. Read
references/streamlit-apps.md. - React + Vite + Express in one Node container — the Python/JS default. Start from
templates/react-vite-app/; everything the browser loads is bundled. Readreferences/python-js-apps.md. - Combined Python + Node — only when you genuinely need a Python backend (ML model, existing Python codebase). Read
references/python-js-apps.md(multi-server section).
3. Which client path?
references/deployment-paths.md covers all three:
- Path A — Claude Desktop / web (MCP-only, no filesystem): Streamlit via
modify_streamlit_data_app/deploy_data_app; Python/JS viamodify_python_js_data_app+create_python_js_data_app_git_credential+ git +deploy_data_app(seereferences/python-js-prod-and-drafts.md; needs a git-capable runner). - Path B — Claude Code / local agent with filesystem + MCP: Edit files locally, push to the app's git repo (Keboola-managed or customer-provided), deploy via MCP or kbagent.
- Path C — CLI agent (
kbagent): Full lifecycle viakbagent data-appcommand group.
4. Any cross-cutting concerns?
| Concern | Reference |
|---|---|
| Reading from / writing to Keboola Storage (RO default uses DuckDB cache) | references/storage-access.md + references/duckdb-caching.md |
| Securing the app (login, SSO, OAuth) | references/authentication.md |
| Cutting DWH costs and speeding up read-only dashboards (default for RO apps) | references/duckdb-caching.md |
| Styling — default Keboola palette and footer | references/styling-guide.md |
| Styling — shadcn/ui, dark mode, ECharts on top of the template | references/styling-react-bundled.md |
| Building a dashboarding-style app | references/dashboard-patterns.md |
| Adding a natural-language assistant to the app | references/kai-integration.md |
| Source repos / canonical docs / UI navigation | references/glossary.md |
Templates index
| Template | Use when |
|---|---|
templates/streamlit/ | New Streamlit app, code or git deployment. |
templates/python-app/ | New Python-only Python/JS app (Flask or similar). |
templates/react-vite-app/ | New Python/JS app — the default (React + Vite + Tailwind + Express). |
templates/python-node-app/ | Adding a Python backend next to the default template's Node server. |
templates/duckdb-cache/ | Adding the DuckDB caching pattern to an existing Python or Node app. |
Need authoritative Keboola docs?
The Keboola MCP server exposes a docs_query tool that searches the official Keboola Connection documentation. When this skill's references don't answer a question — UI navigation specifics, edge cases, recent platform changes — call docs_query to get the canonical answer rather than guessing or relying on stale memory.
Hard rules (apply to every task)
- Never commit
.streamlit/secrets.tomlor any file with real credentials. Add to.gitignorebefore committing. - RO workspace before input mapping. New apps default to the RO workspace pattern. Input mapping is discouraged — see
references/storage-access.md. - Apps must handle
POST /on the root path. Keboola POSTs to/on startup; the React + Vite template answers it in nginx. Streamlit handles this natively; Flask needsmethods=["GET", "POST"]; Express needsapp.all('/'). - No
pip installin Python apps. The base image blocks PEP 668. Useuv syncdriven bypyproject.toml. All Python supervisord commands must useuv run. - Never declare
[program:nginx]inkeboola-config/supervisord/. Nginx is managed by the base image. - Validate data first, code second. When using Keboola MCP, call
get_tableandquery_datato confirm schema before writing SQL. If the project has a semantic layer, check it first viasearch_semantic_context/get_semantic_contextand ground the query in those definitions rather than inventing the calculation. Seereferences/dev-workflow.md. - Pick one Keboola path per session. Before any project-mutating call, run BOTH detection checks (
which kbagent+ scan formcp__*[Kk]eboola*tools) and ask the user which to use if more than one is present. Don't silently pick. Seereferences/deployment-paths.md§Pick one path per session. - MCP-only flows: compose source directly into the tool call. When the chosen path is
modify_streamlit_data_app, thesource_codeargument is the deployment artifact — don't pre-write a local copy. Seereferences/deployment-paths.mdPath A. - For local-dev credentials: pre-fill what you can, ask for what's missing, then offer to run. Never grep the filesystem or scan unrelated env vars for tokens. See
references/storage-access.md§Getting the env vars for local development. - An empty id means create. For any Keboola tool where an empty
configuration_idswitches update into create (modify_python_js_data_app,modify_streamlit_data_app), the id is the decision, not a detail. Never send an empty one without saying in your reply that you are creating a new resource and why no existing one fits. For Python/JS apps a create also provisions a git repo that can never be attached later, andparent_configuration_idis what makes the call a draft instead. Seereferences/python-js-prod-and-drafts.md. - Say what Storage access grants, before you turn it on. The workspace is scoped to the whole project, not to the tables the app uses — the app can read every table in the project's Storage. Tell the user that in your own words when you enable
storage_access, and say it again if the app isno-auth, where anyone with the URL reaches data through it. Enabling it is fine and usually what they want; enabling it silently is not. Seereferences/storage-access.md. - Config does not travel between a draft and its prod app, in either direction. They are separate Storage configurations; a git merge moves source code and nothing else. Nothing is inherited when a draft is created either —
modify_python_js_data_appreads the parent only for its repo and credentials. So whatever you set while iterating (storage_access,storagewritable tables,authentication_type,auto_suspend_after_seconds) is set on the draft alone, and has to be set again on the prod app before you deploy it. The two that bite: withoutstorage_accessprod fails at request time withmissing required env vars: WORKSPACE_ID; without thestorageoutput mapping its writes fail while reads keep working. Both look like code regressions and are not. Seereferences/python-js-prod-and-drafts.md§6. - Log metadata, never data. App logs can leak end-user PII. Log row counts, column names, redacted SQL, which branch of the code answered — never cell values, row contents, response bodies, or raw request values.
console.debug(`${rows.length} rows, columns: ${Object.keys(rows[0] ?? {})}`)✓ —console.debug(rows)or a logged customer email ✗. - Nothing loads from a CDN at runtime. Every script, stylesheet and font the browser loads is bundled at build time or served by the app itself — never
<script src="https://cdn…">. A CDN outage, a compromised package or a page's content policy would break the app from outside it. New Python/JS apps start fromtemplates/react-vite-app/, which already bundles React, Tailwind and Recharts. An app already on another stack keeps it: edit it in its own idiom, add no new CDN dependency, and move it to the template only on the user's yes — offer that when a change would rewrite most of the frontend anyway. - Every Vite app carries
kaiPreview(). It is how Keboola's app preview knows the app rendered, and how the user points you at a part of the page. When you change an existing Vite app whosevite.configlacks it, add it in that same change, without telling the user unless it fails; an older inlinekaiPreviewErrorHandlerdoes not count,kaiPreview()replaces it. Seereferences/python-js-apps.md§Keboola's app preview.
How can the creator link this skill?
Add the canonical catalog link to the repository README so users can inspect current installs and available audits. The publishing guide covers the complete discovery path.
<a href="https://skillzs.dev/skills/keboola/ai-kit/dataapp-development">View dataapp-development on skillZs</a>