framer-mcp

framer-mcp

Enables programmatic interaction with Framer canvas via a local MCP server and companion plugin, allowing automation of page generation, content updates, and component manipulation.

Category
访问服务器

README

Framer Mission Control Protocol (MCP) - Local MVP

Project Overview

This project combines a local MCP server with a companion Framer plugin so developers and external clients can interact with the Framer canvas programmatically. By exposing a secure Tool API, Framer users can automate page generation, update content, and manipulate components directly from scripts or other services.

This MVP is built with Node.js and targets local development and testing.


Architecture

The system consists of three main components:

  1. Framer Plugin:

    • A React-based plugin that runs directly inside the Framer application.
    • It is responsible for executing commands using Framer's official Plugin API.
    • It establishes and maintains a secure WebSocket connection to the MCP Server.
  2. MCP Server:

    • A Node.js server built with Express and WebSocket support.
    • It acts as a secure bridge between API clients (CLI tools, services) and the Framer Plugin.
    • It exposes a REST API (/v1/invoke) for sending commands and manages a per-user request queue to ensure operations are executed sequentially.
  3. Shared Logic:

    • A shared directory contains common code for both the server and plugin, including type definitions, message protocols, and Zod schemas for validation, ensuring consistency across the system.

Data Flow

  1. The user opens the plugin in Framer.
  2. The plugin requests a unique, hashed User ID from the server.
  3. The plugin generates a secret and establishes a WebSocket connection to the server using the User ID and secret.
  4. The plugin UI displays the full MCP URL, including the User ID and secret, for the user to copy.
  5. An API client makes a POST request to the server's /v1/invoke endpoint with the URL, a tool name, and parameters.
  6. The server authenticates the request, validates the parameters, and forwards the command to the correct plugin over the WebSocket connection.
  7. The plugin executes the command using the Framer API and sends the result back to the server.
  8. The server relays the result back to the API client as the HTTP response.

Getting Started

Prerequisites

Steps

  1. Clone the repository:

    git clone https://github.com/tmcpro/framer-mcp
    cd framer-mcp
    
  2. Install dependencies:

    npm install
    
  3. Run the plugin:

    npm run dev --prefix plugin
    
  4. Run the server:

    npm run dev --prefix server
    

    The server starts on http://localhost:3000 and the plugin dev server prints an install URL.

    To run both simultaneously, use npm run dev from the project root.

Auth Modes

The server supports configurable auth for local vs. cloud environments via AUTH_PROVIDER:

  • legacy (default): requires ?id=<USER_ID>&secret=<SECRET> on requests and the plugin WS query. Compatible with existing plugin flows.
  • none: disables server-side auth for local development. Protect with your local network boundary.
  • cf-access: assumes Cloudflare Access is enforcing auth in front of the server. No custom auth logic runs in the app.

Set in server/.env:

AUTH_PROVIDER=none            # local dev
# AUTH_PROVIDER=cf-access     # when deployed behind Cloudflare Access

Configuration

Environment variables control both the plugin and server. The server uses dotenv to load settings from a local .env file (server/.env) during development. For production, configure variables via your hosting platform's secret manager and never commit .env files. Rotate SERVER_SALT regularly in deployed environments.

Plugin

Variable Purpose Default Security Notes
VITE_SERVER_BASE_URL Base URL of the MCP server that the plugin connects to. http://localhost:3000 Exposed in browser; do not put secrets here.
  • Development: create plugin/.env with VITE_SERVER_BASE_URL=http://localhost:3000 to match the default server started by npm run dev.
  • Production: set VITE_SERVER_BASE_URL to your deployed server's URL before building the plugin. For example:
    VITE_SERVER_BASE_URL=https://mcp.example.com npm run build --prefix plugin
    

Server

Variable Purpose Default Security Notes
PORT Port for the HTTP server to listen on. 3000 Non-sensitive.
REQUEST_TIMEOUT_MS Time to wait for plugin responses before timing out. 5000 Non-sensitive.
QUEUE_MAX Maximum number of pending requests allowed per user. 16 Non-sensitive.
SERVER_SALT Salt used to hash user IDs. dev-salt-change-me-in-prod Treat as secret; rotate in production.
IDEMPOTENCY_TTL_MS Duration to cache responses for idempotency. 600000 (10 minutes) Non-sensitive.
NODE_ENV Node.js environment mode. development Set to production in deployments; avoid test.

