Using the Deftform MCP server

The Deftform MCP server connects AI assistants such as Claude Code, Claude Desktop, Cursor or GitHub Copilot in VS Code to your Deftform workspace. It uses the Model Context Protocol. Once it's connected, your assistant can:

  • browse your forms and their fields

  • build new forms and add, edit, reorder or delete fields

  • change form settings, for example close a form or change the completion message

  • read responses, download response PDFs and analyze statistics

  • submit responses to your forms

Requirements

  • A Deftform account with access to the workspace settings

  • An API token for the workspace

  • An MCP client that supports remote servers over Streamable HTTP and lets you send an Authorization header, for example Claude Code, Claude Desktop, Cursor or VS Code

Note: ChatGPT and the claude.ai website aren't supported yet. Their connectors require signing in via OAuth and can't use an API token. Claude Desktop works with the mcp-remote bridge described below.


1. Create an API token

The MCP server uses the same tokens as the Deftform API. Each token belongs to one workspace.

  1. Go to Settings → API + MCP.

  2. Click Create new token and enter a name, for example Claude Code.

  3. Choose the access level:

    • Read-only access: your assistant can view forms, fields and responses. Use this for reporting and analysis.

    • Read & write access: your assistant can also create and change forms, fields and settings, and submit responses.

  4. Copy the token right away. It's shown only once.

Every request needs this header:

Authorization: Bearer YOUR_API_TOKEN

Tip: Create a separate token for each tool or device. If you stop using one, or a token leaks, you can delete just that token on the API settings page. Start with a read-only token if you only want to ask questions about your data.

Tokens created before access levels were introduced have full read & write access.


2. Connect your MCP client

Server URL: https://deftform.com/mcp
Transport: Streamable HTTP

Replace YOUR_API_TOKEN with your token in the examples below.

Generic configuration

Most clients that use an mcpServers config file accept this format:

{
"mcpServers": {
"deftform": {
"type": "http",
"url": "https://deftform.com/mcp",
"headers": {
"Authorization": "Bearer YOUR_API_TOKEN"
}
}
}
}

Claude Code

claude mcp add --transport http deftform https://deftform.com/mcp \
--header "Authorization: Bearer YOUR_API_TOKEN"

Claude Desktop

Claude Desktop's config file can't send custom headers to remote servers directly. Use the mcp-remote bridge instead. You need Node.js installed.

Open Settings → Developer → Edit Config and add:

{
"mcpServers": {
"deftform": {
"command": "npx",
"args": [
"mcp-remote",
"https://deftform.com/mcp",
"--header",
"Authorization:${DEFTFORM_AUTH}"
],
"env": {
"DEFTFORM_AUTH": "Bearer YOUR_API_TOKEN"
}
}
}
}

Restart Claude Desktop after you save the file.

VS Code (GitHub Copilot)

Create .vscode/mcp.json in your project, or run MCP: Open User Configuration to set it up for all projects:

{
"inputs": [
{
"type": "promptString",
"id": "deftform-token",
"description": "Deftform API token",
"password": true
}
],
"servers": {
"deftform": {
"type": "http",
"url": "https://deftform.com/mcp",
"headers": {
"Authorization": "Bearer ${input:deftform-token}"
}
}
}
}

VS Code asks for the token once and stores it securely.

Cursor

Add this to ~/.cursor/mcp.json, or to .cursor/mcp.json in your project:

{
"mcpServers": {
"deftform": {
"url": "https://deftform.com/mcp",
"headers": {
"Authorization": "Bearer YOUR_API_TOKEN"
}
}
}
}

Check the connection

Ask your assistant:

"List my Deftform forms."

If you see your forms, the connection works.


3. How your forms are organized

Workspace
└── Forms (identified by a short id, e.g. "aB3dE9")
├── Fields (identified by a uuid)
└── Responses (identified by a uuid)

Tools refer to items by ID, not by name. A typical flow is:

  1. list-forms to get the form_id

  2. get-form to get the fields and their uuids

  3. add-fields, update-field, list-responses, submit-response, and so on

