Merge pull request #14 from cursor/add-figma-plugin

Add Figma MCP plugin
This commit is contained in:
Eric Zakariasson
2026-02-11 12:58:12 -08:00
committed by GitHub
9 changed files with 228 additions and 0 deletions
+5
View File
@@ -67,6 +67,11 @@
"name": "create-plugin",
"source": "create-plugin",
"description": "Scaffold and validate new Cursor plugins."
},
{
"name": "figma",
"source": "figma",
"description": "Figma MCP integration for design and collaboration."
}
]
}
+24
View File
@@ -0,0 +1,24 @@
name: Validate plugins
on:
pull_request:
paths:
- ".cursor-plugin/marketplace.json"
- "**/plugin.json"
- "schemas/**"
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- name: Install dependencies
run: npm install --no-save ajv ajv-formats
- name: Validate plugin definitions
run: node scripts/validate-plugins.mjs
+15
View File
@@ -0,0 +1,15 @@
{
"name": "figma",
"displayName": "Figma",
"version": "1.0.0",
"description": "Figma MCP integration for design and collaboration.",
"author": {
"name": "Cursor",
"email": "plugins@cursor.com"
},
"homepage": "https://github.com/cursor/plugins",
"repository": "https://github.com/cursor/plugins",
"license": "MIT",
"logo": "./icon.svg",
"keywords": ["figma", "design", "mcp", "collaboration"]
}
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Cursor
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.
+21
View File
@@ -0,0 +1,21 @@
# Figma plugin
Figma MCP integration for design and collaboration.
## Installation
```bash
agent install figma
```
## Components
### MCP Servers
| Server | Description |
|:-------|:------------|
| `Figma` | Design and collaboration platform for teams (SSE) |
## License
MIT
+24
View File
@@ -0,0 +1,24 @@
<svg width="128" height="128" viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_1744_55)">
<mask id="mask0_1744_55" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="128" height="128">
<path d="M0 45.5111C0 29.5807 0 21.6155 3.10026 15.5309C5.82732 10.1788 10.1788 5.82732 15.5309 3.10026C21.6155 0 29.5807 0 45.5111 0H82.4889C98.4193 0 106.384 0 112.469 3.10026C117.821 5.82732 122.173 10.1788 124.9 15.5309C128 21.6155 128 29.5807 128 45.5111V82.4889C128 98.4193 128 106.384 124.9 112.469C122.173 117.821 117.821 122.173 112.469 124.9C106.384 128 98.4193 128 82.4889 128H45.5111C29.5807 128 21.6155 128 15.5309 124.9C10.1788 122.173 5.82732 117.821 3.10026 112.469C0 106.384 0 98.4193 0 82.4889V45.5111Z" fill="url(#paint0_linear_1744_55)"/>
</mask>
<g mask="url(#mask0_1744_55)">
<path d="M0 0H128V128H0V0Z" fill="#EDEDED"/>
</g>
<path d="M37.35 90.6502C37.35 83.2909 43.3157 77.3252 50.675 77.3252H64V90.6502C64 98.0095 58.0343 103.975 50.675 103.975C43.3157 103.975 37.35 98.0095 37.35 90.6502Z" fill="#0ACF83"/>
<path d="M64 63.9998C64 56.6408 69.9657 50.6748 77.325 50.6748C84.684 50.6748 90.65 56.6405 90.65 63.9998C90.65 71.3591 84.684 77.3248 77.325 77.3248C69.9657 77.3248 64 71.3588 64 63.9998Z" fill="#1ABCFE"/>
<path d="M37.35 63.9998C37.35 71.3591 43.3157 77.3248 50.675 77.3248H64V50.6748H50.675C43.3157 50.6748 37.35 56.6405 37.35 63.9998Z" fill="#A259FF"/>
<path d="M64 24.0249V50.6749H77.325C84.6843 50.6749 90.65 44.7092 90.65 37.3499C90.65 29.9907 84.6843 24.0249 77.325 24.0249H64Z" fill="#FF7262"/>
<path d="M37.35 37.3499C37.35 44.7092 43.3157 50.6749 50.675 50.6749H64V24.0249H50.675C43.3157 24.0249 37.35 29.9907 37.35 37.3499Z" fill="#F24E1E"/>
</g>
<defs>
<linearGradient id="paint0_linear_1744_55" x1="64" y1="0" x2="64" y2="128" gradientUnits="userSpaceOnUse">
<stop stop-color="#115BB5"/>
<stop offset="1" stop-color="#2E98E4"/>
</linearGradient>
<clipPath id="clip0_1744_55">
<rect width="128" height="128" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

