FNJ

MCP / API reference

FNJ MCP API reference

FNJ's API is a Model Context Protocol server. Every capability is a tool you call over HTTPS with JSON-RPC 2.0: 19 tools that read and change your FramerToNextJS sites, their CMS, and publish them to your own host. This page lists each one with its parameters and what it returns.

Connecting

Endpoint
https://framertonextjs.com/api/mcp
Protocol
MCP over streamable HTTP. Send JSON-RPC 2.0 requests as POST; tools/list returns every tool with its JSON Schema, and tools/call runs one. The server is stateless, so each request stands alone.
Authentication
An API token sent as Authorization: Bearer <token>. Create one under AI & MCP in your dashboard and revoke it there. MCP clients such as Claude can instead sign in with OAuth from the endpoint URL alone; discovery starts at /.well-known/oauth-protected-resource.
Access
Included with FNJ Lifetime and every AI builder plan; there is no separate API fee. A connection approved as read-only is only offered the tools marked Reads below.

Calling a tool

A tool's result is a JSON object, returned as text in result.content[0].text. Parse that text to read it.

curl https://framertonextjs.com/api/mcp \
  -H "Authorization: Bearer $FNJ_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call",
       "params":{"name":"list_sites","arguments":{}}}'
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [
      { "type": "text", "text": "{\"sites\":[{\"id\":\"cmt8ft6ql0001kz04d08ku5hp\", ...}]}" }
    ]
  }
}

Errors

Tools

list_sites

List sitesReads

List the user's FNJ projects (converted sites): id, name, source, status, and live URL if deployed. Start here when you do not have a site id.

Parameters

None.

Returns

sites: [{ id, name, source (the original Framer URL), output (react, html or builder), status, liveUrl, createdAt }].

Input JSON Schema
{
  "type": "object",
  "properties": {}
}

get_site

Get site detailsReads

Details of one project: its pages (routes), CMS collections, live URL, and how many unpublished edits are pending.

Parameters

siteIdrequired
stringFrom list_sites

Returns

id, name, source, output, status, pages (routes), collections ([{ id, name, items, publishesPages, routePrefix }]), liveUrl, canPublish (true once a host is connected), pendingEdits.

Input JSON Schema
{
  "type": "object",
  "properties": {
    "siteId": {
      "type": "string",
      "description": "From list_sites"
    }
  },
  "required": [
    "siteId"
  ]
}

get_page_text

Read page textReads

The text on one page, as a list of elements: tag, text, and an optional class. This is how you see a page — call it before editing so your oldText matches exactly. Entries marked edited show the current (already changed) wording.

Parameters

siteIdrequired
string
routerequired
stringPage route, e.g. "/" or "/about"

Returns

route, elements ([{ tag, text, class?, edited? }]) and a note. Quote an element's text exactly as oldText for edit_text.

Input JSON Schema
{
  "type": "object",
  "properties": {
    "siteId": {
      "type": "string"
    },
    "route": {
      "type": "string",
      "description": "Page route, e.g. \"/\" or \"/about\""
    }
  },
  "required": [
    "siteId",
    "route"
  ]
}

get_page_outline

Read page outlineReads

The structure of one page: its sections and named containers in order, each with the tag and key you pass to add_section or set_style. Use this before changing layout, spacing or adding anything — get_page_text shows words, this shows the boxes they sit in.

Parameters

siteIdrequired
string
routerequired
stringPage route, e.g. "/" or "/about"

Returns

