# Claude Code skill

A ready-made skill that teaches Claude Code how to use the form2wf MCP server safely: the build sequence, the editing rules that prevent data loss, and the gotchas.

## Install

Skills live in a folder with a `SKILL.md` file. Install it for all your projects:

**macOS / Linux**

terminal:

```bash
mkdir -p ~/.claude/skills/form2wf-mcp
curl -fsSL https://form2wf.com/docs/skills/form2wf-mcp/SKILL.md -o ~/.claude/skills/form2wf-mcp/SKILL.md
```

**Windows (PowerShell)**

powershell:

```powershell
New-Item -ItemType Directory -Force "$HOME\.claude\skills\form2wf-mcp" | Out-Null
Invoke-WebRequest https://form2wf.com/docs/skills/form2wf-mcp/SKILL.md -OutFile "$HOME\.claude\skills\form2wf-mcp\SKILL.md"
```

**One project only**

terminal (in your project):

```bash
mkdir -p .claude/skills/form2wf-mcp
curl -fsSL https://form2wf.com/docs/skills/form2wf-mcp/SKILL.md -o .claude/skills/form2wf-mcp/SKILL.md
```

Then [connect the MCP server](https://form2wf.com/docs/mcp#connect). Claude loads the skill automatically when you mention form2wf, forms that post to n8n, or use the form2wf tools.

[Download SKILL.md](https://form2wf.com/docs/skills/form2wf-mcp/SKILL.md)

## What's in it

SKILL.md:

```
---
name: form2wf-mcp
description: Use when building, inspecting or managing Form2Workflow (form2wf) forms through its MCP server — creating forms that post to n8n webhooks, editing form config/fields, data pages with onload webhooks and tables, embed snippets, submissions, themes, form users and form access. Triggers on form2wf, Form2Workflow, f2wf, "form that posts to my n8n webhook", f2w_ API keys, or the mcp__form2wf__* tools.
---

# form2wf MCP

form2wf renders forms and data pages on any website and sends submissions to
n8n webhooks. Its MCP server lets you manage one organization's forms.

## Connect

- Endpoint: `https://<form2wf-host>/mcp` — stateless Streamable HTTP, POST only.
- Auth: `Authorization: Bearer f2w_...` (or `X-API-Key: f2w_...`). Keys are created
  by an org admin in the dashboard under **API Keys**; the raw key is shown once.
- Claude Code:
  ```bash
  claude mcp add --transport http form2wf https://<form2wf-host>/mcp \
    --header "Authorization: Bearer f2w_..."
  ```
- Call `whoami` first: it returns the org, the key's access level and what it can do.

## Access levels

| Level | Can |
|---|---|
| viewer | Read everything: forms, submissions, usage, folders, themes, roles, form users, access, embed snippets |
| editor | viewer + create/update forms, manage folders |
| admin | editor + delete forms, set form access, create/update themes, create form users, assign them to forms |

A key never exceeds the current role of the member who created it. A 403 in a
tool result means the key's level is too low — say so; don't retry.

## Tools

- Read: `whoami`, `get_config_reference`, `list_forms`, `get_form`, `get_embed_snippet`,
  `list_submissions`, `get_usage`, `list_folders`, `list_themes`, `list_roles`,
  `list_form_users`, `get_form_access`
- Editor: `create_form`, `update_form`, `create_folder`, `rename_folder`, `delete_folder`
- Admin: `delete_form`, `set_form_access`, `create_theme`, `update_theme`,
  `create_form_user`, `assign_form_user`, `unassign_form_user`

## Building a form — the reliable sequence

1. `get_config_reference` once per session. Don't guess config keys; there are
   exactly five top-level keys (`showTitle`, `columns`, `theme_id`, `buttonColor`, `fields`).
2. Ask the user for the n8n **production** webhook URL (`/webhook/...`, not
   `/webhook-test/...`). Without `n8n_webhook_url` the form has no submit button.
3. `create_form` with `name`, `n8n_webhook_url`, and `config.fields`. Give every
   field a unique `id`.
4. `get_embed_snippet` and hand the user the HTML and the hosted URL.
5. Suggest a secret header: `webhook_headers: [{"key":"X-Form2wf-Secret","value":"<random>"}]`
   plus Header Auth on the n8n Webhook node.

## Editing — avoid destroying data

- `update_form` replaces `config` **as a whole**. Always `get_form`, modify the
  returned `config`, send the complete object. Sending only `fields` drops
  `columns`, `showTitle` and `theme_id`.
- Header values come back as `"[redacted]"`. Omit `webhook_headers` to keep them;
  sending the placeholders is rejected.
- Changing `slug` breaks every existing embed and link — confirm with the user first.
- Prefer `update_form` with `is_active: false` over `delete_form` (deletion is permanent
  and removes submission history). Confirm before deleting anything.

## Gotchas

- Forms have a UUID `id` (tools, submit API) and a `slug` (embeds, hosted URL,
  `targetForm`, navigation). Don't mix them up.
- Submitted values reach n8n under `body.data` (JSON). With a `file` field the
  request is multipart with flat fields and no `data` wrapper — don't combine
  `file` with `object`/`array` fields.
- Table `dataSource` is a plain dot path (`customers`), not `{{data.customers}}`.
- Templates (`{{params.x}}`, `{{data.x}}`, `{{row.x}}`) work in `defaultValue`,
  `displayValue`, link/button params and button/row-action webhook URLs — NOT in
  the form's `n8n_webhook_url` or `onload_webhook_url`.
- A target form's onload webhook only sees the page URL's query params, not
  params passed by navigation/row actions. Pass needed values as row-action
  `params` and use them as `defaultValue: "{{params.x}}"`.
- n8n's reply to a submission is not shown to the visitor; success text comes
  from the embed's `f2wf.load(..., { successMessage })` options.
- Failed deliveries (5xx/timeout) are retried up to twice — tell users to make
  workflows idempotent.
- Unchecked checkboxes are omitted from submitted data.
- Rate limit: 60 MCP requests per minute per key owner. Form quota depends on
  plan (Free: 5 forms, 1,000 submissions/month) — `create_form` returns
  `FORM_QUOTA_EXCEEDED` when full.

## Debugging a form that "doesn't work"

1. `get_form` — is `is_active` true? Is `n8n_webhook_url` set and a production URL?
2. `list_submissions` — look at `status` and the stored n8n response/error.
   A 503 means n8n failed, timed out, rejected the call (check its header auth),
   or the webhook is on a private network address (refused unless the org
   allows LAN webhooks).
3. `get_form_access` — a protected form needs the visitor to be an assigned form
   user, or a member of the form's organization.
```

---
Source: https://form2wf.com/docs/claude-skill — Form2Workflow documentation (AI agents)