Create server/.env to override these defaults. For example:

PORT=4000
SERVER_SALT=prod-secret-salt

Install the plugin in Framer:

-   Open the printed Vite URL in your browser and follow the prompt to add the plugin to Framer.
-   In your Framer project, open the plugin from the **Plugins** panel. The small UI should appear in the bottom-right corner.

Verify the connection

-   When the plugin successfully connects to the server, the UI shows `Status: online`.
-   In legacy mode, the UI may display a URL containing a user ID and secret for convenience. In non-legacy modes, you can call the API directly without query parameters.

Send a test request

Confirm everything is working with one of the following, depending on auth mode:
- Legacy:
  ```bash
  curl -X POST "http://localhost:3000/v1/invoke?id=<USER_ID>&secret=<SECRET>" \
    -H 'Content-Type: application/json' \
    -d '{"tool":"project.getInfo","params":{}}'
  ```
- None / Cloudflare Access:
  ```bash
  curl -X POST "http://localhost:3000/v1/invoke" \
    -H 'Content-Type: application/json' \
    -d '{"tool":"project.getInfo","params":{}}'
  ```
A JSON response with project details means the server and plugin are communicating correctly.

Running Tests

This project includes a test suite for the server to ensure its core logic is working correctly.

To run the tests, use the following command:

npm test --prefix server

This will execute all Jest tests located in the server/src/__tests__ directory.


API Reference

The MCP API exposes a single POST /v1/invoke?id=<USER_ID>&secret=<SECRET> endpoint used to run tools in a connected Framer project.

Core capabilities include:

  • Creating, updating, and removing nodes on the canvas.
  • Managing selection and interacting with the editor UI.
  • Inserting components and working with project files and code.

For the formal specification, see the OpenAPI spec.
Additional REST and WebSocket details live in docs/endpoints.md.

curl -X POST "<MCP_URL>/v1/invoke?id=USER_ID&secret=SECRET" -H 'Content-Type: application/json' -d '{"tool":"project.getInfo","params":{}}'

Tool Manifest

GET /v1/tools returns metadata for every available tool. The response begins with a version string that increments whenever tool schemas change so clients know when to refresh their cached manifest.

Each tool object contains:

  • name – unique identifier for the tool.

    "name": "nodes.createFrame"
    
  • description – natural-language summary of the tool.

    "description": "Creates a new frame node."
    
  • paramsSchema – JSON Schema describing required parameters. This structure helps clients construct valid requests.

    "paramsSchema": {
      "type": "object",
      "properties": {
        "attrs": {
          "type": "object",
          "properties": { "name": { "type": "string" } }
        }
      }
    }
    
  • resultSchema – JSON Schema describing the result shape.

    "resultSchema": {
      "type": "object",
      "properties": {
        "node": {
          "type": "object",
          "properties": {
            "id": { "type": "string" },
            "type": { "type": "string" }
          }
        }
      }
    }
    
  • examples – Array of sample {params, result} pairs showing real usage.

    "examples": [
      {
        "params": { "attrs": { "name": "Landing" } },
        "result": { "node": { "id": "123", "type": "FrameNode" } }
      }
    ]
    
  • errors – Array of objects describing possible failures.

    "errors": [
      { "code": "BAD_REQUEST", "message": "Invalid parameters" },
      { "code": "INTERNAL", "message": "Unexpected server error" }
    ]
    

Schemas and examples provide explicit contracts for generating requests and interpreting responses.

Tool Surface

All API calls are made via POST /v1/invoke?id=<USER_ID>&secret=<SECRET>. The body of the request is a JSON object with tool and params keys.

Nodes

  • nodes.createFrame: Creates a new Frame.
    • params: { parentId?: string, attrs?: NodeAttributes }
  • nodes.addText: Adds a new Text layer.
    • params: { parentId?: string, text: string, attrs?: NodeAttributes }
  • nodes.setText: Updates the content of a Text layer.
    • params: { nodeId: string, text: string }
  • nodes.addImageFromUrl: Fetches an image from a URL and adds it to the canvas.
    • params: { parentId?: string, url: string, attrs?: NodeAttributes, fit?: 'cover'|'contain'|'fill' }
  • nodes.addSvg: Adds an SVG as a VectorNode.
    • params: { parentId?: string, svg: string, attrs?: NodeAttributes }
  • nodes.setAttributes: Applies a set of attributes to any node.
    • params: { nodeId: string, attrs: NodeAttributes }
  • nodes.getNode: Retrieves the properties of a single node.
    • params: { nodeId: string }
  • nodes.remove: Deletes a node.
    • params: { nodeId: string }
  • nodes.duplicate: Duplicates a node.
    • params: { nodeId: string, parentId?: string }