route, nodes (the page's sections and elements, each with the tag and key that add_section and set_style anchor on), ambiguous names if any, and a note.

Input JSON Schema
{
  "type": "object",
  "properties": {
    "siteId": {
      "type": "string"
    },
    "route": {
      "type": "string",
      "description": "Page route, e.g. \"/\" or \"/about\""
    }
  },
  "required": [
    "siteId",
    "route"
  ]
}

edit_text

Edit textChanges your site

Change the text of one element on a page. Match it by tag and its exact current text (copy both from get_page_text). The change is saved to the site's draft — visible in the editor immediately, live only after publish.

Parameters

siteIdrequired
string
routerequired
stringThe page you read the text from
tagrequired
stringElement tag from get_page_text, e.g. "h1", "p", "a"
oldTextrequired
stringThe element's current text, exactly as get_page_text returned it
newTextrequired
stringThe replacement text
scope
"site" | "page""site" (default) changes this text wherever it appears — right for headers and footers. "page" limits the change to this route.

Returns

ok, changed (false when the new text equals the current text), edit, pendingEdits. The change is saved to the draft until publish.

Input JSON Schema
{
  "type": "object",
  "properties": {
    "siteId": {
      "type": "string"
    },
    "route": {
      "type": "string",
      "description": "The page you read the text from"
    },
    "tag": {
      "type": "string",
      "description": "Element tag from get_page_text, e.g. \"h1\", \"p\", \"a\""
    },
    "oldText": {
      "type": "string",
      "description": "The element's current text, exactly as get_page_text returned it"
    },
    "newText": {
      "type": "string",
      "description": "The replacement text"
    },
    "scope": {
      "description": "\"site\" (default) changes this text wherever it appears — right for headers and footers. \"page\" limits the change to this route.",
      "type": "string",
      "enum": [
        "site",
        "page"
      ]
    }
  },
  "required": [
    "siteId",
    "route",
    "tag",
    "oldText",
    "newText"
  ]
}

set_style

Set styleChanges your site

Set CSS on one element — padding, margin, background, colour, font size, alignment, gap, border-radius. Applied with !important so it survives the site's own runtime. Pass an empty string for a property to hand it back to the original design.

Parameters

siteIdrequired
string
routerequired
string
tagrequired
stringtag from get_page_outline (or get_page_text for a text element)
keyrequired
stringkey from get_page_outline, or the element's exact text
cssrequired
objectCSS properties in kebab-case, e.g. {"padding-top":"96px","background":"#0b0b0f"}
scope
"site" | "page""page" (default) styles this route only. "site" applies wherever the element appears.

Returns

ok, applied (the CSS saved), target, pendingEdits. Saved to the draft until publish.

Input JSON Schema
{
  "type": "object",
  "properties": {
    "siteId": {
      "type": "string"
    },
    "route": {
      "type": "string"
    },
    "tag": {
      "type": "string",
      "description": "tag from get_page_outline (or get_page_text for a text element)"
    },
    "key": {
      "type": "string",
      "description": "key from get_page_outline, or the element's exact text"
    },
    "css": {
      "type": "object",
      "propertyNames": {
        "type": "string"
      },
      "additionalProperties": {
        "type": "string"
      },
      "description": "CSS properties in kebab-case, e.g. {\"padding-top\":\"96px\",\"background\":\"#0b0b0f\"}"
    },
    "scope": {
      "description": "\"page\" (default) styles this route only. \"site\" applies wherever the element appears.",
      "type": "string",
      "enum": [
        "site",
        "page"
      ]
    }
  },
  "required": [
    "siteId",
    "route",
    "tag",
    "key",
    "css"
  ]
}

add_section

Add sectionChanges your site

Add a new block of content to a page — a call-to-action, a row of cards, a banner — placed relative to a section that is already there. Give it plain semantic HTML with inline styles; it is inserted as written. Re-running with the same sectionId replaces that block instead of adding a second one, so this is also how you revise a section you added.

Parameters

siteIdrequired
string
routerequired
string
sectionIdrequired
stringYour own stable name for this block, e.g. "cta-book-a-call". Reuse it to revise, change it to add another.
anchorTagrequired
stringtag of the element to place it against, from get_page_outline, e.g. "footer"
anchorKey
stringkey of that element from get_page_outline. OMIT IT to target every element with that tag, which is what you usually want: a converted page has one footer per breakpoint, and naming just one would leave the section off mobile.
positionrequired
"before" | "after" | "prepend" | "append""before" / "after" put it outside the anchor; "prepend" / "append" put it inside, first or last
htmlrequired
stringThe block's HTML. Inline styles only — the site's own classes will not apply to new markup. Scripts, frames and event handlers are stripped.
scope
"site" | "page""page" (default) adds it to this route only. "site" adds it wherever the anchor appears.

Returns

ok, sectionId (pass it again to replace the section), placed, pendingEdits. Saved to the draft until publish.

Input JSON Schema
{
  "type": "object",
  "properties": {
    "siteId": {
      "type": "string"
    },
    "route": {
      "type": "string"
    },
    "sectionId": {
      "type": "string",
      "description": "Your own stable name for this block, e.g. \"cta-book-a-call\". Reuse it to revise, change it to add another."
    },
    "anchorTag": {
      "type": "string",
      "description": "tag of the element to place it against, from get_page_outline, e.g. \"footer\""
    },
    "anchorKey": {
      "description": "key of that element from get_page_outline. OMIT IT to target every element with that tag, which is what you usually want: a converted page has one footer per breakpoint, and naming just one would leave the section off mobile.",
      "type": "string"
    },
    "position": {
      "type": "string",
      "enum": [
        "before",
        "after",
        "prepend",
        "append"
      ],
      "description": "\"before\" / \"after\" put it outside the anchor; \"prepend\" / \"append\" put it inside, first or last"
    },
    "html": {
      "type": "string",
      "description": "The block's HTML. Inline styles only — the site's own classes will not apply to new markup. Scripts, frames and event handlers are stripped."
    },
    "scope": {
      "description": "\"page\" (default) adds it to this route only. \"site\" adds it wherever the anchor appears.",
      "type": "string",
      "enum": [
        "site",
        "page"
      ]
    }
  },
  "required": [
    "siteId",
    "route",
    "sectionId",
    "anchorTag",
    "position",
    "html"
  ]
}

create_page

Create pageChanges your site

Add a page to the site at a new URL. It starts as a copy of a page that already exists — pass the closest one as fromRoute — and you then make it its own with add_section, set_style and edit_text. Copying rather than starting blank is what keeps the new page's header, footer, fonts and styling consistent with the rest of the site; a page built from nothing would have none of them. Edits to the copy never touch the page it came from.

Parameters

siteIdrequired
string
routerequired
stringThe new URL path, e.g. "/pricing" or "/blog/my-post". Slashes nest; anything else is kebab-cased.
fromRouterequired
stringRoute of the existing page to copy — from get_site. Pick the closest in layout to what you are building.
title
stringLabel for the Pages list. Does not change the page's <title>; use edit_text for that.

Returns

ok, route, copiedFrom, and a note on what to do next.

Input JSON Schema
{
  "type": "object",
  "properties": {
    "siteId": {
      "type": "string"
    },
    "route": {
      "type": "string",
      "description": "The new URL path, e.g. \"/pricing\" or \"/blog/my-post\". Slashes nest; anything else is kebab-cased."
    },
    "fromRoute": {
      "type": "string",
      "description": "Route of the existing page to copy — from get_site. Pick the closest in layout to what you are building."
    },
    "title": {
      "description": "Label for the Pages list. Does not change the page's <title>; use edit_text for that.",
      "type": "string"
    }
  },
  "required": [
    "siteId",
    "route",
    "fromRoute"
  ]
}

list_collections

List CMS collectionsReads

CMS collections on a site, with their fields — needed before creating or updating items.

Parameters

siteIdrequired
string

Returns

collections: [{ id, name, items, publishesPages, routePrefix, readOnly, fields: [{ key, label, type, required }] }].

Input JSON Schema
{
  "type": "object",
  "properties": {
    "siteId": {
      "type": "string"
    }
  },
  "required": [
    "siteId"
  ]
}

create_collection

Create CMS collectionChanges your site

Create a CMS collection — a repeating content type such as Posts, Team or Testimonials — with the fields you specify. Returns each field's key, which is what create_item expects. The collection stores content and can be read back; it does not by itself publish a page per item, so to show the content put it on a page with add_section.

Parameters

siteIdrequired
string
namerequired
stringWhat the collection is called, e.g. "Blog posts".
fields
array of objectThe fields each item has. Omit for a single Title field, which you can add to later in the CMS panel.
fields[].labelrequired
stringHuman name, e.g. "Cover image". The field key is derived from it.
fields[].typerequired
"text" | "richtext" | "image" | "date" | "url" | "number" | "boolean" | "color" | "enum"
fields[].required
boolean
fields[].options
array of stringenum only: the allowed values, in order.

Returns

ok, collectionId, name, handle, fields ([{ key, label, type, required }]). Use these field keys with create_item.

Input JSON Schema
{
  "type": "object",
  "properties": {
    "siteId": {
      "type": "string"
    },
    "name": {
      "type": "string",
      "description": "What the collection is called, e.g. \"Blog posts\"."
    },
    "fields": {
      "description": "The fields each item has. Omit for a single Title field, which you can add to later in the CMS panel.",
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "label": {
            "type": "string",
            "description": "Human name, e.g. \"Cover image\". The field key is derived from it."
          },
          "type": {
            "default": "text",
            "type": "string",
            "enum": [
              "text",
              "richtext",
              "image",
              "date",
              "url",
              "number",
              "boolean",
              "color",
              "enum"
            ]
          },
          "required": {
            "type": "boolean"
          },
          "options": {
            "description": "enum only: the allowed values, in order.",
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "label",
          "type"
        ],
        "additionalProperties": false
      }
    }
  },
  "required": [
    "siteId",
    "name"
  ]
}

