#
tokens: 36194/50000 55/55 files
lines: off (toggle) GitHub
raw markdown copy
# Directory Structure

```
├── .github
│   └── workflows
│       ├── ci.yml
│       └── publish-oss.yml
├── .gitignore
├── LICENSE.md
├── package-lock.json
├── package.json
├── README.md
├── server.json
├── src
│   ├── index.ts
│   ├── mcp.ts
│   ├── modules
│   │   └── designerAppBridge.ts
│   ├── schemas
│   │   ├── CollectionItemPostSingleSchema.ts
│   │   ├── CollectionItemWithIdInputSchema.ts
│   │   ├── ComponentDomWriteNodesItemSchema.ts
│   │   ├── ComponentPropertyUpdateSchema.ts
│   │   ├── DEElementIDSchema.ts
│   │   ├── DEElementSchema.ts
│   │   ├── index.ts
│   │   ├── OptionFieldSchema.ts
│   │   ├── ReferenceFieldSchema.ts
│   │   ├── RegisterInlineSiteScriptSchema.ts
│   │   ├── SiteIdSchema.ts
│   │   ├── StaticFieldSchema.ts
│   │   ├── WebflowCollectionsCreateRequestSchema.ts
│   │   ├── WebflowCollectionsFieldUpdateSchema.ts
│   │   ├── WebflowCollectionsItemsCreateItemLiveRequestSchema.ts
│   │   ├── WebflowCollectionsItemsCreateItemRequestSchema.ts
│   │   ├── WebflowCollectionsItemsListItemsRequestSortBySchema.ts
│   │   ├── WebflowCollectionsItemsListItemsRequestSortOrderSchema.ts
│   │   ├── WebflowCollectionsItemsUpdateItemsLiveRequestSchema.ts
│   │   ├── WebflowCollectionsItemsUpdateItemsRequestSchema.ts
│   │   ├── WebflowPageDomWriteNodesItemSchema.ts
│   │   └── WebflowPageSchema.ts
│   ├── tools
│   │   ├── aiChat.ts
│   │   ├── cms.ts
│   │   ├── components.ts
│   │   ├── deAsset.ts
│   │   ├── deComponents.ts
│   │   ├── deElement.ts
│   │   ├── dePages.ts
│   │   ├── deStyle.ts
│   │   ├── deVariable.ts
│   │   ├── index.ts
│   │   ├── localDeMCPConnection.ts
│   │   ├── pages.ts
│   │   ├── rules.ts
│   │   ├── scripts.ts
│   │   └── sites.ts
│   ├── types
│   │   └── RPCType.d.ts
│   └── utils
│       ├── appPort.ts
│       ├── formatResponse.ts
│       ├── generateUUIDv4.ts
│       ├── index.ts
│       ├── supportStyles.ts
│       └── typeGuards.ts
├── tsconfig.json
└── webflow_logo.png
```

# Files

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
.env
.prettierrc
.wrangler/
dist/
node_modules/

# System
.DS_Store
.idea/
.vscode/
.cursor
pnpm-lock.yaml

# Private keys
key.pem
key.pem.pub
*.pem
```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
# Webflow's MCP server

A Node.js server implementing Model Context Protocol (MCP) for Webflow using the [Webflow JavaScript SDK](https://github.com/webflow/js-webflow-api). Enable AI agents to interact with Webflow APIs. Learn more about Webflow's Data API in the [developer documentation](https://developers.webflow.com/data/reference).

[![npm shield](https://img.shields.io/npm/v/webflow-mcp-server)](https://www.npmjs.com/package/webflow-mcp-server)
![Webflow](https://img.shields.io/badge/webflow-%23146EF5.svg?style=for-the-badge&logo=webflow&logoColor=white)

## Prerequisites

- [Node.js](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm)
- [NPM](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm)
- [A Webflow Account](https://webflow.com/signup)

## 🚀 Remote installation

Get started by installing Webflow's remote MCP server. The remote server uses OAuth to authenticate with your Webflow sites, and a companion app that syncs your live canvas with your AI agent.

### Requirements

- Node.js 22.3.0 or higher

> Note: The MCP server currently supports Node.js 22.3.0 or higher. If you run into version issues, see the [Node.js compatibility guidance.](https://developers.webflow.com/data/v2.0.0/docs/ai-tools#nodejs-compatibility)

### Cursor

#### Add MCP server to Cursor

1. Go to `Settings → Cursor Settings → MCP & Integrations`.
2. Under MCP Tools, click `+ New MCP Server`.
3. Paste the following configuration into `.cursor/mcp.json` (or add the `webflow` part to your existing configuration):

```json
{
  "mcpServers": {
    "webflow": {
      "url": "https://mcp.webflow.com/sse"
    }
  }
}
```

> Tip: You can create a project-level `mcp.json` to avoid repeated auth prompts across multiple Cursor windows. See Cursor’s docs on [configuration locations.](https://docs.cursor.com/en/context/mcp#configuration-locations)

4. Save and close the file. Cursor will automatically open an OAuth login page where you can authorize Webflow sites to use with the MCP server.

#### Open the Webflow Designer

- Open your site in the Webflow Designer, or ask your AI agent:

```text
Give me a link to open <MY_SITE_NAME> in the Webflow Designer
```

#### Open the MCP Webflow App

1. In the Designer, open the Apps panel (press `E`).
2. Launch your published "Webflow MCP Bridge App".
3. Wait for the app to connect to the MCP server.

#### Write your first prompt

Try these in your AI chat:

```text
Analyze my last 5 blog posts and suggest 3 new topic ideas with SEO keywords
```

```text
Find older blog posts that mention similar topics and add internal links to my latest post
```

```text
Create a hero section card on my home page with a CTA button and responsive design
```

### Claude desktop

#### Add MCP server to Claude desktop

1. Enable developer mode: `Help → Troubleshooting → Enable Developer Mode`.
2. Open developer settings: `File → Settings → Developer`.
3. Click `Get Started` or edit the configuration to open `claude_desktop_config.json` and add:

```json
{
  "mcpServers": {
    "webflow": {
      "command": "npx",
      "args": ["mcp-remote", "https://mcp.webflow.com/sse"]
    }
  }
}
```

4. Save and restart Claude Desktop (`Cmd/Ctrl + R`). An OAuth login page will open to authorize sites.

#### Open the Webflow Designer

- Open your site in the Webflow Designer, or ask your AI agent:

```text
Give me a link to open <MY_SITE_NAME> in the Webflow Designer
```

#### Open the MCP Webflow App

1. In the Designer, open the Apps panel (press `E`).
2. Launch your published "Webflow MCP Bridge App".
3. Wait for the app to connect to the MCP server.

#### Write your first prompt

```text
Analyze my last 5 blog posts and suggest 3 new topic ideas with SEO keywords
```

```text
Find older blog posts that mention similar topics and add internal links to my latest post
```

```text
Create a hero section card on my home page with a CTA button and responsive design
```

### Reset your OAuth token

To reset your OAuth token, run the following command in your terminal.

```bash
rm -rf ~/.mcp-auth
```

### Node.js compatibility

Please see the Node.js [compatibility guidance on Webflow's developer docs.](https://developers.webflow.com/data/v2.0.0/docs/ai-tools#nodejs-compatibility)

---


## Local Installation

You can also configure the MCP server to run locally. This requires:

- Creating and registering your own MCP Bridge App in a Webflow workspace with Admin permissions
- Configuring your AI client to start the local MCP server with a Webflow API token

### 1. Create and publish the MCP bridge app

Before connecting the local MCP server to your AI client, you must create and publish the **Webflow MCP Bridge App** in your workspace.

### Steps

1. **Register a Webflow App**
   - Go to your Webflow Workspace and register a new app.  
   - Follow the official guide: [Register an App](https://developers.webflow.com/data/v2.0.0/docs/register-an-app).

2. **Get the MCP Bridge App code**
   - Option A: Download the latest `bundle.zip` from the [releases page](https://github.com/virat21/webflow-mcp-bridge-app/releases).
   - Option B: Clone the repository and build it:
     ```bash
     git clone https://github.com/virat21/webflow-mcp-bridge-app
     cd webflow-mcp-bridge-app
     ```
     - Then build the project following the repository instructions.

3. **Publish the Designer Extension**
   - Go to **Webflow Dashboard → Workspace settings → Apps & Integrations → Develop → Your App**.
   - Click **“Publish Extension Version”**.
   - Upload your built `bundle.zip` file.

4. **Open the App in Designer**
   - Once published, open the MCP Bridge App from the **Designer → Apps panel** in a site within your workspace.

### 2. Configure your AI client

#### Cursor

Add to `.cursor/mcp.json`:

```json
{
  "mcpServers": {
    "webflow": {
      "command": "npx",
      "args": ["-y", "webflow-mcp-server@latest"],
      "env": {
        "WEBFLOW_TOKEN": "<YOUR_WEBFLOW_TOKEN>"
      }
    }
  }
}
```

#### Claude desktop

Add to `claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "webflow": {
      "command": "npx",
      "args": ["-y", "webflow-mcp-server@latest"],
      "env": {
        "WEBFLOW_TOKEN": "<YOUR_WEBFLOW_TOKEN>"
      }
    }
  }
}
```

### 3. Use the MCP server with the Webflow Designer

- Open your site in the Webflow Designer.
- Open the Apps panel (press `E`) and launch your published “Webflow MCP Bridge App”.
- Wait for the app to connect to the MCP server, then use tools from your AI client.
- If the Bridge App prompts for a local connection URL, call the `get_designer_app_connection_info` tool from your AI client and paste the returned `http://localhost:<port>` URL.

### Optional: Run locally via shell

```bash
WEBFLOW_TOKEN="<YOUR_WEBFLOW_TOKEN>" npx -y webflow-mcp-server@latest
```

```powershell
# PowerShell
$env:WEBFLOW_TOKEN="<YOUR_WEBFLOW_TOKEN>"
npx -y webflow-mcp-server@latest
```

### Reset your OAuth Token

To reset your OAuth token, run the following command in your terminal.

```bash
rm -rf ~/.mcp-auth
```

### Node.js compatibility

