Skip to content

How do I generate and check forms with AI agents? ​

A Formhaus form is a JSON definition, so an agent writes data instead of component code. The JSON Schema at https://formhaus.dev/schema/form-definition.json tells the agent and the editor which keys are valid, the Formhaus Claude Code plugin adds the /formhaus:formhaus-create-form skill that drafts definitions from a description, CSV or screenshot, and the @formhaus/mcp server that checks the result against the real FormEngine with validate_definition and simulate_path.

Definition ​

A support form an agent might produce. The $schema line enables completion and validation in VS Code, Cursor and JetBrains editors.

json
{
  "$schema": "https://formhaus.dev/schema/form-definition.json",
  "id": "support-request",
  "title": "Contact support",
  "submit": { "label": "Send request" },
  "steps": [
    {
      "id": "topic",
      "title": "What is it about?",
      "fields": [
        {
          "key": "topic",
          "type": "select",
          "label": "Topic",
          "validation": { "required": true },
          "options": [
            { "value": "billing", "label": "Billing" },
            { "value": "bug", "label": "Something is broken" },
            { "value": "other", "label": "Other" }
          ]
        },
        {
          "key": "orderId",
          "type": "text",
          "label": "Order number",
          "show": [{ "field": "topic", "eq": "billing" }],
          "validation": { "required": true, "pattern": "^[0-9]{6}$", "patternMessage": "Six digits" }
        }
      ]
    },
    {
      "id": "details",
      "title": "Details",
      "fields": [
        { "key": "message", "type": "textarea", "label": "Describe the problem", "rows": 5, "validation": { "required": true, "minLength": 20 } }
      ]
    },
    {
      "id": "screenshot",
      "title": "Screenshot",
      "show": [{ "field": "topic", "eq": "bug" }],
      "skip": { "label": "No screenshot" },
      "fields": [
        { "key": "screenshot", "type": "file", "label": "Screenshot", "accept": "image/*", "validation": { "required": true } }
      ]
    },
    {
      "id": "contact",
      "title": "How do we reach you?",
      "fields": [
        { "key": "email", "type": "email", "label": "Email", "validation": { "required": true } }
      ]
    }
  ]
}

1. Install the Claude Code plugin ​

bash
claude plugin marketplace add ignsm/formhaus && claude plugin install formhaus@formhaus

Inside a Claude Code session, /plugin marketplace add ignsm/formhaus and /plugin install formhaus@formhaus do the same. The plugin bundles the /formhaus:formhaus-create-form skill and the @formhaus/mcp server, which exposes validate_definition, simulate_path, capabilities and example_definitions.

To add only the server, run claude mcp add formhaus -- npx -y @formhaus/mcp. For Claude Desktop and Cursor, see MCP Server setup.

2. Generate a definition ​

/formhaus:formhaus-create-form

Then describe the form: "Support form: topic select, order number for billing, message, screenshot for bugs, email". The skill infers field types and validation, asks about steps and conditions when the input does not say, and checks the result with the MCP server.

3. Validate it ​

validate_definition with { "definition": <the JSON above> } returns:

json
{ "valid": true, "errors": [], "warnings": [] }

Errors come from the JSON Schema and from checks the engine enforces. A route from details back to topic returns:

json
{
  "valid": false,
  "errors": ["Invalid route from \"details\" to \"topic\": targets must be later steps or null."],
  "warnings": []
}

4. Simulate a path ​

simulate_path with these answers and actions walks the bug branch and skips the screenshot:

json
{
  "answers": { "topic": "bug", "message": "The export button does nothing.", "email": "ann@example.com" },
  "actions": ["next", "next", "skip"]
}

Output, without the validation report and with path entries shown without title:

json
{
  "ok": true,
  "multiStep": true,
  "path": [
    { "id": "topic", "visited": true, "skipped": false, "visibleFields": ["topic"] },
    { "id": "details", "visited": true, "skipped": false, "visibleFields": ["message"] },
    { "id": "screenshot", "visited": true, "skipped": true, "visibleFields": ["screenshot"] },
    { "id": "contact", "visited": true, "skipped": false, "visibleFields": ["email"] }
  ],
  "currentStep": { "id": "contact", "title": "How do we reach you?", "index": 3 },
  "isLastStep": true,
  "trace": [
    { "action": "next", "from": "topic", "to": "details", "moved": true },
    { "action": "next", "from": "details", "to": "screenshot", "moved": true },
    { "action": "skip", "from": "screenshot", "to": "contact", "moved": true }
  ],
  "errors": {},
  "wouldSubmit": true,
  "submitValues": { "topic": "bug", "message": "The export button does nothing.", "email": "ann@example.com" }
}

With "topic": "billing" the screenshot step leaves the path and orderId appears on the first step.

5. Add instructions to your repository ​

Paste this into AGENTS.md or CLAUDE.md so every agent session follows the same rules. Cursor users can copy .cursor/rules/formhaus.mdc into their project's .cursor/rules/ instead, as described in Cursor rule.

markdown
## Forms

Forms are Formhaus JSON definitions rendered with `FormRenderer` from `@formhaus/react` or `@formhaus/vue`.

- Start every definition with `"$schema": "https://formhaus.dev/schema/form-definition.json"`.
- Use `fields` for a single page or `steps` for a multi-step form, never both.
- Put conditional fields and steps in `show` / `showAny`, and branching in `routes`. Route targets must be later steps or `null`.
- Keep visibility, validation and step logic in the definition, not in components.
- After every change, call the `formhaus` MCP tool `validate_definition` and fix all errors and warnings.
- For multi-step forms, call `simulate_path` once per branch and check `path` and `submitValues`.
- Call `capabilities` for the supported field types, validation rules and condition operators.

How it works ​

  • The schema is generated from the TypeScript FormDefinition type and ships in @formhaus/core as @formhaus/core/schema.json.
  • validate_definition runs the schema and validateDefinition() from @formhaus/core. It reports unknown route targets, backward routes, duplicate step ids and branches that fall through into a sibling.
  • simulate_path applies answers as initial values and presses next, back and skip in order. Without actions it presses Next until the last step or the first blocking error.
  • simulate_path does not run custom validators, onStepValidate or lifecycle hooks. Test those in the app.
  • capabilities lists field types, field props, validation rules, condition operators and route semantics, so the agent does not invent keys.