list_items

List CMS itemsReads

Items in a CMS collection (drafts included), newest first.

Parameters

siteIdrequired
string
collectionIdrequired
string
limit
integer

Returns

total, items ([{ id, slug, draft, fields }]), newest first.

Input JSON Schema
{
  "type": "object",
  "properties": {
    "siteId": {
      "type": "string"
    },
    "collectionId": {
      "type": "string"
    },
    "limit": {
      "type": "integer",
      "minimum": 1,
      "maximum": 100
    }
  },
  "required": [
    "siteId",
    "collectionId"
  ]
}

create_item

Create CMS itemChanges your site

Add an item to a CMS collection. `fields` is keyed by the field keys from list_collections. Items are live by default; pass draft: true to keep one unpublished.

Parameters

siteIdrequired
string
collectionIdrequired
string
fieldsrequired
object
slug
string
draft
boolean

Returns

ok, item ({ id, slug, draft, fields }), and a note saying where the item publishes, if anywhere.

Input JSON Schema
{
  "type": "object",
  "properties": {
    "siteId": {
      "type": "string"
    },
    "collectionId": {
      "type": "string"
    },
    "fields": {
      "type": "object",
      "propertyNames": {
        "type": "string"
      },
      "additionalProperties": {}
    },
    "slug": {
      "type": "string"
    },
    "draft": {
      "type": "boolean"
    }
  },
  "required": [
    "siteId",
    "collectionId",
    "fields"
  ]
}

