@nbakka/mcp-appium 2.0.45 → 2.0.46

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/lib/server.js +27 -152
  2. package/package.json +1 -1
package/lib/server.js CHANGED
@@ -476,12 +476,11 @@ const createMcpServer = () => {
476
476
 
477
477
  tool(
478
478
  "mobile_export_figma_images",
479
- "Export only main parent frames from Figma file as PNG images",
479
+ "Export Figma file as PDF",
480
480
  {
481
- figmaUrl: zod_1.z.string().describe("The Figma file URL to export frames from"),
482
- nodeId: zod_1.z.string().optional().describe("Specific node ID from URL (e.g., 13223-1724)")
481
+ figmaUrl: zod_1.z.string().describe("The Figma file URL to export as PDF")
483
482
  },
484
- async ({ figmaUrl, nodeId }) => {
483
+ async ({ figmaUrl }) => {
485
484
  try {
486
485
  // Read Figma credentials from desktop/figma.json file
487
486
  const figmaConfigPath = path.join(os.homedir(), 'Desktop', 'figma.json');
@@ -501,129 +500,61 @@ tool(
501
500
  throw new Error('Figma API token not found in figma.json file. Please ensure the file contains "token" field.');
502
501
  }
503
502
 
504
- // Extract file ID and node ID from Figma URL
503
+ // Extract file ID from Figma URL
505
504
  const fileId = extractFileIdFromUrl(figmaUrl);
506
505
  if (!fileId) {
507
506
  throw new Error('Invalid Figma URL. Unable to extract file ID.');
508
507
  }
509
508
 
510
- // Extract node ID from URL if not provided separately
511
- let targetNodeId = nodeId;
512
- if (!targetNodeId) {
513
- targetNodeId = extractNodeIdFromUrl(figmaUrl);
514
- }
515
-
516
509
  // Set up export directory
517
510
  const exportPath = path.join(os.homedir(), 'Desktop', 'figma');
518
511
 
519
512
  // Create export directory if it doesn't exist
520
513
  await fs.mkdir(exportPath, { recursive: true });
521
514
 
522
- // Clear existing PNG files in the directory
515
+ // Clear existing PDF files in the directory
523
516
  try {
524
517
  const existingFiles = await fs.readdir(exportPath);
525
- const pngFiles = existingFiles.filter(file => file.endsWith('.png'));
526
- for (const file of pngFiles) {
518
+ const pdfFiles = existingFiles.filter(file => file.endsWith('.pdf'));
519
+ for (const file of pdfFiles) {
527
520
  await fs.unlink(path.join(exportPath, file));
528
521
  }
529
522
  } catch (cleanupError) {
530
523
  // Continue if cleanup fails
531
524
  }
532
525
 
533
- // Get file information
534
- const fileResponse = await axios.get(
535
- `https://api.figma.com/v1/files/${fileId}`,
526
+ // Request PDF export of entire file
527
+ const exportResponse = await axios.get(
528
+ `https://api.figma.com/v1/images/${fileId}`,
536
529
  {
537
530
  headers: {
538
531
  'X-Figma-Token': figmaToken
532
+ },
533
+ params: {
534
+ format: 'pdf'
539
535
  }
540
536
  }
541
537
  );
542
538
 
543
- const fileData = fileResponse.data;
544
-
545
- let framesToExport = [];
546
-
547
- if (targetNodeId) {
548
- // If specific node ID provided, find that node and export it
549
- const specificNode = findNodeById(fileData.document, targetNodeId);
550
- if (specificNode && specificNode.type === 'FRAME') {
551
- framesToExport = [specificNode.id];
552
- } else {
553
- return `Node ID ${targetNodeId} not found or is not a frame.`;
554
- }
555
- } else {
556
- // Get only top-level frames (direct children of pages)
557
- framesToExport = extractMainFrames(fileData.document);
558
- }
539
+ const pdfUrl = exportResponse.data.images[fileId];
559
540
 
560
- if (framesToExport.length === 0) {
561
- return `No main frames found in Figma file.`;
541
+ if (!pdfUrl) {
542
+ throw new Error('Failed to get PDF export URL from Figma');
562
543
  }
563
544
 
564
- // Limit to reasonable number to avoid API issues
565
- const limitedFrameIds = framesToExport.slice(0, 10);
545
+ // Download PDF
546
+ const pdfResponse = await axios.get(pdfUrl, {
547
+ responseType: 'arraybuffer'
548
+ });
566
549
 
567
- let successCount = 0;
568
- const batchSize = 5; // Smaller batches for main frames
550
+ // Save PDF to file with timestamp
551
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, -5);
552
+ const pdfPath = path.join(exportPath, `figma-export-${timestamp}.pdf`);
553
+ await fs.writeFile(pdfPath, pdfResponse.data);
569
554
 
570
- // Process frames in batches
571
- for (let batchStart = 0; batchStart < limitedFrameIds.length; batchStart += batchSize) {
572
- const batchEnd = Math.min(batchStart + batchSize, limitedFrameIds.length);
573
- const batchIds = limitedFrameIds.slice(batchStart, batchEnd);
574
-
575
- try {
576
- // Request image exports for current batch
577
- const exportResponse = await axios.get(
578
- `https://api.figma.com/v1/images/${fileId}`,
579
- {
580
- headers: {
581
- 'X-Figma-Token': figmaToken
582
- },
583
- params: {
584
- ids: batchIds.join(','),
585
- format: 'png',
586
- scale: 2
587
- }
588
- }
589
- );
590
-
591
- const imageUrls = exportResponse.data.images;
592
-
593
- // Download and save each image in the batch
594
- for (let i = 0; i < batchIds.length; i++) {
595
- const nodeId = batchIds[i];
596
- const imageUrl = imageUrls[nodeId];
597
-
598
- if (imageUrl) {
599
- try {
600
- const frameNumber = batchStart + i + 1;
601
- const filename = `${frameNumber}.png`;
602
- const filepath = path.join(exportPath, filename);
603
-
604
- // Download image
605
- const imageResponse = await axios.get(imageUrl, {
606
- responseType: 'arraybuffer'
607
- });
608
-
609
- // Save image to file
610
- await fs.writeFile(filepath, imageResponse.data);
611
- successCount++;
612
- } catch (downloadError) {
613
- // Continue with next image if one fails
614
- }
615
- }
616
- }
617
- } catch (batchError) {
618
- // Continue with next batch if one fails
619
- }
620
- }
621
-
622
- return `Figma Export Complete!
623
- Export Path: ${exportPath}
624
- Total main frames found: ${framesToExport.length}
625
- Exported: ${successCount} main frames as PNG files
626
- Files: ${Array.from({length: successCount}, (_, i) => `${i + 1}.png`).join(', ')}`;
555
+ return `Figma PDF Export Complete!
556
+ Export Path: ${pdfPath}
557
+ File: figma-export-${timestamp}.pdf`;
627
558
 
628
559
  } catch (error) {
629
560
  if (error.response && error.response.status === 403) {
@@ -654,62 +585,6 @@ function extractFileIdFromUrl(url) {
654
585
  return null;
655
586
  }
656
587
 
657
- // Helper function to extract node ID from Figma URL
658
- function extractNodeIdFromUrl(url) {
659
- const match = url.match(/node-id=([^&]+)/);
660
- if (match) {
661
- return match[1];
662
- }
663
- return null;
664
- }
665
-
666
- // Helper function to find a specific node by ID
667
- function findNodeById(document, nodeId) {
668
- let foundNode = null;
669
-
670
- function traverse(node) {
671
- if (node.id === nodeId) {
672
- foundNode = node;
673
- return;
674
- }
675
-
676
- if (node.children && Array.isArray(node.children)) {
677
- node.children.forEach(traverse);
678
- }
679
- }
680
-
681
- if (document.children && Array.isArray(document.children)) {
682
- document.children.forEach(page => {
683
- if (page.children && Array.isArray(page.children)) {
684
- page.children.forEach(traverse);
685
- }
686
- });
687
- }
688
-
689
- return foundNode;
690
- }
691
-
692
- // Simple function to extract only top-level frames (direct children of pages)
693
- function extractMainFrames(document) {
694
- const topLevelFrames = [];
695
-
696
- // Only get direct children of pages - no traversing deeper
697
- if (document.children && Array.isArray(document.children)) {
698
- document.children.forEach(page => {
699
- if (page.children && Array.isArray(page.children)) {
700
- // Only look at direct children of the page
701
- page.children.forEach(child => {
702
- if (child.type === 'FRAME') {
703
- topLevelFrames.push(child.id);
704
- }
705
- });
706
- }
707
- });
708
- }
709
-
710
- return topLevelFrames;
711
- }
712
-
713
588
 
714
589
  return server;
715
590
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nbakka/mcp-appium",
3
- "version": "2.0.45",
3
+ "version": "2.0.46",
4
4
  "description": "Appium MCP",
5
5
  "engines": {
6
6
  "node": ">=18"