You don't have to do this yourself. Your assistant looks up the IDs automatically.


4. Available tools

Tool

Type

Description

get-workspace

Read

Gets details about the workspace, its owner and members

list-forms

Read

Lists all forms with their response counts

get-form

Read

Gets a form's settings and all of its fields

list-responses

Read

Lists the responses of a form

get-response

Read

Gets a single response with all answers

get-response-stats

Read

Gets totals, trends, conversion rate and answer distributions

get-response-pdf

Read

Creates a PDF of a response and returns a download link

create-form

Write

Creates a new form

update-form-settings

Write

Updates the settings of a form

add-fields

Write

Adds one or more fields to a form

update-field

Write

Updates the settings of a field

delete-field

Write

Deletes a field

reorder-fields

Write

Changes the order of all fields of a form

submit-response

Write

Submits a new response to a form

With a read-only token, write tools aren't offered to your assistant.

Read tools

get-workspace

Returns details about the workspace the token belongs to.

Parameters: none

Example response

{
"name": "Acme Inc.",
"timezone": "Europe/Berlin",
"locale": "en",
"max_upload_size_mb": 50,
"forms_count": 12,
"owner": { "name": "Jane Doe", "email": "jane@acme.com" },
"members": [
{ "name": "John Smith", "email": "john@acme.com" }
],
"created_at": "2025-03-02T09:12:44+00:00"
}

list-forms

Lists all forms in the workspace, newest first.

Parameter

Type

Required

Description

search

string

no

Only return forms whose name contains this text

Example response

{
"forms": [
{
"id": "aB3dE9",
"name": "Contact us",
"slug": "contact-us-aB3dE9",
"is_closed": false,
"responses_count": 214,
"incomplete_count": 18,
"created_at": "2026-02-11T14:03:21+00:00"
}
]
}

get-form

Returns a form's settings and all of its fields, in the order they appear on the form.

Parameter

Type

Required

Description

form_id

string

yes

ID of the form

Example response (shortened)

{
"id": "aB3dE9",
"name": "Contact us",
"is_closed": false,
"cta_label": "Send message",
"after_message": "Thanks, we'll get back to you soon.",
"fields": [
{
"uuid": "6f1c0a8e-...",
"label": "Email",
"type": "input",
"element_type": "email",
"required": true,
"options": null,
"sorting": 1
},
{
"uuid": "a2d94b17-...",
"label": "Topic",
"type": "select",
"options": ["Sales", "Support", "Other"],
"required": false,
"sorting": 2
}
]
}

list-responses

Lists the responses of a form, newest first.

Parameter

Type

Required

Default

Description

form_id

string

yes

ID of the form

incomplete

boolean

no

both

true returns only incomplete (abandoned) responses, false only completed ones

from

string

no

Only responses created on or after this date (YYYY-MM-DD)

to

string

no

Only responses created on or before this date (YYYY-MM-DD)

page

integer

no

1

Page number

limit

integer

no

25

Responses per page, max. 100

Dates use your workspace timezone.

Example response (shortened)

{
"data": [
{
"uuid": "0d6e2f4a-...",
"number": 214,
"number_formatted": "214",
"responses": [
{ "key": "Email", "value": "alex@example.com" },
{ "key": "Topic", "value": "Support" }
],
"is_incomplete": false,
"referrer": null,
"created_at": "2026-11-12T16:40:09+00:00"
}
],
"meta": { "page": 1, "limit": 25, "total": 214, "has_more": true }
}

get-response

Returns a single response with all answers, in the same format as one item of list-responses.

Parameter

Type

Required

Description

uuid

string

yes

uuid of the response

get-response-stats

Returns aggregated statistics for a form within a date range:

  • totals: responses, incomplete responses, visits and conversion rate

  • a timeline

  • per-field answer counts and option distributions

  • averages for rating and slider fields

  • the latest saved AI summary

Field statistics only count completed responses.

Parameter

Type

Required

Default

Description

form_id

string

yes

ID of the form

from

string

no

30 days before to

Start date (YYYY-MM-DD)

to

string

no

today

End date (YYYY-MM-DD)