update_item

Update CMS itemChanges your site

Change some fields on an existing CMS item. Only the fields you pass are touched.

Parameters

siteIdrequired
string
collectionIdrequired
string
itemIdrequired
string
fieldsrequired
object
draft
boolean

Returns

ok, item ({ id, slug, draft, fields }) after the update.

Input JSON Schema
{
  "type": "object",
  "properties": {
    "siteId": {
      "type": "string"
    },
    "collectionId": {
      "type": "string"
    },
    "itemId": {
      "type": "string"
    },
    "fields": {
      "type": "object",
      "propertyNames": {
        "type": "string"
      },
      "additionalProperties": {}
    },
    "draft": {
      "type": "boolean"
    }
  },
  "required": [
    "siteId",
    "collectionId",
    "itemId",
    "fields"
  ]
}

publish

Publish siteChanges your site

Publish the site: bakes every draft edit and CMS change into the code and redeploys to the user's connected host (their own Vercel/Netlify). Only works once a deploy target is connected — check canPublish on get_site.

Parameters

siteIdrequired
string

Returns

ok, url (the live URL), provider (vercel, netlify or cloudflare), editsApplied. Fails with a message when no host is connected.

Input JSON Schema
{
  "type": "object",
  "properties": {
    "siteId": {
      "type": "string"
    }
  },
  "required": [
    "siteId"
  ]
}

get_site_context

Get Studio site contextReads

Everything about a Studio site in one call: its pages with routes and node counts, its components with props and variants, the breakpoints, site metadata, and any broken routes. Start here before designing or editing — it is one round trip and it gives you every id the edit tools need. Only for sites with a design document; converted Framer sites use get_site and get_page_outline instead.

Parameters

siteIdrequired
string
includeComponents
booleanComponent definitions with their props and variants. Default true.
includeSeo
booleanRun the SEO audit and include its findings. Default false — it walks every page.

Returns

siteId, version, homepage, pages, breakpoints, siteMeta, routeProblems, components (unless includeComponents is false) and seo (with includeSeo).

Input JSON Schema
{
  "type": "object",
  "properties": {
    "siteId": {
      "type": "string"
    },
    "includeComponents": {
      "description": "Component definitions with their props and variants. Default true.",
      "type": "boolean"
    },
    "includeSeo": {
      "description": "Run the SEO audit and include its findings. Default false — it walks every page.",
      "type": "boolean"
    }
  },
  "required": [
    "siteId"
  ]
}

