MCP Tools: Forms and Assets

Five tools cover forms and one covers assets. Between them they can design a form from scratch, read everything people have submitted to it, and enumerate every image, video, audio file and document in your library. What they cannot do is put a form live or put a file into the library — publishing a form is not on the MCP surface at all, and neither is uploading.

Reading forms

list_forms

GET /api/forms · Returns { forms } · Also available in the Leed Assistant

Takes no parameters. Returns form metadata — formId, formName, formPrototype, description, isDirty and modification stamps — not the field definitions. Deleted forms are excluded. This is the call that resolves a human name like “Contact us” into the formId every other tool here wants.

get_form

GET /api/forms/:formId · Returns { form } · Also available in the Leed Assistant

ParameterTypeRequiredDefaultNotes
formIdstringYes—From list_forms

The complete form, including its fields array. Read this before editing: update_form_draft merges what you send, but the fields array is replaced wholesale when you send it, so a model that wants to add one field must send all of them.

Creating and editing a form

create_form and update_form_draft share one field surface. The difference is which parts of it are mandatory.

FieldTypeRequired on createMeaning
formPrototypestringYesThe prototype the form is built from; slugified on save
formNamestringYesThe form’s name in the CMS
buttonstringYesSubmit button label
successCallbackstringYesWhere the visitor goes after a successful submission
failureCallbackstringYesWhere the visitor goes when submission fails
fieldsarray of field objectsYesThe form’s inputs, in order
headingstringNoHeading rendered above the form
descriptionstring (≤ 400 chars)NoShort description; also shown in form lists
detailsstringNoLonger supporting copy
welcomeBackMessagestringNoShown instead of the form to a visitor already identified
leedAutofillbooleanNoPre-fill from what Leed already knows about the visitor
autofill"linkedin" or nullNoThird-party autofill provider; send null to clear it
assetIdstring or nullNoAsset associated with the form
templateIdstring or nullNoTemplate the form renders with
protectedbooleanNoMarks the form system-owned — do not set it
isDirtybooleanNoThe route sets this for you
disabledAt / disabledBytimestamp / stringNoDisables the form
The field object, in full

Every entry in fields is an object with this shape. formFieldId, name, required and type are mandatory on each one.

KeyTypeRequiredMeaning
formFieldIdstringYesStable id for the field
namestringYesThe submitted key — this is what maps onto a contact record
typetexttextareatel
requiredbooleanYesWhether the visitor must fill it in
labelBeforestringNoLabel rendered before the input
labelAfterstringNoLabel rendered after the input
placeHolderstringNoPlaceholder text
valuestringNoDefault value
patternstringNoValidation pattern
rowsnumberNoRows for a textarea
multiplebooleanNoAllow multiple selections
selectedbooleanNoPre-selected state
optionsarray of { label, value }NoChoices for select, radio and checkbox
oninput / onchangestringNoClient-side handlers

Which name values map onto which contact fields — and which are simply captured as extra data — is enumerated in the Form Field Reference.

create_form

POST /api/forms · Returns { form } · Also available in the Leed Assistant

Takes the whole surface above; the six fields marked required must be present. Creating a form also creates its default form action, so the form is immediately wired up to record submissions once it is published.

{
  "formPrototype": "contact",
  "formName": "Contact us",
  "heading": "Talk to us",
  "button": "Send",
  "successCallback": "/thanks/",
  "failureCallback": "/contact/?error=1",
  "fields": [
    {
      "formFieldId": "email",
      "name": "email",
      "type": "email",
      "required": true,
      "labelBefore": "Work email"
    },
    {
      "formFieldId": "message",
      "name": "message",
      "type": "textarea",
      "required": false,
      "labelBefore": "How can we help?",
      "rows": 4
    }
  ]
}

update_form_draft

PUT /api/forms/:formId · Returns { form } · Also available in the Leed Assistant

ParameterTypeRequiredDefaultNotes
formIdstringYes—Which form to edit
any field from the table above—NounchangedPartial — send only what changes

Two forms of refusal are worth knowing. A form marked protected — Leed creates one of these automatically for the Docs MCP reader sign-in — returns 403 form_protected and cannot be edited through this tool at all. And sending a fields array replaces the existing one, so a partial fields array truncates the form.

What you cannot do

Publishing a form and deleting a form are both absent from the MCP surface, not merely restricted. publish_form and delete_form are registered tools that a client is never shown, so a model cannot invoke them even by name. Both are available in the Leed Assistant, where they stop and wait for your approval — see Leed Assistant.

Reading submissions

get_form_fills

GET /api/forms/:formId/fills · Returns { fills } · MCP only

ParameterTypeRequiredDefaultNotes
formIdstringYes—Resolve it with list_forms first
limitinteger, 1–100Nono limitOmitting it returns every submission
offsetinteger, ≥ 0No0Rows to skip

What a submission records, why a preview site never captures one, and how a fill updates the matching contact are all covered in Form Submissions.

Assets

list_assets

GET /api/assets/:type? · Returns { assets } · MCP only

ParameterTypeRequiredDefaultNotes
typeimagevideoaudiodocument

Returns only active assets — anything soft-deleted is filtered out. This is the only asset tool that exists. There is no upload tool, no delete tool, no alt-text tool and no update tool on the MCP surface, in either the MCP or assistant registry. If a model reports that it cannot find a tool to attach an image, it is not misconfigured; the tool does not exist.

Uploading is not an MCP action — here is what it is instead

An AI that has just generated an image, or that you have handed a file to, still needs a real answer. Uploading is a three-step authenticated HTTP flow against the same API the CMS itself uses. It runs under the same session or token as everything else and carries the same asset:create permission check, so it is not a way around any control — it is simply a surface MCP does not cover.

# 1. Ask for a one-shot direct-upload URL from Cloudflare Images.
curl -s -H "Authorization: Bearer $LEED_TOKEN" \
  https://app.leed.ai/api/assets/create/image
# → { "id": "<imageId>", "uploadURL": "https://upload.imagedelivery.net/...", ... }

# 2. PUT the bytes to that URL. No Leed auth header here — the URL is the credential.
curl -s -X PUT --upload-file ./diagram.png "$UPLOAD_URL"

# 3. Register the asset with Leed so it appears in the library.
curl -s -X POST -H "Authorization: Bearer $LEED_TOKEN" \
  -H "content-type: application/json" \
  https://app.leed.ai/api/assets \
  -d '{
        "type": "image",
        "imageId": "<imageId from step 1>",
        "name": "Architecture diagram",
        "filename": "diagram.png",
        "altText": "Three services connected to one database",
        "originalWidth": 1600,
        "originalHeight": 900,
        "svg": false,
        "labels": []
      }'
# → the created asset, including its assetId

altText is required on an image and must be non-empty — Leed generates one automatically for uploads that come through the CMS, but the direct API call will not do it for you. Only once step 3 returns an assetId can a page body reference the image, as ![alt](src){data-assetid="…"}.

Documents, audio and video each have their own GET /api/assets/create/<type> step with different query parameters, and video finalizes through a separate call rather than POST /api/assets. If you are doing this by hand rather than by script, the CMS path is faster and is described in Uploading Images; Asset Library covers what happens to a file after it lands.

If the tool you came here for is not on this page, the Operator MCP Tool Index lists every tool alphabetically with the page that documents its parameters.

ESC