# Building with AI



The [Surface MCP server](/mcp-server/overview) creates the record, hands an agent the binding contract, and statically validates the HTML. Run the loop yourself, or hand it to an agent with a prompt like "build our demo booking form as an SDK form on our landing page".

## The Authoring Loop [#the-authoring-loop]

<Steps>
  <Step title="Plan (optional)">
    `plan_form` with `sdk: true` critiques a blueprint before anything is created.
  </Step>

  <Step title="Create the record">
    `create_form` with `sdk: true`: steps, questions, endings; no styling or routing parameters. Give every question real question text (the responses table reads those labels). Save the `formId` and `previewToken`.
  </Step>

  <Step title="Get the binding map">
    `get_sdk_binding_map` returns, per question: `questionId`, `componentType`, `fieldNames`, option keys, `answerShape`, plus `environmentId`, `apiBaseUrl`, and a ready-to-paste `init` snippet. Use the snippet; a wrong `apiBaseUrl` is the most common failure.
  </Step>

  <Step title="Author the page">
    Write HTML or React against the [binding contract](/build/forms-sdk/binding), copying IDs and option keys from the binding map.
  </Step>

  <Step title="Validate what ships">
    `validate_form_html` checks the markup against the record. Fix every blocker; warnings are silent data loss at runtime.
  </Step>

  <Step title="Preview, publish, deploy">
    Boot with `preview: true` and the `previewToken` to walk the draft with zero writes, then `publish_form` and deploy.
  </Step>

  <Step title="Verify end to end">
    Submit one real test response, `await form.flush()`, read it back with `get_response`, confirm every answer and `meta.surfaceTagStatus: "sdk"`. Without a browser, run headless in [Node](/build/forms-sdk/headless-and-react#running-in-node).
  </Step>
</Steps>

## Validating Framework Pages [#validating-framework-pages]

`validate_form_html` must see the markup the visitor actually receives. Plain HTML: pass the file. Framework page: fetch the rendered HTML (`curl http://localhost:3000/your-page`) and pass it with `containerSelector` set to your boot container. Never validate a hand transcription of the source. If navigation or submit is code-driven, declare `data-surface-nav="js"`.

## Editing an Existing SDK Form [#editing-an-existing-sdk-form]

Every record edit must be followed through to the HTML:

1. `get_form_structure` (with `include: ["params"]`) for the structure and its `revision`.
2. Edit with `add_step`, `update_step`, `add_component`, `update_component`, `remove_component`, or `batch_edit`, passing the revision as `ifRevision` so concurrent edits fail loudly.
3. Re-fetch `get_sdk_binding_map` and update the HTML for every difference.
4. `validate_form_html` until clean, then `publish_form` and deploy together.

If publish and deploy cannot land at the same time, publish first for additions and deploy first for removals and option-key changes. The dashboard's SDK form builder edits the same draft by hand.

## Tools That Refuse on SDK Forms [#tools-that-refuse-on-sdk-forms]

Styling, marketing, layout, and routing tools reject SDK forms: presentation lives in your HTML, navigation in your code. `simulate_response` does not apply either. `validate_form` checks configuration integrity only; a pass says nothing about your page.

## Installing the Skill [#installing-the-skill]

If your agent runner supports skills (Claude Code does):

```bash
cp -r node_modules/@surface-labs/forms-sdk/skills/surface-forms-sdk .claude/skills/
```

The skill includes the workflow, HTML patterns, an editing sync loop, and a debugging runbook.