get_page_context

Get Studio page contextReads

One page's node tree, with each node's id, type, name and text. Styles are off by default and cost tokens — ask for a single breakpoint when you need them. Use the returned ids with batch_edit_design.

Parameters

siteIdrequired
string
pagerequired
stringPage id, or its route like "/pricing"
depth
integerHow deep to walk the tree. Default 4; deeper costs tokens.
includeStyles
booleanInclude each node's styles. Default false — ask for one breakpoint instead when you can.
breakpoint
"desktop" | "tablet" | "mobile"With includeStyles, return only this breakpoint's values.

Returns

siteId, version, page ({ id, name, route, kind, meta, nodes }), tree (the node tree to the requested depth).

Input JSON Schema
{
  "type": "object",
  "properties": {
    "siteId": {
      "type": "string"
    },
    "page": {
      "type": "string",
      "description": "Page id, or its route like \"/pricing\""
    },
    "depth": {
      "description": "How deep to walk the tree. Default 4; deeper costs tokens.",
      "type": "integer",
      "minimum": 0,
      "maximum": 12
    },
    "includeStyles": {
      "description": "Include each node's styles. Default false — ask for one breakpoint instead when you can.",
      "type": "boolean"
    },
    "breakpoint": {
      "description": "With includeStyles, return only this breakpoint's values.",
      "type": "string",
      "enum": [
        "desktop",
        "tablet",
        "mobile"
      ]
    }
  },
  "required": [
    "siteId",
    "page"
  ]
}

list_section_templates

List section templatesReads

The section templates you can drop into a page, the node types you can create, and the breakpoints. Takes no arguments and reads nothing — call it once when you are about to build.

Parameters

None.

Returns

breakpoints, sections ([{ id, name, category, description }]), nodeTypes.

Input JSON Schema
{
  "type": "object",
  "properties": {}
}

audit_design

Audit Studio designReads

Check a Studio site for problems worth fixing before publishing: missing titles and descriptions, images with no source or alt text, empty headings, pages with no sections, instances pointing at deleted components, and broken routes. Returns findings with the id of the thing to fix, so the fixes can go straight into batch_edit_design.

Parameters

siteIdrequired
string

Returns

siteId, version, counts ({ high, medium }), findings (each with the id of the thing to fix).

Input JSON Schema
{
  "type": "object",
  "properties": {
    "siteId": {
      "type": "string"
    }
  },
  "required": [
    "siteId"
  ]
}

batch_edit_design

Edit Studio designChanges your site

Change a Studio site: pages, sections, nodes, per-breakpoint styles and components, as a list of operations applied in order. This is the ONLY tool that writes to a design document, and it is all-or-nothing — if any operation is rejected, none of them are saved and you are told which one failed. Send a whole page as one call rather than one node at a time: the cost is the same. Pass baseVersion from get_site_context or get_page_context to be refused rather than overwrite an edit somebody made while you were working.

Parameters

siteIdrequired
string
operationsrequired
array of object or object or object or object or object or object or object or object or object or object or object or object or object or object or object or object or object or object or object or object or object
baseVersion
integerVersion from get_page_context/get_site_context. Supply it to be refused rather than overwrite somebody else's concurrent edit.

Returns

success, applied (how many operations), version, created (ids of new nodes), changes. All or nothing: if one operation is rejected, nothing is saved.

