I build a form builder (trulyform), and for a while the AI story was the usual one: a "generate with AI" box in the editor, as if everyone was trying to be ChatGPT.
It works, but it's the wrong place for it. If I'm already in Claude or Cursor working out what to ask a client, I don't want to switch tabs and retype it. And the box in my editor doesn't have my context or my skills. The assistant I already use does.
So I put an MCP server in front of the same thing the editor uses. You add one URL, sign in, approve access, and that's it. No API key to paste.
https://trulyform.com/mcp
It works in Claude, Claude Code, ChatGPT, Cursor, Codex, VS Code with GitHub Copilot and Mistral Vibe, and it's on the free plan.
Then you just ask:
Make a client intake form for my design studio. Ask about their business, their goals, and any date they're working toward.
and a draft shows up in your account.
So here is what Claude did:
And the form:
What the assistant gets
The server is small on purpose. The model gets these tools:
-
list_formsandget_formto find and read forms -
create_formandsave_form_draftto make and edit drafts -
list_field_typesto learn the form format -
publish_formto put a specific version live -
list_responses,get_responseandform_statsfor the answers -
read_docsfor everything else
list_field_types turned out to matter more than I expected. Instead of describing the form schema in a long tool description and hoping the model remembers it, the tool returns every question type with its settings, its logic operators, the shape of its answer and a ready-to-edit template. It also hands out fresh ids for new questions and choices. The model copies a template and fills it in, instead of guessing what a dropdown looks like.
What it's not allowed to do
The part I spent the most time on is what the assistant is not allowed to do. Saving never publishes. It can create and edit drafts, read forms and responses, and publish when you tell it to publish that specific version. I didn't want a typo in a prompt to put a half-finished form in front of a real client.
"That specific version" is doing a lot of work in that sentence, so here's how it actually works.
Every draft has a revision number. When the model reads a form, it gets the current draft_revision. To save, it has to send that number back as base_revision. If I changed the form in the browser in the meantime, the numbers don't match, the save is rejected with a draft_conflict error, and the model has to read the form again and redo its change on top of mine. Without that check, an assistant working from a stale copy would quietly overwrite whatever I'd just fixed by hand.
Publishing works the same way. publish_form doesn't publish "whatever the draft is now". It publishes the exact revision I reviewed. So the flow is: the model saves a draft, I open it in the editor and look, I say "publish it". If anything touched the draft between my look and that call, the revision is stale and the publish is refused. It's also marked as a destructive tool, so clients that respect tool annotations ask before running it, and its description tells the model to only call it when I ask for it.
A couple of smaller things fall out of the same idea:
- Model clients retry more than you'd think, usually after a dropped reply. Every save carries a
mutation_id, and a retry with the same id and the same payload counts as the same save, not a second one. -
create_formcan't be made safe to retry that way, since each call makes a new form. Its description tells the model to checklist_formsfirst if a reply got lost, so I don't end up with three copies of "Client intake". - Republishing identical content doesn't create a new version, and responses people already sent stay tied to the version they actually answered.
None of this is specific to MCP. It's what you'd do for any API where two writers can touch the same document. But with an agent as one of the writers, it stops being optional.
Reading the answers
Reading responses has been the more useful half for me than I expected. "Group the free-text answers by theme and tell me how many people said each" is a decent first pass. It's only a first pass, though. It will happily sound confident about 12 responses, so I still skim the raw answers before I act on a summary. trulyform hands over the responses; the summary is the model's work, and I treat it that way.
If you try it and something breaks, tell me here. It's just me, so I'll see it.


Top comments (0)