Skip to content

Commit f428e28

Browse files
Merge pull request #14 from cursor/add-figma-plugin
Add Figma MCP plugin
2 parents 3fea6c1 + 4a91a6e commit f428e28

File tree

9 files changed

+228
-0
lines changed

9 files changed

+228
-0
lines changed

.cursor-plugin/marketplace.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,11 @@
6767
"name": "create-plugin",
6868
"source": "create-plugin",
6969
"description": "Scaffold and validate new Cursor plugins."
70+
},
71+
{
72+
"name": "figma",
73+
"source": "figma",
74+
"description": "Figma MCP integration for design and collaboration."
7075
}
7176
]
7277
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
name: Validate plugins
2+
3+
on:
4+
pull_request:
5+
paths:
6+
- ".cursor-plugin/marketplace.json"
7+
- "**/plugin.json"
8+
- "schemas/**"
9+
10+
jobs:
11+
validate:
12+
runs-on: ubuntu-latest
13+
steps:
14+
- uses: actions/checkout@v4
15+
16+
- uses: actions/setup-node@v4
17+
with:
18+
node-version: 20
19+
20+
- name: Install dependencies
21+
run: npm install --no-save ajv ajv-formats
22+
23+
- name: Validate plugin definitions
24+
run: node scripts/validate-plugins.mjs

figma/.cursor-plugin/plugin.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"name": "figma",
3+
"displayName": "Figma",
4+
"version": "1.0.0",
5+
"description": "Figma MCP integration for design and collaboration.",
6+
"author": {
7+
"name": "Cursor",
8+
"email": "plugins@cursor.com"
9+
},
10+
"homepage": "https://github.com/cursor/plugins",
11+
"repository": "https://github.com/cursor/plugins",
12+
"license": "MIT",
13+
"logo": "./icon.svg",
14+
"keywords": ["figma", "design", "mcp", "collaboration"]
15+
}

figma/LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2026 Cursor
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

figma/README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Figma plugin
2+
3+
Figma MCP integration for design and collaboration.
4+
5+
## Installation
6+
7+
```bash
8+
agent install figma
9+
```
10+
11+
## Components
12+
13+
### MCP Servers
14+
15+
| Server | Description |
16+
|:-------|:------------|
17+
| `Figma` | Design and collaboration platform for teams (SSE) |
18+
19+
## License
20+
21+
MIT

figma/icon.svg

Lines changed: 24 additions & 0 deletions
Loading

figma/mcp.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"mcpServers": {
3+
"figma": {
4+
"url": "https://mcp.figma.com/mcp"
5+
}
6+
}
7+
}

schemas/plugin.schema.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,15 @@
5757
"items": { "type": "string" },
5858
"description": "Keywords for discovery and search."
5959
},
60+
"category": {
61+
"type": "string",
62+
"description": "Plugin category for marketplace classification."
63+
},
64+
"tags": {
65+
"type": "array",
66+
"items": { "type": "string" },
67+
"description": "Tags for filtering and discovery."
68+
},
6069
"commands": {
6170
"$ref": "#/$defs/stringOrStringArray",
6271
"description": "Glob pattern(s) or path(s) to command files."

scripts/validate-plugins.mjs

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
#!/usr/bin/env node
2+
3+
import { readFileSync, existsSync } from "fs";
4+
import { resolve, dirname } from "path";
5+
import { fileURLToPath } from "url";
6+
import Ajv from "ajv";
7+
import addFormats from "ajv-formats";
8+
9+
const __dirname = dirname(fileURLToPath(import.meta.url));
10+
const root = resolve(__dirname, "..");
11+
12+
function loadJSON(path) {
13+
return JSON.parse(readFileSync(path, "utf-8"));
14+
}
15+
16+
const marketplaceSchema = loadJSON(
17+
resolve(root, "schemas/marketplace.schema.json")
18+
);
19+
const pluginSchema = loadJSON(resolve(root, "schemas/plugin.schema.json"));
20+
21+
const ajv = new Ajv({ allErrors: true });
22+
addFormats(ajv);
23+
24+
const validateMarketplace = ajv.compile(marketplaceSchema);
25+
const validatePlugin = ajv.compile(pluginSchema);
26+
27+
let errors = 0;
28+
29+
function fail(message) {
30+
console.error(`ERROR: ${message}`);
31+
errors++;
32+
}
33+
34+
// 1. Validate marketplace.json
35+
const marketplacePath = resolve(root, ".cursor-plugin/marketplace.json");
36+
37+
if (!existsSync(marketplacePath)) {
38+
fail(".cursor-plugin/marketplace.json not found");
39+
process.exit(1);
40+
}
41+
42+
const marketplace = loadJSON(marketplacePath);
43+
44+
if (!validateMarketplace(marketplace)) {
45+
fail("marketplace.json schema validation failed:");
46+
for (const err of validateMarketplace.errors) {
47+
console.error(` ${err.instancePath || "/"}: ${err.message}`);
48+
}
49+
}
50+
51+
// 2. Validate each plugin
52+
for (const entry of marketplace.plugins ?? []) {
53+
const pluginDir = resolve(root, entry.source);
54+
const pluginJsonPath = resolve(pluginDir, ".cursor-plugin/plugin.json");
55+
56+
// Check source directory exists
57+
if (!existsSync(pluginDir)) {
58+
fail(
59+
`Plugin "${entry.name}": source directory "${entry.source}" does not exist`
60+
);
61+
continue;
62+
}
63+
64+
// Check plugin.json exists
65+
if (!existsSync(pluginJsonPath)) {
66+
fail(
67+
`Plugin "${entry.name}": missing .cursor-plugin/plugin.json in "${entry.source}"`
68+
);
69+
continue;
70+
}
71+
72+
const pluginJson = loadJSON(pluginJsonPath);
73+
74+
if (!validatePlugin(pluginJson)) {
75+
fail(
76+
`Plugin "${entry.name}": plugin.json schema validation failed (${entry.source}/.cursor-plugin/plugin.json):`
77+
);
78+
for (const err of validatePlugin.errors) {
79+
const detail =
80+
err.keyword === "additionalProperties"
81+
? `${err.message}: "${err.params.additionalProperty}"`
82+
: err.message;
83+
console.error(` ${err.instancePath || "/"}: ${detail}`);
84+
}
85+
}
86+
87+
// Check that marketplace name matches plugin name
88+
if (pluginJson.name && pluginJson.name !== entry.name) {
89+
fail(
90+
`Plugin "${entry.name}": marketplace name does not match plugin.json name "${pluginJson.name}"`
91+
);
92+
}
93+
}
94+
95+
// 3. Report results
96+
if (errors > 0) {
97+
console.error(`\nValidation failed with ${errors} error(s).`);
98+
process.exit(1);
99+
} else {
100+
console.log("All plugins validated successfully.");
101+
process.exit(0);
102+
}

0 commit comments

Comments
 (0)