@ject-5-fe/figma-plugin 0.1.4 → 0.2.0

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.
Files changed (2) hide show
  1. package/dist/server/server.cjs +265 -197
  2. package/package.json +5 -4
@@ -1,14 +1,10 @@
1
- #!/usr/bin/env node
1
+ "use strict";
2
2
  var __create = Object.create;
3
3
  var __defProp = Object.defineProperty;
4
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __getProtoOf = Object.getPrototypeOf;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __export = (target, all) => {
9
- for (var name in all)
10
- __defProp(target, name, { get: all[name], enumerable: true });
11
- };
12
8
  var __copyProps = (to, from, except, desc) => {
13
9
  if (from && typeof from === "object" || typeof from === "function") {
14
10
  for (let key of __getOwnPropNames(from))
@@ -25,20 +21,116 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
25
21
  isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
22
  mod
27
23
  ));
28
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
24
 
30
25
  // src/server.ts
31
- var server_exports = {};
32
- __export(server_exports, {
33
- compressData: () => compressData
34
- });
35
- module.exports = __toCommonJS(server_exports);
36
26
  var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
37
27
  var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
28
+ var import_fs = __toESM(require("fs"), 1);
29
+ var import_path = __toESM(require("path"), 1);
30
+ var import_zod = require("zod");
31
+
32
+ // src/figmaClient.ts
38
33
  var import_uuid = require("uuid");
39
34
  var import_ws = __toESM(require("ws"), 1);
