palette-mcp 1.0.0 → 1.1.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 (84) hide show
  1. package/dist/components/OrderManagementGuide.d.ts +6 -0
  2. package/dist/components/OrderManagementGuide.js +266 -0
  3. package/dist/index-simple.d.ts +2 -0
  4. package/dist/index-simple.js +139 -0
  5. package/dist/index.d.ts +6 -0
  6. package/dist/index.js +16 -226
  7. package/dist/requests/1762927928451-ajgna9b/SomaComponent.html +63 -0
  8. package/dist/requests/1762927928451-ajgna9b/SomaComponent.tsx +913 -0
  9. package/dist/requests/1762927928451-ajgna9b/metadata.json +9 -0
  10. package/dist/requests/1762931214963-cqlwvxn/SomaComponent.html +63 -0
  11. package/dist/requests/1762931214963-cqlwvxn/SomaComponent.tsx +525 -0
  12. package/dist/requests/1762931214963-cqlwvxn/metadata.json +9 -0
  13. package/dist/requests/1762932805663-m5wkk3a/SomaComponent.html +248 -0
  14. package/dist/requests/1762932805663-m5wkk3a/SomaComponent.tsx +1050 -0
  15. package/dist/requests/1762932805663-m5wkk3a/metadata.json +9 -0
  16. package/dist/requests/1762934645710-b67ldow/SomaComponent.html +193 -0
  17. package/dist/requests/1762934645710-b67ldow/SomaComponent.tsx +307 -0
  18. package/dist/requests/1762934645710-b67ldow/metadata.json +9 -0
  19. package/dist/requests/1762934961257-wwnmpvx/SomaComponent.html +193 -0
  20. package/dist/requests/1762934961257-wwnmpvx/SomaComponent.tsx +932 -0
  21. package/dist/requests/1762934961257-wwnmpvx/metadata.json +9 -0
  22. package/dist/requests/1762935126549-yjdcezr/SomaComponent.html +193 -0
  23. package/dist/requests/1762935126549-yjdcezr/SomaComponent.tsx +847 -0
  24. package/dist/requests/1762935126549-yjdcezr/metadata.json +9 -0
  25. package/dist/requests/1762935353759-fuokdeu/SomaComponent.html +193 -0
  26. package/dist/requests/1762935353759-fuokdeu/SomaComponent.tsx +334 -0
  27. package/dist/requests/1762935353759-fuokdeu/metadata.json +9 -0
  28. package/dist/requests/1762935378891-ckwbabn/SomaComponent.html +193 -0
  29. package/dist/requests/1762935378891-ckwbabn/SomaComponent.tsx +256 -0
  30. package/dist/requests/1762935378891-ckwbabn/metadata.json +9 -0
  31. package/dist/requests/1762935418352-181zqu4/SomaComponent.html +193 -0
  32. package/dist/requests/1762935418352-181zqu4/SomaComponent.tsx +45 -0
  33. package/dist/requests/1762935418352-181zqu4/metadata.json +9 -0
  34. package/dist/requests/1762935438157-vzkcbwy/SomaComponent.html +193 -0
  35. package/dist/requests/1762935438157-vzkcbwy/SomaComponent.tsx +238 -0
  36. package/dist/requests/1762935438157-vzkcbwy/metadata.json +9 -0
  37. package/dist/requests/1762935529749-ukzmiu3/SomaComponent.html +193 -0
  38. package/dist/requests/1762935529749-ukzmiu3/SomaComponent.tsx +138 -0
  39. package/dist/requests/1762935529749-ukzmiu3/metadata.json +9 -0
  40. package/dist/requests/1762935556527-jxelwj4/SomaComponent.html +193 -0
  41. package/dist/requests/1762935556527-jxelwj4/SomaComponent.tsx +138 -0
  42. package/dist/requests/1762935556527-jxelwj4/metadata.json +9 -0
  43. package/dist/requests/1762935579673-g39fqly/SomaComponent.html +193 -0
  44. package/dist/requests/1762935579673-g39fqly/SomaComponent.tsx +138 -0
  45. package/dist/requests/1762935579673-g39fqly/metadata.json +9 -0
  46. package/dist/requests/1762935613556-ogvsekd/SomaComponent.html +193 -0
  47. package/dist/requests/1762935613556-ogvsekd/SomaComponent.tsx +150 -0
  48. package/dist/requests/1762935613556-ogvsekd/metadata.json +9 -0
  49. package/dist/requests/1762935943631-hb2drgf/SomaComponent.html +193 -0
  50. package/dist/requests/1762935943631-hb2drgf/SomaComponent.tsx +150 -0
  51. package/dist/requests/1762935943631-hb2drgf/metadata.json +9 -0
  52. package/dist/requests/1762935954110-m7jb9m7/SomaComponent.html +193 -0
  53. package/dist/requests/1762935954110-m7jb9m7/SomaComponent.tsx +150 -0
  54. package/dist/requests/1762935954110-m7jb9m7/metadata.json +9 -0
  55. package/dist/requests/1762936003095-0jnmlga/SomaComponent.html +193 -0
  56. package/dist/requests/1762936003095-0jnmlga/SomaComponent.tsx +150 -0
  57. package/dist/requests/1762936003095-0jnmlga/metadata.json +9 -0
  58. package/dist/requests/1762937044748-7ubrrua/SomaComponent.html +193 -0
  59. package/dist/requests/1762937044748-7ubrrua/SomaComponent.tsx +138 -0
  60. package/dist/requests/1762937044748-7ubrrua/metadata.json +9 -0
  61. package/dist/server.d.ts +18 -0
  62. package/dist/server.js +246 -0
  63. package/dist/smithery.d.ts +50 -0
  64. package/dist/smithery.js +44 -0
  65. package/dist/test-figma-tools.d.ts +2 -0
  66. package/dist/test-figma-tools.js +55 -0
  67. package/dist/test-mcp-servers.d.ts +2 -0
  68. package/dist/test-mcp-servers.js +166 -0
  69. package/dist/test.d.ts +2 -0
  70. package/dist/test.js +76 -0
  71. package/package.json +4 -5
  72. package/smithery.yaml +8 -57
  73. package/src/components/OrderManagementGuide.tsx +459 -0
  74. package/src/index.ts +43 -0
  75. package/src/server.ts +301 -0
  76. package/src/services/code-generator.ts +1330 -0
  77. package/src/services/design-system.ts +2133 -0
  78. package/src/services/figma.ts +422 -0
  79. package/src/smithery.ts +54 -0
  80. package/src/test-figma-tools.ts +72 -0
  81. package/src/test-mcp-servers.ts +180 -0
  82. package/src/test.ts +89 -0
  83. package/src/utils/figma-mcp-client.ts +193 -0
  84. package/src/utils/request-manager.ts +101 -0
