AI agents
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
mkdir -p ~/.claude/skills/form2wf-mcp
curl -fsSL https://form2wf.com/docs/skills/form2wf-mcp/SKILL.md -o ~/.claude/skills/form2wf-mcp/SKILL.mdWindows (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)
mkdir -p .claude/skills/form2wf-mcp
curl -fsSL https://form2wf.com/docs/skills/form2wf-mcp/SKILL.md -o .claude/skills/form2wf-mcp/SKILL.mdThen connect the MCP server. Claude loads the skill automatically when you mention form2wf, forms that post to n8n, or use the form2wf tools.
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.