Selection

  • selection.get: Gets the currently selected node IDs.
    • params: {}
  • selection.set: Sets the current selection.
    • params: { nodeIds: string[] }

Editor

  • editor.zoomIntoView: Zooms the editor viewport to fit a specific node.
    • params: { nodeId: string }
  • editor.notify: Shows a notification toast in the Framer UI.
    • params: { message: string, durationMs?: number }

Components

  • components.list: Lists all available components in the project.
    • params: {}
  • components.insertByUrl: Inserts a component using its import URL.
    • params: { insertUrl: string, parentId?: string, attrs?: NodeAttributes, props?: object }
  • components.insertByName: Inserts a component by its name.
    • params: { name: string, parentId?: string, attrs?: NodeAttributes, props?: object }

Project & Code

  • project.getInfo: Retrieves the current project's ID and name.
    • params: {}
  • code.createFile: Creates a new code file (e.g., a React component).
    • params: { path: string, template?: 'component'|'override'|'empty' }
  • code.readFile: Reads the content of a code file.
    • params: { fileId?: string, path?: string }
  • code.updateFile: Updates the content of a code file.
    • params: { fileId?: string, path?: string, code: string }

Project Structure

framer-mcp/
├─ .gitignore
├─ package.json              # Root dependencies and scripts
├─ README.md                 # This file
├─ shared/                   # Shared types, schemas, and protocols
│  ├─ protocol.ts
│  ├─ schemas.ts
│  └─ tools.ts
├─ server/                   # The Node.js MCP Server
│  ├─ package.json
│  ├─ jest.config.js
│  ├─ tsconfig.json
│  └─ src/
│     ├─ __tests__/          # Server-side tests
│     ├─ index.ts            # Express app setup and server entrypoint
│     ├─ router.invoke.ts    # Handles the main /invoke endpoint
│     └─ ws.hub.ts           # Manages WebSocket connections
└─ plugin/                   # The Framer Plugin
   ├─ package.json
   ├─ vite.config.ts
   ├─ tsconfig.json
   └─ src/
      ├─ main.tsx            # Plugin entrypoint, connects to server
      ├─ ui/App.tsx          # The React component for the plugin UI
      └─ handlers/           # Logic for executing Tool API commands

Local Usage Examples

  • Start plugin + server: npm run dev
  • With AUTH_PROVIDER=none, you can invoke without id/secret once the plugin shows “online”:
curl -X POST "http://localhost:3000/v1/invoke" \
  -H 'Content-Type: application/json' \
  -d '{"tool":"project.getInfo","params":{}}'
  • JSON-RPC (MCP-style) over HTTP (useful for adapters):
curl -X POST "http://localhost:3000/v1/mcp" \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":"1","method":"tool/list","params":{}}'

Cloudflare Agents (Remote MCP)

Recommended for production: deploy behind Cloudflare and use Cloudflare Access instead of custom auth.

  • Protect the origin with Cloudflare Access (Application type: Web → include WS). Set AUTH_PROVIDER=cf-access on the server.
  • Point the plugin VITE_SERVER_BASE_URL to your Access-protected origin.
  • For Cloudflare Agents: use the Remote MCP Server guide to register an MCP server that proxies to this API so external agents can call tools. Implement a small Worker that translates MCP JSON-RPC methods (tool/list, tool_code/invoke) to this server’s REST endpoints.

Local dev remains unchanged (AUTH_PROVIDER=none).

Configuration Summary

  • Server: see server/.env.example for all supported settings. Copy to server/.env and adjust as needed.
  • Plugin: copy plugin/.env.example to plugin/.env and set VITE_SERVER_BASE_URL.
  • Typical local setup: AUTH_PROVIDER=none, VITE_SERVER_BASE_URL=http://localhost:3000.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选