@autobest-ui/agent 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +69 -10
- package/mcp/figma-mcp-bridge/LICENSE +21 -0
- package/mcp/figma-mcp-bridge/README.md +25 -0
- package/mcp/figma-mcp-bridge/config.toml.example +10 -0
- package/mcp/figma-mcp-bridge/index.test.js +47 -0
- package/mcp/figma-mcp-bridge/package.json +16 -0
- package/mcp/figma-mcp-bridge/skills/figma-bridge/SKILL.md +98 -0
- package/mcp/figma-mcp-bridge/src/index.js +68 -0
- package/mcp/figma-mcp-bridge/src/server.js +159 -0
- package/mcp/figma-mcp-bridge/src/tools/context.js +75 -0
- package/mcp/figma-mcp-bridge/src/tools/index.js +1727 -0
- package/mcp/figma-mcp-bridge/src/tools/mutations.js +4423 -0
- package/mcp/figma-mcp-bridge/src/tools/nodes.js +78 -0
- package/mcp/figma-mcp-bridge/src/tools/pages.js +55 -0
- package/mcp/figma-mcp-bridge/src/websocket.js +255 -0
- package/mcp/rag-mcp-bridge/README.md +24 -12
- package/package.json +9 -4
- package/plugins/figma-plugin/LICENSE +21 -0
- package/plugins/figma-plugin/README.md +25 -0
- package/plugins/figma-plugin/code.js +6608 -0
- package/plugins/figma-plugin/manifest.json +32 -0
- package/plugins/figma-plugin/scripts/setup.mjs +72 -0
- package/plugins/figma-plugin/scripts/setup.test.mjs +45 -0
- package/plugins/figma-plugin/ui.html +235 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* figma_get_nodes tool
|
|
3
|
+
* Returns detailed information about specific nodes by their IDs
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export const nodesTool = {
|
|
7
|
+
name: 'figma_get_nodes',
|
|
8
|
+
description: 'Get detailed information about specific Figma nodes by their IDs. Returns node properties including type, position, size, fills, strokes, auto-layout (including layoutWrap and counterAxisSpacing), clipsContent, node-level boundVariables, explicitVariableModes, and more.',
|
|
9
|
+
inputSchema: {
|
|
10
|
+
type: 'object',
|
|
11
|
+
properties: {
|
|
12
|
+
nodeIds: {
|
|
13
|
+
type: 'array',
|
|
14
|
+
items: { type: 'string' },
|
|
15
|
+
description: 'Array of Figma node IDs (e.g., ["1:23", "4:56"])'
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
required: ['nodeIds']
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export async function handleGetNodes(bridge, args) {
|
|
23
|
+
if (!bridge.isConnected()) {
|
|
24
|
+
return {
|
|
25
|
+
content: [{
|
|
26
|
+
type: 'text',
|
|
27
|
+
text: JSON.stringify({
|
|
28
|
+
error: {
|
|
29
|
+
code: 'NOT_CONNECTED',
|
|
30
|
+
message: 'Figma plugin is not connected. Please open Figma and run the Autobest Figma Plugin.'
|
|
31
|
+
}
|
|
32
|
+
}, null, 2)
|
|
33
|
+
}],
|
|
34
|
+
isError: true
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const { nodeIds, depth } = args;
|
|
39
|
+
|
|
40
|
+
if (!nodeIds || !Array.isArray(nodeIds) || nodeIds.length === 0) {
|
|
41
|
+
return {
|
|
42
|
+
content: [{
|
|
43
|
+
type: 'text',
|
|
44
|
+
text: JSON.stringify({
|
|
45
|
+
error: {
|
|
46
|
+
code: 'INVALID_PARAMS',
|
|
47
|
+
message: 'nodeIds must be a non-empty array of node IDs'
|
|
48
|
+
}
|
|
49
|
+
}, null, 2)
|
|
50
|
+
}],
|
|
51
|
+
isError: true
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
const result = await bridge.sendCommand('get_nodes', { nodeIds, depth });
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
content: [{
|
|
60
|
+
type: 'text',
|
|
61
|
+
text: JSON.stringify(result, null, 2)
|
|
62
|
+
}]
|
|
63
|
+
};
|
|
64
|
+
} catch (error) {
|
|
65
|
+
return {
|
|
66
|
+
content: [{
|
|
67
|
+
type: 'text',
|
|
68
|
+
text: JSON.stringify({
|
|
69
|
+
error: {
|
|
70
|
+
code: error.code || 'UNKNOWN_ERROR',
|
|
71
|
+
message: error.message
|
|
72
|
+
}
|
|
73
|
+
}, null, 2)
|
|
74
|
+
}],
|
|
75
|
+
isError: true
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* figma_list_pages tool
|
|
3
|
+
* Returns all pages in the current Figma document
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export const pagesTool = {
|
|
7
|
+
name: 'figma_list_pages',
|
|
8
|
+
description: 'List all pages in the current Figma document. Returns page IDs, names, and indicates which page is currently active.',
|
|
9
|
+
inputSchema: {
|
|
10
|
+
type: 'object',
|
|
11
|
+
properties: {},
|
|
12
|
+
required: []
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export async function handleListPages(bridge) {
|
|
17
|
+
if (!bridge.isConnected()) {
|
|
18
|
+
return {
|
|
19
|
+
content: [{
|
|
20
|
+
type: 'text',
|
|
21
|
+
text: JSON.stringify({
|
|
22
|
+
error: {
|
|
23
|
+
code: 'NOT_CONNECTED',
|
|
24
|
+
message: 'Figma plugin is not connected. Please open Figma and run the Autobest Figma Plugin.'
|
|
25
|
+
}
|
|
26
|
+
}, null, 2)
|
|
27
|
+
}],
|
|
28
|
+
isError: true
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
const result = await bridge.sendCommand('list_pages', {});
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
content: [{
|
|
37
|
+
type: 'text',
|
|
38
|
+
text: JSON.stringify(result, null, 2)
|
|
39
|
+
}]
|
|
40
|
+
};
|
|
41
|
+
} catch (error) {
|
|
42
|
+
return {
|
|
43
|
+
content: [{
|
|
44
|
+
type: 'text',
|
|
45
|
+
text: JSON.stringify({
|
|
46
|
+
error: {
|
|
47
|
+
code: error.code || 'UNKNOWN_ERROR',
|
|
48
|
+
message: error.message
|
|
49
|
+
}
|
|
50
|
+
}, null, 2)
|
|
51
|
+
}],
|
|
52
|
+
isError: true
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import { EventEmitter } from 'events';
|
|
2
|
+
import { WebSocketServer } from 'ws';
|
|
3
|
+
|
|
4
|
+
const DEFAULT_PORT = 3055;
|
|
5
|
+
const REQUEST_TIMEOUT = 30000; // 30 seconds
|
|
6
|
+
const HEARTBEAT_INTERVAL = 30000; // 30 seconds
|
|
7
|
+
|
|
8
|
+
export class FigmaBridge extends EventEmitter {
|
|
9
|
+
constructor(port = DEFAULT_PORT) {
|
|
10
|
+
super();
|
|
11
|
+
this.port = port;
|
|
12
|
+
this.wss = null;
|
|
13
|
+
this.connection = null;
|
|
14
|
+
this.connectionState = 'disconnected';
|
|
15
|
+
this.documentInfo = null;
|
|
16
|
+
this.pendingRequests = new Map();
|
|
17
|
+
this.requestIdCounter = 0;
|
|
18
|
+
this.heartbeatInterval = null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async start() {
|
|
22
|
+
const maxPort = DEFAULT_PORT + 15;
|
|
23
|
+
|
|
24
|
+
const tryPort = port => {
|
|
25
|
+
return new Promise((resolve, reject) => {
|
|
26
|
+
const wss = new WebSocketServer({ port });
|
|
27
|
+
|
|
28
|
+
wss.on('listening', () => {
|
|
29
|
+
// eslint-disable-next-line no-console
|
|
30
|
+
console.error(`[FigmaBridge] WebSocket server listening on port ${port}`);
|
|
31
|
+
this.wss = wss;
|
|
32
|
+
this.port = port;
|
|
33
|
+
resolve();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
wss.on('error', error => {
|
|
37
|
+
if (error.code === 'EADDRINUSE') {
|
|
38
|
+
// eslint-disable-next-line no-console
|
|
39
|
+
console.error(`[FigmaBridge] Port ${port} in use, trying ${port + 1}`);
|
|
40
|
+
wss.close();
|
|
41
|
+
if (port < maxPort) {
|
|
42
|
+
tryPort(port + 1).then(resolve, reject);
|
|
43
|
+
} else {
|
|
44
|
+
reject(new Error(`Could not find available port (tried ${DEFAULT_PORT}-${maxPort})`));
|
|
45
|
+
}
|
|
46
|
+
} else {
|
|
47
|
+
reject(error);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
wss.on('connection', ws => this._handleConnection(ws));
|
|
52
|
+
});
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
return tryPort(this.port);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
_handleConnection(ws) {
|
|
59
|
+
// eslint-disable-next-line no-console
|
|
60
|
+
console.error('[FigmaBridge] Plugin connected');
|
|
61
|
+
|
|
62
|
+
// Replace existing connection if any
|
|
63
|
+
if (this.connection) {
|
|
64
|
+
// eslint-disable-next-line no-console
|
|
65
|
+
console.error('[FigmaBridge] Replacing existing connection');
|
|
66
|
+
this.connection.close(1000, 'New connection');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
this.connection = ws;
|
|
70
|
+
this.connectionState = 'connecting';
|
|
71
|
+
|
|
72
|
+
ws.on('message', data => this._handleMessage(data));
|
|
73
|
+
ws.on('close', () => this._handleClose());
|
|
74
|
+
ws.on('error', error => this._handleError(error));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
_handleMessage(data) {
|
|
78
|
+
let message;
|
|
79
|
+
try {
|
|
80
|
+
message = JSON.parse(data.toString());
|
|
81
|
+
} catch (error) {
|
|
82
|
+
// eslint-disable-next-line no-console
|
|
83
|
+
console.error('[FigmaBridge] Failed to parse message:', error);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Handle handshake from plugin
|
|
88
|
+
if (message.type === 'handshake') {
|
|
89
|
+
this.documentInfo = message.payload;
|
|
90
|
+
this.connectionState = 'connected';
|
|
91
|
+
// eslint-disable-next-line no-console
|
|
92
|
+
console.error(`[FigmaBridge] Handshake complete: ${message.payload.fileName}`);
|
|
93
|
+
|
|
94
|
+
// Send handshake acknowledgment
|
|
95
|
+
this.connection.send(
|
|
96
|
+
JSON.stringify({
|
|
97
|
+
type: 'handshake_ack',
|
|
98
|
+
payload: {
|
|
99
|
+
serverVersion: '0.1.0',
|
|
100
|
+
sessionId: `sess_${Date.now()}`
|
|
101
|
+
}
|
|
102
|
+
})
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
this._startHeartbeat();
|
|
106
|
+
this.emit('connected', this.documentInfo);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Handle pong (heartbeat response)
|
|
111
|
+
if (message.type === 'pong') {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Handle command response
|
|
116
|
+
if (message.responseTo) {
|
|
117
|
+
const pending = this.pendingRequests.get(message.responseTo);
|
|
118
|
+
if (pending) {
|
|
119
|
+
this.pendingRequests.delete(message.responseTo);
|
|
120
|
+
clearTimeout(pending.timeout);
|
|
121
|
+
|
|
122
|
+
if (message.payload?.error) {
|
|
123
|
+
pending.reject(
|
|
124
|
+
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
|
125
|
+
new BridgeError(
|
|
126
|
+
message.payload.error.code || 'UNKNOWN_ERROR',
|
|
127
|
+
message.payload.error.message || 'Unknown error',
|
|
128
|
+
message.payload.error.details
|
|
129
|
+
)
|
|
130
|
+
);
|
|
131
|
+
} else {
|
|
132
|
+
pending.resolve(message.payload);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
_handleClose() {
|
|
139
|
+
// eslint-disable-next-line no-console
|
|
140
|
+
console.error('[FigmaBridge] Connection closed');
|
|
141
|
+
this.connectionState = 'disconnected';
|
|
142
|
+
this.connection = null;
|
|
143
|
+
this.documentInfo = null;
|
|
144
|
+
this._stopHeartbeat();
|
|
145
|
+
|
|
146
|
+
// Reject all pending requests
|
|
147
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
148
|
+
for (const [id, pending] of this.pendingRequests) {
|
|
149
|
+
clearTimeout(pending.timeout);
|
|
150
|
+
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
|
151
|
+
pending.reject(new BridgeError('CONNECTION_CLOSED', 'Connection closed'));
|
|
152
|
+
}
|
|
153
|
+
this.pendingRequests.clear();
|
|
154
|
+
|
|
155
|
+
this.emit('disconnected');
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
_handleError(error) {
|
|
159
|
+
// eslint-disable-next-line no-console
|
|
160
|
+
console.error('[FigmaBridge] WebSocket error:', error.message);
|
|
161
|
+
this.emit('error', error);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
_startHeartbeat() {
|
|
165
|
+
this._stopHeartbeat();
|
|
166
|
+
this.heartbeatInterval = setInterval(() => {
|
|
167
|
+
if (this.connection && this.connectionState === 'connected') {
|
|
168
|
+
this.connection.send(
|
|
169
|
+
JSON.stringify({
|
|
170
|
+
type: 'ping',
|
|
171
|
+
timestamp: Date.now()
|
|
172
|
+
})
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
}, HEARTBEAT_INTERVAL);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
_stopHeartbeat() {
|
|
179
|
+
if (this.heartbeatInterval) {
|
|
180
|
+
clearInterval(this.heartbeatInterval);
|
|
181
|
+
this.heartbeatInterval = null;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Send a command to the Figma plugin and wait for response
|
|
187
|
+
* @param {string} command - Command name
|
|
188
|
+
* @param {object} payload - Command payload
|
|
189
|
+
* @returns {Promise<object>} Response payload
|
|
190
|
+
*/
|
|
191
|
+
async sendCommand(command, payload = {}) {
|
|
192
|
+
if (this.connectionState !== 'connected') {
|
|
193
|
+
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
|
194
|
+
throw new BridgeError(
|
|
195
|
+
'NOT_CONNECTED',
|
|
196
|
+
'Figma plugin is not connected. Please open Figma and run the Autobest Figma Plugin.'
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const requestId = `req_${++this.requestIdCounter}`;
|
|
201
|
+
|
|
202
|
+
return new Promise((resolve, reject) => {
|
|
203
|
+
const timeout = setTimeout(() => {
|
|
204
|
+
this.pendingRequests.delete(requestId);
|
|
205
|
+
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
|
206
|
+
reject(new BridgeError('TIMEOUT', `Command "${command}" timed out after ${REQUEST_TIMEOUT}ms`));
|
|
207
|
+
}, REQUEST_TIMEOUT);
|
|
208
|
+
|
|
209
|
+
this.pendingRequests.set(requestId, { resolve, reject, timeout });
|
|
210
|
+
|
|
211
|
+
this.connection.send(
|
|
212
|
+
JSON.stringify({
|
|
213
|
+
requestId,
|
|
214
|
+
command,
|
|
215
|
+
payload
|
|
216
|
+
})
|
|
217
|
+
);
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
isConnected() {
|
|
222
|
+
return this.connectionState === 'connected';
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
getDocumentInfo() {
|
|
226
|
+
return this.documentInfo;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
async stop() {
|
|
230
|
+
this._stopHeartbeat();
|
|
231
|
+
|
|
232
|
+
if (this.connection) {
|
|
233
|
+
this.connection.close(1000, 'Server shutdown');
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (this.wss) {
|
|
237
|
+
return new Promise(resolve => {
|
|
238
|
+
this.wss.close(() => {
|
|
239
|
+
// eslint-disable-next-line no-console
|
|
240
|
+
console.error('[FigmaBridge] WebSocket server stopped');
|
|
241
|
+
resolve();
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
export class BridgeError extends Error {
|
|
249
|
+
constructor(code, message, details = null) {
|
|
250
|
+
super(message);
|
|
251
|
+
this.name = 'BridgeError';
|
|
252
|
+
this.code = code;
|
|
253
|
+
this.details = details;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
@@ -4,30 +4,42 @@
|
|
|
4
4
|
|
|
5
5
|
## 启动
|
|
6
6
|
|
|
7
|
-
先启动 PRD Knowledge HTTP API
|
|
7
|
+
先启动 PRD Knowledge HTTP API。直接在终端验证时,必须在同一条命令中提供 API 根地址:
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
|
|
10
|
+
RAG_API_BASE_URL="http://127.0.0.1:3000/api/knowledge" \
|
|
11
|
+
npx --yes \
|
|
12
|
+
--package=@autobest-ui/agent@latest \
|
|
13
|
+
autobest-rag-mcp
|
|
11
14
|
```
|
|
12
15
|
|
|
13
|
-
`@latest` 可以替换为明确版本,例如 `@1.0.0
|
|
16
|
+
`@latest` 可以替换为明确版本,例如 `@1.0.0`。启动成功后,服务会通过 stdio 持续等待 MCP 客户端请求,并且不会向标准输出写日志;终端看起来没有响应属于正常状态,可按 `Ctrl+C` 停止。
|
|
14
17
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
| 变量 | 默认值 | 说明 |
|
|
18
|
-
| --- | --- | --- |
|
|
19
|
-
| `RAG_API_BASE_URL` | 无,必填 | PRD Knowledge HTTP API 根地址,必须通过 `config.toml` 的 `[mcp_servers.rag-mcp-bridge.env]` 配置 |
|
|
20
|
-
| `RAG_MCP_HTTP_TIMEOUT_MS` | `120000` | 单次 HTTP 请求超时,单位为毫秒 |
|
|
21
|
-
|
|
22
|
-
示例:
|
|
18
|
+
供 Codex 长期使用时,将下面的完整配置加入 `~/.codex/config.toml`,然后重启 Codex:
|
|
23
19
|
|
|
24
20
|
```toml
|
|
21
|
+
[mcp_servers.rag-mcp-bridge]
|
|
22
|
+
command = "npx"
|
|
23
|
+
args = ["--yes", "--package=@autobest-ui/agent@latest", "autobest-rag-mcp"]
|
|
24
|
+
startup_timeout_sec = 30
|
|
25
|
+
tool_timeout_sec = 120
|
|
26
|
+
enabled = true
|
|
27
|
+
|
|
25
28
|
[mcp_servers.rag-mcp-bridge.env]
|
|
26
29
|
RAG_API_BASE_URL = "http://127.0.0.1:3000/api/knowledge"
|
|
27
30
|
RAG_MCP_HTTP_TIMEOUT_MS = "120000"
|
|
28
31
|
```
|
|
29
32
|
|
|
30
|
-
|
|
33
|
+
完整配置文件也可直接参考 [config.toml.example](config.toml.example)。
|
|
34
|
+
|
|
35
|
+
## 环境变量
|
|
36
|
+
|
|
37
|
+
| 变量 | 默认值 | 说明 |
|
|
38
|
+
| --- | --- | --- |
|
|
39
|
+
| `RAG_API_BASE_URL` | 无,必填 | PRD Knowledge HTTP API 根地址,必须通过 `config.toml` 的 `[mcp_servers.rag-mcp-bridge.env]` 配置 |
|
|
40
|
+
| `RAG_MCP_HTTP_TIMEOUT_MS` | `120000` | 单次 HTTP 请求超时,单位为毫秒 |
|
|
41
|
+
|
|
42
|
+
`RAG_API_BASE_URL` 仅在 MCP 客户端配置中提供,服务代码不包含默认 API 地址。
|
|
31
43
|
|
|
32
44
|
## 工具
|
|
33
45
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@autobest-ui/agent",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Autobest Agent skills/plugins/mcp assets + sync cli",
|
|
6
6
|
"files": [
|
|
@@ -19,22 +19,27 @@
|
|
|
19
19
|
"autobest-agent-sync": "./bin/sync-assets.mjs",
|
|
20
20
|
"autobest-azurepr-mcp": "./mcp/azurepr-mcp-bridge/index.js",
|
|
21
21
|
"autobest-delivery-setup": "./plugins/autobest-delivery/scripts/setup.mjs",
|
|
22
|
-
"autobest-rag-mcp": "./mcp/rag-mcp-bridge/index.js"
|
|
22
|
+
"autobest-rag-mcp": "./mcp/rag-mcp-bridge/index.js",
|
|
23
|
+
"figma-mcp-bridge": "./mcp/figma-mcp-bridge/src/index.js",
|
|
24
|
+
"figma-plugin": "./plugins/figma-plugin/scripts/setup.mjs"
|
|
23
25
|
},
|
|
24
26
|
"scripts": {
|
|
25
27
|
"mcp:azurepr": "node ./mcp/azurepr-mcp-bridge/index.js",
|
|
28
|
+
"mcp:figma": "node ./mcp/figma-mcp-bridge/src/index.js",
|
|
26
29
|
"mcp:rag": "node ./mcp/rag-mcp-bridge/index.js",
|
|
27
30
|
"test": "npm run test:skills && npm run test:mcp && npm run test:plugin",
|
|
28
31
|
"test:skills": "node --test ./bin/sync-assets.test.mjs",
|
|
29
32
|
"test:mcp:azurepr": "node --test ./mcp/azurepr-mcp-bridge/index.test.js",
|
|
33
|
+
"test:mcp:figma": "node --test ./mcp/figma-mcp-bridge/index.test.js",
|
|
30
34
|
"test:mcp:rag": "node --test ./mcp/rag-mcp-bridge/index.test.js",
|
|
31
|
-
"test:mcp": "npm run test:mcp:azurepr && npm run test:mcp:rag",
|
|
32
|
-
"test:plugin": "node --test ./plugins/autobest-delivery/scripts/setup.test.mjs"
|
|
35
|
+
"test:mcp": "npm run test:mcp:azurepr && npm run test:mcp:figma && npm run test:mcp:rag",
|
|
36
|
+
"test:plugin": "node --test ./plugins/autobest-delivery/scripts/setup.test.mjs ./plugins/figma-plugin/scripts/setup.test.mjs"
|
|
33
37
|
},
|
|
34
38
|
"dependencies": {
|
|
35
39
|
"@modelcontextprotocol/sdk": "1.30.0",
|
|
36
40
|
"diff": "9.0.0",
|
|
37
41
|
"fs-extra": "11.3.0",
|
|
42
|
+
"ws": "8.18.3",
|
|
38
43
|
"zod": "4.4.3"
|
|
39
44
|
},
|
|
40
45
|
"publishConfig": {
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Magic Spells
|
|
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.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# figma-plugin
|
|
2
|
+
|
|
3
|
+
`figma-plugin` 是与 `figma-mcp-bridge` 配套的 Figma Development Plugin。它在 Figma 内运行,并通过本机 WebSocket 与 MCP 通信。
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx --yes --package=@autobest-ui/agent@latest figma-plugin
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
安装器会将插件文件同步到稳定目录:
|
|
12
|
+
|
|
13
|
+
```text
|
|
14
|
+
~/.autobest-agent/figma-plugins/figma-plugin
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
随后在 Figma 中执行 **Plugins -> Development -> Import plugin from manifest**,选择命令输出路径中的 `manifest.json`。再次执行安装命令会更新同一目录,无需重新导入。
|
|
18
|
+
|
|
19
|
+
卸载本地文件:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npx --yes --package=@autobest-ui/agent@latest figma-plugin uninstall
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
卸载后,Figma Development Plugins 列表中的记录需要在 Figma 内移除。
|