FormNode MCP
Use FormNode's MCP server with Claude-compatible clients or OpenAI agent code to inspect, create, and update forms from the real app domain.
1. Open Settings > API and create a dedicated MCP key.
2. Use Build and update for most agentic form-building workflows.
3. Use Everything only if the model also needs to create submissions.
4. Rotate or revoke each key independently if you connect multiple clients.
Download the FormNode Agent skill when your client supports local agent skills or reusable instructions. It packages the safety checklist, workflow patterns, and API/MCP operating rules agents should follow while the docs and live schemas remain the source of truth.
Current version: 2026.06.01. The download response includes x-formnode-skill-sha256 so you can verify the zip your client received.
Writable MCP clients can create, list, inspect, and cancel approvals.
For most GUI-built forms, configure the form's automation webhook first, then create approvals without passing a separate callbackUrl.
If you need a one-off destination such as an n8n wait/resume URL, pass callbackUrl explicitly when creating the approval.
REST details and payload examples for approval creation, approval callbacks, and normal submission webhooks live in the API reference.
Use list_agent_testable_forms to find forms with demo scenarios, dynamic fields, or Matrix fields. Then call smoke_test_form with a formId, optional scenarioId, and any dependency values.
Smoke tests are read-only. They load webhook-backed options through FormNode's public resolver path, report Matrix row and column counts, return a dry-run payload preview, and do not create submissions, dispatch webhooks, or store browser screenshots.
For shared forms, provide organizationId unless the selected scenario already supplies the intended organization context.
For CSAT, NPS, feedback, and questionnaire flows, pass purpose="csat" and presentationMode="survey" to create_form or update_form.
Survey presentation changes the respondent layout only. Submissions, tenant scope, dynamic fields, prefill, human verification, and webhook delivery stay on the same production paths as classic forms.
Set isGlobal=true when one form should be shared across organizations. Use allowedOrganizationIds to restrict which organizations get links. An empty allowlist means every organization in the workspace can use the form.
Set isGlobal=false for a form tied to its owning organization only. FormNode validates every allowed organization against the workspace before saving the scope.
For dynamic fields that should call your own automation endpoint, prefer submissionWebhookType="custom" for form submissions and dataSourceType="custom" plus customWebhookUrl for dynamic option fields. This is the most predictable path for self-hosted n8n, Make, Pipedream, Zapier, workers, and other HTTPS webhook platforms.
If a dynamic option field needs an organization integration mapping but should not show a visible parent selector, set orgContextParam. FormNode resolves the mapping server-side after workspace and organization validation.
Agents can also set initialValueWebhookUrl on a dynamic field when the form should open with the current organization-specific value. The URL is called server-side with the same orgContextParam injection and is never exposed in the public form payload.
Update the client onboarding form:
- add a dynamic multiselect named people
- use customWebhookUrl=https://automation.example.com/webhook/list-people
- use initialValueWebhookUrl=https://automation.example.com/webhook/current-people
- set valueKey=id and labelKey=name
- set orgContextParam.queryKey=parentId
- set orgContextParam.mappingKey=hudu.companyId
- do not add a visible organization selectorHidden fields are intentionally excluded from normal submissions. If an agent needs to carry operational context such as a ticket ID, company ID, contact ID, or pod context, the field must set systemField=true. Use systemContextKey when the incoming prefill key differs from the field name.
{
"id": "ticket_id",
"type": "text",
"name": "ticket_id",
"label": "Ticket ID",
"hidden": true,
"required": false,
"systemField": true,
"systemContextKey": "ticketId"
}https://app.formnode.io/mcphttps://app.formnode.io/.well-known/mcp.json{
"mcpServers": {
"formnode": {
"type": "streamable-http",
"url": "https://app.formnode.io/mcp",
"headers": {
"Authorization": "Bearer fn_sk_..."
}
}
}
}import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
});
const response = await client.responses.create({
model: "gpt-4.1",
tools: [
{
type: "mcp",
server_label: "formnode",
server_url: "https://app.formnode.io/mcp",
headers: {
Authorization: "Bearer fn_sk_...",
},
},
],
input: "Create a customer onboarding form for Acme and configure its webhook destination.",
});
console.log(response.output_text);import { Agent, run, MCPServerStreamableHttp } from "@openai/agents";
const formnode = new MCPServerStreamableHttp({
name: "formnode",
url: "https://app.formnode.io/mcp",
requestInit: {
headers: {
Authorization: "Bearer fn_sk_...",
},
},
});
const agent = new Agent({
name: "FormNode Assistant",
instructions: "Help build and manage FormNode forms.",
mcpServers: [formnode],
});
const result = await run(
agent,
"Create an onboarding form for Acme with webhook routing and dropdown options."
);
console.log(result.finalOutput);Use one key per client so access can be revoked cleanly.
Start with View only if you want the model to inspect your setup before making changes.
Use Build and update for form creation, field edits, mappings, and webhook configuration.
Use Everything only when you intentionally want submission writes.
For writable clients, keep human approval enabled in the client unless you have a controlled agent workflow and you trust the prompts driving it.