@@ -0,0 +1,422 @@
1
+ import axios from 'axios';
2
+ import { FigmaMCPClient } from '../utils/figma-mcp-client.js';
3
+
4
+ export interface FigmaNode {
5
+ id: string;
6
+ name: string;
7
+ type: string;
8
+ visible?: boolean;
9
+ children?: FigmaNode[];
10
+ absoluteBoundingBox?: {
11
+ x: number;
12
+ y: number;
13
+ width: number;
14
+ height: number;
15
+ };
16
+ fills?: Array<{
17
+ type: string;
18
+ color?: {
19
+ r: number;
20
+ g: number;
21
+ b: number;
22
+ a: number;
23
+ };
24
+ gradientStops?: Array<{
25
+ position: number;
26
+ color: {
27
+ r: number;
28
+ g: number;
29
+ b: number;
30
+ a: number;
31
+ };
32
+ }>;
33
+ }>;
34
+ strokes?: Array<{
35
+ type: string;
36
+ color?: {
37
+ r: number;
38
+ g: number;
39
+ b: number;
40
+ a: number;
41
+ };
42
+ strokeWeight?: number;
43
+ }>;
44
+ cornerRadius?: number;
45
+ characters?: string;
46
+ style?: {
47
+ fontFamily?: string;
48
+ fontSize?: number;
49
+ fontWeight?: number;
50
+ textAlignHorizontal?: string;
51
+ textAlignVertical?: string;
52
+ };
53
+ layoutMode?: 'NONE' | 'HORIZONTAL' | 'VERTICAL';
54
+ primaryAxisSizingMode?: 'FIXED' | 'AUTO';
55
+ counterAxisSizingMode?: 'FIXED' | 'AUTO';
56
+ paddingLeft?: number;
57
+ paddingRight?: number;
58
+ paddingTop?: number;
59
+ paddingBottom?: number;
60
+ itemSpacing?: number;
61
+ }
62
+
63
+ export interface FigmaFile {
64
+ document: FigmaNode;
65
+ components: Record<string, FigmaNode>;
66
+ styles: Record<string, any>;
67
+ name: string;
68
+ lastModified: string;
69
+ thumbnailUrl: string;
70
+ }
71
+
72
+ export interface FigmaAnalysis {
73
+ totalNodes: number;
74
+ componentCount: number;
75
+ frameCount: number;
76
+ textCount: number;
77
+ availableComponents: string[];
78
+ suggestedMappings: Array<{
79
+ figmaComponent: string;
80
+ designSystemComponent: string;
81
+ confidence: number;
82
+ }>;
83
+ }
84
+
85
+ export class FigmaService {
86
+ private accessToken: string;
87
+ private baseUrl = 'https://api.figma.com/v1';
88
+ private mcpClient: FigmaMCPClient | null = null;
89
+ private useMCP: boolean;
90
+
91
+ constructor(useMCP: boolean = true, mcpBaseUrl?: string) {
92
+ this.accessToken = process.env.FIGMA_ACCESS_TOKEN || '';
93
+ this.useMCP = useMCP;
94
+
95
+ if (useMCP) {
96
+ const mcpUrl = mcpBaseUrl || process.env.FIGMA_MCP_SERVER_URL || 'http://127.0.0.1:3845/mcp';
97
+ this.mcpClient = new FigmaMCPClient(mcpUrl);
98
+ }
99
+
100
+ if (!this.accessToken) {
101
+ console.warn('환경 변수에서 FIGMA_ACCESS_TOKEN을 찾을 수 없습니다.');
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Figma URL에서 파일 ID 추출
107
+ */
108
+ private extractFileId(url: string): string {
109
+ // /file/ 또는 /design/ 경로에서 파일 ID 추출
110
+ const match = url.match(/\/(?:file|design)\/([a-zA-Z0-9]+)/);
111
+ if (match) {
112
+ return match[1];
113
+ }
114
+ // 이미 파일 ID인 경우
115
+ if (/^[a-zA-Z0-9]+$/.test(url)) {
116
+ return url;
117
+ }
118
+ throw new Error('잘못된 Figma URL 형식입니다.');
119
+ }
120
+
121
+ /**
122
+ * Figma URL에서 node-id 추출
123
+ */
124
+ extractNodeId(url: string): string | undefined {
125
+ const match = url.match(/[?&]node-id=([^&]+)/);
126
+ if (match) {
127
+ // node-id는 URL 인코딩되어 있을 수 있으므로 디코딩
128
+ return decodeURIComponent(match[1]);
129
+ }
130
+ return undefined;
131
+ }
132
+
133
+ /**
134
+ * MCP 응답을 FigmaFile 형식으로 변환
135
+ */
136
+ private transformMCPResponseToFigmaFile(mcpData: any): FigmaFile {
137
+ // MCP 응답 형식에 따라 변환 로직 구현
138
+ // Figma MCP 서버의 응답 구조에 맞게 조정 필요
139
+ if (!mcpData) {
140
+ throw new Error('MCP 응답 데이터가 없습니다.');
141
+ }
142
+
143
+ // MCP 응답이 이미 FigmaFile 형식인 경우
144
+ if (mcpData.document) {
145
+ return {
146
+ document: mcpData.document,
147
+ components: mcpData.components || {},
148
+ styles: mcpData.styles || {},
149
+ name: mcpData.name || 'Untitled',
150
+ lastModified: mcpData.lastModified || new Date().toISOString(),
151
+ thumbnailUrl: mcpData.thumbnailUrl || '',
152
+ };
153
+ }
154
+
155
+ // MCP 응답이 다른 형식인 경우 변환
156
+ // content 배열에서 데이터 추출 (MCP 도구 응답 형식)
157
+ if (mcpData.content && Array.isArray(mcpData.content)) {
158
+ const textContent = mcpData.content.find((c: any) => c.type === 'text');
159
+ if (textContent) {
160
+ try {
161
+ const parsed = JSON.parse(textContent.text);
162
+ return this.transformMCPResponseToFigmaFile(parsed);
163
+ } catch {
164
+ // JSON이 아닌 경우 처리
165
+ }
166
+ }
167
+ }
168
+
169
+ // 기본 구조로 변환 시도
170
+ return {
171
+ document: mcpData.document || mcpData.node || { id: 'root', name: 'Document', type: 'DOCUMENT', children: [] },
172
+ components: mcpData.components || {},
173
+ styles: mcpData.styles || {},
174
+ name: mcpData.name || 'Untitled',
175
+ lastModified: mcpData.lastModified || new Date().toISOString(),
176
+ thumbnailUrl: mcpData.thumbnailUrl || '',
177
+ };
178
+ }
179
+
180
+ /**
181
+ * Figma 파일 데이터 가져오기
182
+ * MCP 서버를 우선 사용하고, 실패 시 기존 REST API로 폴백
183
+ */
184
+ async getFigmaData(url: string, nodeId?: string): Promise<FigmaFile> {
185
+ const fileId = this.extractFileId(url);
186
+
187
+ // MCP 클라이언트 사용 시도
188
+ if (this.useMCP && this.mcpClient !== null) {
189
+ try {
190
+ const isAvailable = await this.mcpClient.isAvailable();
191
+ if (isAvailable) {
192
+ const mcpData = nodeId
193
+ ? await this.mcpClient.getNodeData(fileId, nodeId)
194
+ : await this.mcpClient.getFileData(fileId, nodeId);
195
+
196
+ if (mcpData) {
197
+ try {
198
+ return this.transformMCPResponseToFigmaFile(mcpData);
199
+ } catch (error) {
200
+ console.warn('MCP 응답 변환 실패, REST API로 폴백:', error);
201
+ // 폴백으로 REST API 사용
202
+ }
203
+ }
204
+ }
205
+ } catch (error) {
206
+ console.warn('Figma MCP 서버 연결 실패, REST API로 폴백:', error);
207
+ // 폴백으로 REST API 사용
208
+ }
209
+ }
210
+
211
+ // REST API 폴백
212
+ if (!this.accessToken) {
213
+ throw new Error('Figma 액세스 토큰이 필요합니다. MCP 서버도 사용할 수 없습니다.');
214
+ }
215
+
216
+ try {
217
+ const response = await axios.get(`${this.baseUrl}/files/${fileId}`, {
218
+ headers: {
219
+ 'X-Figma-Token': this.accessToken,
220
+ },
221
+ params: {
222
+ ids: nodeId || undefined,
223
+ },
224
+ });
225
+
226
+ return {
227
+ document: response.data.document,
228
+ components: response.data.components || {},
229
+ styles: response.data.styles || {},
230
+ name: response.data.name,
231
+ lastModified: response.data.lastModified,
232
+ thumbnailUrl: response.data.thumbnailUrl,
233
+ };
234
+ } catch (error) {
235
+ if (axios.isAxiosError(error)) {
236
+ throw new Error(`Figma API error: ${error.response?.data?.message || error.message}`);
237
+ }
238
+ throw error;
239
+ }
240
+ }
241
+
242
+ /**
243
+ * Figma 파일 구조 분석
244
+ */
245
+ async analyzeFigmaFile(url: string): Promise<string> {
246
+ const fileData = await this.getFigmaData(url);
247
+
248
+ const analysis = this.analyzeFileStructure(fileData);
249
+
250
+ return this.formatAnalysis(analysis);
251
+ }
252
+
253
+ /**
254
+ * 파일 구조 분석 및 컴포넌트 정보 추출
255
+ */
256
+ private analyzeFileStructure(file: FigmaFile): FigmaAnalysis {
257
+ const stats = this.countNodes(file.document);
258
+ const availableComponents = Object.keys(file.components);
259
+
260
+ return {
261
+ totalNodes: stats.total,
262
+ componentCount: stats.components,
263
+ frameCount: stats.frames,
264
+ textCount: stats.text,
265
+ availableComponents,
266
+ suggestedMappings: this.suggestComponentMappings(availableComponents),
267
+ };
268
+ }
269
+
270
+ /**
271
+ * 파일에 있는 다른 유형의 노드 수 세기
272
+ */
273
+ private countNodes(node: FigmaNode): {
274
+ total: number;
275
+ components: number;
276
+ frames: number;
277
+ text: number;
278
+ } {
279
+ let total = 1;
280
+ let components = node.type === 'COMPONENT' ? 1 : 0;
281
+ let frames = node.type === 'FRAME' ? 1 : 0;
282
+ let text = node.type === 'TEXT' ? 1 : 0;
283
+
284
+ if (node.children) {
285
+ for (const child of node.children) {
286
+ const childStats = this.countNodes(child);
287
+ total += childStats.total;
288
+ components += childStats.components;
289
+ frames += childStats.frames;
290
+ text += childStats.text;
291
+ }
292
+ }
293
+
294
+ return { total, components, frames, text };
295
+ }
296
+
297
+ /**
298
+ * Figma 컴포넌트와 디자인 시스템 컴포넌트 간의 매핑 제안
299
+ */
300
+ private suggestComponentMappings(figmaComponents: string[]): Array<{
301
+ figmaComponent: string;
302
+ designSystemComponent: string;
303
+ confidence: number;
304
+ }> {
305
+ const mappings: Array<{
306
+ figmaComponent: string;
307
+ designSystemComponent: string;
308
+ confidence: number;
309
+ }> = [];
310
+
311
+ // 일반적인 컴포넌트 이름 매핑
312
+ const commonMappings: Record<string, string[]> = {
313
+ 'button': ['Button', 'Btn', 'PrimaryButton', 'SecondaryButton'],
314
+ 'input': ['Input', 'TextField', 'TextInput'],
315
+ 'card': ['Card', 'Panel', 'Container'],
316
+ 'modal': ['Modal', 'Dialog', 'Popup'],
317
+ 'header': ['Header', 'Navbar', 'Navigation'],
318
+ 'footer': ['Footer', 'BottomBar'],
319
+ 'sidebar': ['Sidebar', 'Drawer', 'Navigation'],
320
+ 'form': ['Form', 'FormGroup'],
321
+ 'table': ['Table', 'DataTable'],
322
+ 'list': ['List', 'ListItem'],
323
+ 'avatar': ['Avatar', 'ProfileImage'],
324
+ 'badge': ['Badge', 'Tag', 'Label'],
325
+ 'tooltip': ['Tooltip', 'Popover'],
326
+ 'dropdown': ['Dropdown', 'Select'],
327
+ 'checkbox': ['Checkbox', 'CheckBox'],
328
+ 'radio': ['Radio', 'RadioButton'],
329
+ 'switch': ['Switch', 'Toggle'],
330
+ 'slider': ['Slider', 'Range'],
331
+ 'progress': ['Progress', 'ProgressBar'],
332
+ 'spinner': ['Spinner', 'Loader'],
333
+ 'alert': ['Alert', 'Notification'],
334
+ 'breadcrumb': ['Breadcrumb', 'Breadcrumbs'],
335
+ 'pagination': ['Pagination', 'Pager'],
336
+ 'tabs': ['Tabs', 'TabList'],
337
+ 'accordion': ['Accordion', 'Collapse'],
338
+ 'carousel': ['Carousel', 'Slider'],
339
+ 'stepper': ['Stepper', 'Steps'],
340
+ };
341
+
342
+ for (const figmaComponent of figmaComponents) {
343
+ const lowerName = figmaComponent.toLowerCase();
344
+
345
+ for (const [key, possibleNames] of Object.entries(commonMappings)) {
346
+ for (const possibleName of possibleNames) {
347
+ if (lowerName.includes(key) || lowerName.includes(possibleName.toLowerCase())) {
348
+ mappings.push({
349
+ figmaComponent,
350
+ designSystemComponent: possibleName,
351
+ confidence: 0.8,
352
+ });
353
+ break;
354
+ }
355
+ }
356
+ }
357
+ }
358
+
359
+ return mappings;
360
+ }
361
+
362
+ /**
363
+ * 분석 결과 표시 형식 지정
364
+ */
365
+ private formatAnalysis(analysis: FigmaAnalysis): string {
366
+ let result = `## Figma File Analysis\n\n`;
367
+ result += `**File Statistics:**\n`;
368
+ result += `- Total Nodes: ${analysis.totalNodes}\n`;
369
+ result += `- Components: ${analysis.componentCount}\n`;
370
+ result += `- Frames: ${analysis.frameCount}\n`;
371
+ result += `- Text Elements: ${analysis.textCount}\n\n`;
372
+
373
+ if (analysis.availableComponents.length > 0) {
374
+ result += `**Available Components:**\n`;
375
+ analysis.availableComponents.forEach(comp => {
376
+ result += `- ${comp}\n`;
377
+ });
378
+ result += `\n`;
379
+ }
380
+
381
+ if (analysis.suggestedMappings.length > 0) {
382
+ result += `**Suggested Component Mappings:**\n`;
383
+ analysis.suggestedMappings.forEach(mapping => {
384
+ result += `- ${mapping.figmaComponent} → ${mapping.designSystemComponent} (${Math.round(mapping.confidence * 100)}% confidence)\n`;
385
+ });
386
+ }
387
+
388
+ return result;
389
+ }
390
+
391
+ /**
392
+ * Figma 파일에서 디자인 토큰 추출
393
+ */
394
+ extractDesignTokens(file: FigmaFile): Record<string, any> {
395
+ const tokens: Record<string, any> = {
396
+ colors: {},
397
+ typography: {},
398
+ spacing: {},
399
+ borderRadius: {},
400
+ };
401
+
402
+ // 스타일에서 색상 추출
403
+ for (const [key, style] of Object.entries(file.styles)) {
404
+ if (style.styleType === 'FILL') {
405
+ tokens.colors[key] = style.description || key;
406
+ }
407
+ }
408
+
409
+ // 타이포그래피 추출
410
+ for (const [key, style] of Object.entries(file.styles)) {
411
+ if (style.styleType === 'TEXT') {
412
+ tokens.typography[key] = {
413
+ fontFamily: style.fontFamily,
414
+ fontSize: style.fontSize,
415
+ fontWeight: style.fontWeight,
416
+ };
417
+ }
418
+ }
419
+
420
+ return tokens;
421
+ }
422
+ }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Palette MCP Server - Smithery Remote 배포용
3
+ *
4
+ * Smithery.ai에서 호스팅될 때 사용됩니다.
5
+ * Smithery가 이 파일을 로드하고 createServer 함수를 호출합니다.
6
+ */
7
+
8
+ import { z } from 'zod';
9
+ import { createPaletteServer, tools } from './server.js';
10
+
11
+ // Smithery 설정 스키마 정의
12
+ export const configSchema = z.object({
13
+ FIGMA_ACCESS_TOKEN: z
14
+ .string()
15
+ .describe('Figma Personal Access Token (https://www.figma.com/developers/api#access-tokens)'),
16
+ GITHUB_TOKEN: z
17
+ .string()
18
+ .describe('GitHub Personal Access Token for design system packages'),
19
+ FIGMA_MCP_SERVER_URL: z
20
+ .string()
21
+ .default('http://127.0.0.1:3845/mcp')
22
+ .describe('Figma Dev Mode MCP server URL'),
23
+ });
24
+
25
+ // Smithery 설정 타입
26
+ type SmitheryConfig = z.infer<typeof configSchema>;
27
+
28
+ /**
29
+ * Smithery에서 호출하는 서버 생성 함수
30
+ *
31
+ * @param config - Smithery에서 전달받은 사용자 설정
32
+ * @returns MCP 서버 인스턴스
33
+ */
34
+ export default function createServer({ config }: { config: SmitheryConfig }) {
35
+ // 환경변수 설정
36
+ process.env.FIGMA_ACCESS_TOKEN = config.FIGMA_ACCESS_TOKEN;
37
+ process.env.GITHUB_TOKEN = config.GITHUB_TOKEN;
38
+ process.env.FIGMA_MCP_SERVER_URL = config.FIGMA_MCP_SERVER_URL;
39
+
40
+ // 공통 서버 생성 로직 사용
41
+ const server = createPaletteServer({
42
+ figmaAccessToken: config.FIGMA_ACCESS_TOKEN,
43
+ githubToken: config.GITHUB_TOKEN,
44
+ figmaMcpServerUrl: config.FIGMA_MCP_SERVER_URL,
45
+ });
46
+
47
+ console.error('Palette server created for Smithery (Remote mode)');
48
+
49
+ // Smithery가 기대하는 형식으로 반환
50
+ return server;
51
+ }
52
+
53
+ // Tools 정보도 export (Smithery가 capabilities 탐지에 사용할 수 있음)
54
+ export { tools };
@@ -0,0 +1,72 @@
1
+ #!/usr/bin/env node
2
+
3
+ import dotenv from 'dotenv';
4
+ import { fileURLToPath } from 'url';
5
+ import { dirname, join } from 'path';
6
+
7
+ const __filename = fileURLToPath(import.meta.url);
8
+ const __dirname = dirname(__filename);
9
+
10
+ dotenv.config({ path: join(__dirname, '..', '.env') });
11
+
12
+ import { FigmaMCPClient } from './utils/figma-mcp-client.js';
13
+
14
+ async function main() {
15
+ const client = new FigmaMCPClient();
16
+
17
+ console.log('Figma Desktop MCP 서버에 연결 중...\n');
18
+
19
+ try {
20
+ const isAvailable = await client.isAvailable();
21
+ if (!isAvailable) {
22
+ console.error('❌ Figma Desktop MCP 서버를 사용할 수 없습니다.');
23
+ console.log('Figma Desktop 앱이 실행 중이고 MCP 서버가 활성화되어 있는지 확인하세요.');
24
+ process.exit(1);
25
+ }
26
+
27
+ console.log('✅ Figma Desktop MCP 서버 연결 성공!\n');
28
+
29
+ console.log('도구 목록 조회 중...');
30
+
31
+ // 직접 sendRequest를 호출해서 원본 응답 확인
32
+ // @ts-ignore - private 메서드이지만 테스트를 위해 접근
33
+ const rawResult = await (client as any).sendRequest('tools/list');
34
+ console.log('원본 응답:', JSON.stringify(rawResult, null, 2));
35
+
36
+ const tools = await client.listTools();
37
+
38
+ console.log(`파싱된 도구 목록:`, JSON.stringify(tools, null, 2));
39
+
40
+ if (tools.length === 0) {
41
+ console.log('⚠️ 사용 가능한 도구가 없습니다.');
42
+ console.log('\n💡 확인 사항:');
43
+ console.log('1. Figma Desktop 앱이 실행 중인지 확인하세요.');
44
+ console.log('2. Figma Desktop 앱의 Preferences에서 "Enable Dev Mode MCP Server"가 활성화되어 있는지 확인하세요.');
45
+ console.log('3. Figma 파일이 열려있는지 확인하세요.');
46
+ return;
47
+ }
48
+
49
+ console.log(`📋 총 ${tools.length}개의 도구를 찾았습니다:\n`);
50
+
51
+ tools.forEach((tool: any, index: number) => {
52
+ console.log(`${index + 1}. ${tool.name}`);
53
+ if (tool.description) {
54
+ console.log(` 설명: ${tool.description}`);
55
+ }
56
+ if (tool.inputSchema?.properties) {
57
+ console.log(` 파라미터:`);
58
+ Object.entries(tool.inputSchema.properties).forEach(([key, value]: [string, any]) => {
59
+ console.log(` - ${key}: ${value.type || 'any'} ${value.description ? `(${value.description})` : ''}`);
60
+ });
61
+ }
62
+ console.log('');
63
+ });
64
+
65
+ } catch (error) {
66
+ console.error('❌ 오류 발생:', error);
67
+ process.exit(1);
68
+ }
69
+ }
70
+
71
+ main();
72
+