+7
View File
@@ -0,0 +1,7 @@
{
"mcpServers": {
"figma": {
"url": "https://mcp.figma.com/mcp"
}
}
}
+9
View File
@@ -57,6 +57,15 @@
"items": { "type": "string" },
"description": "Keywords for discovery and search."
},
"category": {
"type": "string",
"description": "Plugin category for marketplace classification."
},
"tags": {
"type": "array",
"items": { "type": "string" },
"description": "Tags for filtering and discovery."
},
"commands": {
"$ref": "#/$defs/stringOrStringArray",
"description": "Glob pattern(s) or path(s) to command files."
+102
View File
@@ -0,0 +1,102 @@
#!/usr/bin/env node
import { readFileSync, existsSync } from "fs";
import { resolve, dirname } from "path";
import { fileURLToPath } from "url";
import Ajv from "ajv";
import addFormats from "ajv-formats";
const __dirname = dirname(fileURLToPath(import.meta.url));
const root = resolve(__dirname, "..");
function loadJSON(path) {
return JSON.parse(readFileSync(path, "utf-8"));
}
const marketplaceSchema = loadJSON(
resolve(root, "schemas/marketplace.schema.json")
);
const pluginSchema = loadJSON(resolve(root, "schemas/plugin.schema.json"));
const ajv = new Ajv({ allErrors: true });
addFormats(ajv);
const validateMarketplace = ajv.compile(marketplaceSchema);
const validatePlugin = ajv.compile(pluginSchema);
let errors = 0;
function fail(message) {
console.error(`ERROR: ${message}`);
errors++;
}
// 1. Validate marketplace.json
const marketplacePath = resolve(root, ".cursor-plugin/marketplace.json");
if (!existsSync(marketplacePath)) {
fail(".cursor-plugin/marketplace.json not found");
process.exit(1);
}
const marketplace = loadJSON(marketplacePath);
if (!validateMarketplace(marketplace)) {
fail("marketplace.json schema validation failed:");
for (const err of validateMarketplace.errors) {
console.error(` ${err.instancePath || "/"}: ${err.message}`);
}
}
// 2. Validate each plugin
for (const entry of marketplace.plugins ?? []) {
const pluginDir = resolve(root, entry.source);
const pluginJsonPath = resolve(pluginDir, ".cursor-plugin/plugin.json");
// Check source directory exists
if (!existsSync(pluginDir)) {
fail(
`Plugin "${entry.name}": source directory "${entry.source}" does not exist`
);
continue;
}
// Check plugin.json exists
if (!existsSync(pluginJsonPath)) {
fail(
`Plugin "${entry.name}": missing .cursor-plugin/plugin.json in "${entry.source}"`
);
continue;
}
const pluginJson = loadJSON(pluginJsonPath);
if (!validatePlugin(pluginJson)) {
fail(
`Plugin "${entry.name}": plugin.json schema validation failed (${entry.source}/.cursor-plugin/plugin.json):`
);
for (const err of validatePlugin.errors) {
const detail =
err.keyword === "additionalProperties"
? `${err.message}: "${err.params.additionalProperty}"`
: err.message;
console.error(` ${err.instancePath || "/"}: ${detail}`);
}
}
// Check that marketplace name matches plugin name
if (pluginJson.name && pluginJson.name !== entry.name) {
fail(
`Plugin "${entry.name}": marketplace name does not match plugin.json name "${pluginJson.name}"`
);
}
}
// 3. Report results
if (errors > 0) {
console.error(`\nValidation failed with ${errors} error(s).`);
process.exit(1);
} else {
console.log("All plugins validated successfully.");
process.exit(0);
}