group_by

string

no

day

Timeline grouping: day, week or month

The date range can be up to 366 days.

Example response (shortened)

{
"form": { "id": "aB3dE9", "name": "Contact us" },
"range": { "from": "2026-10-14", "to": "2026-11-13", "timezone": "Europe/Berlin", "group_by": "day" },
"totals": { "responses": 48, "incomplete": 6, "visits": 520, "conversion_rate": 9.23 },
"timeline": [
{ "period": "2026-10-14", "responses": 2, "incomplete": 0, "visits": 17 }
],
"fields": [
{
"uuid": "a2d94b17-...",
"label": "Topic",
"type": "select",
"answered": 45,
"options": [
{ "value": "Support", "count": 29 },
{ "value": "Sales", "count": 12 }
],
"other": []
}
],
"ai_summary": null
}

get-response-pdf

Creates a PDF of a single response and returns a download link.

Parameter

Type

Required

Description

uuid

string

yes

uuid of the response

Example response

{ "pdf_url": "https://..." }

Write tools

Write tools need a token with read & write access. Changes take effect immediately, also on forms that are already live.

Tip: When you build a new form, ask your assistant to create it closed (is_closed: true). Open it once you've reviewed it in the form builder.

create-form

Creates a new form. Add fields afterwards with add-fields.

Parameter

Type

Required

Description

name

string

yes

Form name (max. 255 characters)

description

string

no

Description shown below the form title

cta_label

string

no

Label of the submit button

after_message

string

no

Message shown after submission

after_redirect_url

string

no

URL to redirect to after submission

after_redirect_delay

integer

no

Seconds to wait before redirecting (0–60)

is_closed

boolean

no

Create the form closed for responses

closed_message

string

no

Message shown while the form is closed

show_formtitle

boolean

no

Show the form title (default: true)

include_response_in_email

boolean

no

Include the response in the notification email

capture_location

boolean

no

Capture the respondent's location

captcha

string

no

altcha or turnstile

seo_title

string

no

SEO title

seo_description

string

no

SEO description

seo_allow_bots

boolean

no

Allow search engines to index the form (default: true)

Example response

{
"id": "Xy7pQ2",
"uuid": "3c5e9a1d-...",
"name": "Event registration",
"slug": "event-registration-Xy7pQ2",
"created_at": "2026-11-13T10:15:00+00:00"
}

update-form-settings

Updates the settings of a form. Only the settings you send are changed. Besides form_id, you can send any of these:

  • General: name, description, is_closed, closed_message, responses_limit

  • Buttons and labels: cta_label, cta_label_continue, submit_aria_label, progress_label, show_formtitle, show_multipage_progress

  • After submission: completion_heading, after_message, after_redirect_url, after_redirect_delay

  • Emails: after_message_email_subject, after_message_email, send_pdf_to_respondent, admin_email_subject, admin_email_note, admin_email_attach_pdf, include_response_in_email

  • Response numbers: number_prefix, number_suffix

  • Protection and data: captcha (altcha, turnstile, recaptcha or none), capture_location, ai_summary_enabled

  • Integrations: slack_enabled, discord_enabled, hubspot_enabled, google_sheets_enabled

  • SEO: seo_title, seo_description, seo_allow_bots

Example response

{
"id": "aB3dE9",
"name": "Contact us",
"updated": ["is_closed", "closed_message"],
"updated_at": "2026-11-13T10:20:31+00:00"
}

add-fields

Adds up to 50 fields to a form in one call. Fields are added at the end, or at a specific position.

Parameter

Type

Required

Description

form_id

string

yes

ID of the form

fields

array

yes

The fields to add, in order (1–50)

position

integer

no

0-based position to insert the fields at. If you leave it out, fields are added at the end.

Each field needs a type. These types are available:

Type

Element

input

Short answer. Set element_type to text (default), email, number, phone or datepicker.

textarea

Long answer

select

Dropdown

radio

Single choice

checkbox

Multiple choice

ranking

Ranking

countryselect

Country dropdown

rating

Rating (1–5 or 1–10)