40
- var import_zlib = require("zlib");
41
- var import_zod = require("zod");
35
+ function createFigmaClient({
36
+ logger: logger2,
37
+ serverUrl: serverUrl2,
38
+ wsUrlBase
39
+ }) {
40
+ let ws = null;
41
+ const pendingRequests = /* @__PURE__ */ new Map();
42
+ function connectToFigma2(port = 3055) {
43
+ if (ws && ws.readyState === import_ws.default.OPEN) {
44
+ logger2.info("Already connected to Figma");
45
+ return;
46
+ }
47
+ const wsUrl = serverUrl2 === "localhost" ? `${wsUrlBase}:${port}` : wsUrlBase;
48
+ logger2.info(`Connecting to Figma socket server at ${wsUrl}...`);
49
+ ws = new import_ws.default(wsUrl);
50
+ ws.on("open", () => {
51
+ logger2.info("Connected to Figma socket server");
52
+ });
53
+ ws.on("message", (data) => {
54
+ try {
55
+ const json = JSON.parse(data);
56
+ const myResponse = json.message;
57
+ logger2.debug(`Received message: ${JSON.stringify(myResponse)}`);
58
+ logger2.log("myResponse" + JSON.stringify(myResponse));
59
+ if (myResponse.id && pendingRequests.has(myResponse.id) && myResponse.result) {
60
+ const request = pendingRequests.get(myResponse.id);
61
+ clearTimeout(request.timeout);
62
+ if (myResponse.error) {
63
+ logger2.error(`Error from Figma: ${myResponse.error}`);
64
+ request.reject(new Error(myResponse.error));
65
+ } else if (myResponse.result) {
66
+ request.resolve(myResponse.result);
67
+ }
68
+ pendingRequests.delete(myResponse.id);
69
+ } else {
70
+ logger2.info(
71
+ `Received broadcast message: ${JSON.stringify(myResponse)}`
72
+ );
73
+ }
74
+ } catch (error) {
75
+ logger2.error(
76
+ `Error parsing message: ${error instanceof Error ? error.message : String(error)}`
77
+ );
78
+ }
79
+ });
80
+ ws.on("error", (error) => {
81
+ logger2.error(`Socket error: ${error}`);
82
+ });
83
+ ws.on("close", () => {
84
+ logger2.info("Disconnected from Figma socket server");
85
+ ws = null;
86
+ for (const [id, request] of pendingRequests.entries()) {
87
+ clearTimeout(request.timeout);
88
+ request.reject(new Error("Connection closed"));
89
+ pendingRequests.delete(id);
90
+ }
91
+ logger2.info("Attempting to reconnect in 2 seconds...");
92
+ setTimeout(() => connectToFigma2(port), 2e3);
93
+ });
94
+ }
95
+ function sendCommandToFigma2(command, params = {}) {
96
+ return new Promise((resolve, reject) => {
97
+ if (!ws || ws.readyState !== import_ws.default.OPEN) {
98
+ connectToFigma2();
99
+ reject(new Error("Not connected to Figma. Attempting to connect..."));
100
+ return;
101
+ }
102
+ const id = (0, import_uuid.v4)();
103
+ const request = {
104
+ id,
105
+ type: "message",
106
+ message: {
107
+ id,
108
+ command,
109
+ params: {
110
+ ...params
111
+ }
112
+ }
113
+ };
114
+ const timeout = setTimeout(() => {
115
+ if (pendingRequests.has(id)) {
116
+ pendingRequests.delete(id);
117
+ logger2.error(`Request ${id} to Figma timed out after 30 seconds`);
118
+ reject(new Error("Request to Figma timed out"));
119
+ }
120
+ }, 3e4);
121
+ pendingRequests.set(id, { resolve, reject, timeout });
122
+ logger2.info(`Sending command to Figma: ${command}`);
123
+ logger2.debug(`Request details: ${JSON.stringify(request)}`);
124
+ ws.send(JSON.stringify(request));
125
+ });
126
+ }
127
+ return {
128
+ connectToFigma: connectToFigma2,
129
+ sendCommandToFigma: sendCommandToFigma2
130
+ };
131
+ }
132
+
133
+ // src/server.ts
42
134
  var logger = {
43
135
  info: (message) => process.stderr.write(`[INFO] ${message}
44
136
  `),
@@ -51,66 +143,95 @@ var logger = {
51
143
  log: (message) => process.stderr.write(`[LOG] ${message}
52
144
  `)
53
145
  };
54
- function compressData(data) {
55
- const jsonString = JSON.stringify(data);
56
- const compressed = (0, import_zlib.gzipSync)(jsonString);
57
- const base64 = compressed.toString("base64");
58
- const originalBytes = Buffer.byteLength(jsonString, "utf8");
59
- console.info(
60
- `Compression: ${originalBytes} bytes -> ${compressed.length} bytes (ratio ${(compressed.length / originalBytes * 100).toFixed(1)}%)`
146
+ var BASE_DIR = import_path.default.join(__dirname, "../../", "rules");
147
+ function levenshtein(a, b) {
148
+ const m = a.length;
149
+ const n = b.length;
150
+ if (m === 0) return n;
151
+ if (n === 0) return m;
152
+ const dp = Array.from(
153
+ { length: m + 1 },
154
+ () => Array(n + 1).fill(0)
61
155
  );
62
- return base64;
156
+ for (let i = 0; i <= m; i++) dp[i][0] = i;
157
+ for (let j = 0; j <= n; j++) dp[0][j] = j;
158
+ for (let i = 1; i <= m; i++) {
159
+ for (let j = 1; j <= n; j++) {
160
+ const cost = a[i - 1] === b[j - 1] ? 0 : 1;
161
+ dp[i][j] = Math.min(
162
+ dp[i - 1][j] + 1,
163
+ // 삭제
164
+ dp[i][j - 1] + 1,
165
+ // 삽입
166
+ dp[i - 1][j - 1] + cost
167
+ // 교체
168
+ );
169
+ }
170
+ }
171
+ return dp[m][n];
63
172
  }
64
- var ws = null;
65
- var pendingRequests = /* @__PURE__ */ new Map();
66
- var server = new import_mcp.McpServer({
67
- name: "@jongh/figma-plugin",
68
- version: "1.0.0"
69
- });
173
+ function getSimilarityScore(target, candidate) {
174
+ if (target === candidate) return 100;
175
+ if (candidate.startsWith(target) || target.startsWith(candidate)) return 95;
176
+ if (candidate.includes(target) || target.includes(candidate)) return 85;
177
+ const distance = levenshtein(target, candidate);
178
+ const maxLen = Math.max(target.length, candidate.length);
179
+ const normalized = 1 - distance / maxLen;
180
+ const score = Math.floor(normalized * 80);
181
+ return score > 0 ? score : 0;
182
+ }
183
+ var STORIES_EXT = ".stories.mdx";
184
+ function findSimilarStoriesFiles(baseDir, componentName) {
185
+ const compName = componentName.toLowerCase();
186
+ const files = import_fs.default.readdirSync(baseDir).filter((file) => file.toLowerCase().endsWith(STORIES_EXT));
187
+ const scored = files.map((file) => {
188
+ const baseName = file.toLowerCase().replace(new RegExp(`${STORIES_EXT.replace(".", "\\.")}$`), "");
189
+ return {
190
+ file,
191
+ baseName,
192
+ score: getSimilarityScore(compName, baseName)
193
+ };
194
+ }).filter(({ score }) => score > 0).sort((a, b) => b.score - a.score);
195
+ return scored;
196
+ }
197
+ var server = new import_mcp.McpServer(
198
+ {
199
+ name: "@jongh/figma-plugin",
200
+ version: "1.0.0"
201
+ },
202
+ {
203
+ capabilities: {
204
+ logging: logger,
205
+ resources: {
206
+ subscribe: true,
207
+ listChanged: true
208
+ }
209
+ }
210
+ }
211
+ );
70
212
  var args = process.argv.slice(2);
71
213
  var serverArg = args.find((arg) => arg.startsWith("--server="));
72
214
  var serverUrl = serverArg ? serverArg.split("=")[1] : "localhost";
73
215
  var WS_URL = serverUrl === "localhost" ? `ws://${serverUrl}` : `wss://${serverUrl}`;
74
- server.tool(
75
- "get_document_info",
76
- "Get detailed information about the current Figma document",
77
- {},
78
- async () => {
79
- try {
80
- const result = await sendCommandToFigma("get_document_info");
81
- return {
82
- content: [
83
- {
84
- type: "text",
85
- text: JSON.stringify(result)
86
- }
87
- ]
88
- };
89
- } catch (error) {
90
- return {
91
- content: [
92
- {
93
- type: "text",
94
- text: `Error getting document info: ${error instanceof Error ? error.message : String(error)}`
95
- }
96
- ]
97
- };
98
- }
99
- }
100
- );
101
- server.tool(
216
+ var { connectToFigma, sendCommandToFigma } = createFigmaClient({
217
+ logger,
218
+ serverUrl,
219
+ wsUrlBase: WS_URL
220
+ });
221
+ server.registerTool(
102
222
  "get_selection",
103
- "Get information about the current selection in Figma",
104
- {},
223
+ {
224
+ description: "Get information about the current selection in Figma",
225
+ inputSchema: {}
226
+ },
105
227
  async () => {
106
228
  try {
107
229
  const result = await sendCommandToFigma("get_selection");
108
- const compressed = compressData(result);
109
230
  return {
110
231
  content: [
111
232
  {
112
233
  type: "text",
113
- text: compressed
234
+ text: `${result}`
114
235
  }
115
236
  ]
116
237
  };
@@ -126,38 +247,11 @@ server.tool(
126
247
  }
127
248
  }
128
249
  );
129
- server.tool(
130
- "get_node_info",
131
- "Get detailed information about a specific node in Figma",
250
+ server.registerPrompt(
251
+ "design system components",
132
252
  {
133
- nodeId: import_zod.z.string().describe("The ID of the node to get information about")
253
+ description: "help analyze design system common components"
134
254
  },
135
- async ({ nodeId }) => {
136
- try {
137
- const result = await sendCommandToFigma("get_node_info", { nodeId });
138
- return {
139
- content: [
140
- {
141
- type: "text",
142
- text: JSON.stringify(result)
143
- }
144
- ]
145
- };
146
- } catch (error) {
147
- return {
148
- content: [
149
- {
150
- type: "text",
151
- text: `Error getting node info: ${error instanceof Error ? error.message : String(error)}`
152
- }
153
- ]
154
- };
155
- }
156
- }
157
- );
158
- server.prompt(
159
- "connection_troubleshooting",
160
- "Help users troubleshoot connection issues",
161
255
  () => {
162
256
  return {
163
257
  messages: [
@@ -165,23 +259,8 @@ server.prompt(
165
259
  role: "assistant",
166
260
  content: {
167
261
  type: "text",
168
- text: `When users report connection issues, ask them to check:
169
-
170
- 1. Figma Plugin Connection:
171
- - Did you click the "Connect" button in the Figma plugin?
172
- - Is the Figma plugin running and showing a connected status?
173
- - Try refreshing the plugin or restarting it if needed
174
-
175
- 2. MCP Connection in Cursor:
176
- - Go to Cursor Settings/Preferences
177
- - Check if the MCP server connection is properly configured(green light)
178
- - Verify the MCP server is running and connected
179
- - Look for any connection error messages in the settings
180
-
181
- If both are properly connected and issues persist, try:
182
- - Restarting both the Figma plugin and Cursor
183
- - Checking if the WebSocket connection is blocked by firewall
184
- - Verifying the correct server URL and port configuration`
262
+ text: `\uACF5\uD1B5 \uCEF4\uD3EC\uB10C\uD2B8\uC758 \uAD6C\uD604 \uC815\uBCF4\uB97C \uD30C\uC545\uD558\uC5EC \uCF54\uB4DC \uC81C\uC791\uC5D0 \uD65C\uC6A9\uD574\uC57C \uD569\uB2C8\uB2E4. \uCF54\uB4DC \uC0AC\uC6A9 \uC608\uC2DC, \uC124\uBA85 \uB4F1\uC744 \uC815\uD655\uD558\uAC8C \uD30C\uC545\uD558\uC5EC \uC2E4\uC81C \uCF54\uB4DC \uC81C\uC791\uC5D0 \uC815\uD655\uD558\uAC8C \uD65C\uC6A9\uD560 \uC218 \uC788\uC5B4\uC57C \uD569\uB2C8\uB2E4.
263
+ \uD2B9\uD788 Figma \uC778\uD130\uD398\uC774\uC2A4\uC640 \uCF54\uB4DC \uAC04\uC758 \uAD6C\uD604 \uCC28\uC774\uB97C \uC815\uD655\uD558\uAC8C \uD30C\uC545\uD558\uACE0 \uCF54\uB4DC\uC5D0 \uB9DE\uAC8C \uD65C\uC6A9\uD574\uC57C \uD569\uB2C8\uB2E4.`
185
264
  }
186
265
  }
187
266
  ],
@@ -189,9 +268,85 @@ If both are properly connected and issues persist, try:
189
268
  };
190
269
  }
191
270
  );
192
- server.prompt(
271
+ server.registerTool(
272
+ "get_rule_of_components",
273
+ {
274
+ title: "Rule of design system component",
275
+ description: [
276
+ "\uB514\uC790\uC778 \uC2DC\uC2A4\uD15C\uC5D0\uC11C \uC0AC\uC6A9\uD558\uB294 \uACF5\uD1B5 \uCEF4\uD3EC\uB10C\uD2B8\uC758 \uAD6C\uD604 \uADDC\uCE59(\uAD6C\uC870, props, \uC2A4\uD0C0\uC77C \uB4F1)\uC744 \uC870\uD68C\uD558\uB294 MCP \uB3C4\uAD6C\uC785\uB2C8\uB2E4.",
277
+ "Figma \uB514\uC790\uC778\uC5D0\uC11C \uC0AC\uC6A9\uB41C \uACF5\uD1B5 \uCEF4\uD3EC\uB10C\uD2B8\uC758 \uC774\uB984\uC744 componentName \uC778\uC790\uB85C \uB118\uACA8 \uD638\uCD9C\uD558\uBA74,",
278
+ "\uD574\uB2F9 \uCEF4\uD3EC\uB10C\uD2B8\uC758 \uAD6C\uD604 \uC815\uBCF4\uB97C \uBC18\uD658\uD558\uC5EC \u2018MCP\uB97C \uD1B5\uD574 \uC0AC\uC6A9\uB41C \uACF5\uD1B5 \uCEF4\uD3EC\uB10C\uD2B8\uC758 \uAD6C\uD604 \uC815\uBCF4\uB97C \uD30C\uC545\uD55C \uB4A4\u2019 \uD398\uC774\uC9C0 \uCF54\uB4DC\uB97C \uC791\uC131\uD560 \uB54C \uC0AC\uC6A9\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.",
279
+ "\uC608: Figma\uC5D0\uC11C Button \uCEF4\uD3EC\uB10C\uD2B8\uAC00 \uC4F0\uC600\uB2E4\uBA74 componentName\uC5D0 'button'\uC744 \uB123\uC5B4 \uC774 \uB3C4\uAD6C\uB97C \uD638\uCD9C\uD574 \uAD6C\uD604 \uADDC\uCE59\uC744 \uC870\uD68C\uD558\uC138\uC694."
280
+ ].join(" "),
281
+ inputSchema: import_zod.z.object({
282
+ componentName: import_zod.z.string().describe(
283
+ "Figma/\uB514\uC790\uC778 \uC2DC\uC2A4\uD15C\uC5D0\uC11C \uC0AC\uC6A9\uB41C \uACF5\uD1B5 \uCEF4\uD3EC\uB10C\uD2B8 \uC774\uB984 (\uC608: 'button', 'input', 'modal')"
284
+ )
285
+ })
286
+ },
287
+ async ({ componentName }) => {
288
+ const similarFiles = findSimilarStoriesFiles(BASE_DIR, componentName);
289
+ const compName = componentName.toLocaleLowerCase();
290
+ const texts = similarFiles.map(
291
+ ({ file }) => import_fs.default.readFileSync(import_path.default.join(BASE_DIR, file), "utf-8")
292
+ );
293
+ return {
294
+ content: [
295
+ {
296
+ type: "text",
297
+ text: `${texts.join("")}`
298
+ }
299
+ ]
300
+ };
301
+ }
302
+ );
303
+ server.registerResource(
304
+ "file",
305
+ // 리소스 ID
306
+ new import_mcp.ResourceTemplate("file://{path}/", {
307
+ list: async () => {
308
+ const paths = import_fs.default.readdirSync(BASE_DIR);
309
+ return {
310
+ resources: paths.map((path2) => ({
311
+ uri: `file://${path2}`,
312
+ name: path2,
313
+ title: path2,
314
+ description: "local file from design-rules",
315
+ mimeType: "text/plain",
316
+ // 필요시 확장자 보고 바꾸셔도 됩니다
317
+ _meta: {
318
+ filePath: path2
319
+ }
320
+ }))
321
+ };
322
+ }
323
+ }),
324
+ {
325
+ title: "design rule",
326
+ description: "important rules and guidelines for resolving design data"
327
+ },
328
+ // resources/read 핸들러
329
+ async (uri, props) => {
330
+ const text = import_fs.default.readFileSync(
331
+ import_path.default.join(BASE_DIR, props.path),
332
+ "utf-8"
333
+ );
334
+ return {
335
+ contents: [
336
+ {
337
+ uri: uri.href,
338
+ mimeType: "text/plain",
339
+ text
340
+ }
341
+ ]
342
+ };
343
+ }
344
+ );
345
+ server.registerPrompt(
193
346
  "data_analysis_strategy",
194
- "Best practices for analyzing Figma design data",
347
+ {
348
+ description: "Best practices for analyzing Figma design data"
349
+ },
195
350
  () => {
196
351
  return {
197
352
  messages: [
@@ -217,6 +372,8 @@ server.prompt(
217
372
  - Identify responsive design patterns
218
373
  - Note alignment and positioning strategies
219
374
 
375
+
376
+
220
377
 
221
378
  `
222
379
  }
@@ -226,91 +383,6 @@ server.prompt(
226
383
  };
227
384
  }
228
385
  );
229
- function connectToFigma(port = 3055) {
230
- if (ws && ws.readyState === import_ws.default.OPEN) {
231
- logger.info("Already connected to Figma");
232
- return;
233
- }
234
- const wsUrl = serverUrl === "localhost" ? `${WS_URL}:${port}` : WS_URL;
235
- logger.info(`Connecting to Figma socket server at ${wsUrl}...`);
236
- ws = new import_ws.default(wsUrl);
237
- ws.on("open", () => {
238
- logger.info("Connected to Figma socket server");
239
- });
240
- ws.on("message", (data) => {
241
- try {
242
- const json = JSON.parse(data);
243
- const myResponse = json.message;
244
- logger.debug(`Received message: ${JSON.stringify(myResponse)}`);
245
- logger.log("myResponse" + JSON.stringify(myResponse));
246
- if (myResponse.id && pendingRequests.has(myResponse.id) && myResponse.result) {
247
- const request = pendingRequests.get(myResponse.id);
248
- clearTimeout(request.timeout);
249
- if (myResponse.error) {
250
- logger.error(`Error from Figma: ${myResponse.error}`);
251
- request.reject(new Error(myResponse.error));
252
- } else {
253
- if (myResponse.result) {
254
- request.resolve(myResponse.result);
255
- }
256
- }
257
- pendingRequests.delete(myResponse.id);
258
- } else {
259
- logger.info(`Received broadcast message: ${JSON.stringify(myResponse)}`);
260
- }
261
- } catch (error) {
262
- logger.error(
263
- `Error parsing message: ${error instanceof Error ? error.message : String(error)}`
264
- );
265
- }
266
- });
267
- ws.on("error", (error) => {
268
- logger.error(`Socket error: ${error}`);
269
- });
270
- ws.on("close", () => {
271
- logger.info("Disconnected from Figma socket server");
272
- ws = null;
273
- for (const [id, request] of pendingRequests.entries()) {
274
- clearTimeout(request.timeout);
275
- request.reject(new Error("Connection closed"));
276
- pendingRequests.delete(id);
277
- }
278
- logger.info("Attempting to reconnect in 2 seconds...");
279
- setTimeout(() => connectToFigma(port), 2e3);
280
- });
281
- }
282
- function sendCommandToFigma(command, params = {}) {
283
- return new Promise((resolve, reject) => {
284
- if (!ws || ws.readyState !== import_ws.default.OPEN) {
285
- connectToFigma();
286
- reject(new Error("Not connected to Figma. Attempting to connect..."));
287
- return;
288
- }
289
- const id = (0, import_uuid.v4)();
290
- const request = {
291
- id,
292
- type: "message",
293
- message: {
294
- id,
295
- command,
296
- params: {
297
- ...params
298
- }
299
- }
300
- };
301
- const timeout = setTimeout(() => {
302
- if (pendingRequests.has(id)) {
303
- pendingRequests.delete(id);
304
- logger.error(`Request ${id} to Figma timed out after 30 seconds`);
305
- reject(new Error("Request to Figma timed out"));
306
- }
307
- }, 3e4);
308
- pendingRequests.set(id, { resolve, reject, timeout });
309
- logger.info(`Sending command to Figma: ${command}`);
310
- logger.debug(`Request details: ${JSON.stringify(request)}`);
311
- ws.send(JSON.stringify(request));
312
- });
313
- }
314
386
  async function main() {
315
387
  try {
316
388
  connectToFigma();
@@ -330,7 +402,3 @@ main().catch((error) => {
330
402
  );
331
403
  process.exit(1);
332
404
  });
333
- // Annotate the CommonJS export names for ESM import in node:
334
- 0 && (module.exports = {
335
- compressData
336
- });
package/package.json CHANGED
@@ -1,20 +1,21 @@
1
1
  {
2
2
  "name": "@ject-5-fe/figma-plugin",
3
- "version": "0.1.4",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "dev": "plugma dev",
7
7
  "socket": "tsx src/socket.ts",
8
8
  "start": "concurrently \"yarn run socket\" \"yarn run dev\" --names \"SOCKET,MCP\" --prefix-colors \"blue,green\"",
9
- "build:server": "tsup"
9
+ "build:server": "tsup",
10
+ "export-icons": "tsx src/cli/exportIcons.ts"
10
11
  },
11
12
  "bin": "dist/server/server.cjs",
12
13
  "files": [
13
14
  "dist/server/server.cjs"
14
15
  ],
15
16
  "dependencies": {
16
- "@modelcontextprotocol/sdk": "^1",
17
- "es-toolkit": "^1.41.0",
17
+ "@modelcontextprotocol/sdk": "^1.24",
18
+ "es-toolkit": "^1.39.10",
18
19
  "react": "^18.3.1",
19
20
  "react-dom": "^18.3.1",
20
21
  "uuid": "^11",