Developers/SDK/Build an App with Your Own AI Agent
Build an App with Your Own AI Agent
Use your own coding agent (Claude Code, Cursor, or similar) to author a full Supero app locally, then validate, publish, and deploy it over the Supero MCP server.
The deal: the backend is locked, the UI is entirely yours
This page is for teams who want to drive the whole build with their own AI coding agent instead of the built-in AI Assistant — pointing Claude Code, Cursor, or a similar tool at a written specification and letting it author real files on your machine. The Supero MCP server is a bridge, not a generator: your agent designs the schema and writes every line of the front end; MCP only validates what you authored against the live platform, publishes it, and deploys it. No code is generated on Supero's side and none of your source is executed during validation.
Every build sits on one side of a fixed line. Call the left column exactly as documented — never reimplement it, never fight it. The right column is yours with no template and no fixed navigation.
| LOCKED — call it exactly | YOURS — full creative freedom |
|---|---|
| The HTTP API (CRUD, transactional services, auth, public reads) | Every pixel: layout, navigation, animation, components |
| The client.* JS runtime (auth + CRUD + refs + transactional) | Routing model, page structure, state management |
| The services.* integrations (email, Stripe, AI, Slack, …) | Visual design, theme, typography, motion |
| The Python SDK (the supero package: app setup, CLI) | Data-fetching strategy, caching, optimistic updates |
| The record contract (uuid, name, display_name, created_at, …) | Dashboards, charts, kanban boards, calendars, wizards |
| Transactional state machines (order, booking, payment, …) | How and when you surface each state transition |
| The packaging contract — the file shapes below | ui/app.js and any modules/assets you add under ui/ |
| The RBAC engine and server-side policy enforcement | How you reflect permissions in the UI |
ℹ️ Two failure modes this boundary prevents
Reimplementing locked things (your own auth, your own fetch wrapper, a hardcoded API base URL) diverges from platform behavior in ways that break later. Fighting locked things (wrong argument count to a call, sending auth headers on a public read, inventing a method that does not exist) tends to fail silently — a blank screen or a spinner, not a console error.
The locked API you call: client.*, services.*, and the record contract
Two globals are guaranteed to exist before ui/app.js runs: client (auth, CRUD, references, transactional services) and services (fire-and-forget integration wrappers). Never re-declare, reinitialize, or reimplement either — the runtime scripts that provide them load before your code does.
javascript
var cfg = window.__SUPERO_CONFIG || {};
// Auth — 5 positional args; tenant is ALWAYS '' so the server resolves the
// signed-in user's own tenant (multi-tenant users 401 if you pass a fixed tenant name)
await client.login(cfg.domain, email, password, cfg.project, '');
// CRUD — bare or namespaced schema name; both resolve
var leads = await client.getObjects('lead');
var created = await client.createObjectWithRefs('lead',
{ display_name: 'Jane Doe', contact_name: 'Jane Doe', email: '[email protected]', lead_state: 'new' },
[{ ref_name: 'Company', ref_uuid: companyUuid }]);
if (created.refErrors.length) {
showToast('Created, but a link failed: ' + created.refErrors.map(function (e) { return e.ref_name; }).join(', '), 'warning');
}
// Filter/sort/search — the real server-side query API for lists and dashboards
var hot = await client.queryObjects('lead',
{ op: 'and', conditions: [{ field: 'lead_state', op: 'eq', value: 'qualified' }] },
{ sort_by: 'value', sort_order: 'desc', limit: 50 });| Record field | Notes |
|---|---|
| uuid | Primary key — key on this, never id |
| name / display_name | name is a slug; createObject auto-derives it from display_name when omitted |
| created_at / updated_at | Timestamps, set by the server |
| parent_type / parent_uuid | Where the record sits in the hierarchy — set these to create a child record |
| fq_name | Fully-qualified path, used internally for multi-tenant routing |
services.* wraps the integrations your config.py services list imports — email, sms, stripe, ai, slack, calendar, and more. Each call is fire-and-forget: one promise, no retry, no state. Always handle rejection and design the UI to degrade if a service is not configured yet.
javascript
services.email.send({ to: lead.email, subject: 'Welcome', bodyHtml: '<p>Thanks for reaching out!</p>' })
.then(function () { showToast('Email sent', 'success'); })
.catch(function (e) { showToast('Email failed: ' + e.message, 'error'); });⚠️ Two different AI paths
A chat that answers questions over your app's own data should use the built-in agentic assistant (the aiChatStream global, or the ready-made AIChatPanel component) — it is on by default and needs no service import. services.ai is a separate integration for when your app logic itself needs to call an AI feature (summarize, classify, generate); it must be imported and configured like any other integration, so do not wire your in-app assistant to it.
Two ways to build the UI — pick exactly one
Supero supports exactly two front-end patterns for ui/app.js. Choose deliberately, per app, and never mix them — mounting your own React tree alongside a call to AppShell.render() gives you two React roots fighting over the DOM.
| Path | Shape | When to use it |
|---|---|---|
| A — AppShell.render() (fast path) | app.js is roughly 50 lines of configuration: a tabs array plus one AppShell.render({...}) call | Plain CRUD apps where you want speed over a bespoke look — the library renders a complete standard app: login, sidebar, CRUD tables, modals, dashboard |
| B — full custom UI | app.js mounts your own React tree and renders whatever you want, calling the same client.*/services.* primitives underneath | Advanced navigation, bespoke layouts, complex interactions, a designed brand experience — this is what your own AI agent is best suited to build |
A Path-B app.js starts from a mount pattern that satisfies three rules at once: route on the URL hash (so Back/Forward and deep links work), mount into a sibling container rather than the library's own root, and dismiss the platform's first-paint splash screen — the library only removes that splash inside AppShell.render(), which a custom app never calls.
javascript
(function () {
var h = React.createElement; // React is a global — never import or re-declare it
function App() {
var [route, setRoute] = React.useState(window.location.hash || '#/');
var [authed, setAuthed] = React.useState(client.isAuthenticated());
React.useEffect(function () {
function onHash() { setRoute(window.location.hash || '#/'); }
window.addEventListener('hashchange', onHash);
return function () { window.removeEventListener('hashchange', onHash); };
}, []);
if (!authed) return h(LoginScreen, { onDone: function () { setAuthed(true); } });
return h(Shell, { route: route }); // your app
}
function mountApp() {
var pl = document.getElementById('supero-preloader');
if (pl && pl.parentNode) pl.parentNode.removeChild(pl); // dismiss the boot splash — required in Path B
var el = document.getElementById('myapp-root');
if (!el) { el = document.createElement('div'); el.id = 'myapp-root'; document.body.appendChild(el); }
ReactDOM.createRoot(el).render(h(App, null)); // a SIBLING container, not the library's own root
}
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', mountApp);
else mountApp();
})();That is an abbreviated version to show the shape — the full pattern (including the async auth-rehydration guard, needed because the client restores its session from local storage asynchronously) is in the build specification your agent fetches at the start of the session.
Connect your agent to Supero over MCP
Point your MCP client (Claude Code, Cursor, or any MCP-capable agent) at Supero's MCP endpoint and authenticate with an API key sent as X-API-Key — never as a Bearer token.
http
POST https://api.supero.dev/mcp/v1/messages
X-API-Key: ak_... (an opaque, scoped secret)| Key scope | What it can build |
|---|---|
| Domain-admin key | Can create projects (plan permitting) and build into any project in the domain |
| Project-admin key | Pinned to a single project — can build into that one project only |
| Tenant-scoped key | Cannot build apps — the build tools require a domain- or project-admin key |
ℹ️ A domain-wide key does not pick a project for you
A project-scoped key is already pinned; a domain-wide credential still has to select or create a project before authoring begins. Use the project tools below to do that — never scan the local filesystem or read local project state for this, since the connection carries all the context you need.
The build flow: select a project, validate, publish, deploy
Once connected, the build tools take you from a fresh idea to a running app without leaving the MCP session — the same flow walked through end to end in Build an App over MCP. Local deploy is always available; a managed cloud preview is available where enabled for your platform.
| Tool | What it does |
|---|---|
| build_whoami | Resolves your key's role, domain, and plan — call this first to decide the flow |
| build_get_skills | Fetches the build specification your agent authors against, plus the SDK version floor to target |
| build_list_projects / build_get_project | Find or inspect a project — build_get_project returns the schema_namespace you must set as the literal namespace on every schema |
| build_create_project | Domain-admin only — creates a fresh project and returns its API key once |
| build_validate | Validates your authored bundle against the live platform before you publish — structural checks only, your code is never executed |
| build_publish | Packages your files, uploads them, and records a new version — returns a version id and a download URL; runs build_validate first unless you force it |
| build_deploy | target: local hands you run instructions using the project's own key; a managed cloud-preview target is also available where enabled |
| build_smoke_test | Checks a deployed app actually loads, dismisses the boot splash, and reads a row — run it after every deploy |
text
build_whoami
build_get_skills → author schemas.py / config.py / setup.py / ui/app.js locally
build_create_project {name:"acme-store"} (or build_list_projects → build_get_project)
build_validate {project_uuid, files:{...}} → fix any errors it reports
build_publish {project_uuid, files:{...}} → version_uuid, download_url
build_deploy {project_uuid, target:"local"} → run instructions with the project's own keySend your files as a map of {relative_path: content} — a leading folder name like acme-store/ is stripped automatically. Author at least the eight required files; do not ship .env, ui/index.html, ui/config.js, ui/server.py, or a Dockerfile — the CLI generates those locally, and they are dropped if you include them in a publish.
Validation, secret hygiene, and versioning
build_validate is structural and AST-only: it checks the file manifest, syntax, import-safety, that config.py exports AppConfig, schema validity, and that every schema's namespace matches the project's schema_namespace. Nothing you send is executed on the platform.
🚨 Never inline a secret
Publishing drops any secret-pathed file automatically (everything under .ssh/.aws/.gnupg, private-key files, credential files) and rejects content that contains an inline API key. Keep real credentials out of every file you send — .env.example is a template, not a place for a live key.
- •Iterate by editing locally, then re-running build_validate → build_publish → build_deploy — each publish records a new version, and re-publishing bumps it.
- •A validation error you can't explain from your own edit is almost always a namespace mismatch or a missing required file — re-check schemas.py against the project's schema_namespace and the eight-file manifest above.
- •Keep requirements.txt unpinned (just supero) so the CLI can manage the version floor for you as the SDK evolves.
Next steps
- •Anatomy of a Supero App — the full file-by-file reference for everything a bundle can contain
- •Python SDK — the supero package your run.sh installs and the CLI commands it wraps
- •Marketplace: Publish, Import & Personalize — how to distribute an app you have built beyond your own project
- •Schema Reference — the complete field, reference, and namespace model your schemas.py relies on
On this page