Please see the Node.js [compatibility guidance on Webflow's developer docs.](https://developers.webflow.com/data/v2.0.0/docs/ai-tools#nodejs-compatibility)

## ❓ Troubleshooting

If you are having issues starting the server in your MCP client e.g. Cursor or Claude Desktop, please try the following.

### Make sure you have a valid Webflow API token

1. Go to [Webflow's API Playground](https://developers.webflow.com/data/reference/token/authorized-by), log in and generate a token, then copy the token from the Request Generator
2. Replace `YOUR_WEBFLOW_TOKEN` in your MCP client configuration with the token you copied
3. Save and **restart** your MCP client

### Make sure you have the Node and NPM installed

- [Node.js](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm)
- [NPM](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm)

Run the following commands to confirm you have Node and NPM installed:

```shell
node -v
npm -v
```

### Clear your NPM cache

Sometimes clearing your [NPM cache](https://docs.npmjs.com/cli/v8/commands/npm-cache) can resolve issues with `npx`.

```shell
npm cache clean --force
```

### Fix NPM global package permissions

If `npm -v` doesn't work for you but `sudo npm -v` does, you may need to fix NPM global package permissions. See the official [NPM docs](https://docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally) for more information.

Note: if you are making changes to your shell configuration, you may need to restart your shell for changes to take effect.

## 🛠️ Available tools

See the `./tools` directory for a list of available tools

# 🗣️ Prompts & resources

This implementation **doesn't** include `prompts` or `resources` from the MCP specification. However, this may change in the future when there is broader support across popular MCP clients.

## 📄 Webflow developer resources

- [Webflow API Documentation](https://developers.webflow.com/data/reference)
- [Webflow JavaScript SDK](https://github.com/webflow/js-webflow-api)

## ⚠️ Known limitations

### Static page content updates

The `pages_update_static_content` endpoint currently only supports updates to localized static pages in secondary locales. Updates to static content in the default locale aren't supported and will result in errors.

```

--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------

```markdown
MIT License

Copyright (c) 2025 Webflow

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

```

--------------------------------------------------------------------------------
/src/utils/index.ts:
--------------------------------------------------------------------------------

```typescript
export * from "./formatResponse";
export * from "./typeGuards";
export * from "./supportStyles";
export * from "./appPort";
export * from "./generateUUIDv4";

```

--------------------------------------------------------------------------------
/src/types/RPCType.d.ts:
--------------------------------------------------------------------------------

```typescript
import { WebflowClient } from "webflow-api";

export type RPCType = {
  callTool: (toolName: string, args?: any) => Promise<any>;
  getClient: () => WebflowClient;
};

```

--------------------------------------------------------------------------------
/src/utils/typeGuards.ts:
--------------------------------------------------------------------------------

```typescript
interface ApiError {
  status: number;
  message?: string;
}

export function isApiError(error: unknown): error is ApiError {
  return typeof error === "object" && error !== null && "status" in error;
}

```

--------------------------------------------------------------------------------
/src/schemas/WebflowCollectionsItemsListItemsRequestSortOrderSchema.ts:
--------------------------------------------------------------------------------

```typescript
import { z } from "zod";

export const WebflowCollectionsItemsListItemsRequestSortOrderSchema = z
  .enum(["asc", "desc"])
  .optional()
  .describe("Order to sort the items by. Allowed values: asc, desc.");

```

--------------------------------------------------------------------------------
/src/schemas/WebflowCollectionsItemsListItemsRequestSortBySchema.ts:
--------------------------------------------------------------------------------

```typescript
import { z } from "zod";

export const WebflowCollectionsItemsListItemsRequestSortBySchema = z
  .enum(["lastPublished", "name", "slug"])
  .optional()
  .describe(
    "Field to sort the items by. Allowed values: lastPublished, name, slug."
  );

```

--------------------------------------------------------------------------------
/src/schemas/WebflowCollectionsItemsUpdateItemsRequestSchema.ts:
--------------------------------------------------------------------------------

```typescript
import { z } from "zod";
import { CollectionItemWithIdInputSchema } from "./CollectionItemWithIdInputSchema";

export const WebflowCollectionsItemsUpdateItemsRequestSchema = z.object({
  items: z.array(CollectionItemWithIdInputSchema).optional(),
});

```

--------------------------------------------------------------------------------
/src/schemas/SiteIdSchema.ts:
--------------------------------------------------------------------------------

```typescript
import { z } from "zod";

export const SiteIdSchema = {
  siteId: z
    .string()
    .describe(
      "The ID of the site. DO NOT ASSUME site id. ALWAYS ask user for site id if not already provided or known. use sites_list tool to fetch all sites and then ask user to select one of them."
    ),
};

```

--------------------------------------------------------------------------------
/src/schemas/ComponentPropertyUpdateSchema.ts:
--------------------------------------------------------------------------------

```typescript
import { z } from "zod";

export const ComponentPropertyUpdateSchema = z.array(
  z.object({
    propertyId: z.string().describe("Unique identifier for the property."),
    text: z.string().describe("New value for the property in this locale."),
  })
).describe("Array of properties to update for this component.");
```

--------------------------------------------------------------------------------
/src/schemas/WebflowCollectionsItemsCreateItemRequestSchema.ts:
--------------------------------------------------------------------------------

```typescript
import { z } from "zod";
import { CollectionItemPostSingleSchema } from "./CollectionItemPostSingleSchema";

// NOTE: Cursor agent seems to struggle when provided with z.union(...), so we simplify the type here
export const WebflowCollectionsItemsCreateItemRequestSchema = z.object({
  items: z.array(CollectionItemPostSingleSchema).optional(),
});

```

--------------------------------------------------------------------------------
/src/utils/generateUUIDv4.ts:
--------------------------------------------------------------------------------

```typescript
export function generateUUIDv4(): string {
  const bytes = crypto.getRandomValues(new Uint8Array(16));

  bytes[6] = (bytes[6] & 0x0f) | 0x40;
  bytes[8] = (bytes[8] & 0x3f) | 0x80;

  return [...bytes]
    .map((b, i) => {
      const hex = b.toString(16).padStart(2, "0");
      return [4, 6, 8, 10].includes(i) ? `-${hex}` : hex;
    })
    .join("");
}

```

--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------

```json
{
  "compilerOptions": {
    "target": "es2017",
    "module": "es2022",
    "strict": true,
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "moduleResolution": "bundler",
    "skipLibCheck": true,
    "rootDir": "src",
    "outDir": "dist"
  },
  "include": ["worker-configuration.d.ts", "src/**/*.ts"],
  "exclude": ["node_modules", "dist"]
}

```

--------------------------------------------------------------------------------
/server.json:
--------------------------------------------------------------------------------

```json
{
  "$schema": "https://static.modelcontextprotocol.io/schemas/2025-10-17/server.schema.json",
  "name": "com.webflow/mcp",
  "description": "AI-powered design and management for Webflow Sites",
  "repository": {
    "url": "https://github.com/webflow/mcp-server",
    "source": "github"
  },
  "version": "2.0.0",
  "remotes": [
    {
      "type": "streamable-http",
      "url": "https://mcp.webflow.com/mcp"
    }
  ]
}
```

--------------------------------------------------------------------------------
/src/schemas/WebflowCollectionsFieldUpdateSchema.ts:
--------------------------------------------------------------------------------

```typescript
import z from "zod";

export const WebflowCollectionsFieldUpdateSchema = z
  .object({
    isRequired: z
      .boolean()
      .optional()
      .describe("Indicates if the field is required in a collection."),
    displayName: z.string().optional().describe("Name of the field."),
    helpText: z.string().optional().describe("Help text for the field."),
  })
  .describe("Request schema to update collection field metadata.");

```

--------------------------------------------------------------------------------
/src/schemas/CollectionItemPostSingleSchema.ts:
--------------------------------------------------------------------------------

```typescript
import { z } from "zod";

export const CollectionItemPostSingleSchema = z.object({
  id: z.string().optional(),
  cmsLocaleId: z.string().optional(),
  lastPublished: z.string().optional(),
  lastUpdated: z.string().optional(),
  createdOn: z.string().optional(),
  isArchived: z.boolean().optional(),
  isDraft: z.boolean().optional(),
  fieldData: z.record(z.any()).and(
    z.object({
      name: z.string(),
      slug: z.string(),
    })
  ),
});

```

--------------------------------------------------------------------------------
/src/schemas/DEElementIDSchema.ts:
--------------------------------------------------------------------------------

```typescript
import { z } from "zod";

export const DEElementIDSchema = {
  id: z
    .object({
      component: z
        .string()
        .describe(
          "The component id of the element to perform action on."
        ),
      element: z
        .string()
        .describe(
          "The element id of the element to perform action on."
        ),
    })
    .describe(
      "The id of the element to perform action on, you can find it from id field on element. e.g id:{component:123,element:456}."
    ),
};

```

--------------------------------------------------------------------------------
/src/utils/formatResponse.ts:
--------------------------------------------------------------------------------

```typescript
export function formatResponse(response: any) {
  return {
    content: [{ type: "text" as "text", text: JSON.stringify(response) }],
  };
}

// https://modelcontextprotocol.io/docs/concepts/tools#error-handling-2
export function formatErrorResponse(error: any) {
  return {
    isError: true,
    content: [
      {
        type: "text" as "text",
        text: JSON.stringify({
          name: error.name ?? "",
          message: error.message ?? "",
          error: error,
        }),
      },
    ],
  };
}

```

--------------------------------------------------------------------------------
/src/schemas/WebflowCollectionsCreateRequestSchema.ts:
--------------------------------------------------------------------------------

```typescript
import z from "zod";

// NOTE: Cursor agent seems to struggle when provided with z.union(...), so we simplify the type here
export const WebflowCollectionsCreateRequestSchema = z.object({
  displayName: z
    .string()
    .describe(
      "Name of the collection. Each collection must have a unique name within the site."
    ),
  singularName: z.string().describe("Singular name of the collection."),
  slug: z
    .string()
    .optional()
    .describe("Slug of the collection in the site URL structure. "),
});

```

--------------------------------------------------------------------------------
/src/schemas/ComponentDomWriteNodesItemSchema.ts:
--------------------------------------------------------------------------------

```typescript
import { z } from "zod";

export const ComponentDomWriteNodesItemSchema = z.union([
  z.object({
    nodeId: z.string().describe("Unique identifier for the node."),
    text: z.string().describe("HTML content of the node, including the HTML tag."),
  }).describe("Text node to be updated."),
  z.object({
    nodeId: z.string().describe("Unique identifier for the node."),
    propertyOverrides: z.array(
      z.object({
        propertyId: z.string().describe("Unique identifier for the property."),
        text: z.string().describe("Value used to override a component property."),
      })
    ).describe("Properties to override for this locale's component instances."),
  }).describe("Update text property overrides of a component instance."),
]).array();
```

--------------------------------------------------------------------------------
/src/tools/localDeMCPConnection.ts:
--------------------------------------------------------------------------------

```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { RPCType } from "../types/RPCType";

import {
  formatErrorResponse,
  formatResponse,
} from "../utils/formatResponse";
export function registerLocalDeMCPConnectionTools(
  server: McpServer,
  rpc: RPCType
) {
  const localDeMCPConnectionToolRPCCall = async () => {
    return rpc.callTool("local_de_mcp_connection_tool", {});
  };

  server.tool(
    "get_designer_app_connection_info",
    "Get Webflow MCP App Connection Info. if user ask to get Webflow MCP app connection info, use this tool",
    {},
    async () => {
      try {
        return formatResponse(
          await localDeMCPConnectionToolRPCCall()
        );
      } catch (error) {
        return formatErrorResponse(error);
      }
    }
  );
}

```

--------------------------------------------------------------------------------
/src/schemas/ReferenceFieldSchema.ts:
--------------------------------------------------------------------------------

```typescript
import { z } from "zod";

export const ReferenceFieldSchema = z.object({
  id: z.string().optional().describe("Unique identifier for the Field."),
  isEditable: z
    .boolean()
    .optional()
    .describe("Indicates if the field is editable."),
  isRequired: z
    .boolean()
    .optional()
    .describe("Indicates if the field is required."),
  type: z
    .union([z.literal("MultiReference"), z.literal("Reference")])
    .describe("Type of the field. Choose of these appropriate field types."),
  displayName: z.string().describe("Name of the field."),
  helpText: z.string().optional().describe("Help text for the field."),
  metadata: z
    .object({
      collectionId: z.string(),
    })
    .describe(
      "ID of the referenced collection. Use this only for Reference and MultiReference fields."
    ),
});

```

--------------------------------------------------------------------------------
/src/tools/index.ts:
--------------------------------------------------------------------------------

```typescript
// Data API Tools
export { registerAiChatTools } from "./aiChat";
export { registerCmsTools } from "./cms";
export { registerComponentsTools } from "./components";
export { registerPagesTools } from "./pages";
export { registerScriptsTools } from "./scripts";
export { registerSiteTools } from "./sites";
// Designer API Tools
export { registerDEAssetTools } from "./deAsset";
export { registerDEComponentsTools } from "./deComponents";
export { registerDEElementTools } from "./deElement";
export { registerDEPagesTools } from "./dePages";
export { registerDEStyleTools } from "./deStyle";
export { registerDEVariableTools } from "./deVariable";

// Rules Tools
export { registerRulesTools } from "./rules";

// Only valid for OSS MCP Version, local MCP connection tools
export { registerLocalDeMCPConnectionTools } from "./localDeMCPConnection";

```

--------------------------------------------------------------------------------
/.github/workflows/publish-oss.yml:
--------------------------------------------------------------------------------

```yaml
name: Publish MCP Server

on:
  push:
    tags:
      - 'v*.*.*'

jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Update server.json version from tag
        run: |
          VERSION=${GITHUB_REF_NAME#v}
          echo "Updating server.json to version $VERSION"
          jq --arg v "$VERSION" '.version = $v' server.json > server.json.tmp && mv server.json.tmp server.json

      - name: Download mcp-publisher
        run: |
          curl -L "https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_linux_amd64" -o mcp-publisher
          chmod +x mcp-publisher

      - name: Login and Publish to MCP Registry
        run: |
          ./mcp-publisher login dns --domain webflow.com --private-key ${{ secrets.OSS_PRIVATE_KEY }}
          ./mcp-publisher publish

```

--------------------------------------------------------------------------------
/src/schemas/OptionFieldSchema.ts:
--------------------------------------------------------------------------------

```typescript
import z from "zod";

export const OptionFieldSchema = z.object({
  id: z.string().optional().describe("Unique identifier for the Field."),
  isEditable: z
    .boolean()
    .optional()
    .describe("Indicates if the field is editable."),
  isRequired: z
    .boolean()
    .optional()
    .describe("Indicates if the field is required."),
  type: z
    .literal("Option")
    .describe('Type of the field. Set this to "Option".'),
  displayName: z.string().describe("Name of the field."),
  helpText: z.string().optional().describe("Help text for the field."),
  metadata: z.object({
    options: z.array(
      z
        .object({
          name: z.string().describe("Name of the option."),
          id: z
            .string()
            .optional()
            .describe("Unique identifier for the option."),
        })
        .describe("Array of options for the field.")
    ),
  }),
});

```

--------------------------------------------------------------------------------
/src/schemas/StaticFieldSchema.ts:
--------------------------------------------------------------------------------

```typescript
import z from "zod";

export const StaticFieldSchema = z.object({
  id: z.string().optional().describe("Unique identifier for the Field."),
  isEditable: z
    .boolean()
    .optional()
    .describe("Indicates if the field is editable."),
  isRequired: z
    .boolean()
    .optional()
    .describe("Indicates if the field is required."),
  type: z
    .union([
      z.literal("Color"),
      z.literal("DateTime"),
      z.literal("Email"),
      z.literal("File"),
      z.literal("Image"),
      z.literal("Link"),
      z.literal("MultiImage"),
      z.literal("Number"),
      z.literal("Phone"),
      z.literal("PlainText"),
      z.literal("RichText"),
      z.literal("Switch"),
      z.literal("Video"),
    ])
    .describe("Type of the field. Choose of these appropriate field types."),
  displayName: z.string().describe("Name of the field."),
  helpText: z.string().optional().describe("Help text for the field."),
});

```

--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------

```yaml
name: ci

on: [push]

jobs:
  compile:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout repo
        uses: actions/checkout@v3

      - name: Set up node
        uses: actions/setup-node@v3

      - name: Compile
        run: npm install && npm run build

  publish:
    needs: [ compile ]
    if: github.event_name == 'push' && contains(github.ref, 'refs/tags/')
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repo
        uses: actions/checkout@v3
      - name: Set up node
        uses: actions/setup-node@v3
      - name: Install dependencies
        run: npm install
      - name: Build
        run: npm run build

      - name: Publish to npm
        run: |
          npm config set //registry.npmjs.org/:_authToken ${NPM_TOKEN}
          if [[ ${GITHUB_REF} == *alpha* ]]; then
            npm publish --access public --tag alpha
          elif [[ ${GITHUB_REF} == *beta* ]]; then
            npm publish --access public --tag beta
          else
            npm publish --access public
          fi
        env:
          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

```

--------------------------------------------------------------------------------
/src/schemas/RegisterInlineSiteScriptSchema.ts:
--------------------------------------------------------------------------------

```typescript
import { z } from "zod";

export const RegisterInlineSiteScriptSchema = z
  .object({
    sourceCode: z
      .string()
      .describe(
        "The inline script source code (hosted by Webflow). Inline scripts are limited to 2000 characters."
      ),
    version: z
      .string()
      .describe(
        "A Semantic Version (SemVer) string, denoting the version of the script."
      ),
    canCopy: z
      .boolean()
      .optional()
      .describe(
        "Indicates whether the script can be copied on site duplication and transfer."
      ),
    displayName: z
      .string()
      .describe(
        "User-facing name for the script. Must be between 1 and 50 alphanumeric characters."
      ),
    location: z
      .string()
      .optional()
      .describe(
        'Location where the script is applied. Allowed values: "header", "footer".'
      ),
    attributes: z
      .record(z.any())
      .optional()
      .describe(
        "Developer-specified key/value pairs to be applied as attributes to the script."
      ),
  })
  .describe("Request schema to register an inline script for a site.");

```

--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------

```typescript
#!/usr/bin/env node

import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { WebflowClient } from "webflow-api";
import {
  createMcpServer,
  registerDesignerTools,
  registerLocalTools,
  registerMiscTools,
  registerTools,
} from "./mcp";
import { initDesignerAppBridge } from "./modules/designerAppBridge";

// Verify WEBFLOW_TOKEN exists
if (!process.env.WEBFLOW_TOKEN) {
  throw new Error("WEBFLOW_TOKEN is missing");
}

// Create a Webflow client
const webflowClient = new WebflowClient({
  accessToken: process.env.WEBFLOW_TOKEN,
});

// Return the Webflow client
function getClient() {
  return webflowClient;
}

// Configure and run local MCP server (stdio transport)
async function run() {
  const server = createMcpServer();
  const { callTool } = await initDesignerAppBridge();
  registerMiscTools(server);
  registerTools(server, getClient);
  registerDesignerTools(server, {
    callTool,
    getClient,
  });

  //Only valid for OSS MCP Version.
  registerLocalTools(server, {
    callTool,
    getClient,
  });

  const transport = new StdioServerTransport();
  await server.connect(transport);
}
run();

```

--------------------------------------------------------------------------------
/src/schemas/WebflowPageDomWriteNodesItemSchema.ts:
--------------------------------------------------------------------------------

```typescript
import { z } from "zod";

export const WebflowPageDomWriteNodesItemSchema = z
  .union([
    z
      .object({
        nodeId: z.string().describe("Unique identifier for the node."),
        text: z
          .string()
          .describe(
            "HTML content of the node, including the HTML tag. The HTML tags must be the same as what’s returned from the Get Content endpoint."
          ),
      })
      .describe("Text node to be updated."),
    z
      .object({
        nodeId: z.string().describe("Unique identifier for the node."),
        propertyOverrides: z.array(
          z
            .object({
              propertyId: z
                .string()
                .describe("Unique identifier for the property."),
              text: z
                .string()
                .describe(
                  "Value used to override a component property; must be type-compatible to prevent errors."
                ),
            })
            .describe(
              "Properties to override for this locale’s component instances."
            )
        ),
      })
      .describe("Update text property overrides of a component instance."),
  ])
  .array();

```

--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------

```json
{
  "name": "webflow-mcp-server",
  "version": "1.0.0",
  "bin": "dist/index.js",
  "files": [
    "dist"
  ],
  "scripts": {
    "start": "concurrently \"npm run dev:local\" \"npm run inspector:local\"",
    "dev:local": "npm run build:watch",
    "inspector:local": "npx @modelcontextprotocol/inspector -- nodemon --env-file=.env -q --watch dist dist/index.js",
    "dev:cf": "wrangler dev src/index.worker.ts",
    "inspector:cf": "npx @modelcontextprotocol/inspector",
    "deploy:cf": "npm run build && wrangler deploy",
    "types:cf": "wrangler types",
    "build": "tsup src/index.ts src/index.worker.ts --external=cloudflare:workers --dts --clean",
    "build:watch": "tsup src/index.ts src/index.worker.ts --external=cloudflare:workers --dts --watch"
  },
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.8.0",
    "agents": "^0.0.59",
    "cors": "^2.8.5",
    "express": "^5.1.0",
    "socket.io": "^4.8.1",
    "webflow-api": "3.1.1",
    "zod": "^3.24.2"
  },
  "devDependencies": {
    "@types/cors": "^2.8.19",
    "@types/express": "^5.0.3",
    "@types/node": "^22.13.13",
    "concurrently": "^9.1.2",
    "nodemon": "^3.1.9",
    "tsup": "^8.4.0",
    "typescript": "^5.8.2"
  }
}

```

--------------------------------------------------------------------------------
/src/schemas/CollectionItemWithIdInputSchema.ts:
--------------------------------------------------------------------------------

```typescript
import { z } from "zod";

export const CollectionItemWithIdInputSchema = z
  .object({
    id: z.string().describe("Unique identifier for the item."),
    cmsLocaleId: z
      .string()
      .optional()
      .describe("Unique identifier for the locale of the CMS Item."),
    lastPublished: z
      .string()
      .optional()
      .describe("Date when the item was last published."),
    lastUpdated: z
      .string()
      .optional()
      .describe("Date when the item was last updated."),
    createdOn: z
      .string()
      .optional()
      .describe("Date when the item was created."),
    isArchived: z
      .boolean()
      .optional()
      .describe("Indicates if the item is archived."),
    isDraft: z
      .boolean()
      .optional()
      .describe("Indicates if the item is a draft."),
    fieldData: z.record(z.any()).and(
      z.object({
        name: z.string().optional().describe("Name of the field."),
        slug: z
          .string()
          .optional()
          .describe(
            "URL structure of the Item in your site. Note: Updates to an item slug will break all links referencing the old slug."
          ),
      })
    ),
  })
  .describe("Collection item update request schema.");

```

--------------------------------------------------------------------------------
/src/schemas/WebflowCollectionsItemsCreateItemLiveRequestSchema.ts:
--------------------------------------------------------------------------------

```typescript
import { z } from "zod";

export const WebflowCollectionsItemsCreateItemLiveRequestSchema = z.object({
  items: z
    .array(
      z.object({
        id: z.string().optional(),
        cmsLocaleId: z
          .string()
          .optional()
          .describe("Unique identifier for the locale of the CMS Item."),
        lastPublished: z
          .string()
          .optional()
          .describe("Date when the item was last published."),
        lastUpdated: z
          .string()
          .optional()
          .describe("Date when the item was last updated."),
        createdOn: z
          .string()
          .optional()
          .describe("Date when the item was created."),
        isArchived: z
          .boolean()
          .optional()
          .describe("Indicates if the item is archived."),
        isDraft: z
          .boolean()
          .optional()
          .describe("Indicates if the item is a draft."),
        fieldData: z.record(z.any()).and(
          z.object({
            name: z.string().describe("Name of the field."),
            slug: z
              .string()
              .describe(
                "URL structure of the Item in your site. Note: Updates to an item slug will break all links referencing the old slug."
              ),
          })
        ),
      })
    )
    .optional()
    .describe("Array of items to be created."),
});

```

--------------------------------------------------------------------------------
/src/tools/aiChat.ts:
--------------------------------------------------------------------------------

```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { randomUUID } from "crypto";
import { z } from "zod";

const BASE_URL = "https://developers.webflow.com/";
const X_FERN_HOST = "developers.webflow.com";

export function registerAiChatTools(server: McpServer) {
  server.tool(
    "ask_webflow_ai",
    "Ask Webflow AI about anything related to Webflow API.",
    { message: z.string() },
    async ({ message }) => {
      const result = await postChat(message);
      return {
        content: [{ type: "text", text: result }],
      };
    }
  );
}

async function postChat(message: string) {
  const response = await fetch(`${BASE_URL}/api/fern-docs/search/v2/chat`, {
    method: "POST",
    headers: {
      "content-type": "application/json",
      "x-fern-host": X_FERN_HOST,
    },
    body: JSON.stringify({
      messages: [{ role: "user", parts: [{ type: "text", text: message }] }],
      conversationId: randomUUID(),
      url: BASE_URL,
      source: "mcp",
    }),
  });

  const result = await streamToString(response);
  return result;
}

async function streamToString(response: Response) {
  const reader = response.body?.getReader();
  if (!reader) {
    throw new Error("!reader");
  }

  let result = "";
  while (true) {
    const { done, value } = await reader.read();
    if (done) break;

    // Convert the Uint8Array to a string and append
    result += new TextDecoder().decode(value);
  }

  return result;
}

```

--------------------------------------------------------------------------------
/src/schemas/WebflowCollectionsItemsUpdateItemsLiveRequestSchema.ts:
--------------------------------------------------------------------------------

```typescript
import { z } from "zod";

// request: Webflow.collections.ItemsUpdateItemsLiveRequest
export const WebflowCollectionsItemsUpdateItemsLiveRequestSchema = z.object({
  items: z
    .array(
      z.object({
        id: z.string(),
        cmsLocaleId: z
          .string()
          .optional()
          .describe("Unique identifier for the locale of the CMS Item."),
        lastPublished: z
          .string()
          .optional()
          .describe("Date when the item was last published."),
        lastUpdated: z
          .string()
          .optional()
          .describe("Date when the item was last updated."),
        createdOn: z
          .string()
          .optional()
          .describe("Date when the item was created."),
        isArchived: z
          .boolean()
          .optional()
          .describe("Indicates if the item is archived."),
        isDraft: z
          .boolean()
          .optional()
          .describe("Indicates if the item is a draft."),
        fieldData: z
          .record(z.any())
          .and(
            z.object({
              name: z.string().optional().describe("Name of the field."),
              slug: z
                .string()
                .optional()
                .describe(
                  "URL structure of the Item in your site. Note: Updates to an item slug will break all links referencing the old slug."
                ),
            })
          )
          .optional()
          .describe("Array of items to be updated."),
      })
    )
    .optional(),
});

```

--------------------------------------------------------------------------------
/src/utils/appPort.ts:
--------------------------------------------------------------------------------

```typescript
import net from "net";

/**
 * Checks if a given port is free (not currently in use).
 * @param port - The port number to check.
 * @returns A Promise that resolves to `true` if the port is free, `false` if it's in use.
 */
export function isPortFree(port: number): Promise<boolean> {
  return new Promise((resolve) => {
    const tester = net
      .createServer()
      .once("error", (err: NodeJS.ErrnoException) => {
        if (err.code === "EADDRINUSE") {
          resolve(false); // Port is in use
        } else {
          resolve(false); // Unexpected error
        }
      })
      .once("listening", () => {
        tester.close(() => resolve(true)); // Port is free
      })
      .listen(port);
  });
}

/**
 * Finds the first available port in the given range.
 * @param startPort - The starting port number to check.
 * @param endPort - The ending port number to check.
 * @returns A Promise that resolves to the first free port number.
 * @throws Error if no free port is found in the range.
 */
export async function getFreePort(
  startPort: number,
  endPort: number
): Promise<number> {
  if (startPort > endPort) {
    throw new Error(
      `Invalid port range: startPort (${startPort}) must be <= endPort (${endPort})`
    );
  }

  if (startPort < 1 || endPort > 65535) {
    throw new Error(
      `Port numbers must be between 1 and 65535`
    );
  }

  for (let port = startPort; port <= endPort; port++) {
    if (await isPortFree(port)) {
      return port;
    }
  }

  throw new Error(
    `No free port found in range ${startPort}-${endPort}`
  );
}

```

--------------------------------------------------------------------------------
/src/schemas/index.ts:
--------------------------------------------------------------------------------

```typescript
export { CollectionItemPostSingleSchema } from "./CollectionItemPostSingleSchema";
export { CollectionItemWithIdInputSchema } from "./CollectionItemWithIdInputSchema";
export { ComponentDomWriteNodesItemSchema } from "./ComponentDomWriteNodesItemSchema";
export { ComponentPropertyUpdateSchema } from "./ComponentPropertyUpdateSchema";
export { OptionFieldSchema } from "./OptionFieldSchema";
export { ReferenceFieldSchema } from "./ReferenceFieldSchema";
export { RegisterInlineSiteScriptSchema } from "./RegisterInlineSiteScriptSchema";
export { StaticFieldSchema } from "./StaticFieldSchema";
export { WebflowCollectionsCreateRequestSchema } from "./WebflowCollectionsCreateRequestSchema";
export { WebflowCollectionsFieldUpdateSchema } from "./WebflowCollectionsFieldUpdateSchema";
export { WebflowCollectionsItemsCreateItemLiveRequestSchema } from "./WebflowCollectionsItemsCreateItemLiveRequestSchema";
export { WebflowCollectionsItemsCreateItemRequestSchema } from "./WebflowCollectionsItemsCreateItemRequestSchema";
export { WebflowCollectionsItemsListItemsRequestSortBySchema } from "./WebflowCollectionsItemsListItemsRequestSortBySchema";
export { WebflowCollectionsItemsListItemsRequestSortOrderSchema } from "./WebflowCollectionsItemsListItemsRequestSortOrderSchema";
export { WebflowCollectionsItemsUpdateItemsLiveRequestSchema } from "./WebflowCollectionsItemsUpdateItemsLiveRequestSchema";
export { WebflowCollectionsItemsUpdateItemsRequestSchema } from "./WebflowCollectionsItemsUpdateItemsRequestSchema";
export { WebflowPageDomWriteNodesItemSchema } from "./WebflowPageDomWriteNodesItemSchema";
export { WebflowPageSchema } from "./WebflowPageSchema";
export { SiteIdSchema } from "./SiteIdSchema";
export { DEElementSchema } from "./DEElementSchema";
export { DEElementIDSchema } from "./DEElementIDSchema";

```

--------------------------------------------------------------------------------
/src/mcp.ts:
--------------------------------------------------------------------------------

```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { WebflowClient } from "webflow-api";
import {
  registerAiChatTools,
  registerCmsTools,
  registerComponentsTools,
  registerDEAssetTools,
  registerDEComponentsTools,
  registerDEElementTools,
  registerDEPagesTools,
  registerPagesTools,
  registerScriptsTools,
  registerSiteTools,
  registerDEStyleTools,
  registerDEVariableTools,
  registerRulesTools,
  registerLocalDeMCPConnectionTools,
} from "./tools";
import { RPCType } from "./types/RPCType";

const packageJson = require("../package.json") as any;

// Create an MCP server
export function createMcpServer() {
  return new McpServer(
    {
      name: packageJson.name,
      version: packageJson.version,
    },
    {
      instructions: `These tools give you access to the Webflow's Data API. If you are ever unsure about anything Webflow API-related, use the "ask_webflow_ai" tool.`,
    }
  );
}

// Common request options, including User-Agent header
export const requestOptions = {
  headers: {
    "User-Agent": `Webflow MCP Server/${packageJson.version}`,
  },
};

// Register tools
export function registerTools(
  server: McpServer,
  getClient: () => WebflowClient
) {
  registerAiChatTools(server);
  registerCmsTools(server, getClient);
  registerComponentsTools(server, getClient);
  registerPagesTools(server, getClient);
  registerScriptsTools(server, getClient);
  registerSiteTools(server, getClient);
}

export function registerDesignerTools(
  server: McpServer,
  rpc: RPCType
) {
  registerDEAssetTools(server, rpc);
  registerDEComponentsTools(server, rpc);
  registerDEElementTools(server, rpc);
  registerDEPagesTools(server, rpc);
  registerDEStyleTools(server, rpc);
  registerDEVariableTools(server, rpc);
}

export function registerMiscTools(server: McpServer) {
  registerRulesTools(server);
}

/**
 * IMPORTANT: registerLocalTools is only valid for OSS MCP Version
 */
export function registerLocalTools(
  server: McpServer,
  rpc: RPCType
) {
  registerLocalDeMCPConnectionTools(server, rpc);
}

```

--------------------------------------------------------------------------------
/src/tools/sites.ts:
--------------------------------------------------------------------------------

```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { WebflowClient } from "webflow-api";
import { z } from "zod";
import { requestOptions } from "../mcp";
import { formatErrorResponse, formatResponse } from "../utils";

export function registerSiteTools(
  server: McpServer,
  getClient: () => WebflowClient
) {
  // GET https://api.webflow.com/v2/sites
  server.tool(
    "sites_list",
    "List all sites accessible to the authenticated user. Returns basic site information including site ID, name, and last published date.",
    async () => {
      try {
        const response = await getClient().sites.list(requestOptions);
        return formatResponse(response);
      } catch (error) {
        return formatErrorResponse(error);
      }
    }
  );

  // GET https://api.webflow.com/v2/sites/:site_id
  server.tool(
    "sites_get",
    "Get detailed information about a specific site including its settings, domains, and publishing status.",
    {
      site_id: z.string().describe("Unique identifier for the site."),
    },
    async ({ site_id }) => {
      try {
        const response = await getClient().sites.get(site_id, requestOptions);
        return formatResponse(response);
      } catch (error) {
        return formatErrorResponse(error);
      }
    }
  );

  // POST https://api.webflow.com/v2/sites/:site_id/publish
  server.tool(
    "sites_publish",
    "Publish a site to specified domains. This will make the latest changes live on the specified domains.",
    {
      site_id: z.string().describe("Unique identifier for the site."),
      customDomains: z
        .string()
        .array()
        .optional()
        .describe("Array of custom domains to publish the site to."),
      publishToWebflowSubdomain: z
        .boolean()
        .optional()
        .default(false)
        .describe("Whether to publish to the Webflow subdomain."),
    },
    async ({ site_id, customDomains, publishToWebflowSubdomain }) => {
      try {
        const response = await getClient().sites.publish(
          site_id,
          {
            customDomains,
            publishToWebflowSubdomain,
          },
          requestOptions
        );
        return formatResponse(response);
      } catch (error) {
        return formatErrorResponse(error);
      }
    }
  );
}

```

--------------------------------------------------------------------------------
/src/schemas/WebflowPageSchema.ts:
--------------------------------------------------------------------------------

```typescript
import { z } from "zod";

export const WebflowPageSchema = z.object({
  id: z.string().describe("Unique identifier for a Page."),
  siteId: z.string().optional().describe("Unique identifier for the Site."),
  title: z.string().optional().describe("Title of the page."),
  slug: z
    .string()
    .optional()
    .describe("Slug of the page (derived from title)."),
  parentId: z
    .string()
    .optional()
    .describe("Unique identifier for the parent folder."),
  collectionId: z
    .string()
    .optional()
    .describe(
      "Unique identifier for the linked collection, NULL id the Page is not part of a collection."
    ),
  createdOn: z.date().optional().describe("Date when the page was created."),
  lastUpdated: z
    .date()
    .optional()
    .describe("Date when the page was last updated."),
  archived: z
    .boolean()
    .optional()
    .describe("Indicates if the page is archived."),
  draft: z.boolean().optional().describe("Indicates if the page is a draft."),
  canBranch: z
    .boolean()
    .optional()
    .describe("Indicates if the page can be branched."),
  isBranch: z
    .boolean()
    .optional()
    .describe("Indicates if the page is Branch of another page."),
  isMembersOnly: z
    .boolean()
    .optional()
    .describe(
      "Indicates whether the Page is restricted by Memberships Controls."
    ),
  seo: z
    .object({
      title: z
        .string()
        .optional()
        .describe("The Page title shown in search engine results."),
      description: z
        .string()
        .optional()
        .describe("The Page description shown in search engine results."),
    })
    .optional()
    .describe("SEO-related fields for the page."),
  openGraph: z
    .object({
      title: z
        .string()
        .optional()
        .describe("The title supplied to Open Graph annotations."),
      titleCopied: z
        .boolean()
        .optional()
        .describe(
          "Indicates the Open Graph title was copied from the SEO title."
        ),
      description: z
        .string()
        .optional()
        .describe("The description supplied to Open Graph annotations."),
      descriptionCopied: z
        .boolean()
        .optional()
        .describe(
          "Indicates the Open Graph description was copied from the SEO description."
        ),
    })
    .optional(),
  localeId: z
    .string()
    .optional()
    .describe(
      "Unique identifier for the page locale. Applicable when using localization."
    ),
  publishedPath: z
    .string()
    .optional()
    .describe("Relative path of the published page."),
});

```

--------------------------------------------------------------------------------
/src/tools/dePages.ts:
--------------------------------------------------------------------------------

```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { RPCType } from "../types/RPCType";
import z from "zod";
import { SiteIdSchema } from "../schemas";
import {
  formatErrorResponse,
  formatResponse,
} from "../utils";

export function registerDEPagesTools(
  server: McpServer,
  rpc: RPCType
) {
  const pageToolRPCCall = async (
    siteId: string,
    actions: any
  ) => {
    return rpc.callTool("page_tool", {
      siteId,
      actions: actions || [],
    });
  };

  server.tool(
    "de_page_tool",
    "Designer Tool - Page tool to perform actions like create page, create page folder, get current page, switch page",
    {
      ...SiteIdSchema,
      actions: z.array(
        z.object({
          create_page: z
            .object({
              page_name: z
                .string()
                .describe("The name of the page to create"),
              meta_title: z
                .string()
                .describe(
                  "The meta title of the page to create"
                ),
              meta_description: z
                .string()
                .optional()
                .describe(
                  "The meta description of the page to create"
                ),
              page_parent_folder_id: z
                .string()
                .optional()
                .describe(
                  "The id of the parent page folder to create the page in"
                ),
            })
            .optional()
            .describe("Create new page"),
          create_page_folder: z
            .object({
              page_folder_name: z
                .string()
                .describe(
                  "The name of the page folder to create"
                ),
              page_folder_parent_id: z
                .string()
                .optional()
                .describe(
                  "The id of the parent page folder to create the page folder in"
                ),
            })
            .optional()
            .describe("Create new page folder"),

          get_current_page: z
            .boolean()
            .optional()
            .describe(
              "Get current page active on webflow designer"
            ),
          switch_page: z
            .object({
              page_id: z
                .string()
                .describe(
                  "The id of the page to switch to"
                ),
            })
            .optional()
            .describe(
              "Switch to a page on webflow designer"
            ),
        })
      ),
    },
    async ({ siteId, actions }) => {
      try {
        return formatResponse(
          await pageToolRPCCall(siteId, actions)
        );
      } catch (error) {
        return formatErrorResponse(error);
      }
    }
  );
}

```

--------------------------------------------------------------------------------
/src/schemas/DEElementSchema.ts:
--------------------------------------------------------------------------------

```typescript
import { z } from "zod";

export const DEElementSchema = z.object({
  type: z
    .enum([
      "Container",
      "Section",
      "DivBlock",
      "Heading",
      "TextBlock",
      "Paragraph",
      "Button",
      "TextLink",
      "LinkBlock",
      "Image",
      "DOM",
    ])
    .describe(
      "The type of element to create. with DOM type you can create any element. make sure you pass dom_config if you are creating a DOM element."
    ),
  set_style: z
    .object({
      style_names: z
        .array(z.string())
        .describe("The style names to set on the element."),
    })
    .optional()
    .describe(
      "Set style on the element. it will remove all other styles on the element. and set only the styles passed in style_names."
    ),
  set_text: z
    .object({
      text: z
        .string()
        .describe("The text to set on the element."),
    })
    .optional()
    .describe(
      "Set text on the element. only valid for text block, paragraph, heading, button, text link, link block."
    ),
  set_link: z
    .object({
      link_type: z
        .enum([
          "url",
          "file",
          "page",
          "element",
          "email",
          "phone",
        ])
        .describe(
          "The type of link to set on the element."
        ),
      link: z
        .string()
        .describe("The link to set on the element."),
    })
    .optional()
    .describe(
      "Set link on the element. only valid for button, text link, link block."
    ),
  set_heading_level: z
    .object({
      heading_level: z
        .number()
        .min(1)
        .max(6)
        .describe(
          "The heading level to set on the element."
        ),
    })
    .optional()
    .describe(
      "Set heading level on the element. only valid for heading."
    ),
  set_image_asset: z
    .object({
      image_asset_id: z
        .string()
        .describe(
          "The image asset id to set on the element."
        ),
      alt_text: z
        .string()
        .optional()
        .describe(
          "The alt text to set on the image. if not provided it will inherit from the image asset."
        ),
    })
    .optional()
    .describe(
      "Set image asset on the element. only valid for image."
    ),
  set_dom_config: z
    .object({
      dom_tag: z
        .string()
        .describe(
          "The tag of the DOM element to create. for example span, code, etc."
        ),
    })
    .optional()
    .describe(
      "Set DOM config on the element. only valid for DOM element."
    ),
  set_attributes: z
    .object({
      attributes: z
        .array(
          z.object({
            name: z
              .string()
              .describe(
                "The name of the attribute to set."
              ),
            value: z
              .string()
              .describe(
                "The value of the attribute to set."
              ),
          })
        )
        .describe("The attributes to set on the element."),
    })
    .optional()
    .describe("Set attributes on the element."),
});

```

--------------------------------------------------------------------------------
/src/tools/deComponents.ts:
--------------------------------------------------------------------------------

```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { RPCType } from "../types/RPCType";
import z from "zod";
import {
  DEElementIDSchema,
  SiteIdSchema,
} from "../schemas";
import {
  formatErrorResponse,
  formatResponse,
} from "../utils";

export function registerDEComponentsTools(
  server: McpServer,
  rpc: RPCType
) {
  const componentsToolRPCCall = async (
    siteId: string,
    actions: any
  ) => {
    return rpc.callTool("component_tool", {
      siteId,
      actions: actions || [],
    });
  };

  server.tool(
    "de_component_tool",
    "Designer tool - Component tool to perform actions like create component instances, get all components and more.",
    {
      ...SiteIdSchema,
      actions: z.array(
        z.object({
          check_if_inside_component_view: z
            .boolean()
            .optional()
            .describe(
              "Check if inside component view. this helpful to make changes to the component"
            ),
          transform_element_to_component: z
            .object({
              ...DEElementIDSchema,
              name: z
                .string()
                .describe("The name of the component"),
            })
            .optional()
            .describe(
              "Transform an element to a component"
            ),
          insert_component_instance: z
            .object({
              parent_element_id: DEElementIDSchema.id,
              component_id: z
                .string()
                .describe(
                  "The id of the component to insert"
                ),
              creation_position: z
                .enum(["append", "prepend"])
                .describe(
                  "The position to create component instance on. append to the end of the parent element or prepend to the beginning of the parent element. as child of the parent element."
                ),
            })
            .optional()
            .describe(
              "Insert a component on current active page."
            ),
          open_component_view: z
            .object({
              component_instance_id: DEElementIDSchema.id,
            })
            .optional()
            .describe(
              "Open a component instance view for changes or reading."
            ),
          close_component_view: z
            .boolean()
            .optional()
            .describe(
              "Close a component instance view. it will close and open the page view."
            ),
          get_all_components: z
            .boolean()
            .optional()
            .describe(
              "Get all components, only valid if you are connected to Webflow Designer."
            ),
          rename_component: z
            .object({
              component_id: z
                .string()
                .describe(
                  "The id of the component to rename"
                ),
              new_name: z
                .string()
                .describe("The name of the component"),
            })
            .optional()
            .describe("Rename a component."),
        })
      ),
    },
    async ({ siteId, actions }) => {
      try {
        return formatResponse(
          await componentsToolRPCCall(siteId, actions)
        );
      } catch (error) {
        return formatErrorResponse(error);
      }
    }
  );
}

```

--------------------------------------------------------------------------------
/src/tools/deAsset.ts:
--------------------------------------------------------------------------------

```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { RPCType } from "../types/RPCType";
import z from "zod";
import { SiteIdSchema } from "../schemas";
import {
  formatErrorResponse,
  formatResponse,
} from "../utils";

export function registerDEAssetTools(
  server: McpServer,
  rpc: RPCType
) {
  const assetToolRPCCall = async (
    siteId: string,
    actions: any
  ) => {
    return rpc.callTool("asset_tool", {
      siteId,
      actions: actions || [],
    });
  };

  const getImagePreviewFromURL = async (
    url: string,
    siteId: string
  ) => {
    const response = await fetch(url);
    const contentType =
      response.headers.get("content-type");
    if (!contentType || !contentType.startsWith("image/")) {
      throw new Error(
        `Expected an image but received MIME type: ${
          contentType || "unknown"
        }`
      );
    }
    const arrayBuffer = await response.arrayBuffer();
    const binary = String.fromCharCode(
      ...new Uint8Array(arrayBuffer)
    );
    const base64 = btoa(binary);
    return { data: base64, mimeType: contentType, siteId };
  };

  server.tool(
    "asset_tool",
    "Designer Tool - Asset tool to perform actions like create folder, get all assets and folders, update assets and folders",
    {
      ...SiteIdSchema,
      actions: z.array(
        z.object({
          create_folder: z
            .object({
              name: z
                .string()
                .describe(
                  "The name of the folder to create"
                ),
              parent_folder_id: z
                .string()
                .optional()
                .describe(
                  "The id of the parent folder to move the folder to."
                ),
            })
            .optional()
            .describe("Create a folder on the site"),
          get_all_assets_and_folders: z
            .object({
              query: z
                .enum(["all", "folders", "assets"])
                .describe(
                  "Query to get all assets and folders on the site"
                ),
              filter_assets_by_ids: z
                .array(z.string())
                .describe("Filter assets by ids")
                .optional(),
            })
            .optional()
            .describe(
              "Get all assets and folders on the site"
            ),
          update_asset: z
            .object({
              asset_id: z
                .string()
                .describe("The id of the asset to update"),
              name: z
                .string()
                .optional()
                .describe(
                  "The name of the asset to update"
                ),
              alt_text: z
                .string()
                .optional()
                .describe(
                  "The alt text of the asset to update"
                ),
              parent_folder_id: z
                .string()
                .optional()
                .describe(
                  "The id of the parent folder to move the asset to."
                ),
            })
            .optional()
            .describe("Update an asset on the site"),
        })
      ),
    },
    async ({ siteId, actions }) => {
      try {
        return formatResponse(
          await assetToolRPCCall(siteId, actions)
        );
      } catch (error) {
        return formatErrorResponse(error);
      }
    }
  );

  server.tool(
    "get_image_preview",

    "Designer Tool - Get image preview from url. this is helpful to get image preview from url.",
    {
      url: z
        .string()
        .describe(
          "The URL of the image to get the preview from"
        ),
      ...SiteIdSchema,
    },
    async ({ url, siteId }) => {
      try {
        const { data, mimeType } =
          await getImagePreviewFromURL(url, siteId);
        return {
          content: [
            {
              type: "image",
              data,
              mimeType,
            },
          ],
        };
      } catch (error) {
        return formatErrorResponse(error);
      }
    }
  );
}

```

--------------------------------------------------------------------------------
/src/tools/scripts.ts:
--------------------------------------------------------------------------------

```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { WebflowClient } from "webflow-api";
import { ScriptApplyLocation } from "webflow-api/api/types/ScriptApplyLocation";
import { z } from "zod";
import { requestOptions } from "../mcp";
import { RegisterInlineSiteScriptSchema } from "../schemas";
import { formatErrorResponse, formatResponse, isApiError } from "../utils";

export function registerScriptsTools(
  server: McpServer,
  getClient: () => WebflowClient
) {
  // GET https://api.webflow.com/v2/sites/:site_id/registered_scripts
  server.tool(
    "site_registered_scripts_list",
    "List all registered scripts for a site. To apply a script to a site or page, first register it via the Register Script endpoints, then apply it using the relevant Site or Page endpoints.",
    {
      site_id: z.string().describe("Unique identifier for the site."),
    },
    async ({ site_id }) => {
      try {
        const response = await getClient().scripts.list(
          site_id,
          requestOptions
        );
        return formatResponse(response);
      } catch (error) {
        return formatErrorResponse(error);
      }
    }
  );

  // GET https://api.webflow.com/v2/sites/:site_id/custom_code
  server.tool(
    "site_applied_scripts_list",
    "Get all scripts applied to a site by the App. To apply a script to a site or page, first register it via the Register Script endpoints, then apply it using the relevant Site or Page endpoints.",
    {
      site_id: z.string().describe("Unique identifier for the site."),
    },
    async ({ site_id }) => {
      try {
        const response = await getClient().sites.scripts.getCustomCode(
          site_id,
          requestOptions
        );
        return formatResponse(response);
      } catch (error) {
        return formatErrorResponse(error);
      }
    }
  );

  // POST https://api.webflow.com/v2/sites/:site_id/registered_scripts/inline
  server.tool(
    "add_inline_site_script",
    "Register an inline script for a site. Inline scripts are limited to 2000 characters. ",
    {
      site_id: z.string().describe("Unique identifier for the site."),
      request: RegisterInlineSiteScriptSchema,
    },
    async ({ site_id, request }) => {
      const registerScriptResponse = await getClient().scripts.registerInline(
        site_id,
        {
          sourceCode: request.sourceCode,
          version: request.version,
          displayName: request.displayName,
          canCopy: request.canCopy !== undefined ? request.canCopy : true,
        },
        requestOptions
      );

      let existingScripts: any[] = [];
      try {
        const allScriptsResponse =
          await getClient().sites.scripts.getCustomCode(
            site_id,
            requestOptions
          );
        existingScripts = allScriptsResponse.scripts || [];
      } catch (error) {
        formatErrorResponse(error);
        existingScripts = [];
      }

      const newScript = {
        id: registerScriptResponse.id ?? " ",
        location:
          request.location === "footer"
            ? ScriptApplyLocation.Footer
            : ScriptApplyLocation.Header,
        version: registerScriptResponse.version ?? " ",
        attributes: request.attributes,
      };

      existingScripts.push(newScript);

      const addedSiteCustomCoderesponse =
        await getClient().sites.scripts.upsertCustomCode(
          site_id,
          {
            scripts: existingScripts,
          },
          requestOptions
        );

      return formatResponse(registerScriptResponse);
    }
  );

  server.tool(
    "delete_all_site_scripts",
    {
      site_id: z.string(),
    },
    async ({ site_id }) => {
      try {
        const response = await getClient().sites.scripts.deleteCustomCode(
          site_id,
          requestOptions
        );
        return formatResponse("Custom Code Deleted");
      } catch (error) {
        // If it's a 404, we'll try to clear the scripts another way
        if (isApiError(error) && error.status === 404) {
          return formatResponse(error.message ?? "No custom code found");
        }
        throw error;
      }
    }
  );
}

```

--------------------------------------------------------------------------------
/src/modules/designerAppBridge.ts:
--------------------------------------------------------------------------------

```typescript
import express from "express";
import http from "http";
import {
  Socket,
  Server as SocketIOServer,
} from "socket.io";
import cors from "cors";
import { RPCType } from "../types/RPCType";
import { generateUUIDv4, getFreePort } from "../utils";

type returnType = {
  callTool: RPCType["callTool"];
};

const START_PORT = 1338;
const END_PORT = 1638;

const initRPC = (
  io: SocketIOServer,
  port: number
): returnType => {
  const url = `http://localhost:${port}`;
  const siteIdToSocketMap = new Map<string, Set<Socket>>();
  const pendingToolResponse = new Map<
    string,
    (response: any) => void
  >();

  io.on("connection", (socket) => {
    const { siteId } = socket.handshake.query as {
      siteId: string;
    };
    if (!siteId) {
      socket.emit("error", "Site ID is required");
      setTimeout(() => {
        socket.disconnect();
      }, 1000);
      return;
    }

    if (!siteIdToSocketMap.has(siteId)) {
      siteIdToSocketMap.set(siteId, new Set());
    }
    siteIdToSocketMap.get(siteId)!.add(socket);

    socket.emit("connection-confirmation", {
      siteId,
      message: "Connected to Webflow MCP",
    });

    socket.on("tool-call-response", (data) => {
      const { requestId, data: responseData } = data as {
        requestId: string;
        data: any;
      };
      if (!requestId) {
        return;
      }
      if (!pendingToolResponse.has(requestId)) {
        return;
      }
      const toolResponse =
        pendingToolResponse.get(requestId);
      if (toolResponse) {
        toolResponse(responseData);
        pendingToolResponse.delete(requestId);
      }
    });

    socket.on("disconnect", () => {
      if (siteIdToSocketMap.has(siteId)) {
        siteIdToSocketMap.get(siteId)?.delete(socket);
      }
    });
  });
  const callTool = (toolName: string, args: any) => {
    if (toolName === "local_de_mcp_connection_tool") {
      return Promise.resolve({
        status: true,
        message: `Share this url with the user to connect to the Webflow Designer App. ${url}. Please share complete url with the USER.`,
        url,
      });
    }
    const { siteId } = args as any;
    if (!siteId) {
      return Promise.resolve({
        status: false,
        error: "Site ID is required",
      });
    }
    const requestId = `${siteId}-${generateUUIDv4()}`;
    return new Promise((resolve) => {
      if (
        siteIdToSocketMap.has(siteId) &&
        siteIdToSocketMap.get(siteId)!.size > 0
      ) {
        const sockets = siteIdToSocketMap.get(siteId)!;
        for (const socket of sockets) {
          socket.emit("call-tool", {
            toolName,
            args,
            siteId,
            requestId,
          });
        }
        const cleanup = () => {
          clearTimeout(timerId);
          pendingToolResponse.delete(requestId);
        };
        const timerId = setTimeout(() => {
          cleanup();
          resolve({
            error: `Tool call timed out, Please check Webflow Designer MCP app is running on Webflow Designer or restart the Webflow Designer App. make sure you are using correct url ${url} on app.`,
          });
        }, 20000); //20 seconds
        const toolResponse = (data: any) => {
          cleanup();
          resolve(data);
        };
        pendingToolResponse.set(requestId, toolResponse);
      } else {
        resolve({
          status: false,
          error:
            "No active Designer app connection to the site, Please make sure Designer app is open and connected, or check the site id is valid, all site ids can be found using the sites_list tool.",
        });
      }
    });
  };

  return {
    callTool,
  };
};

export const initDesignerAppBridge =
  async (): Promise<returnType> => {
    // Initialize Express app
    const app = express();
    app.use(cors()); // Enable CORS for all routes

    // Create HTTP server using the Express app
    const server = http.createServer(app);

    // Initialize Socket.IO with the HTTP server
    const io = new SocketIOServer(server, {
      cors: {
        origin: "*", // Allow connections from any origin
        methods: ["GET", "POST", "PUT", "DELETE", "PATCH"], // Allow specified HTTP methods
      },
      pingTimeout: 20000, // Close connection after 20s of inactivity
      transports: ["websocket", "polling"], // Enable both WebSocket and HTTP polling
    });

    app.get("/", (_, res) => {
      res.send("Webflow MCP is running");
    });

    try {
      const port = await getFreePort(START_PORT, END_PORT);
      server.listen(port);

      const rpc = initRPC(io, port);

      return rpc;
    } catch (e) {
      return {
        callTool: () => {
          return Promise.resolve({
            status: false,
            error: `Unable to find a free port to start the Webflow Designer App Bridge. Please make sure you have port ${START_PORT}-${END_PORT} free.`,
          });
        },
      };
    }
  };

```

--------------------------------------------------------------------------------
/src/tools/pages.ts:
--------------------------------------------------------------------------------

```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { WebflowClient } from "webflow-api";
import { z } from "zod";
import { requestOptions } from "../mcp";
import {
  WebflowPageDomWriteNodesItemSchema,
  WebflowPageSchema,
} from "../schemas";
import { formatErrorResponse, formatResponse } from "../utils";

export function registerPagesTools(
  server: McpServer,
  getClient: () => WebflowClient
) {
  // GET https://api.webflow.com/v2/sites/:site_id/pages
  server.tool(
    "pages_list",
    "List all pages within a site. Returns page metadata including IDs, titles, and slugs.",
    {
      site_id: z
        .string()
        .describe("The site’s unique ID, used to list its pages."),
      localeId: z
        .string()
        .optional()
        .describe(
          "Unique identifier for a specific locale. Applicable when using localization."
        ),
      limit: z
        .number()
        .optional()
        .describe("Maximum number of records to be returned (max limit: 100)"),
      offset: z
        .number()
        .optional()
        .describe(
          "Offset used for pagination if the results have more than limit records."
        ),
    },
    async ({ site_id, localeId, limit, offset }) => {
      try {
        const response = await getClient().pages.list(
          site_id,
          {
            localeId,
            limit,
            offset,
          },
          requestOptions
        );
        return formatResponse(response);
      } catch (error) {
        return formatErrorResponse(error);
      }
    }
  );

  // GET https://api.webflow.com/v2/pages/:page_id
  server.tool(
    "pages_get_metadata",
    "Get metadata for a specific page including SEO settings, Open Graph data, and page status (draft/published).",
    {
      page_id: z.string().describe("Unique identifier for the page."),
      localeId: z
        .string()
        .optional()
        .describe(
          "Unique identifier for a specific locale. Applicable when using localization."
        ),
    },
    async ({ page_id, localeId }) => {
      try {
        const response = await getClient().pages.getMetadata(
          page_id,
          {
            localeId,
          },
          requestOptions
        );
        return formatResponse(response);
      } catch (error) {
        return formatErrorResponse(error);
      }
    }
  );

  // PUT https://api.webflow.com/v2/pages/:page_id
  server.tool(
    "pages_update_page_settings",
    "Update page settings including SEO metadata, Open Graph data, slug, and publishing status.",
    {
      page_id: z.string().describe("Unique identifier for the page."),
      localeId: z
        .string()
        .optional()
        .describe(
          "Unique identifier for a specific locale. Applicable when using localization."
        ),
      body: WebflowPageSchema,
    },
    async ({ page_id, localeId, body }) => {
      try {
        const response = await getClient().pages.updatePageSettings(
          page_id,
          {
            localeId,
            body,
          },
          requestOptions
        );
        return formatResponse(response);
      } catch (error) {
        return formatErrorResponse(error);
      }
    }
  );

  // GET https://api.webflow.com/v2/pages/:page_id/dom
  server.tool(
    "pages_get_content",
    "Get the content structure and data for a specific page including all elements and their properties.",
    {
      page_id: z.string().describe("Unique identifier for the page."),
      localeId: z
        .string()
        .optional()
        .describe(
          "Unique identifier for a specific locale. Applicable when using localization."
        ),
      limit: z
        .number()
        .optional()
        .describe("Maximum number of records to be returned (max limit: 100)"),
      offset: z
        .number()
        .optional()
        .describe(
          "Offset used for pagination if the results have more than limit records."
        ),
    },
    async ({ page_id, localeId, limit, offset }) => {
      try {
        const response = await getClient().pages.getContent(
          page_id,
          {
            localeId,
            limit,
            offset,
          },
          requestOptions
        );
        return formatResponse(response);
      } catch (error) {
        return formatErrorResponse(error);
      }
    }
  );

  // POST https://api.webflow.com/v2/pages/:page_id/dom
  server.tool(
    "pages_update_static_content",
    "Update content on a static page in secondary locales by modifying text nodes and property overrides.",
    {
      page_id: z.string().describe("Unique identifier for the page."),
      localeId: z
        .string()
        .describe(
          "Unique identifier for a specific locale. Applicable when using localization."
        ),
      nodes: WebflowPageDomWriteNodesItemSchema,
    },
    async ({ page_id, localeId, nodes }) => {
      try {
        const response = await getClient().pages.updateStaticContent(
          page_id,
          {
            localeId,
            nodes,
          },
          requestOptions
        );
        return formatResponse(response);
      } catch (error) {
        return formatErrorResponse(error);
      }
    }
  );
}

```

--------------------------------------------------------------------------------
/src/tools/components.ts:
--------------------------------------------------------------------------------

```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { WebflowClient } from "webflow-api";
import { z } from "zod";
import { requestOptions } from "../mcp";
import {
  ComponentDomWriteNodesItemSchema,
  ComponentPropertyUpdateSchema
} from "../schemas";
import { formatErrorResponse, formatResponse } from "../utils";

export function registerComponentsTools(
  server: McpServer,
  getClient: () => WebflowClient
) {
  // GET https://api.webflow.com/v2/sites/:site_id/components
  server.tool(
    "components_list",
    "List all components in a site. Returns component metadata including IDs, names, and versions.",
    {
      site_id: z.string().describe("Unique identifier for the Site."),
      limit: z
        .number()
        .optional()
        .describe("Maximum number of records to be returned (max limit: 100)"),
      offset: z
        .number()
        .optional()
        .describe(
          "Offset used for pagination if the results have more than limit records."
        ),
    },
    async ({ site_id, limit, offset }) => {
      try {
        const response = await getClient().components.list(
          site_id,
          {
            limit,
            offset,
          },
          requestOptions
        );
        return formatResponse(response);
      } catch (error) {
        return formatErrorResponse(error);
      }
    }
  );

  // GET https://api.webflow.com/v2/sites/:site_id/components/:component_id/dom
  server.tool(
    "components_get_content",
    "Get the content structure and data for a specific component including text, images, and nested components.",
    {
      site_id: z.string().describe("Unique identifier for the Site."),
      component_id: z.string().describe("Unique identifier for the Component."),
      localeId: z
        .string()
        .optional()
        .describe(
          "Unique identifier for a specific locale. Applicable when using localization."
        ),
      limit: z
        .number()
        .optional()
        .describe("Maximum number of records to be returned (max limit: 100)"),
      offset: z
        .number()
        .optional()
        .describe(
          "Offset used for pagination if the results have more than limit records."
        ),
    },
    async ({ site_id, component_id, localeId, limit, offset }) => {
      try {
        const response = await getClient().components.getContent(
          site_id,
          component_id,
          {
            localeId,
            limit,
            offset,
          },
          requestOptions
        );
        return formatResponse(response);
      } catch (error) {
        return formatErrorResponse(error);
      }
    }
  );

  // POST https://api.webflow.com/v2/sites/:site_id/components/:component_id/dom
  server.tool(
    "components_update_content",
    "Update content on a component in secondary locales by modifying text nodes and property overrides.",
    {
      site_id: z.string().describe("Unique identifier for the Site."),
      component_id: z.string().describe("Unique identifier for the Component."),
      localeId: z
        .string()
        .describe(
          "Unique identifier for a specific locale. Applicable when using localization."
        ),
      nodes: ComponentDomWriteNodesItemSchema,
    },
    async ({ site_id, component_id, localeId, nodes }) => {
      try {
        const response = await getClient().components.updateContent(
          site_id,
          component_id,
          {
            localeId,
            nodes,
          },
          requestOptions
        );
        return formatResponse(response);
      } catch (error) {
        return formatErrorResponse(error);
      }
    }
  );

  // GET https://api.webflow.com/v2/sites/:site_id/components/:component_id/properties
  server.tool(
    "components_get_properties",
    "Get component properties including default values and configuration for a specific component.",
    {
      site_id: z.string().describe("Unique identifier for the Site."),
      component_id: z.string().describe("Unique identifier for the Component."),
      localeId: z
        .string()
        .optional()
        .describe(
          "Unique identifier for a specific locale. Applicable when using localization."
        ),
      limit: z
        .number()
        .optional()
        .describe("Maximum number of records to be returned (max limit: 100)"),
      offset: z
        .number()
        .optional()
        .describe(
          "Offset used for pagination if the results have more than limit records."
        ),
    },
    async ({ site_id, component_id, localeId, limit, offset }) => {
      try {
        const response = await getClient().components.getProperties(
          site_id,
          component_id,
          {
            localeId,
            limit,
            offset,
          },
          requestOptions
        );
        return formatResponse(response);
      } catch (error) {
        return formatErrorResponse(error);
      }
    }
  );

  // POST https://api.webflow.com/v2/sites/:site_id/components/:component_id/properties
  server.tool(
    "components_update_properties",
    "Update component properties for localization to customize behavior in different languages.",
    {
      site_id: z.string().describe("Unique identifier for the Site."),
      component_id: z.string().describe("Unique identifier for the Component."),
      localeId: z
        .string()
        .describe(
          "Unique identifier for a specific locale. Applicable when using localization."
        ),
      properties: ComponentPropertyUpdateSchema,
    },
    async ({ site_id, component_id, localeId, properties }) => {
      try {
        const response = await getClient().components.updateProperties(
          site_id,
          component_id,
          {
            localeId,
            properties,
          },
          requestOptions
        );
        return formatResponse(response);
      } catch (error) {
        return formatErrorResponse(error);
      }
    }
  );
}

```

--------------------------------------------------------------------------------
/src/tools/deStyle.ts:
--------------------------------------------------------------------------------

```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { RPCType } from "../types/RPCType";
import z from "zod";
import { SiteIdSchema } from "../schemas";
import {
  formatErrorResponse,
  formatResponse,
  supportDEStyles,
} from "../utils";

export function registerDEStyleTools(
  server: McpServer,
  rpc: RPCType
) {
  const styleToolRPCCall = async (
    siteId: string,
    actions: any
  ) => {
    return rpc.callTool("style_tool", {
      siteId,
      actions: actions || [],
    });
  };

  server.tool(
    "style_tool",
    "Designer Tool - Style tool to perform actions like create style, get all styles, update styles",
    {
      ...SiteIdSchema,
      actions: z.array(
        z.object({
          create_style: z
            .object({
              name: z
                .string()
                .describe("The name of the style"),
              properties: z
                .array(
                  z.object({
                    property_name: z
                      .string()
                      .describe("The name of the property"),
                    property_value: z
                      .string()
                      .optional()
                      .describe(
                        "The value of the property"
                      ),
                    variable_as_value: z
                      .string()
                      .optional()
                      .describe(
                        "The variable id to use as the value"
                      ),
                  })
                )
                .describe(
                  "The properties of the style. if you are looking to link a variable as the value, then use the variable_as_value field. but do not use both property_value and variable_as_value"
                ),
              parent_style_name: z
                .string()
                .optional()
                .describe(
                  "The name of the parent style to create the new style in. this will use to create combo class"
                ),
            })
            .optional()
            .describe("Create a new style"),
          get_styles: z
            .object({
              skip_properties: z
                .boolean()
                .optional()
                .describe(
                  "Whether to skip the properties of the style. to get minimal data."
                ),
              include_all_breakpoints: z
                .boolean()
                .optional()
                .describe(
                  "Whether to include all breakpoints styles or not. very data intensive."
                ),
              query: z
                .enum(["all", "filtered"])
                .describe(
                  "The query to get all styles or filtered styles"
                ),
              filter_ids: z
                .array(z.string())
                .optional()
                .describe(
                  "The ids of the styles to filter by. should be used with query filtered"
                ),
            })
            .optional()
            .describe("Get all styles"),
          update_style: z
            .object({
              style_name: z
                .string()
                .describe(
                  "The name of the style to update"
                ),
              breakpoint_id: z
                .enum([
                  "xxl",
                  "xl",
                  "large",
                  "main",
                  "medium",
                  "small",
                  "tiny",
                ])
                .optional()
                .describe(
                  "The breakpoint to update the style for"
                ),
              pseudo: z
                .enum([
                  "noPseudo",
                  "nth-child(odd)",
                  "nth-child(even)",
                  "first-child",
                  "last-child",
                  "hover",
                  "active",
                  "pressed",
                  "visited",
                  "focus",
                  "focus-visible",
                  "focus-within",
                  "placeholder",
                  "empty",
                  "before",
                  "after",
                ])
                .optional()
                .describe(
                  "The pseudo class to update the style for"
                ),
              properties: z
                .array(
                  z.object({
                    property_name: z
                      .string()
                      .describe("The name of the property"),
                    property_value: z
                      .string()
                      .optional()
                      .describe(
                        "The value of the property"
                      ),
                    variable_as_value: z
                      .string()
                      .optional()
                      .describe(
                        "The variable id to use as the value"
                      ),
                  })
                )
                .optional()
                .describe(
                  "The properties to update or add to the style for"
                ),
              remove_properties: z
                .array(z.string())
                .optional()
                .describe(
                  "The properties to remove from the style"
                ),
            })
            .optional()
            .describe("Update a style"),
        })
      ),
    },
    async ({ siteId, actions }) => {
      try {
        return formatResponse(
          await styleToolRPCCall(siteId, actions)
        );
      } catch (error) {
        return formatErrorResponse(error);
      }
    }
  );

  server.tool(
    "de_learn_more_about_styles",
    "Designer tool - Learn more about styles supported by Webflow Designer." +
      "Please do not use any other styles which is not supported by Webflow Designer." +
      "Please use the long-form alias of a CSS property when managing styles. For example, the property row-gap has a long-form alias of grid-row-gap, margin has long-form alias of margin-top, margin-right, margin-bottom, margin-left, etc.",
    {},
    async ({}) => formatResponse(supportDEStyles)
  );
}

```

--------------------------------------------------------------------------------
/src/tools/deVariable.ts:
--------------------------------------------------------------------------------

```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { RPCType } from "../types/RPCType";
import z from "zod";
import { SiteIdSchema } from "../schemas";
import {
  formatErrorResponse,
  formatResponse,
} from "../utils";

export function registerDEVariableTools(
  server: McpServer,
  rpc: RPCType
) {
  const variableToolRPCCall = async (
    siteId: string,
    actions: any
  ) => {
    return rpc.callTool("variable_tool", {
      siteId,
      actions: actions || [],
    });
  };

  server.tool(
    "variable_tool",
    "Designer Tool - Variable tool to perform actions like create variable, get all variables, update variable",
    {
      ...SiteIdSchema,
      actions: z.array(
        z.object({
          create_variable_collection: z
            .object({
              name: z
                .string()
                .describe(
                  "The name of the variable collection to create"
                ),
            })
            .optional()
            .describe("Create a new variable collection"),
          create_variable_mode: z
            .object({
              variable_collection_id: z
                .string()
                .describe(
                  "The id of the variable collection to create the variable mode in"
                ),
              name: z
                .string()
                .describe(
                  "The name of the variable mode to create"
                ),
            })
            .optional()
            .describe(
              "Create a new variable mode in a variable collection"
            ),
          get_variable_collections: z
            .object({
              query: z
                .enum(["all", "filtered"])
                .describe(
                  "The query to get all variable collections"
                ),
              filter_collections_by_ids: z
                .array(z.string())
                .optional()
                .describe(
                  "The ids of the variable collections to filter by"
                ),
            })
            .optional()
            .describe(
              "Get all variable collections and its modes"
            ),
          get_variables: z
            .object({
              variable_collection_id: z
                .string()
                .describe(
                  "The id of the variable collection to get the variables from"
                ),
              include_all_modes: z
                .boolean()
                .optional()
                .describe(
                  "Whether to include all modes or not"
                ),
              filter_variables_by_ids: z
                .array(z.string())
                .optional()
                .describe(
                  "The ids of the variables to filter by"
                ),
            })
            .optional()
            .describe(
              "Get all variables in a variable collection and its modes"
            ),
          create_color_variable: z
            .object({
              variable_collection_id: z.string(),
              variable_name: z.string(),
              value: z.object({
                static_value: z.string().optional(),
                existing_variable_id: z.string().optional(),
              }),
            })
            .optional()
            .describe("Create a new color variable"),
          create_size_variable: z
            .object({
              variable_collection_id: z.string(),
              variable_name: z.string(),
              value: z.object({
                static_value: z
                  .object({
                    value: z.number(),
                    unit: z.string(),
                  })
                  .optional(),
                existing_variable_id: z.string().optional(),
              }),
            })
            .optional()
            .describe("Create a new size variable"),
          create_number_variable: z
            .object({
              variable_collection_id: z.string(),
              variable_name: z.string(),
              value: z.object({
                static_value: z.number().optional(),
                existing_variable_id: z.string().optional(),
              }),
            })
            .optional()
            .describe("Create a new number variable"),
          create_percentage_variable: z
            .object({
              variable_collection_id: z.string(),
              variable_name: z.string(),
              value: z.object({
                static_value: z.number().optional(),
                existing_variable_id: z.string().optional(),
              }),
            })
            .optional()
            .describe("Create a new percentage variable"),
          create_font_family_variable: z
            .object({
              variable_collection_id: z.string(),
              variable_name: z.string(),
              value: z.object({
                static_value: z.string().optional(),
                existing_variable_id: z.string().optional(),
              }),
            })
            .optional()
            .describe("Create a new font family variable"),
          update_color_variable: z
            .object({
              variable_collection_id: z.string(),
              variable_id: z.string(),
              mode_id: z.string().optional(),
              value: z.object({
                static_value: z.string().optional(),
                existing_variable_id: z.string().optional(),
              }),
            })
            .optional()
            .describe("Update a color variable"),
          update_size_variable: z
            .object({
              variable_collection_id: z.string(),
              variable_id: z.string(),
              mode_id: z.string().optional(),
              value: z.object({
                static_value: z
                  .object({
                    value: z.number(),
                    unit: z.string(),
                  })
                  .optional(),
                existing_variable_id: z.string().optional(),
              }),
            })
            .optional()
            .describe("Update a size variable"),
          update_number_variable: z
            .object({
              variable_collection_id: z.string(),
              variable_id: z.string(),
              mode_id: z.string().optional(),
              value: z.object({
                static_value: z.number().optional(),
                existing_variable_id: z.string().optional(),
              }),
            })
            .optional()
            .describe("Update a number variable"),
          update_percentage_variable: z
            .object({
              variable_collection_id: z.string(),
              variable_id: z.string(),
              mode_id: z.string().optional(),
              value: z.object({
                static_value: z.number().optional(),
                existing_variable_id: z.string().optional(),
              }),
            })
            .optional()
            .describe("Update a percentage variable"),
          update_font_family_variable: z
            .object({
              variable_collection_id: z.string(),
              variable_id: z.string(),
              mode_id: z.string().optional(),
              value: z.object({
                static_value: z.string().optional(),
                existing_variable_id: z.string().optional(),
              }),
            })
            .optional()
            .describe("Update a font family variable"),
        })
      ),
    },
    async ({ siteId, actions }) => {
      try {
        return formatResponse(
          await variableToolRPCCall(siteId, actions)
        );
      } catch (error) {
        return formatErrorResponse(error);
      }
    }
  );
}

```

--------------------------------------------------------------------------------
/src/tools/deElement.ts:
--------------------------------------------------------------------------------

```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { RPCType } from "../types/RPCType";
import z from "zod";
import {
  SiteIdSchema,
  DEElementIDSchema,
  DEElementSchema,
} from "../schemas";
import {
  formatErrorResponse,
  formatResponse,
} from "../utils";

export const registerDEElementTools = (
  server: McpServer,
  rpc: RPCType
) => {
  const elementBuilderRPCCall = async (
    siteId: string,
    actions: any
  ) => {
    return rpc.callTool("element_builder", {
      siteId,
      actions: actions || [],
    });
  };

  const elementToolRPCCall = async (
    siteId: string,
    actions: any
  ) => {
    return rpc.callTool("element_tool", {
      siteId,
      actions: actions || [],
    });
  };

  server.tool(
    "element_builder",
    "Designer Tool - Element builder to create element on current active page. only create elements upto max 3 levels deep. divide your elements into smaller elements to create complex structures. recall this tool to create more elements. but max level is upto 3 levels. you can have as many children as you want. but max level is 3 levels.",
    {
      ...SiteIdSchema,
      actions: z.array(
        z.object({
          parent_element_id: z
            .object({
              component: z
                .string()
                .describe(
                  "The component id of the element to perform action on."
                ),
              element: z
                .string()
                .describe(
                  "The element id of the element to perform action on."
                ),
            })
            .describe(
              "The id of the parent element to create element on, you can find it from id field on element. e.g id:{component:123,element:456}."
            ),
          creation_position: z
            .enum(["append", "prepend"])
            .describe(
              "The position to create element on. append to the end of the parent element or prepend to the beginning of the parent element. as child of the parent element."
            ),
          element_schema: DEElementSchema.extend({
            children: z
              .array(
                DEElementSchema.extend({
                  children: z
                    .array(
                      DEElementSchema.extend({
                        children: z
                          .array(
                            DEElementSchema.extend({
                              children: z
                                .array(DEElementSchema)
                                .optional(),
                            })
                          )
                          .optional(),
                      })
                    )
                    .optional(),
                })
              )
              .optional()
              .describe(
                "The children of the element. only valid for container, section, div block, valid DOM elements."
              ),
          }).describe(
            "element schema of element to create."
          ),
        })
      ),
    },
    async ({ actions, siteId }) => {
      try {
        return formatResponse(
          await elementBuilderRPCCall(siteId, actions)
        );
      } catch (error) {
        return formatErrorResponse(error);
      }
    }
  );

  server.tool(
    "element_tool",
    "Designer Tool - Element tool to perform actions like get all elements, get selected element, select element on current active page. and more",
    {
      ...SiteIdSchema,
      actions: z.array(
        z.object({
          get_all_elements: z
            .object({
              query: z
                .enum(["all"])
                .describe("Query to get all elements"),
              include_style_properties: z
                .boolean()
                .optional()
                .describe("Include style properties"),
              include_all_breakpoint_styles: z
                .boolean()
                .optional()
                .describe("Include all breakpoints styles"),
            })
            .optional()
            .describe(
              "Get all elements on the current active page"
            ),
          get_selected_element: z
            .boolean()
            .optional()
            .describe(
              "Get selected element on the current active page"
            ),
          select_element: z
            .object({
              ...DEElementIDSchema,
            })
            .optional()
            .describe(
              "Select an element on the current active page"
            ),
          add_or_update_attribute: z
            .object({
              ...DEElementIDSchema,
              attributes: z
                .array(
                  z.object({
                    name: z
                      .string()
                      .describe(
                        "The name of the attribute to add or update."
                      ),
                    value: z
                      .string()
                      .describe(
                        "The value of the attribute to add or update."
                      ),
                  })
                )
                .describe(
                  "The attributes to add or update."
                ),
            })
            .optional()
            .describe(
              "Add or update an attribute on the element"
            ),
          remove_attribute: z
            .object({
              ...DEElementIDSchema,
              attribute_names: z
                .array(z.string())
                .describe(
                  "The names of the attributes to remove."
                ),
            })
            .optional()
            .describe(
              "Remove an attribute from the element"
            ),
          update_id_attribute: z
            .object({
              ...DEElementIDSchema,
              new_id: z
                .string()
                .describe(
                  "The new #id of the element to update the id attribute to."
                ),
            })
            .optional()
            .describe(
              "Update the #id attribute of the element"
            ),
          set_text: z
            .object({
              ...DEElementIDSchema,
              text: z
                .string()
                .describe(
                  "The text to set on the element."
                ),
            })
            .optional()
            .describe("Set text on the element"),
          set_style: z
            .object({
              ...DEElementIDSchema,
              style_names: z
                .array(z.string())
                .describe(
                  "The style names to set on the element."
                ),
            })
            .optional()
            .describe(
              "Set style on the element. it will remove all other styles on the element. and set only the styles passed in style_names."
            ),
          set_link: z
            .object({
              ...DEElementIDSchema,
              linkType: z
                .enum([
                  "url",
                  "file",
                  "page",
                  "element",
                  "email",
                  "phone",
                ])
                .describe(
                  "The type of the link to update."
                ),
              link: z
                .string()
                .describe(
                  "The link to set on the element. for page pass page id, for element pass json string of id object. e.g id:{component:123,element:456}. for email pass email address. for phone pass phone number. for file pass asset id. for url pass url."
                ),
            })
            .optional()
            .describe("Set link on the element"),
          set_heading_level: z
            .object({
              ...DEElementIDSchema,
              heading_level: z
                .number()
                .min(1)
                .max(6)
                .describe(
                  "The heading level to set on the element. 1 to 6."
                ),
            })
            .optional()
            .describe(
              "Set heading level on the heading element."
            ),
          set_image_asset: z
            .object({
              ...DEElementIDSchema,
              image_asset_id: z
                .string()
                .describe(
                  "The image asset id to set on the element."
                ),
            })
            .optional()
            .describe(
              "Set image asset on the image element"
            ),
        })
      ),
    },
    async ({ actions, siteId }) => {
      try {
        return formatResponse(
          await elementToolRPCCall(siteId, actions)
        );
      } catch (error) {
        return formatErrorResponse(error);
      }
    }
  );
};

```

--------------------------------------------------------------------------------
/src/tools/rules.ts:
--------------------------------------------------------------------------------

```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";

export function registerRulesTools(server: McpServer) {
  server.tool(
    "webflow_guide_tool",
    "Provides essential guidelines and best practices for effectively using the Webflow tools. Call this tool to understand recommended workflows and important considerations before performing actions. ALWAYS CALL THIS TOOL FIRST BEFORE CALLING ANY OTHER TOOLS.  ALWAYS CALL THIS TOOL FIRST BEFORE CALLING ANY OTHER TOOLS. ",
    {},
    async ({}) => ({
      content: [
        {
          type: "text",
          text:
            `Webflow Tool Usage Guidelines:\n` +
            `- These rules are essential for using tools correctly and helping users achieve their goals. Follow all instructions precisely.\n` +
            `\n` +
            `General Rules:\n` +
            `-- Data Tools are REST API calls, and Designer Tools are UI tools. You must use the correct tool for the action you want to perform.\n` +
            `-- Webflow does not support shorthand css properties. You must use the longhand property names. For example, the property row-gap has a long-form alias of grid-row-gap, margin has long-form alias of margin-top, margin-right, margin-bottom, margin-left, etc. to learn more about supported styles, use de_learn_more_about_styles tool.\n` +
            `-- Do not assume site ID. If a tool requires site_id, you must pass it explicitly. If you're not sure about the site ID, ask the user for it.\n` +
            `-- Always plan your actions before calling any tool. Do not invoke tools randomly without understanding the full workflow.\n` +
            `-- After updating or creating an element, the updated/created element is not automatically selected. If you need more information about that element, use element_tool > select_element with the appropriate element ID to select and inspect it.\n` +
            `-- Do not use CSS shorthand properties when updating or creating styles. Always use longhand property names like "margin-top", "padding-left", "border-width", etc.\n` +
            `-- When creating or updating elements, most users prefer using existing styles. You should reuse styles if they exist, unless the user explicitly wants new ones.\n` +
            `\n` +
            `Element Tool Usage:\n` +
            `-- To get detailed information about the currently selected element, use element_tool > get_selected_element.\n` +
            `-- To select a specific element by its ID, use element_tool > select_element.\n` +
            `-- To retrieve all elements on the current page, use element_tool > get_all_elements. If you only need element structure or class names, set include_style_properties and include_all_breakpoint_styles to false to avoid excessive data and context overflow. Set them to true only if you specifically need style details and all breakpoints.\n` +
            `-- To add or update attributes on an element, use element_tool > add_or_update_attribute. Only do this if the element's metadata shows canHaveAttributes: true.\n` +
            `-- To remove an attribute from an element, use element_tool > remove_attribute. This action is also valid only if canHaveAttributes is true for the element.\n` +
            `-- To update the ID attribute of an element, use element_tool > update_id_attribute. This sets a custom ID (#id) for the element. Do not pass the '#' character in the ID string. If the element already has an ID, you can read it using the domId property.\n` +
            `-- To set one or more styles on an element, use element_tool > set_style. You must pass one or more valid style names that already exist. If multiple styles are applied, they are treated as combo classes.\n` +
            `-- To set or update a link for Button, TextLink, or LinkBlock elements, use element_tool > set_link.\n` +
            `-- To set an image asset on an Image element, use element_tool > set_image_asset. Make sure you pass a valid asset_id. You can retrieve available assets via asset_tool > get_all_assets_and_folders.\n` +
            `-- To update the heading level for a Heading element, use element_tool > set_heading_level. Valid heading levels are integers from 1 to 6, which correspond to h1 through h6.\n` +
            `\n` +
            `Element Builder Tool:\n` +
            `-- To create a new element, use element_builder. Pass the type of element you want to create. After creation, use element_tool > select_element to select the element and gather additional details if needed.\n` +
            `\n` +
            `Asset Tool Usage:\n` +
            `-- To create an asset folder, use asset_tool > create_folder. Pass the name of the folder. To create a nested folder, pass parent_folder_id. Otherwise, the folder will be created in the root directory.\n` +
            `-- To retrieve assets and folders, use asset_tool > get_all_assets_and_folders. You can use query as "all", "folders", or "assets". To limit data, use filter_assets_by_ids or search query. Fetch only what you need to avoid context overload.\n` +
            `-- To update an asset, use asset_tool > update_asset. Pass asset_id and optionally pass name, parent_folder_id, or alt_text to update one or more fields.\n` +
            `\n` +
            `Page Tool Usage:\n` +
            `-- To create a page, use page_tool > create_page. Pass the page name. After creation, the system will automatically switch to the new page. The tool returns the page info.\n` +
            `-- To create a page folder, use page_tool > create_page_folder. Pass the folder name. To create a nested folder, also pass parent_id. Otherwise, the folder is created in the root.\n` +
            `-- To get information about the current page, use page_tool > get_current_page. It returns page metadata.\n` +
            `-- To switch to a different page, use page_tool > switch_page. Pass the page_id to switch to that page.\n` +
            `\n` +
            `Style Tool Usage:\n` +
            `-- To create a new style, use style_tool > create_style. Pass the name. For combo classes, pass parent_style_name. Always use longhand property names when defining style properties.\n` +
            `-- To update a style, use style_tool > update_style. If breakpoint is not provided, it defaults to the main breakpoint. If pseudo is not provided, it defaults to "noPseudo".\n` +
            `-- To get existing styles, use style_tool > get_styles. Use filters or queries to retrieve only what's needed. This tool may return a lot of data.\n` +
            `-- Breakpoint-specific style behavior:\n` +
            `---- Styles set on xxl (1920px) apply to screens ≥ 1920px.\n` +
            `---- Styles set on xl (1440px) apply to screens ≥ 1440px.\n` +
            `---- Styles set on large (1280px) apply to screens ≥ 1280px.\n` +
            `---- Styles set on main apply to all devices unless overridden at other breakpoints.\n` +
            `---- Styles set on medium (tablet) apply to screens ≤ 991px.\n` +
            `---- Styles set on small (mobile landscape) apply to screens ≤ 767px.\n` +
            `---- Styles set on tiny (mobile portrait) apply to screens ≤ 478px.\n` +
            `\n` +
            `Variable Tool Usage:\n` +
            `-- To create a variable collection, use variable_tool > create_variable_collection. Pass the name.\n` +
            `-- To create a variable mode, use variable_tool > create_variable_mode. Pass the name and variable_collection_id.\n` +
            `-- To retrieve variable collections, use variable_tool > get_variable_collections. You can filter by query or use filter_collections_by_ids.\n` +
            `-- To retrieve variables, use variable_tool > get_variables. You can filter by query or filter_variables_by_ids.\n` +
            `-- To create variables of different types, use:\n` +
            `---- variable_tool > create_color_variable\n` +
            `---- variable_tool > create_size_variable\n` +
            `---- variable_tool > create_number_variable\n` +
            `---- variable_tool > create_percentage_variable\n` +
            `---- variable_tool > create_font_family_variable\n` +
            `-- In all create_*_variable tools, pass name and variable_collection_id.\n` +
            `-- To update any variable, use the corresponding update tool (e.g., update_color_variable) with name and variable_collection_id.\n` +
            `-- To bind a variable with another, use the existing_variable_id field.\n` +
            `-- In Webflow, variables are linked to styles and function like CSS custom properties.\n` +
            `\n` +
            `CMS Data Tool Usage:\n` +
            `-- To get a list of CMS collections, use cms_tool > get_collection_list. Pass the site_id.\n` +
            `-- To get a CMS collection details, use cms_tool > get_collection_details. Pass the collection_id.\n` +
            `-- To create a new CMS collection, use cms_tool > create_collection. Pass the name and site_id.\n` +
            `-- To create a new CMS collection static field, use cms_tool > create_collection_static_field. Pass the collection_id and data.\n` +
            `-- To create a new CMS collection option field, use cms_tool > create_collection_option_field. Pass the collection_id and data.\n` +
            `-- To create a new CMS collection reference field, use cms_tool > create_collection_reference_field. Pass the collection_id and data.\n` +
            `-- To update a CMS collection field, use cms_tool > update_collection_field. Pass the collection_id and field_id and data.\n` +
            `-- To create a new CMS collection item, use cms_tool > create_collection_items_live. Pass the collection_id and data.\n` +
            `-- To update a CMS collection item, use cms_tool > update_collection_items_live. Pass the collection_id and data.\n` +
            `-- To publish a CMS collection item, use cms_tool > publish_collection_items. Pass the collection_id and item_ids.\n` +
            `-- To delete a CMS collection item, use cms_tool > delete_collection_items. Pass the collection_id and items.\n` +
            `\n` +
            `Pages Data Tool Usage:\n` +
            `-- To get a list of pages, use pages_tool > get_pages_list. Pass the site_id.\n` +
            `-- To get the metadata of a page, use pages_tool > get_page_metadata. Pass the page_id.\n` +
            `-- To update page settings, use pages_tool > update_page_settings. Pass the page_id and data.\n` +
            `-- To get the content of a page, use pages_tool > get_page_content. Pass the page_id.\n` +
            `-- To update the static content of a page, use pages_tool > update_page_static_content. Pass the page_id and localeId and nodes.\n` +
            `\n` +
            `### Important rules for creating elements.\n` +
            `-- Always create styles first if you plan to apply them while creating the element. This ensures style references are valid at the time of creation.\n` +
            `-- Always plan out your actions before calling element_builder. Know exactly what type of element to create, what styles or attributes to apply, and how you will use it.\n` +
            `-- Once an element is created using element_builder, it is not automatically selected. To inspect or modify it, use element_tool > select_element and pass the element ID returned from the creation response.\n` +
            `-- Only Container, Section, DivBlock, some valid DOM elements can have children.\n`,
        },
      ],
    })
  );
}

```

--------------------------------------------------------------------------------
/src/tools/cms.ts:
--------------------------------------------------------------------------------

```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { WebflowClient } from "webflow-api";
import { z } from "zod";
import { requestOptions } from "../mcp";
import {
  OptionFieldSchema,
  ReferenceFieldSchema,
  StaticFieldSchema,
  WebflowCollectionsCreateRequestSchema,
  WebflowCollectionsFieldUpdateSchema,
  WebflowCollectionsItemsCreateItemLiveRequestSchema,
  WebflowCollectionsItemsCreateItemRequestSchema,
  WebflowCollectionsItemsListItemsRequestSortBySchema,
  WebflowCollectionsItemsListItemsRequestSortOrderSchema,
  WebflowCollectionsItemsUpdateItemsLiveRequestSchema,
  WebflowCollectionsItemsUpdateItemsRequestSchema,
} from "../schemas";
import { formatErrorResponse, formatResponse } from "../utils";

export function registerCmsTools(
  server: McpServer,
  getClient: () => WebflowClient
) {
  // GET https://api.webflow.com/v2/sites/:site_id/collections
  server.tool(
    "collections_list",
    "List all CMS collections in a site. Returns collection metadata including IDs, names, and schemas.",
    {
      site_id: z.string().describe("Unique identifier for the Site."),
    },
    async ({ site_id }) => {
      try {
        const response = await getClient().collections.list(
          site_id,
          requestOptions
        );
        return formatResponse(response);
      } catch (error) {
        return formatErrorResponse(error);
      }
    }
  );

  // GET https://api.webflow.com/v2/collections/:collection_id
  server.tool(
    "collections_get",
    "Get detailed information about a specific CMS collection including its schema and field definitions.",
    {
      collection_id: z
        .string()
        .describe("Unique identifier for the Collection."),
    },
    async ({ collection_id }) => {
      try {
        const response = await getClient().collections.get(
          collection_id,
          requestOptions
        );
        return formatResponse(response);
      } catch (error) {
        return formatErrorResponse(error);
      }
    }
  );

  // POST https://api.webflow.com/v2/sites/:site_id/collections
  server.tool(
    "collections_create",
    "Create a new CMS collection in a site with specified name and schema.",
    {
      site_id: z.string().describe("Unique identifier for the Site."),
      request: WebflowCollectionsCreateRequestSchema,
    },
    async ({ site_id, request }) => {
      try {
        const response = await getClient().collections.create(
          site_id,
          request,
          requestOptions
        );
        return formatResponse(response);
      } catch (error) {
        return formatErrorResponse(error);
      }
    }
  );

  // POST https://api.webflow.com/v2/collections/:collection_id/fields
  server.tool(
    "collection_fields_create_static",
    "Create a new static field in a CMS collection (e.g., text, number, date, etc.).",
    {
      collection_id: z
        .string()
        .describe("Unique identifier for the Collection."),
      request: StaticFieldSchema,
    },
    async ({ collection_id, request }) => {
      try {
        const response = await getClient().collections.fields.create(
          collection_id,
          request,
          requestOptions
        );
        return formatResponse(response);
      } catch (error) {
        return formatErrorResponse(error);
      }
    }
  );

  // POST https://api.webflow.com/v2/collections/:collection_id/fields
  server.tool(
    "collection_fields_create_option",
    "Create a new option field in a CMS collection with predefined choices.",
    {
      collection_id: z
        .string()
        .describe("Unique identifier for the Collection."),
      request: OptionFieldSchema,
    },
    async ({ collection_id, request }) => {
      try {
        const response = await getClient().collections.fields.create(
          collection_id,
          request,
          requestOptions
        );
        return formatResponse(response);
      } catch (error) {
        return formatErrorResponse(error);
      }
    }
  );

  // POST https://api.webflow.com/v2/collections/:collection_id/fields
  server.tool(
    "collection_fields_create_reference",
    "Create a new reference field in a CMS collection that links to items in another collection.",
    {
      collection_id: z
        .string()
        .describe("Unique identifier for the Collection."),
      request: ReferenceFieldSchema,
    },
    async ({ collection_id, request }) => {
      try {
        const response = await getClient().collections.fields.create(
          collection_id,
          request,
          requestOptions
        );
        return formatResponse(response);
      } catch (error) {
        return formatErrorResponse(error);
      }
    }
  );

  // PATCH https://api.webflow.com/v2/collections/:collection_id/fields/:field_id
  server.tool(
    "collection_fields_update",
    "Update properties of an existing field in a CMS collection.",
    {
      collection_id: z
        .string()
        .describe("Unique identifier for the Collection."),
      field_id: z.string().describe("Unique identifier for the Field."),
      request: WebflowCollectionsFieldUpdateSchema,
    },
    async ({ collection_id, field_id, request }) => {
      try {
        const response = await getClient().collections.fields.update(
          collection_id,
          field_id,
          request,
          requestOptions
        );
        return formatResponse(response);
      } catch (error) {
        return formatErrorResponse(error);
      }
    }
  );

  // POST https://api.webflow.com/v2/collections/:collection_id/items/live
  // NOTE: Cursor agent seems to struggle when provided with z.union(...), so we simplify the type here
  server.tool(
    "collections_items_create_item_live",
    "Create and publish new items in a CMS collection directly to the live site.",
    {
      collection_id: z
        .string()
        .describe("Unique identifier for the Collection."),
      request: WebflowCollectionsItemsCreateItemLiveRequestSchema,
    },
    async ({ collection_id, request }) => {
      try {
        const response = await getClient().collections.items.createItemLive(
          collection_id,
          request,
          requestOptions
        );
        return formatResponse(response);
      } catch (error) {
        return formatErrorResponse(error);
      }
    }
  );

  // PATCH https://api.webflow.com/v2/collections/:collection_id/items/live
  server.tool(
    "collections_items_update_items_live",
    "Update and publish existing items in a CMS collection directly to the live site.",
    {
      collection_id: z
        .string()
        .describe("Unique identifier for the Collection."),
      request: WebflowCollectionsItemsUpdateItemsLiveRequestSchema,
    },
    async ({ collection_id, request }) => {
      try {
        const response = await getClient().collections.items.updateItemsLive(
          collection_id,
          request,
          requestOptions
        );
        return formatResponse(response);
      } catch (error) {
        return formatErrorResponse(error);
      }
    }
  );

  // GET https://api.webflow.com/v2/collections/:collection_id/items
  server.tool(
    "collections_items_list_items",
    "List items in a CMS collection with optional filtering and sorting.",
    {
      collection_id: z
        .string()
        .describe("Unique identifier for the Collection."),
      cmsLocaleId: z
        .string()
        .optional()
        .describe("Unique identifier for the locale of the CMS Item."),
      limit: z
        .number()
        .optional()
        .describe("Maximum number of records to be returned (max limit: 100)"),
      offset: z
        .number()
        .optional()
        .describe(
          "Offset used for pagination if the results have more than limit records."
        ),
      name: z.string().optional().describe("Name of the field."),
      slug: z
        .string()
        .optional()
        .describe(
          "URL structure of the Item in your site. Note: Updates to an item slug will break all links referencing the old slug."
        ),
      sortBy: WebflowCollectionsItemsListItemsRequestSortBySchema,
      sortOrder: WebflowCollectionsItemsListItemsRequestSortOrderSchema,
    },
    async ({
      collection_id,
      cmsLocaleId,
      offset,
      limit,
      name,
      slug,
      sortBy,
      sortOrder,
    }) => {
      try {
        const response = await getClient().collections.items.listItems(
          collection_id,
          {
            cmsLocaleId,
            offset,
            limit,
            name,
            slug,
            sortBy,
            sortOrder,
          },
          requestOptions
        );
        return formatResponse(response);
      } catch (error) {
        return formatErrorResponse(error);
      }
    }
  );

  // POST https://api.webflow.com/v2/collections/:collection_id/items
  server.tool(
    "collections_items_create_item",
    "Create new items in a CMS collection as drafts.",
    {
      collection_id: z.string(),
      request: WebflowCollectionsItemsCreateItemRequestSchema,
    },
    async ({ collection_id, request }) => {
      try {
        const response = await getClient().collections.items.createItem(
          collection_id,
          request,
          requestOptions
        );
        return formatResponse(response);
      } catch (error) {
        return formatErrorResponse(error);
      }
    }
  );

  // PATCH https://api.webflow.com/v2/collections/:collection_id/items
  server.tool(
    "collections_items_update_items",
    "Update existing items in a CMS collection as drafts.",
    {
      collection_id: z
        .string()
        .describe("Unique identifier for the Collection."),
      request: WebflowCollectionsItemsUpdateItemsRequestSchema,
    },
    async ({ collection_id, request }) => {
      try {
        const response = await getClient().collections.items.updateItems(
          collection_id,
          request,
          requestOptions
        );
        return formatResponse(response);
      } catch (error) {
        return formatErrorResponse(error);
      }
    }
  );

  // POST https://api.webflow.com/v2/collections/:collection_id/items/publish
  server.tool(
    "collections_items_publish_items",
    "Publish draft items in a CMS collection to make them live.",
    {
      collection_id: z
        .string()
        .describe("Unique identifier for the Collection."),
      itemIds: z
        .array(z.string())
        .describe("Array of item IDs to be published."),
    },
    async ({ collection_id, itemIds }) => {
      try {
        const response = await getClient().collections.items.publishItem(
          collection_id,
          {
            itemIds: itemIds,
          },
          requestOptions
        );
        return formatResponse(response);
      } catch (error) {
        return formatErrorResponse(error);
      }
    }
  );


   // DEL https://api.webflow.com/v2/collections/:collection_id/items/
   server.tool(
    "collections_items_delete_item",
    "Delete an item in a CMS collection. Items will only be deleted in the primary locale unless a cmsLocaleId is included in the request. ",
    {
      collection_id: z.string().describe("Unique identifier for the Collection."),
      itemId: z.string().describe("Item ID to be deleted."),
      cmsLocaleIds: z.string().optional().describe("Unique identifier for the locale of the CMS Item."),
    },
    async ({ collection_id, itemId, cmsLocaleIds }) => {
      try {
      const response = await getClient().collections.items.deleteItem(
        collection_id,
        itemId,
        { cmsLocaleId: cmsLocaleIds},
        requestOptions
      );
      return formatResponse(JSON.stringify("Item deleted"));
    } catch (error) {
      return formatErrorResponse(error);
    }
    }
  );
}

```

--------------------------------------------------------------------------------
/src/utils/supportStyles.ts:
--------------------------------------------------------------------------------

```typescript
export const supportDEStyles = [
  {
    property: "accent-color",
    supportedValues: "string or ColorVariable",
    example: "#ff5733",
  },
  {
    property: "align-content",
    supportedValues: "string",
    example: "center",
  },
  {
    property: "align-items",
    supportedValues: "string",
    example: "flex-start",
  },
  {
    property: "align-self",
    supportedValues: "string",
    example: "stretch",
  },
  {
    property: "animation-delay",
    supportedValues: "string",
    example: "2s",
  },
  {
    property: "animation-direction",
    supportedValues: "string",
    example: "alternate",
  },
  {
    property: "animation-duration",
    supportedValues: "string",
    example: "1s",
  },
  {
    property: "animation-fill-mode",
    supportedValues: "string",
    example: "forwards",
  },
  {
    property: "animation-iteration-count",
    supportedValues: "string",
    example: "infinite",
  },
  {
    property: "animation-name",
    supportedValues: "string",
    example: "slidein",
  },
  {
    property: "animation-play-state",
    supportedValues: "string",
    example: "paused",
  },
  {
    property: "animation-timing-function",
    supportedValues: "string",
    example: "ease-in-out",
  },
  {
    property: "appearance",
    supportedValues: "string",
    example: "none",
  },
  {
    property: "backdrop-filter",
    supportedValues: "string",
    example: "blur(5px)",
  },
  {
    property: "backface-visibility",
    supportedValues: "string",
    example: "hidden",
  },
  {
    property: "background-attachment",
    supportedValues: "string",
    example: "fixed",
  },
  {
    property: "background-blend-mode",
    supportedValues: "string",
    example: "multiply",
  },
  {
    property: "background-clip",
    supportedValues: "string",
    example: "border-box",
  },
  {
    property: "background-color",
    supportedValues: "string or ColorVariable",
    example: "#e0e0e0",
  },
  {
    property: "background-image",
    supportedValues: "string",
    example: "url('image.jpg')",
  },
  {
    property: "background-origin",
    supportedValues: "string",
    example: "padding-box",
  },
  {
    property: "background-position",
    supportedValues: "string",
    example: "top right",
  },
  {
    property: "background-position-x",
    supportedValues: "string or SizeVariable",
    example: "50%",
  },
  {
    property: "background-position-y",
    supportedValues: "string or SizeVariable",
    example: "50%",
  },
  {
    property: "background-repeat",
    supportedValues: "string",
    example: "repeat-x",
  },
  {
    property: "background-size",
    supportedValues: "string",
    example: "cover",
  },
  {
    property: "block-size",
    supportedValues: "string or SizeVariable",
    example: "100px",
  },
  {
    property: "border-block-end-color",
    supportedValues: "string or ColorVariable",
    example: "#000000",
  },
  {
    property: "border-block-end-style",
    supportedValues: "string",
    example: "dotted",
  },
  {
    property: "border-block-end-width",
    supportedValues: "string or SizeVariable",
    example: "3px",
  },
  {
    property: "border-block-start-color",
    supportedValues: "string or ColorVariable",
    example: "#333333",
  },
  {
    property: "border-block-start-style",
    supportedValues: "string",
    example: "solid",
  },
  {
    property: "border-block-start-width",
    supportedValues: "string or SizeVariable",
    example: "2px",
  },
  {
    property: "border-bottom-color",
    supportedValues: "string or ColorVariable",
    example: "#f44336",
  },
  {
    property: "border-bottom-left-radius",
    supportedValues: "string or SizeVariable",
    example: "4px",
  },
  {
    property: "border-bottom-right-radius",
    supportedValues: "string or SizeVariable",
    example: "4px",
  },
  {
    property: "border-bottom-style",
    supportedValues: "string",
    example: "groove",
  },
  {
    property: "border-bottom-width",
    supportedValues: "string or SizeVariable",
    example: "1px",
  },
  {
    property: "border-collapse",
    supportedValues: "string",
    example: "collapse",
  },
  {
    property: "border-end-end-radius",
    supportedValues: "string or SizeVariable",
    example: "10px",
  },
  {
    property: "border-end-start-radius",
    supportedValues: "string or SizeVariable",
    example: "10px",
  },
  {
    property: "border-image-outset",
    supportedValues: "string or SizeVariable",
    example: "5px",
  },
  {
    property: "border-image-repeat",
    supportedValues: "string",
    example: "stretch",
  },
  {
    property: "border-image-slice",
    supportedValues: "string",
    example: "30%",
  },
  {
    property: "border-image-source",
    supportedValues: "string",
    example: "url('border.png')",
  },
  {
    property: "border-image-width",
    supportedValues: "string or SizeVariable",
    example: "10px",
  },
  {
    property: "border-inline-end-color",
    supportedValues: "string or ColorVariable",
    example: "#4CAF50",
  },
  {
    property: "border-inline-end-style",
    supportedValues: "string",
    example: "inset",
  },
  {
    property: "border-inline-end-width",
    supportedValues: "string or SizeVariable",
    example: "4px",
  },
  {
    property: "border-inline-start-color",
    supportedValues: "string or ColorVariable",
    example: "#2196F3",
  },
  {
    property: "border-inline-start-style",
    supportedValues: "string",
    example: "outset",
  },
  {
    property: "border-inline-start-width",
    supportedValues: "string or SizeVariable",
    example: "3px",
  },
  {
    property: "border-left-color",
    supportedValues: "string or ColorVariable",
    example: "#9C27B0",
  },
  {
    property: "border-left-style",
    supportedValues: "string",
    example: "dashed",
  },
  {
    property: "border-left-width",
    supportedValues: "string or SizeVariable",
    example: "2px",
  },
  {
    property: "border-right-color",
    supportedValues: "string or ColorVariable",
    example: "#FFEB3B",
  },
  {
    property: "border-right-style",
    supportedValues: "string",
    example: "double",
  },
  {
    property: "border-right-width",
    supportedValues: "string or SizeVariable",
    example: "1px",
  },
  {
    property: "border-start-end-radius",
    supportedValues: "string or SizeVariable",
    example: "5px",
  },
  {
    property: "border-start-start-radius",
    supportedValues: "string or SizeVariable",
    example: "5px",
  },
  {
    property: "border-top-color",
    supportedValues: "string or ColorVariable",
    example: "#3F51B5",
  },
  {
    property: "border-top-left-radius",
    supportedValues: "string or SizeVariable",
    example: "20px",
  },
  {
    property: "border-top-right-radius",
    supportedValues: "string or SizeVariable",
    example: "20px",
  },
  {
    property: "border-top-style",
    supportedValues: "string",
    example: "ridge",
  },
  {
    property: "border-top-width",
    supportedValues: "string or SizeVariable",
    example: "2px",
  },
  {
    property: "bottom",
    supportedValues: "string or SizeVariable",
    example: "0",
  },
  {
    property: "box-shadow",
    supportedValues: "string",
    example: "10px 5px 5px black",
  },
  {
    property: "box-sizing",
    supportedValues: "string",
    example: "border-box",
  },
  {
    property: "break-after",
    supportedValues: "string",
    example: "auto",
  },
  {
    property: "break-before",
    supportedValues: "string",
    example: "always",
  },
  {
    property: "break-inside",
    supportedValues: "string",
    example: "avoid",
  },
  {
    property: "caption-side",
    supportedValues: "string",
    example: "bottom",
  },
  {
    property: "caret-color",
    supportedValues: "string or ColorVariable",
    example: "blue",
  },
  {
    property: "clear",
    supportedValues: "string",
    example: "both",
  },
  {
    property: "clip",
    supportedValues: "string",
    example: "rect(0,0,0,0)",
  },
  {
    property: "clip-path",
    supportedValues: "string",
    example: "circle(50%)",
  },
  {
    property: "clip-rule",
    supportedValues: "string",
    example: "evenodd",
  },
  {
    property: "color",
    supportedValues: "string or ColorVariable",
    example: "#FF9800",
  },
  {
    property: "color-interpolation",
    supportedValues: "string",
    example: "sRGB",
  },
  {
    property: "color-interpolation-filters",
    supportedValues: "string",
    example: "linearRGB",
  },
  {
    property: "column-count",
    supportedValues: "string",
    example: "3",
  },
  {
    property: "column-gap",
    supportedValues: "string or SizeVariable",
    example: "20px",
  },
  {
    property: "column-rule-color",
    supportedValues: "string or ColorVariable",
    example: "#607D8B",
  },
  {
    property: "column-rule-style",
    supportedValues: "string",
    example: "solid",
  },
  {
    property: "column-rule-width",
    supportedValues: "string or SizeVariable",
    example: "1px",
  },
  {
    property: "column-span",
    supportedValues: "string",
    example: "all",
  },
  {
    property: "column-width",
    supportedValues: "string or SizeVariable",
    example: "200px",
  },
  {
    property: "content",
    supportedValues: "string",
    example: "'Hello'",
  },
  {
    property: "cursor",
    supportedValues: "string",
    example: "pointer",
  },
  {
    property: "cx",
    supportedValues: "string",
    example: "50",
  },
  {
    property: "cy",
    supportedValues: "string",
    example: "50",
  },
  {
    property: "direction",
    supportedValues: "string",
    example: "ltr",
  },
  {
    property: "display",
    supportedValues: "string",
    example: "flex",
  },
  {
    property: "dominant-baseline",
    supportedValues: "string",
    example: "alphabetic",
  },
  {
    property: "empty-cells",
    supportedValues: "string",
    example: "show",
  },
  {
    property: "fill",
    supportedValues: "string",
    example: "#f00",
  },
  {
    property: "fill-opacity",
    supportedValues: "string",
    example: "0.5",
  },
  {
    property: "fill-rule",
    supportedValues: "string",
    example: "nonzero",
  },
  {
    property: "filter",
    supportedValues: "string",
    example: "blur(2px)",
  },
  {
    property: "flex-basis",
    supportedValues: "string or SizeVariable",
    example: "auto",
  },
  {
    property: "flex-direction",
    supportedValues: "string",
    example: "row",
  },
  {
    property: "flex-grow",
    supportedValues: "string",
    example: "1",
  },
  {
    property: "flex-shrink",
    supportedValues: "string",
    example: "1",
  },
  {
    property: "flex-wrap",
    supportedValues: "string",
    example: "wrap",
  },
  {
    property: "float",
    supportedValues: "string",
    example: "right",
  },
  {
    property: "flood-color",
    supportedValues: "string or ColorVariable",
    example: "#00BCD4",
  },
  {
    property: "flood-opacity",
    supportedValues: "string",
    example: "0.7",
  },
  {
    property: "font-family",
    supportedValues: "string or FontFamilyVariable",
    example: "Arial, sans-serif",
  },
  {
    property: "font-kerning",
    supportedValues: "string",
    example: "normal",
  },
  {
    property: "font-optical-sizing",
    supportedValues: "string",
    example: "auto",
  },
  {
    property: "font-size",
    supportedValues: "string or SizeVariable",
    example: "16px",
  },
  {
    property: "font-stretch",
    supportedValues: "string",
    example: "condensed",
  },
  {
    property: "font-style",
    supportedValues: "string",
    example: "italic",
  },
  {
    property: "font-variant-alternates",
    supportedValues: "string",
    example: "normal",
  },
  {
    property: "font-variant-caps",
    supportedValues: "string",
    example: "small-caps",
  },
  {
    property: "font-variant-east-asian",
    supportedValues: "string",
    example: "normal",
  },
  {
    property: "font-variant-ligatures",
    supportedValues: "string",
    example: "none",
  },
  {
    property: "font-variant-numeric",
    supportedValues: "string",
    example: "ordinal",
  },
  {
    property: "font-weight",
    supportedValues: "string",
    example: "bold",
  },
  {
    property: "grid-auto-columns",
    supportedValues: "string",
    example: "minmax(100px, auto)",
  },
  {
    property: "grid-auto-flow",
    supportedValues: "string",
    example: "row dense",
  },
  {
    property: "grid-auto-rows",
    supportedValues: "string",
    example: "auto",
  },
  {
    property: "grid-column-end",
    supportedValues: "string",
    example: "span 2",
  },
  {
    property: "grid-column-gap",
    supportedValues: "string",
    example: "10px",
  },
  {
    property: "grid-column-start",
    supportedValues: "string",
    example: "1",
  },
  {
    property: "grid-row-end",
    supportedValues: "string",
    example: "3",
  },
  {
    property: "grid-row-gap",
    supportedValues: "string or SizeVariable",
    example: "20px",
  },
  {
    property: "grid-row-start",
    supportedValues: "string",
    example: "1",
  },
  {
    property: "grid-template-areas",
    supportedValues: "string",
    example: "'header header'",
  },
  {
    property: "grid-template-columns",
    supportedValues: "string",
    example: "50px 100px",
  },
  {
    property: "grid-template-rows",
    supportedValues: "string",
    example: "auto",
  },
  {
    property: "height",
    supportedValues: "string or SizeVariable",
    example: "100vh",
  },
  {
    property: "image-orientation",
    supportedValues: "string",
    example: "90deg",
  },
  {
    property: "image-rendering",
    supportedValues: "string",
    example: "auto",
  },
  {
    property: "inline-size",
    supportedValues: "string or SizeVariable",
    example: "200px",
  },
  {
    property: "inset-block-end",
    supportedValues: "string or SizeVariable",
    example: "20px",
  },
  {
    property: "inset-block-start",
    supportedValues: "string or SizeVariable",
    example: "5px",
  },
  {
    property: "inset-inline-end",
    supportedValues: "string or SizeVariable",
    example: "10px",
  },
  {
    property: "inset-inline-start",
    supportedValues: "string or SizeVariable",
    example: "10px",
  },
  {
    property: "isolation",
    supportedValues: "string",
    example: "isolate",
  },
  {
    property: "justify-content",
    supportedValues: "string",
    example: "space-between",
  },
  {
    property: "justify-items",
    supportedValues: "string",
    example: "stretch",
  },
  {
    property: "justify-self",
    supportedValues: "string",
    example: "center",
  },
  {
    property: "left",
    supportedValues: "string or SizeVariable",
    example: "50px",
  },
  {
    property: "letter-spacing",
    supportedValues: "string or SizeVariable",
    example: "0.5em",
  },
  {
    property: "lighting-color",
    supportedValues: "string or ColorVariable",
    example: "white",
  },
  {
    property: "line-break",
    supportedValues: "string",
    example: "strict",
  },
  {
    property: "line-height",
    supportedValues: "string or SizeVariable",
    example: "1.5",
  },
  {
    property: "list-style-image",
    supportedValues: "string",
    example: "url('star.png')",
  },
  {
    property: "list-style-position",
    supportedValues: "string",
    example: "inside",
  },
  {
    property: "list-style-type",
    supportedValues: "string",
    example: "disc",
  },
  {
    property: "margin-block-end",
    supportedValues: "string or SizeVariable",
    example: "15px",
  },
  {
    property: "margin-block-start",
    supportedValues: "string or SizeVariable",
    example: "15px",
  },
  {
    property: "margin-bottom",
    supportedValues: "string or SizeVariable",
    example: "20px",
  },
  {
    property: "margin-inline-end",
    supportedValues: "string or SizeVariable",
    example: "10px",
  },
  {
    property: "margin-inline-start",
    supportedValues: "string or SizeVariable",
    example: "10px",
  },
  {
    property: "margin-left",
    supportedValues: "string or SizeVariable",
    example: "30px",
  },
  {
    property: "margin-right",
    supportedValues: "string or SizeVariable",
    example: "30px",
  },
  {
    property: "margin-top",
    supportedValues: "string or SizeVariable",
    example: "10px",
  },
  {
    property: "marker-end",
    supportedValues: "string",
    example: "url('arrowhead.svg')",
  },
  {
    property: "marker-mid",
    supportedValues: "string",
    example: "url('dot.svg')",
  },
  {
    property: "marker-start",
    supportedValues: "string",
    example: "url('circle.svg')",
  },
  {
    property: "mask-type",
    supportedValues: "string",
    example: "luminance",
  },
  {
    property: "max-block-size",
    supportedValues: "string or SizeVariable",
    example: "100px",
  },
  {
    property: "max-height",
    supportedValues: "string or SizeVariable",
    example: "200px",
  },
  {
    property: "max-inline-size",
    supportedValues: "string or SizeVariable",
    example: "300px",
  },
  {
    property: "max-width",
    supportedValues: "string or SizeVariable",
    example: "80%",
  },
  {
    property: "min-block-size",
    supportedValues: "string or SizeVariable",
    example: "50px",
  },
  {
    property: "min-height",
    supportedValues: "string or SizeVariable",
    example: "100px",
  },
  {
    property: "min-inline-size",
    supportedValues: "string or SizeVariable",
    example: "150px",
  },
  {
    property: "min-width",
    supportedValues: "string or SizeVariable",
    example: "60px",
  },
  {
    property: "mix-blend-mode",
    supportedValues: "string",
    example: "multiply",
  },
  {
    property: "object-fit",
    supportedValues: "string",
    example: "cover",
  },
  {
    property: "object-position",
    supportedValues: "string",
    example: "center top",
  },
  {
    property: "offset-anchor",
    supportedValues: "string",
    example: "auto",
  },
  {
    property: "offset-distance",
    supportedValues: "string or SizeVariable",
    example: "10px",
  },
  {
    property: "offset-path",
    supportedValues: "string",
    example: "path('M10 80 Q 95 10 180 80')",
  },
  {
    property: "offset-rotate",
    supportedValues: "string",
    example: "auto",
  },
  {
    property: "opacity",
    supportedValues: "string",
    example: "0.5",
  },
  {
    property: "order",
    supportedValues: "string",
    example: "2",
  },
  {
    property: "outline-color",
    supportedValues: "string or ColorVariable",
    example: "#FF5722",
  },
  {
    property: "outline-offset",
    supportedValues: "string or SizeVariable",
    example: "2px",
  },
  {
    property: "outline-style",
    supportedValues: "string",
    example: "dashed",
  },
  {
    property: "outline-width",
    supportedValues: "string or SizeVariable",
    example: "3px",
  },
  {
    property: "overflow-wrap",
    supportedValues: "string",
    example: "break-word",
  },
  {
    property: "overflow-x",
    supportedValues: "string",
    example: "auto",
  },
  {
    property: "overflow-y",
    supportedValues: "string",
    example: "scroll",
  },
  {
    property: "overscroll-behavior-block",
    supportedValues: "string",
    example: "contain",
  },
  {
    property: "overscroll-behavior-inline",
    supportedValues: "string",
    example: "none",
  },
  {
    property: "padding-block-end",
    supportedValues: "string or SizeVariable",
    example: "25px",
  },
  {
    property: "padding-block-start",
    supportedValues: "string or SizeVariable",
    example: "25px",
  },
  {
    property: "padding-bottom",
    supportedValues: "string or SizeVariable",
    example: "15px",
  },
  {
    property: "padding-inline-end",
    supportedValues: "string or SizeVariable",
    example: "20px",
  },
  {
    property: "padding-inline-start",
    supportedValues: "string or SizeVariable",
    example: "20px",
  },
  {
    property: "padding-left",
    supportedValues: "string or SizeVariable",
    example: "10px",
  },
  {
    property: "padding-right",
    supportedValues: "string or SizeVariable",
    example: "10px",
  },
  {
    property: "padding-top",
    supportedValues: "string or SizeVariable",
    example: "10px",
  },
  {
    property: "paint-order",
    supportedValues: "string",
    example: "fill stroke markers",
  },
  {
    property: "perspective",
    supportedValues: "string or SizeVariable",
    example: "500px",
  },
  {
    property: "perspective-origin",
    supportedValues: "string",
    example: "50% 50%",
  },
  {
    property: "pointer-events",
    supportedValues: "string",
    example: "none",
  },
  {
    property: "position",
    supportedValues: "string",
    example: "absolute",
  },
  {
    property: "r",
    supportedValues: "string or SizeVariable",
    example: "50px",
  },
  {
    property: "resize",
    supportedValues: "string",
    example: "both",
  },
  {
    property: "right",
    supportedValues: "string or SizeVariable",
    example: "0px",
  },
  {
    property: "rotate",
    supportedValues: "string",
    example: "45deg",
  },
  {
    property: "row-gap",
    supportedValues: "string or SizeVariable",
    example: "20px",
  },
  {
    property: "rx",
    supportedValues: "string or SizeVariable",
    example: "10px",
  },
  {
    property: "ry",
    supportedValues: "string or SizeVariable",
    example: "10px",
  },
  {
    property: "scale",
    supportedValues: "string",
    example: "1.2",
  },
  {
    property: "scroll-behavior",
    supportedValues: "string",
    example: "smooth",
  },
  {
    property: "scroll-margin-block-end",
    supportedValues: "string or SizeVariable",
    example: "10px",
  },
  {
    property: "scroll-margin-block-start",
    supportedValues: "string or SizeVariable",
    example: "10px",
  },
  {
    property: "scroll-margin-inline-end",
    supportedValues: "string or SizeVariable",
    example: "10px",
  },
  {
    property: "scroll-margin-inline-start",
    supportedValues: "string or SizeVariable",
    example: "10px",
  },
  {
    property: "scroll-padding-block-end",
    supportedValues: "string or SizeVariable",
    example: "20px",
  },
  {
    property: "scroll-padding-block-start",
    supportedValues: "string or SizeVariable",
    example: "20px",
  },
  {
    property: "scroll-padding-inline-end",
    supportedValues: "string or SizeVariable",
    example: "20px",
  },
  {
    property: "scroll-padding-inline-start",
    supportedValues: "string or SizeVariable",
    example: "20px",
  },
  {
    property: "shape-image-threshold",
    supportedValues: "string",
    example: "0.3",
  },
  {
    property: "shape-margin",
    supportedValues: "string or SizeVariable",
    example: "15px",
  },
  {
    property: "shape-outside",
    supportedValues: "string",
    example: "circle(50%)",
  },
  {
    property: "shape-rendering",
    supportedValues: "string",
    example: "auto",
  },
  {
    property: "stop-color",
    supportedValues: "string or ColorVariable",
    example: "#0D47A1",
  },
  {
    property: "stop-opacity",
    supportedValues: "string",
    example: "0.8",
  },
  {
    property: "stroke",
    supportedValues: "string or ColorVariable",
    example: "black",
  },
  {
    property: "stroke-dasharray",
    supportedValues: "string",
    example: "5, 10",
  },
  {
    property: "stroke-dashoffset",
    supportedValues: "string or SizeVariable",
    example: "5px",
  },
  {
    property: "stroke-linecap",
    supportedValues: "string",
    example: "round",
  },
  {
    property: "stroke-linejoin",
    supportedValues: "string",
    example: "bevel",
  },
  {
    property: "stroke-miterlimit",
    supportedValues: "string",
    example: "10",
  },
  {
    property: "stroke-opacity",
    supportedValues: "string",
    example: "1",
  },
  {
    property: "stroke-width",
    supportedValues: "string or SizeVariable",
    example: "3px",
  },
  {
    property: "tab-size",
    supportedValues: "string or SizeVariable",
    example: "4",
  },
  {
    property: "table-layout",
    supportedValues: "string",
    example: "fixed",
  },
  {
    property: "text-align",
    supportedValues: "string",
    example: "justify",
  },
  {
    property: "text-align-last",
    supportedValues: "string",
    example: "center",
  },
  {
    property: "text-anchor",
    supportedValues: "string",
    example: "start",
  },
  {
    property: "text-decoration",
    supportedValues: "string",
    example: "underline",
  },
  {
    property: "text-decoration-color",
    supportedValues: "string or ColorVariable",
    example: "red",
  },
  {
    property: "text-decoration-line",
    supportedValues: "string",
    example: "overline",
  },
  {
    property: "text-decoration-skip-ink",
    supportedValues: "string",
    example: "auto",
  },
  {
    property: "text-decoration-style",
    supportedValues: "string",
    example: "dotted",
  },
  {
    property: "text-emphasis-color",
    supportedValues: "string or ColorVariable",
    example: "green",
  },
  {
    property: "text-emphasis-position",
    supportedValues: "string",
    example: "under right",
  },
  {
    property: "text-emphasis-style",
    supportedValues: "string",
    example: "filled circle",
  },
  {
    property: "text-indent",
    supportedValues: "string or SizeVariable",
    example: "20px",
  },
  {
    property: "text-overflow",
    supportedValues: "string",
    example: "ellipsis",
  },
  {
    property: "text-rendering",
    supportedValues: "string",
    example: "optimizeLegibility",
  },
  {
    property: "text-shadow",
    supportedValues: "string",
    example: "2px 2px 5px grey",
  },
  {
    property: "text-transform",
    supportedValues: "string",
    example: "uppercase",
  },
  {
    property: "text-underline-position",
    supportedValues: "string",
    example: "under",
  },
  {
    property: "top",
    supportedValues: "string or SizeVariable",
    example: "100px",
  },
  {
    property: "touch-action",
    supportedValues: "string",
    example: "pan-right",
  },
  {
    property: "transform",
    supportedValues: "string",
    example: "rotate(45deg)",
  },
  {
    property: "transform-origin",
    supportedValues: "string",
    example: "top left",
  },
  {
    property: "transform-style",
    supportedValues: "string",
    example: "preserve-3d",
  },
  {
    property: "transition-delay",
    supportedValues: "string",
    example: "0.5s",
  },
  {
    property: "transition-duration",
    supportedValues: "string",
    example: "300ms",
  },
  {
    property: "transition-property",
    supportedValues: "string",
    example: "opacity",
  },
  {
    property: "transition-timing-function",
    supportedValues: "string",
    example: "ease-in-out",
  },
  {
    property: "translate",
    supportedValues: "string or SizeVariable",
    example: "10px, 20px",
  },
  {
    property: "unicode-bidi",
    supportedValues: "string",
    example: "bidi-override",
  },
  {
    property: "vector-effect",
    supportedValues: "string",
    example: "non-scaling-stroke",
  },
  {
    property: "vertical-align",
    supportedValues: "string",
    example: "middle",
  },
  {
    property: "visibility",
    supportedValues: "string",
    example: "hidden",
  },
  {
    property: "white-space",
    supportedValues: "string",
    example: "nowrap",
  },
  {
    property: "width",
    supportedValues: "string or SizeVariable",
    example: "50%",
  },
  {
    property: "will-change",
    supportedValues: "string",
    example: "transform",
  },
  {
    property: "word-break",
    supportedValues: "string",
    example: "break-word",
  },
  {
    property: "word-spacing",
    supportedValues: "string or SizeVariable",
    example: "5px",
  },
  {
    property: "writing-mode",
    supportedValues: "string",
    example: "vertical-rl",
  },
  {
    property: "x",
    supportedValues: "string or SizeVariable",
    example: "5px",
  },
  {
    property: "y",
    supportedValues: "string or SizeVariable",
    example: "10px",
  },
  {
    property: "z-index",
    supportedValues: "string",
    example: "10",
  },
  {
    property: "-webkit-line-clamp",
    supportedValues: "string",
    example: "3",
  },
  {
    property: "-webkit-text-fill-color",
    supportedValues: "string or ColorVariable",
    example: "#FF5722",
  },
  {
    property: "-webkit-text-stroke-color",
    supportedValues: "string or ColorVariable",
    example: "#4CAF50",
  },
  {
    property: "-webkit-text-stroke-width",
    supportedValues: "string or SizeVariable",
    example: "1px",
  },
];

```