Input JSON Schema
{
  "type": "object",
  "properties": {
    "siteId": {
      "type": "string"
    },
    "operations": {
      "minItems": 1,
      "maxItems": 200,
      "type": "array",
      "items": {
        "oneOf": [
          {
            "type": "object",
            "properties": {
              "op": {
                "type": "string",
                "const": "create_page"
              },
              "name": {
                "type": "string"
              },
              "kind": {
                "type": "string",
                "enum": [
                  "404",
                  "page"
                ]
              }
            },
            "required": [
              "op",
              "name"
            ],
            "additionalProperties": false
          },
          {
            "type": "object",
            "properties": {
              "op": {
                "type": "string",
                "const": "rename_page"
              },
              "pageId": {
                "type": "string"
              },
              "name": {
                "type": "string"
              }
            },
            "required": [
              "op",
              "pageId",
              "name"
            ],
            "additionalProperties": false
          },
          {
            "type": "object",
            "properties": {
              "op": {
                "type": "string",
                "const": "duplicate_page"
              },
              "pageId": {
                "type": "string"
              }
            },
            "required": [
              "op",
              "pageId"
            ],
            "additionalProperties": false
          },
          {
            "type": "object",
            "properties": {
              "op": {
                "type": "string",
                "const": "delete_page"
              },
              "pageId": {
                "type": "string"
              }
            },
            "required": [
              "op",
              "pageId"
            ],
            "additionalProperties": false
          },
          {
            "type": "object",
            "properties": {
              "op": {
                "type": "string",
                "const": "move_page"
              },
              "pageId": {
                "type": "string"
              },
              "toIndex": {
                "type": "integer",
                "minimum": 0,
                "maximum": 9007199254740991
              }
            },
            "required": [
              "op",
              "pageId",
              "toIndex"
            ],
            "additionalProperties": false
          },
          {
            "type": "object",
            "properties": {
              "op": {
                "type": "string",
                "const": "set_homepage"
              },
              "pageId": {
                "type": "string"
              }
            },
            "required": [
              "op",
              "pageId"
            ],
            "additionalProperties": false
          },
          {
            "type": "object",
            "properties": {
              "op": {
                "type": "string",
                "const": "set_page_meta"
              },
              "pageId": {
                "type": "string"
              },
              "title": {
                "type": "string"
              },
              "description": {
                "type": "string"
              },
              "slug": {
                "type": "string"
              }
            },
            "required": [
              "op",
              "pageId"
            ],
            "additionalProperties": false
          },
          {
            "type": "object",
            "properties": {
              "op": {
                "type": "string",
                "const": "insert_section"
              },
              "parentId": {
                "type": "string",
                "description": "Usually the page root's id"
              },
              "sectionId": {
                "type": "string",
                "description": "From list_section_templates"
              }
            },
            "required": [
              "op",
              "parentId",
              "sectionId"
            ],
            "additionalProperties": false
          },
          {
            "type": "object",
            "properties": {
              "op": {
                "type": "string",
                "const": "insert_node"
              },
              "parentId": {
                "type": "string"
              },
              "afterId": {
                "description": "Insert after this sibling instead of appending",
                "type": "string"
              },
              "type": {
                "type": "string",
                "enum": [
                  "section",
                  "container",
                  "heading",
                  "text",
                  "button",
                  "link",
                  "image",
                  "video"
                ]
              },
              "name": {
                "type": "string"
              },
              "text": {
                "type": "string"
              },
              "src": {
                "type": "string"
              },
              "alt": {
                "type": "string"
              },
              "href": {
                "type": "string"
              },
              "styles": {
                "description": "Desktop styles for the new node",
                "type": "object",
                "propertyNames": {
                  "type": "string"
                },
                "additionalProperties": {
                  "type": [
                    "string",
                    "number"
                  ]
                }
              }
            },
            "required": [
              "op",
              "parentId",
              "type"
            ],
            "additionalProperties": false
          },
          {
            "type": "object",
            "properties": {
              "op": {
                "type": "string",
                "const": "update_node"
              },
              "nodeId": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "text": {
                "type": "string"
              },
              "src": {
                "type": "string"
              },
              "alt": {
                "type": "string"
              },
              "href": {
                "type": "string"
              },
              "htmlId": {
                "type": "string"
              }
            },
            "required": [
              "op",
              "nodeId"
            ],
            "additionalProperties": false
          },
          {
            "type": "object",
            "properties": {
              "op": {
                "type": "string",
                "const": "delete_node"
              },
              "nodeId": {
                "type": "string"
              }
            },
            "required": [
              "op",
              "nodeId"
            ],
            "additionalProperties": false
          },
          {
            "type": "object",
            "properties": {
              "op": {
                "type": "string",
                "const": "duplicate_node"
              },
              "nodeId": {
                "type": "string"
              }
            },
            "required": [
              "op",
              "nodeId"
            ],
            "additionalProperties": false
          },
          {
            "type": "object",
            "properties": {
              "op": {
                "type": "string",
                "const": "move_node"
              },
              "nodeId": {
                "type": "string"
              },
              "parentId": {
                "type": "string",
                "description": "New parent"
              },
              "index": {
                "type": "integer",
                "minimum": 0,
                "maximum": 9007199254740991
              }
            },
            "required": [
              "op",
              "nodeId",
              "parentId"
            ],
            "additionalProperties": false
          },
          {
            "type": "object",
            "properties": {
              "op": {
                "type": "string",
                "const": "set_style"
              },
              "nodeId": {
                "type": "string"
              },
              "breakpoint": {
                "description": "Default desktop. tablet/mobile layer OVER desktop — they do not replace it.",
                "type": "string",
                "enum": [
                  "desktop",
                  "tablet",
                  "mobile"
                ]
              },
              "styles": {
                "type": "object",
                "propertyNames": {
                  "type": "string"
                },
                "additionalProperties": {
                  "type": [
                    "string",
                    "number"
                  ]
                },
                "description": "StyleProps keys — fontSize, paddingY, gap, display, gridTemplateColumns, color, background… See lib/design/types.ts."
              },
              "replace": {
                "description": "Replace this breakpoint's styles instead of merging. Default false.",
                "type": "boolean"
              }
            },
            "required": [
              "op",
              "nodeId",
              "styles"
            ],
            "additionalProperties": false
          },
          {
            "type": "object",
            "properties": {
              "op": {
                "type": "string",
                "const": "clear_style"
              },
              "nodeId": {
                "type": "string"
              },
              "breakpoint": {
                "type": "string",
                "enum": [
                  "tablet",
                  "mobile"
                ],
                "description": "Drop this breakpoint's overrides so it inherits desktop again"
              },
              "keys": {
                "description": "Only these properties. Omit to clear the whole breakpoint.",
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            },
            "required": [
              "op",
              "nodeId",
              "breakpoint"
            ],
            "additionalProperties": false
          },
          {
            "type": "object",
            "properties": {
              "op": {
                "type": "string",
                "const": "create_component"
              },
              "nodeId": {
                "type": "string",
                "description": "Node to turn into a component master"
              },
              "name": {
                "type": "string"
              }
            },
            "required": [
              "op",
              "nodeId",
              "name"
            ],
            "additionalProperties": false
          },
          {
            "type": "object",
            "properties": {
              "op": {
                "type": "string",
                "const": "insert_component"
              },
              "parentId": {
                "type": "string"
              },
              "componentId": {
                "type": "string"
              }
            },
            "required": [
              "op",
              "parentId",
              "componentId"
            ],
            "additionalProperties": false
          },
          {
            "type": "object",
            "properties": {
              "op": {
                "type": "string",
                "const": "detach_instance"
              },
              "nodeId": {
                "type": "string"
              }
            },
            "required": [
              "op",
              "nodeId"
            ],
            "additionalProperties": false
          },
          {
            "type": "object",
            "properties": {
              "op": {
                "type": "string",
                "const": "rename_component"
              },
              "componentId": {
                "type": "string"
              },
              "name": {
                "type": "string"
              }
            },
            "required": [
              "op",
              "componentId",
              "name"
            ],
            "additionalProperties": false
          },
          {
            "type": "object",
            "properties": {
              "op": {
                "type": "string",
                "const": "delete_component"
              },
              "componentId": {
                "type": "string"
              }
            },
            "required": [
              "op",
              "componentId"
            ],
            "additionalProperties": false
          },
          {
            "type": "object",
            "properties": {
              "op": {
                "type": "string",
                "const": "set_instance_prop"
              },
              "nodeId": {
                "type": "string"
              },
              "key": {
                "type": "string"
              },
              "value": {
                "type": [
                  "string",
                  "number",
                  "boolean"
                ]
              }
            },
            "required": [
              "op",
              "nodeId",
              "key",
              "value"
            ],
            "additionalProperties": false
          }
        ]
      }
    },
    "baseVersion": {
      "description": "Version from get_page_context/get_site_context. Supply it to be refused rather than overwrite somebody else's concurrent edit.",
      "type": "integer",
      "minimum": -9007199254740991,
      "maximum": 9007199254740991
    }
  },
  "required": [
    "siteId",
    "operations"
  ]
}

Connecting an agent rather than writing a client? Start with the MCP overview.

Ready to convert your website?

Paste your published Framer URL to generate a production-ready project in minutes.

Example: https://your-site.framer.websiteYour published site’s address — the one visitors see. A custom domain works too.