slider

Slider

timepicker

Time picker

address

Address

signature

eSignature

fileupload

File upload

optincheckbox

Opt-in checkbox, for example for consent

hidden

Hidden field

paragraph

Paragraph / text

divider

Divider

spacer

Spacer

page

Page break for multi-page forms

Settings available for all types:

Setting

Type

Description

label

string

Plain-text label. Required, except for divider, spacer and page.

required

boolean

Whether an answer is required

placeholder

string

Placeholder text

text_help

string

Help text below the field

subtext

string

Text below the label. Basic formatting allowed (see below).

hide_label

boolean

Visually hide the label

display_inline

boolean

Display choices inline

readonly

boolean

Make the field read-only

include_in_pdf_export

boolean

Include paragraph content in PDF exports

aria_label

string

Accessible label for screen readers

aria_description

string

Accessible description for screen readers

custom_key

string

Key used instead of the field uuid in submissions and webhooks. Letters, numbers, dashes and underscores; must be unique within the form.

Settings for specific types:

Type

Settings

input

element_type, time_enabled (datepicker), is_sender_email and send_confirmation_email (email), phone_allow_lookup, phone_default_country (auto or a 2-letter country code) and phone_allowed_countries (e.g. de,at,ch) (phone)

select, radio, checkbox, ranking

options (required, up to 250 choices), delimiter (checkbox only, default ;)

rating

rating_limit (5 or 10), rating_label_min, rating_label_max

slider

slider_min, slider_max, slider_step, slider_range, slider_default_value (e.g. 50, or 20,80 in range mode), slider_show_value

timepicker

time_format (12 or 24), time_interval (1–60 minutes), time_min and time_max (HH:MM), time_unavailable (e.g. 12:00,13:00-13:59)

address

address_visible_fields (any of street, street2, city, state, zip, country), address_enable_autocomplete

fileupload

fileupload_info_text, fileupload_limit_text

paragraph

text (required)

optincheckbox

text (the consent text; defaults to the label), optin_value

hidden

hidden_default

Settings that don't apply to a field's type are ignored.

Example request

{
"form_id": "Xy7pQ2",
"fields": [
{ "type": "paragraph", "label": "Intro", "text": "<p>Register for our <strong>Product Day</strong>.</p>" },
{ "type": "input", "label": "Full name", "required": true },
{ "type": "input", "element_type": "email", "label": "Email", "required": true },
{ "type": "radio", "label": "Which session?", "options": ["Morning", "Afternoon"], "required": true },
{ "type": "optincheckbox", "label": "Privacy", "text": "I agree to the <a href=\"https://acme.com/privacy\">privacy policy</a>.", "required": true }
]
}

The response contains the new fields, in the same format as in get-form.

update-field

Updates the settings of a field. Only the settings you send are changed. You can send the same settings as in add-fields, except type. A field's type can't be changed.

Parameter

Type

Required

Description

form_id

string

yes

ID of the form

field_uuid

string

yes

uuid of the field

settings

no

Any setting from add-fields that applies to the field's type

Returns the updated field.

Note: If other fields have conditional logic based on this field, changing its options doesn't update those conditions. Check them in the form builder afterwards.

delete-field

Deletes a field from a form, including any conditional logic that refers to it. Existing responses keep their answers, but the field is no longer shown on the form or included in exports.

Parameter

Type

Required

Description

form_id

string

yes

ID of the form

field_uuid

string

yes

uuid of the field

Example response

{ "uuid": "a2d94b17-...", "deleted": true }

Deleting is marked as a destructive action, so most MCP clients ask you to confirm it first.

reorder-fields

Sets the order of all fields of a form.

Parameter

Type

Required

Description

form_id

string

yes

ID of the form

field_uuids

array

yes

Every field uuid of the form, exactly once, in the new order

To insert new fields at a specific place, use the position parameter of add-fields instead.

submit-response

Submits a new response to a form, for example to import data or test a form.

Parameter

Type

Required

Description

form_id

string

yes

ID of the form

data

object

yes

Answers keyed by field uuid (or custom key)

referrer

