AI · ENGINEERING · 2026-05-08 · Createrun Engineering · 9 min read
Why we built our own MCP server (and what it does)
Most low-code platforms are bolting LLMs onto code-completion and calling it AI. We wanted something different: an AI agent that could actuallyauthorour platform's artifacts. Here's how we got there.
The starting problem
Createrun is a low-code platform: customers author Forms, Queries, Workflows, and BPM flows in a browser-based Editor IDE. Some of those artifacts are simple — a contact form with three fields. Some are not — a 12-stage supplier-audit workflow with conditional manager-only approvals, dynamic stage targets, and 20 forms tied together by foreign keys.
We watched customers paste descriptions of those complex artifacts into ChatGPT and try to extract usable JSON. The output was often wrong: hallucinated field types ("number" instead of "text + Integer + currency contentType"), inline buttons in items (forbidden by our validator), missing two-layer polymorphism on DynamicBox properties. The agent didn't know our shape rules; we didn't have a way to teach it.
Why MCP — and not just an OpenAPI spec
We could have published an OpenAPI spec for our gate methods and let agents call them via function-calling. We tried. Three problems:
- Tool discovery in agent context windows is expensive.Hundreds of granular gate methods inflate the system prompt past usable limits.
- OpenAPI doesn't model two-layer polymorphism well.Our DynamicBox has a ClientValue.Type discriminator + provider-specific $attributes. Roundtripping that through OpenAPI's discriminator field produces JSON that real agents reliably get wrong.
- No standard way to surface examples.We needed canonical $attributes envelopes per provider, retrievable on demand — not duplicated into every system prompt.
Model Context Protocol solved all three. Tools are discovered lazily. Resources expose typed examples (createrun://field-types,createrun://dynamicbox-types) the agent can fetch when needed. And tools are typed at the protocol level, not just at the JSON-schema level.
What we shipped
A broad, continuously expanding tool surface across capability areas — Session, Form, Query, Workflow, Meta, Compile, Design, Data, Batch. The shape:
// Tool description template
// Purpose: ...
// PRECONDITIONS: ...
// EXAMPLE: ...
// RETURNS: ...
// ERRORS: ...
add_field(formRef, name, type, contentType?, options?)
update_field(formRef, fieldRef, ...)
remove_field(formRef, fieldRef)
add_part(formRef, type, ...)
remove_page(formRef, pageRef)
set_form_actions(formRef, actions[])
convert_to_bpm(formRef, stages[])
...
Tool counts change with every release; the current live/partial/planned surface is versioned in the public capability matrix.
The headline isconvert_to_bpm. Hand-rolling a BPM workflow for an existing form means injecting a WORKFLOW Part, defining a Flow with stages, adding a "Waiting" indicator, generating child runtime forms, and wiring stage-targeted Approve/Reject actions.convert_to_bpmdoes it atomically, in one transaction, validated end-to-end.
Atomic batches
Real authoring is multi-step: add a field, add another field, change a third one, set form-level actions, then compile. If any step fails, you don't want a half-modified form sitting in production.
We added batch transactions:begin_batchreturns a batch ID; subsequent mutating tools queue against it;commit_batchapplies all-or-nothing (best-effort atomicity, by design). Reads pass through.rollback_batchcleans up. Cross-op references viaformRef=formName(notformId) so the agent doesn't have to read back the GUID after each create.
Test mode
Agent runs need to be reproducible — both for our regression suite and for customer debugging.set_test_mode capturedumps every payload to per-session local test dumps. Replay later underdryrunto validate behaviour without touching the live database. We use it for our scenario suite and for customer support sessions.
FormValidator chokepoint
Every shape mutation flows through a single validator. The agent cannot author "type=number" hallucinations or inline buttons in Items; the validator rejects fail-fast with a specific error code. The validator is the source of truth — when we fixed the type catalogue, the agent's failure mode shifted from "produces invalid forms" to "asks for clarification."
What's next
Sub-mutators for queries and workflows (add_query_column, add_join, add_filter; add_workflow_step, add_transition, set_trigger). Phase 4 Data CRUD — runtime form data CRUD blocked behindpublishrather thancompilebecause the user-data tables ship with publish. Server-side git triggers for true bootstrap (solution → repo → clone) by the agent.
Try it
Wire the Createrun MCP server into the MCP-aware AI client your team already uses — Claude Code, Codex, or Cursor (instructions on/ai) — orrequest a sandboxand we'll set you up with a Createrun instance + the agent already configured.
←Back to blog