@seed-design/mcp 0.0.5 → 0.0.15
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/bin/main.mjs +16904 -0
- package/bin/socket.mjs +155 -0
- package/package.json +8 -3
- package/src/bin/main.ts +52 -0
- package/src/bin/socket.ts +194 -0
- package/src/logger.ts +32 -34
- package/src/prompts.ts +27 -0
- package/src/responses.ts +90 -0
- package/src/tools.ts +436 -0
- package/src/types.ts +67 -0
- package/src/websocket.ts +248 -0
- package/bin/index.mjs +0 -4964
- package/src/bin/index.ts +0 -34
- package/src/config.ts +0 -166
- package/src/figma.ts +0 -211
- package/src/index.ts +0 -36
- package/src/save-image.ts +0 -16
- package/src/server.ts +0 -288
package/bin/socket.mjs
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Store clients by channel
|
|
3
|
+
const channels = new Map();
|
|
4
|
+
function handleConnection(ws) {
|
|
5
|
+
// Don't add to clients immediately - wait for channel join
|
|
6
|
+
console.log("New client connected");
|
|
7
|
+
// Send welcome message to the new client
|
|
8
|
+
ws.send(JSON.stringify({
|
|
9
|
+
type: "system",
|
|
10
|
+
message: "Please join a channel to start chatting"
|
|
11
|
+
}));
|
|
12
|
+
ws.close = ()=>{
|
|
13
|
+
console.log("Client disconnected");
|
|
14
|
+
// Remove client from their channel
|
|
15
|
+
channels.forEach((clients, channelName)=>{
|
|
16
|
+
if (clients.has(ws)) {
|
|
17
|
+
clients.delete(ws);
|
|
18
|
+
// Notify other clients in same channel
|
|
19
|
+
clients.forEach((client)=>{
|
|
20
|
+
if (client.readyState === WebSocket.OPEN) {
|
|
21
|
+
client.send(JSON.stringify({
|
|
22
|
+
type: "system",
|
|
23
|
+
message: "A user has left the channel",
|
|
24
|
+
channel: channelName
|
|
25
|
+
}));
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
const server = Bun.serve({
|
|
33
|
+
port: 3055,
|
|
34
|
+
// uncomment this to allow connections in windows wsl
|
|
35
|
+
// hostname: "0.0.0.0",
|
|
36
|
+
fetch (req, server) {
|
|
37
|
+
// Handle CORS preflight
|
|
38
|
+
if (req.method === "OPTIONS") {
|
|
39
|
+
return new Response(null, {
|
|
40
|
+
headers: {
|
|
41
|
+
"Access-Control-Allow-Origin": "*",
|
|
42
|
+
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
|
|
43
|
+
"Access-Control-Allow-Headers": "Content-Type, Authorization"
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
// Handle WebSocket upgrade
|
|
48
|
+
const success = server.upgrade(req, {
|
|
49
|
+
headers: {
|
|
50
|
+
"Access-Control-Allow-Origin": "*"
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
if (success) {
|
|
54
|
+
return; // Upgraded to WebSocket
|
|
55
|
+
}
|
|
56
|
+
// Return response for non-WebSocket requests
|
|
57
|
+
return new Response("WebSocket server running", {
|
|
58
|
+
headers: {
|
|
59
|
+
"Access-Control-Allow-Origin": "*"
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
},
|
|
63
|
+
websocket: {
|
|
64
|
+
open: handleConnection,
|
|
65
|
+
message (ws, message) {
|
|
66
|
+
try {
|
|
67
|
+
console.log("Received message from client:", message);
|
|
68
|
+
const data = JSON.parse(message);
|
|
69
|
+
if (data.type === "join") {
|
|
70
|
+
const channelName = data.channel;
|
|
71
|
+
if (!channelName || typeof channelName !== "string") {
|
|
72
|
+
ws.send(JSON.stringify({
|
|
73
|
+
type: "error",
|
|
74
|
+
message: "Channel name is required"
|
|
75
|
+
}));
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
// Create channel if it doesn't exist
|
|
79
|
+
if (!channels.has(channelName)) {
|
|
80
|
+
channels.set(channelName, new Set());
|
|
81
|
+
}
|
|
82
|
+
// Add client to channel
|
|
83
|
+
const channelClients = channels.get(channelName);
|
|
84
|
+
channelClients.add(ws);
|
|
85
|
+
// Notify client they joined successfully
|
|
86
|
+
ws.send(JSON.stringify({
|
|
87
|
+
type: "system",
|
|
88
|
+
message: `Joined channel: ${channelName}`,
|
|
89
|
+
channel: channelName
|
|
90
|
+
}));
|
|
91
|
+
console.log("Sending message to client:", data.id);
|
|
92
|
+
ws.send(JSON.stringify({
|
|
93
|
+
type: "system",
|
|
94
|
+
message: {
|
|
95
|
+
id: data.id,
|
|
96
|
+
result: "Connected to channel: " + channelName
|
|
97
|
+
},
|
|
98
|
+
channel: channelName
|
|
99
|
+
}));
|
|
100
|
+
// Notify other clients in channel
|
|
101
|
+
channelClients.forEach((client)=>{
|
|
102
|
+
if (client !== ws && client.readyState === WebSocket.OPEN) {
|
|
103
|
+
client.send(JSON.stringify({
|
|
104
|
+
type: "system",
|
|
105
|
+
message: "A new user has joined the channel",
|
|
106
|
+
channel: channelName
|
|
107
|
+
}));
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
// Handle regular messages
|
|
113
|
+
if (data.type === "message") {
|
|
114
|
+
const channelName = data.channel;
|
|
115
|
+
if (!channelName || typeof channelName !== "string") {
|
|
116
|
+
ws.send(JSON.stringify({
|
|
117
|
+
type: "error",
|
|
118
|
+
message: "Channel name is required"
|
|
119
|
+
}));
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const channelClients = channels.get(channelName);
|
|
123
|
+
if (!channelClients || !channelClients.has(ws)) {
|
|
124
|
+
ws.send(JSON.stringify({
|
|
125
|
+
type: "error",
|
|
126
|
+
message: "You must join the channel first"
|
|
127
|
+
}));
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
// Broadcast to all clients in the channel
|
|
131
|
+
channelClients.forEach((client)=>{
|
|
132
|
+
if (client.readyState === WebSocket.OPEN) {
|
|
133
|
+
console.log("Broadcasting message to client:", data.message);
|
|
134
|
+
client.send(JSON.stringify({
|
|
135
|
+
type: "broadcast",
|
|
136
|
+
message: data.message,
|
|
137
|
+
sender: client === ws ? "You" : "User",
|
|
138
|
+
channel: channelName
|
|
139
|
+
}));
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
} catch (err) {
|
|
144
|
+
console.error("Error handling message:", err);
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
close (ws) {
|
|
148
|
+
// Remove client from their channel
|
|
149
|
+
channels.forEach((clients)=>{
|
|
150
|
+
clients.delete(ws);
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
console.log(`WebSocket server running on port ${server.port}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@seed-design/mcp",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.15",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/daangn/seed-design.git",
|
|
@@ -14,19 +14,24 @@
|
|
|
14
14
|
"bin",
|
|
15
15
|
"src"
|
|
16
16
|
],
|
|
17
|
-
"bin":
|
|
17
|
+
"bin": {
|
|
18
|
+
"main": "./bin/main.mjs",
|
|
19
|
+
"socket": "./bin/socket.mjs"
|
|
20
|
+
},
|
|
18
21
|
"scripts": {
|
|
19
22
|
"clean": "rm -rf lib",
|
|
23
|
+
"dev": "bun run --watch ./src/bin/socket.ts",
|
|
20
24
|
"build": "bunchee",
|
|
21
25
|
"lint:publish": "bun publint"
|
|
22
26
|
},
|
|
23
27
|
"dependencies": {
|
|
24
28
|
"@modelcontextprotocol/sdk": "^1.7.0",
|
|
25
|
-
"@seed-design/figma": "0.0.
|
|
29
|
+
"@seed-design/figma": "0.0.15",
|
|
26
30
|
"express": "^4.21.2",
|
|
27
31
|
"yargs": "^17.7.2"
|
|
28
32
|
},
|
|
29
33
|
"devDependencies": {
|
|
34
|
+
"@types/bun": "^1.2.8",
|
|
30
35
|
"@types/yargs": "^17.0.33",
|
|
31
36
|
"typescript": "^5.4.5"
|
|
32
37
|
},
|
package/src/bin/main.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
|
+
import { logger } from "../logger";
|
|
6
|
+
import { createFigmaWebSocketClient } from "../websocket";
|
|
7
|
+
import { registerTools } from "../tools";
|
|
8
|
+
import { registerPrompts } from "../prompts";
|
|
9
|
+
|
|
10
|
+
// Add command line argument parsing
|
|
11
|
+
const args = process.argv.slice(2);
|
|
12
|
+
const serverArg = args.find((arg) => arg.startsWith("--server="));
|
|
13
|
+
const serverUrl = serverArg ? serverArg.split("=")[1] : "localhost";
|
|
14
|
+
|
|
15
|
+
// Create Figma WebSocket client
|
|
16
|
+
const figmaClient = createFigmaWebSocketClient(serverUrl);
|
|
17
|
+
|
|
18
|
+
// Create MCP server
|
|
19
|
+
const server = new McpServer({
|
|
20
|
+
name: "SEED Design MCP",
|
|
21
|
+
version: "1.0.0",
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// Register tools and prompts
|
|
25
|
+
registerTools(server, figmaClient);
|
|
26
|
+
registerPrompts(server);
|
|
27
|
+
|
|
28
|
+
// Start the server
|
|
29
|
+
async function main() {
|
|
30
|
+
try {
|
|
31
|
+
// Try to connect to Figma socket server
|
|
32
|
+
figmaClient.connectToFigma();
|
|
33
|
+
} catch (error) {
|
|
34
|
+
logger.warn(
|
|
35
|
+
`Could not connect to Figma initially: ${error instanceof Error ? error.message : String(error)}`,
|
|
36
|
+
);
|
|
37
|
+
logger.warn("Will try to connect when the first command is sent");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Start the MCP server with stdio transport
|
|
41
|
+
const transport = new StdioServerTransport();
|
|
42
|
+
await server.connect(transport);
|
|
43
|
+
logger.info("FigmaMCP server running on stdio");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Run the server
|
|
47
|
+
main().catch((error) => {
|
|
48
|
+
logger.error(
|
|
49
|
+
`Error starting FigmaMCP server: ${error instanceof Error ? error.message : String(error)}`,
|
|
50
|
+
);
|
|
51
|
+
process.exit(1);
|
|
52
|
+
});
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import type { Server, ServerWebSocket } from "bun";
|
|
2
|
+
|
|
3
|
+
// Store clients by channel
|
|
4
|
+
const channels = new Map<string, Set<ServerWebSocket<any>>>();
|
|
5
|
+
|
|
6
|
+
function handleConnection(ws: ServerWebSocket<any>) {
|
|
7
|
+
// Don't add to clients immediately - wait for channel join
|
|
8
|
+
console.log("New client connected");
|
|
9
|
+
|
|
10
|
+
// Send welcome message to the new client
|
|
11
|
+
ws.send(
|
|
12
|
+
JSON.stringify({
|
|
13
|
+
type: "system",
|
|
14
|
+
message: "Please join a channel to start chatting",
|
|
15
|
+
}),
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
ws.close = () => {
|
|
19
|
+
console.log("Client disconnected");
|
|
20
|
+
|
|
21
|
+
// Remove client from their channel
|
|
22
|
+
channels.forEach((clients, channelName) => {
|
|
23
|
+
if (clients.has(ws)) {
|
|
24
|
+
clients.delete(ws);
|
|
25
|
+
|
|
26
|
+
// Notify other clients in same channel
|
|
27
|
+
clients.forEach((client) => {
|
|
28
|
+
if (client.readyState === WebSocket.OPEN) {
|
|
29
|
+
client.send(
|
|
30
|
+
JSON.stringify({
|
|
31
|
+
type: "system",
|
|
32
|
+
message: "A user has left the channel",
|
|
33
|
+
channel: channelName,
|
|
34
|
+
}),
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const server = Bun.serve({
|
|
44
|
+
port: 3055,
|
|
45
|
+
// uncomment this to allow connections in windows wsl
|
|
46
|
+
// hostname: "0.0.0.0",
|
|
47
|
+
fetch(req: Request, server: Server) {
|
|
48
|
+
// Handle CORS preflight
|
|
49
|
+
if (req.method === "OPTIONS") {
|
|
50
|
+
return new Response(null, {
|
|
51
|
+
headers: {
|
|
52
|
+
"Access-Control-Allow-Origin": "*",
|
|
53
|
+
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
|
|
54
|
+
"Access-Control-Allow-Headers": "Content-Type, Authorization",
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Handle WebSocket upgrade
|
|
60
|
+
const success = server.upgrade(req, {
|
|
61
|
+
headers: {
|
|
62
|
+
"Access-Control-Allow-Origin": "*",
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
if (success) {
|
|
67
|
+
return; // Upgraded to WebSocket
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Return response for non-WebSocket requests
|
|
71
|
+
return new Response("WebSocket server running", {
|
|
72
|
+
headers: {
|
|
73
|
+
"Access-Control-Allow-Origin": "*",
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
},
|
|
77
|
+
websocket: {
|
|
78
|
+
open: handleConnection,
|
|
79
|
+
message(ws: ServerWebSocket<any>, message: string | Buffer) {
|
|
80
|
+
try {
|
|
81
|
+
console.log("Received message from client:", message);
|
|
82
|
+
const data = JSON.parse(message as string);
|
|
83
|
+
|
|
84
|
+
if (data.type === "join") {
|
|
85
|
+
const channelName = data.channel;
|
|
86
|
+
if (!channelName || typeof channelName !== "string") {
|
|
87
|
+
ws.send(
|
|
88
|
+
JSON.stringify({
|
|
89
|
+
type: "error",
|
|
90
|
+
message: "Channel name is required",
|
|
91
|
+
}),
|
|
92
|
+
);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Create channel if it doesn't exist
|
|
97
|
+
if (!channels.has(channelName)) {
|
|
98
|
+
channels.set(channelName, new Set());
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Add client to channel
|
|
102
|
+
const channelClients = channels.get(channelName)!;
|
|
103
|
+
channelClients.add(ws);
|
|
104
|
+
|
|
105
|
+
// Notify client they joined successfully
|
|
106
|
+
ws.send(
|
|
107
|
+
JSON.stringify({
|
|
108
|
+
type: "system",
|
|
109
|
+
message: `Joined channel: ${channelName}`,
|
|
110
|
+
channel: channelName,
|
|
111
|
+
}),
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
console.log("Sending message to client:", data.id);
|
|
115
|
+
|
|
116
|
+
ws.send(
|
|
117
|
+
JSON.stringify({
|
|
118
|
+
type: "system",
|
|
119
|
+
message: {
|
|
120
|
+
id: data.id,
|
|
121
|
+
result: "Connected to channel: " + channelName,
|
|
122
|
+
},
|
|
123
|
+
channel: channelName,
|
|
124
|
+
}),
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
// Notify other clients in channel
|
|
128
|
+
channelClients.forEach((client) => {
|
|
129
|
+
if (client !== ws && client.readyState === WebSocket.OPEN) {
|
|
130
|
+
client.send(
|
|
131
|
+
JSON.stringify({
|
|
132
|
+
type: "system",
|
|
133
|
+
message: "A new user has joined the channel",
|
|
134
|
+
channel: channelName,
|
|
135
|
+
}),
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Handle regular messages
|
|
143
|
+
if (data.type === "message") {
|
|
144
|
+
const channelName = data.channel;
|
|
145
|
+
if (!channelName || typeof channelName !== "string") {
|
|
146
|
+
ws.send(
|
|
147
|
+
JSON.stringify({
|
|
148
|
+
type: "error",
|
|
149
|
+
message: "Channel name is required",
|
|
150
|
+
}),
|
|
151
|
+
);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const channelClients = channels.get(channelName);
|
|
156
|
+
if (!channelClients || !channelClients.has(ws)) {
|
|
157
|
+
ws.send(
|
|
158
|
+
JSON.stringify({
|
|
159
|
+
type: "error",
|
|
160
|
+
message: "You must join the channel first",
|
|
161
|
+
}),
|
|
162
|
+
);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Broadcast to all clients in the channel
|
|
167
|
+
channelClients.forEach((client) => {
|
|
168
|
+
if (client.readyState === WebSocket.OPEN) {
|
|
169
|
+
console.log("Broadcasting message to client:", data.message);
|
|
170
|
+
client.send(
|
|
171
|
+
JSON.stringify({
|
|
172
|
+
type: "broadcast",
|
|
173
|
+
message: data.message,
|
|
174
|
+
sender: client === ws ? "You" : "User",
|
|
175
|
+
channel: channelName,
|
|
176
|
+
}),
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
} catch (err) {
|
|
182
|
+
console.error("Error handling message:", err);
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
close(ws: ServerWebSocket<any>) {
|
|
186
|
+
// Remove client from their channel
|
|
187
|
+
channels.forEach((clients) => {
|
|
188
|
+
clients.delete(ws);
|
|
189
|
+
});
|
|
190
|
+
},
|
|
191
|
+
},
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
console.log(`WebSocket server running on port ${server.port}`);
|
package/src/logger.ts
CHANGED
|
@@ -1,43 +1,41 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Custom logging module that writes to stderr instead of stdout
|
|
3
|
+
* to avoid being captured in MCP protocol communication
|
|
3
4
|
*/
|
|
4
|
-
export interface Logger {
|
|
5
|
-
log(...args: any[]): void;
|
|
6
|
-
error(...args: any[]): void;
|
|
7
|
-
}
|
|
8
5
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
error: () => {},
|
|
15
|
-
};
|
|
6
|
+
export const logger = {
|
|
7
|
+
/**
|
|
8
|
+
* Log an informational message
|
|
9
|
+
*/
|
|
10
|
+
info: (message: string) => process.stderr.write(`[INFO] ${message}\n`),
|
|
16
11
|
|
|
17
|
-
/**
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Log a debug message
|
|
14
|
+
*/
|
|
15
|
+
debug: (message: string) => process.stderr.write(`[DEBUG] ${message}\n`),
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Log a warning message
|
|
19
|
+
*/
|
|
20
|
+
warn: (message: string) => process.stderr.write(`[WARN] ${message}\n`),
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Log an error message
|
|
24
|
+
*/
|
|
25
|
+
error: (message: string) => process.stderr.write(`[ERROR] ${message}\n`),
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Log a general message
|
|
29
|
+
*/
|
|
30
|
+
log: (message: string) => process.stderr.write(`[LOG] ${message}\n`),
|
|
23
31
|
};
|
|
24
32
|
|
|
25
33
|
/**
|
|
26
|
-
*
|
|
34
|
+
* Format an error for logging
|
|
27
35
|
*/
|
|
28
|
-
export function
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
data: args,
|
|
34
|
-
});
|
|
35
|
-
},
|
|
36
|
-
error: (...args: any[]) => {
|
|
37
|
-
sendLoggingMessage({
|
|
38
|
-
level: "error",
|
|
39
|
-
data: args,
|
|
40
|
-
});
|
|
41
|
-
},
|
|
42
|
-
};
|
|
36
|
+
export function formatError(error: unknown): string {
|
|
37
|
+
if (error instanceof Error) {
|
|
38
|
+
return error.message;
|
|
39
|
+
}
|
|
40
|
+
return String(error);
|
|
43
41
|
}
|
package/src/prompts.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
|
|
3
|
+
export function registerPrompts(server: McpServer): void {
|
|
4
|
+
server.prompt("read_design_strategy", "Best practices for reading Figma designs", (_extra) => {
|
|
5
|
+
return {
|
|
6
|
+
messages: [
|
|
7
|
+
{
|
|
8
|
+
role: "assistant",
|
|
9
|
+
content: {
|
|
10
|
+
type: "text",
|
|
11
|
+
text: `When reading Figma designs, follow these best practices:
|
|
12
|
+
|
|
13
|
+
1. Start with selection:
|
|
14
|
+
- First use get_selection() to understand the current selection
|
|
15
|
+
- If no selection ask user to select single or multiple nodes
|
|
16
|
+
|
|
17
|
+
2. Get node infos of the selected nodes:
|
|
18
|
+
- Use get_nodes_info() to get the information of the selected nodes
|
|
19
|
+
- If no selection ask user to select single or multiple nodes
|
|
20
|
+
`,
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
],
|
|
24
|
+
description: "Best practices for reading Figma designs",
|
|
25
|
+
};
|
|
26
|
+
});
|
|
27
|
+
}
|
package/src/responses.ts
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { formatError } from "./logger";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Helper type for a tool response content item
|
|
5
|
+
*/
|
|
6
|
+
export type ContentItem =
|
|
7
|
+
| { type: "text"; text: string }
|
|
8
|
+
| { type: "image"; data: string; mimeType: string };
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Helper type for a tool response
|
|
12
|
+
*/
|
|
13
|
+
export type ToolResponse = {
|
|
14
|
+
content: ContentItem[];
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Format an object response
|
|
19
|
+
*/
|
|
20
|
+
export function formatObjectResponse(result: unknown): ToolResponse {
|
|
21
|
+
return {
|
|
22
|
+
content: [
|
|
23
|
+
{
|
|
24
|
+
type: "text",
|
|
25
|
+
text: JSON.stringify(result),
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Format a text response
|
|
33
|
+
*/
|
|
34
|
+
export function formatTextResponse(text: string): ToolResponse {
|
|
35
|
+
return {
|
|
36
|
+
content: [
|
|
37
|
+
{
|
|
38
|
+
type: "text",
|
|
39
|
+
text,
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Format an image response
|
|
47
|
+
*/
|
|
48
|
+
export function formatImageResponse(imageData: string, mimeType = "image/png"): ToolResponse {
|
|
49
|
+
return {
|
|
50
|
+
content: [
|
|
51
|
+
{
|
|
52
|
+
type: "image",
|
|
53
|
+
data: imageData,
|
|
54
|
+
mimeType,
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Format an error response
|
|
62
|
+
*/
|
|
63
|
+
export function formatErrorResponse(toolName: string, error: unknown): ToolResponse {
|
|
64
|
+
return {
|
|
65
|
+
content: [
|
|
66
|
+
{
|
|
67
|
+
type: "text",
|
|
68
|
+
text: `Error in ${toolName}: ${formatError(error)}`,
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Format a progress response with initial message
|
|
76
|
+
*/
|
|
77
|
+
export function formatProgressResponse(initialMessage: string, result: unknown): ToolResponse {
|
|
78
|
+
return {
|
|
79
|
+
content: [
|
|
80
|
+
{
|
|
81
|
+
type: "text",
|
|
82
|
+
text: initialMessage,
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
type: "text",
|
|
86
|
+
text: typeof result === "string" ? result : JSON.stringify(result),
|
|
87
|
+
},
|
|
88
|
+
],
|
|
89
|
+
};
|
|
90
|
+
}
|