string

no

Source of the submission, e.g. claude

Checkbox answers are arrays, address answers are objects. Required fields must be filled in.

Example response

{
"uuid": "9b2a7c3e-...",
"number": 215,
"created_at": "2026-11-13T10:31:12+00:00"
}

5. Example prompts

You don't need to call tools yourself. Describe what you want, and your assistant picks the right tools.

Build forms

  • "Create a closed form called 'Event registration' with fields for full name, email, company, a single-choice question for the session (Morning or Afternoon) and a required privacy consent checkbox."

  • "Turn this list of questions into a feedback survey with a 1–10 rating at the end: …"

  • "Add a page break after the contact details in my 'Job application' form, and move the file upload for the CV to the second page."

Edit forms

  • "Make the phone field in 'Contact us' optional and limit it to Germany, Austria and Switzerland."

  • "Add 'Partnership' as an option to the topic dropdown in 'Contact us'."

  • "Remove the 'Fax' field from all my forms."

  • "Close the 'Summer raffle' form and show 'The raffle has ended. Thanks for taking part!' instead."

Analyze responses

  • "How many responses did 'Contact us' get last month, and what's the conversion rate?"

  • "Which topics are selected most often in 'Contact us' this year? Group the trend by month."

  • "Summarize the comments from the last 50 responses of our customer feedback survey."

  • "Download the PDF of the latest response to 'Job application'."


6. Limits and good to know

  • Rate limit: 60 requests per minute per workspace. If you exceed it, the server responds with 429 Too Many Requests. Wait a moment and try again.

  • Access: A token only gives access to the workspace it was created in.

  • Field limits: Up to 50 fields per add-fields call, 500 fields per form and 250 options per field.

  • Formatting: Labels, options, placeholders and help texts are plain text. Any HTML is removed.

    • Paragraphs allow h1–h3, p, br, b, strong, em, i, u, ul, ol, li and a.

    • Opt-in texts allow a, strong, b, i and em.

    • Subtexts and file upload texts allow p, br, b, strong, em, i, u and a.

    • Links must start with https://, http:// or mailto:.

    • Ask your assistant to write HTML, not Markdown.

  • Builder only: HTML/code, image and embed elements, conditional logic and field validation rules can only be added in the form builder.

  • Live changes: Changes to a form that's already published are visible to respondents immediately. Adding a required field affects everyone who fills out the form from then on.

  • Deleting forms: You can't delete forms through MCP. Do this in Deftform.


7. Troubleshooting

Problem

Cause and fix

401 Unauthorized

The token is missing, wrong or deleted. Check the Authorization: Bearer … header, or create a new token.

429 Too Many Requests

You've hit the rate limit. Wait a minute and try again.

ChatGPT or claude.ai can't connect, or ask you to sign in

These connectors require OAuth, which isn't supported yet. Use Claude Code, Claude Desktop, Cursor or VS Code with an API token instead.

"Tool [create-form] not found." or write tools are missing

Your token has read-only access. Create a token with read & write access. If you've just changed the token, reconnect the server or restart your client, because some clients cache the tool list.

"Form not found." / "Field not found." / "Response not found."

The ID is wrong, the item was deleted, or it belongs to another workspace. Ask your assistant to list the items again.

"Fields of type html can only be edited in the form builder."

HTML/code, image and embed elements can't be changed through MCP. Edit them in the form builder.

"The fields.0.options field is required."

Dropdown, single choice, multiple choice and ranking fields need at least one option.

"The fields.0.custom_key has already been taken."

Another field in the form already uses this custom key. Choose a different one.

"A form can have at most 500 fields."

The form has reached the maximum number of fields.

"Provide the uuids of all fields of the form exactly once."

reorder-fields needs the complete list of field uuids. Ask your assistant to load the form with get-form first.

"This form is closed and not accepting responses."

Reopen the form with update-form-settings (is_closed: false) before submitting responses.

"The date range must not exceed 366 days."

Use a shorter range in get-response-stats.

Still stuck? Contact us with your MCP client, the prompt you used and the error message.


Was this article helpful?