A field-service platform built from your AI editor over MCP
Point Claude Code, Cursor or Copilot at Supero and let it author, validate, gate and deploy the whole stack — inside your roles, against the live platform.
An agent builds the whole work-order stack from your editor over MCP — and is not allowed to tell you it worked. It validates against the live platform, deploys to a throwaway URL, and must prove the running app loads before it takes a public address.
What we are building
A regional HVAC company dispatches thirty technicians. Office staff raise work orders, assign them to a technician and a time window, and track them to completion. Technicians work from a phone: today's jobs, directions, parts used, before-and-after photos, and a customer signature at the end. Supervisors approve anything over a cost threshold. Customers get an SMS when the technician is on the way.
We built it without opening the dashboard. That part is not the interesting one. The interesting one is that the agent doing the building was not allowed to tell us it worked: it validated the bundle against the live platform rather than against its own assumptions, deployed to a throwaway URL, and had to prove the running app loaded and read data before it could take a public address.
Connecting the editor — and what your key can do
Supero exposes an MCP server speaking JSON-RPC 2.0 over a single Streamable HTTP endpoint. Every initialize, tools/list and tools/call is a POST to the same path; there is no per-tool route and no domain segment in the URL, because scope comes entirely from the credential.
# Claude Code — no config file to edit
claude mcp add --transport http supero \
https://api.supero.dev/mcp/v1/messages \
--header "X-API-Key: ak_..."
# then check /mcp inside Claude Code — "supero" should be listed as connectedCursor takes the same URL and header in ~/.cursor/mcp.json; VS Code Copilot takes it in .vscode/mcp.json with "type": "http", then Agent mode. Any MCP client that speaks Streamable HTTP and JSON-RPC 2.0 works — those three simply have first-class instructions.
| Credential | Resolves to | Can build into |
|---|---|---|
| Domain-admin key | domain_admin | Any project in the domain — and can create new ones |
| Project-admin key | project_admin | That one project only |
| Tenant-scoped key | tenant_user | Refused — cannot run build tools |
| Plain JWT session | jwt | Read-only calls; refused for publish, deploy, teardown |
A project-admin key that targets a different project's project_uuid is refused with an explicit error rather than a bare permission failure — which is what you want when an agent is choosing arguments on your behalf. Replacing a project's entire app additionally requires a domain-admin key.
A build session, tool by tool
The tool surface is bigger than one session needs. Grouped by what stage it belongs to:
| Group | Tools | For |
|---|---|---|
| Docs & planning | build_get_skills, build_get_examples, build_plan, build_get_service_contract | The build spec, a complete reference app, a checklist for an idea, and a service's real state machine |
| Project | build_whoami, build_list_projects, build_get_project, build_create_project, build_update_project, build_set_project_mode, build_replace_project | Resolve scope, find or create the target, read its schema_namespace |
| Build lifecycle | build_validate, build_publish, build_stage_bundle, build_get_bundle, build_deploy, build_deploy_status, build_logs, build_go_live, build_teardown | Validate, version, deploy, watch, retire |
| Capabilities | build_list_capabilities, build_recommend_integrations, build_configure_services | The live services catalog, tiered integration recommendations, sandbox keys |
| Quality gates | build_doctor, build_smoke_test, build_e2e_test, build_e2e_test_status | Preflight, does-it-actually-load, and (where enabled) the behavioural suite |
| Data sources | build_list_data_sources, build_connect_data_source, build_discover_source, build_bind_data_source, build_run_data_source, build_list_bound_schemas | Connect the customer's own database, API or warehouse |
The app is a directory bundle with a fixed shape
The agent is not inventing a project layout. A Supero app has exact paths the CLI expects — it does not scan for scripts or infer structure. Knowing which files are yours and which are regenerated is the difference between a productive session and an afternoon of edits that silently vanish.
| File | Yours? | Purpose |
|---|---|---|
schemas.py | Yes | Data model — exports ALL_SCHEMAS and PUBLIC_SCHEMAS |
config.py | Yes | One AppConfig dataclass: identity, tenants, users, services |
setup.py | Yes | AppSetup(...).run() — registers, seeds, wires policies and workflows |
ui_schemas.py or ui/app.js | Yes — pick one | Declarative UI grammar, or a hand-written front end. Never both. |
__main__.py, requirements.txt, run.sh, .env.example | Yes | Entry point, the supero dependency, launcher, config template |
ui/index.html, ui/config.js, ui/server.py, ui/*.js runtime | No — regenerated every run | The layered SDK UI runtime |
Dockerfile, docker-compose.yml | No — written once if missing | A committed custom version is left alone |
- Bundle limits: 300 files, 2 MB per file, roughly 8 MB inline. Past that — or when your model's output cap truncates a large file — use
build_stage_bundlefor an out-of-band upload and pass the returnedfile_idasfiles_ref. - Put the same
namespaceliteral on every schema dict; it must be a plain string, not a variable or an f-string. - In
config.py,"workflows"must be the last entry inservicesif the list is non-empty. - Read
appNamespaceandpublicSchemasfromwindow.__SUPERO_CONFIGrather than hardcoding them — a hardcoded value diverges fromschemas.pyand the runtime cannot reconcile the two.
What the agent actually assembles
Have the agent call build_get_service_contract for each of these before it writes any UI. It returns the real state machine, the operation names and the default UI schemas — which is precisely the information a model would otherwise hallucinate.
check_availability is an overlap check, not a hold — but book_slot does take a real lock, so two dispatchers picking the identical window are resolved safely and the loser is refused. Partially overlapping windows fall back to read-then-write, so handle a refusal as a normal race in the UI rather than an error toast. Remember the refusal arrives as HTTP 200 with success:false and status_code: 409 in the body.The approval threshold is a workflow, not a code branch: bind @update:field:work_order, condition on the cost, and call approval when it exceeds the limit. The technician's photos go through the attachment service (which owns its own upload/scan/archive lifecycle) into your own S3 bucket. The "on my way" SMS is a service_call step.
Three gates, and what each one actually catches
An agent that writes code and declares victory is a liability. The gates exist so "it works" is a checked claim rather than an assertion.
| Gate | When | Catches |
|---|---|---|
build_validate | Before publish | Bundle shape, schema validity and platform contract violations — AST-only, so your code is never executed |
build_doctor | Before publish | Silent deploy-killers: the things that pass validation and still produce a blank page |
build_smoke_test | After deploy | Whether the deployed URL actually loads and reads data — not whether it compiled. Pass the expected bundle hash and it also confirms the deploy really rolled, rather than serving a cached older app. |
For a change request on an app you already published, have the agent start from build_get_bundle rather than re-authoring from scratch. That pulls the current published files — including any hand-tuned UI — so edits build on top of prior work instead of quietly discarding it.
What this build does not give you
- Offline is yours to build. Technicians work in basements and dead zones, and nothing here queues a job update, a photo or a signature for later. The generated mobile app is online-first. If your engineers lose signal daily, that is the first thing you will add and it is not a small feature.
- Agent-driven sessions are less predictable than the guided builder: one can converge in two hours or need three attempts. The gates are what stop an unlucky session from becoming a broken deploy — do not skip them to save time.
build_go_liveneeds a plan that includes cloud hosting; it refuses with a billing message otherwise.- The behavioural test gate is off unless your deployment enables it.
build_replace_projectis destructive and needs a domain-admin key. Keep production in live mode so it is refused there.
How to run the session
Mint the narrowest key that will do the job
Use a project-admin key unless the agent genuinely needs to create projects. Keep it out of source control — Claude Code stores MCP config in local machine config; add project-level Cursor/Copilot config files to .gitignore. Rotate from the dashboard if it leaks.
⏱ ~5 minutes
Make the agent orient before it writes
Insist on build_whoami first, then build_get_skills and build_get_examples. A model that skips these will produce plausible-looking code against a platform shape it invented.
⏱ ~10 minutes
Pin the services before the UI
Call build_list_capabilities for what is actually available, build_recommend_integrations with the app description for a tiered suggestion, and build_get_service_contract per service. Then have the agent write schemas.py with extends pointing at the right bases.
⏱ ~30 minutes
Author, validate, repeat
This is the bulk of the session and it is genuinely iterative: author the bundle, run build_validate, fix, re-run. Because validation is AST-only against the live platform, the loop is fast and nothing runs your code to find out it was wrong.
⏱ ~3–4 hours
Deploy ephemeral, gate, then go live
Deploy with target: cloud_ephemeral for a short-lived managed preview and run the gates against it. Only when those pass should build_go_live mint the permanent URL — take the hostname from the public_url it returns rather than constructing one, and note that going live needs a plan that includes cloud hosting. Use build_logs when a gate fails and build_teardown when a preview has served its purpose.
⏱ ~1 hour
Connect the customer's existing systems
Field service rarely starts from nothing — parts and pricing usually live in an ERP. The same MCP session can connect it: build_connect_data_source → build_discover_source → build_bind_data_source, then choose live or synced reads per table.
⏱ ~1 hour
Time to deliver
| Phase | Agent-driven on Supero | A field-service SaaS you configure, or build |
|---|---|---|
| Editor connected, scope resolved | ~10 min | n/a |
| Data model + CRUD + admin screens | ~1 h (authored + validated) | 2–3 weeks |
| Scheduling, approvals, attachments | 0 — inherited via extends | 3–4 weeks |
| Push, SMS, calendar, object storage | ~45 min of configuration | 2–3 weeks |
| Mobile app for technicians (online-only) | ~half a day (Expo, same backend) | 4–6 weeks |
| Quality gates + deploy | ~1 h | 1–2 weeks of CI work |
| Total | ~1 day | ~3 months |
What the gates cover, and what they never will
The gates are the whole argument, and they are also the part you can verify without trusting us: deliberately break a deploy and run build_smoke_test against it. Watch it refuse once, early, and the rest of the session becomes an ordinary engineering loop rather than an act of faith.
What no gate covers is judgement. Nothing here checks that the work-order model matches how your dispatchers actually work, and no smoke test was ever going to tell you the technicians needed offline capture. The agent removes the typing. The specification is still yours.
Build this on Supero
Describe it and get schemas, APIs, SDKs and a governed multi-tenant backend — or point your own AI editor at Supero over MCP and build it there.